diff --git a/examples/compiled/area_overlay_with_y2.png b/examples/compiled/area_overlay_with_y2.png new file mode 100644 index 0000000000..8e489363d6 Binary files /dev/null and b/examples/compiled/area_overlay_with_y2.png differ diff --git a/examples/compiled/area_overlay_with_y2.svg b/examples/compiled/area_overlay_with_y2.svg new file mode 100644 index 0000000000..0ac6b6cde0 --- /dev/null +++ b/examples/compiled/area_overlay_with_y2.svg @@ -0,0 +1 @@ +200520062007200820092010date0200400600800price \ No newline at end of file diff --git a/examples/compiled/area_overlay_with_y2.vg.json b/examples/compiled/area_overlay_with_y2.vg.json new file mode 100644 index 0000000000..1f3b7fd7fa --- /dev/null +++ b/examples/compiled/area_overlay_with_y2.vg.json @@ -0,0 +1,166 @@ +{ + "$schema": "https://vega.github.io/schema/vega/v5.json", + "description": "Google's stock price over time.", + "background": "white", + "padding": 5, + "width": 200, + "height": 200, + "style": "cell", + "data": [ + { + "name": "source_0", + "url": "data/stocks.csv", + "format": {"type": "csv", "parse": {"date": "date"}}, + "transform": [{"type": "filter", "expr": "datum.symbol==='GOOG'"}] + }, + { + "name": "data_0", + "source": "source_0", + "transform": [ + { + "type": "filter", + "expr": "(isDate(datum[\"date\"]) || (isValid(datum[\"date\"]) && isFinite(+datum[\"date\"]))) && isValid(datum[\"price\"]) && isFinite(+datum[\"price\"])" + } + ] + } + ], + "marks": [ + { + "name": "layer_0_marks", + "type": "area", + "style": ["area"], + "sort": {"field": "datum[\"date\"]"}, + "from": {"data": "source_0"}, + "encode": { + "update": { + "opacity": {"value": 0.7}, + "orient": {"value": "vertical"}, + "fill": {"value": "#4c78a8"}, + "description": { + "signal": "\"date: \" + (timeFormat(datum[\"date\"], '%b %d, %Y')) + \"; price: \" + (format(datum[\"price\"], \"\"))" + }, + "x": {"scale": "x", "field": "date"}, + "y": {"scale": "y", "field": "price"}, + "y2": {"scale": "y", "value": 0}, + "defined": { + "signal": "isValid(datum[\"date\"]) && isFinite(+datum[\"date\"]) && isValid(datum[\"price\"]) && isFinite(+datum[\"price\"])" + } + } + } + }, + { + "name": "layer_1_marks", + "type": "line", + "style": ["line"], + "sort": {"field": "datum[\"date\"]"}, + "from": {"data": "source_0"}, + "encode": { + "update": { + "stroke": {"value": "#4c78a8"}, + "description": { + "signal": "\"date: \" + (timeFormat(datum[\"date\"], '%b %d, %Y')) + \"; price: \" + (format(datum[\"price\"], \"\"))" + }, + "x": {"scale": "x", "field": "date"}, + "y": {"scale": "y", "field": "price"}, + "defined": { + "signal": "isValid(datum[\"date\"]) && isFinite(+datum[\"date\"]) && isValid(datum[\"price\"]) && isFinite(+datum[\"price\"])" + } + } + } + }, + { + "name": "layer_2_marks", + "type": "symbol", + "style": ["point"], + "from": {"data": "data_0"}, + "encode": { + "update": { + "opacity": {"value": 1}, + "fill": {"value": "#4c78a8"}, + "ariaRoleDescription": {"value": "point"}, + "description": { + "signal": "\"date: \" + (timeFormat(datum[\"date\"], '%b %d, %Y')) + \"; price: \" + (format(datum[\"price\"], \"\"))" + }, + "x": {"scale": "x", "field": "date"}, + "y": {"scale": "y", "field": "price"} + } + } + } + ], + "scales": [ + { + "name": "x", + "type": "time", + "domain": { + "fields": [ + {"data": "source_0", "field": "date"}, + {"data": "data_0", "field": "date"} + ] + }, + "range": [0, {"signal": "width"}] + }, + { + "name": "y", + "type": "linear", + "domain": { + "fields": [ + {"data": "source_0", "field": "price"}, + [0], + {"data": "data_0", "field": "price"} + ] + }, + "range": [{"signal": "height"}, 0], + "nice": true, + "zero": true + } + ], + "axes": [ + { + "scale": "x", + "orient": "bottom", + "gridScale": "y", + "grid": true, + "tickCount": {"signal": "ceil(width/40)"}, + "domain": false, + "labels": false, + "aria": false, + "maxExtent": 0, + "minExtent": 0, + "ticks": false, + "zindex": 0 + }, + { + "scale": "y", + "orient": "left", + "gridScale": "x", + "grid": true, + "tickCount": {"signal": "ceil(height/40)"}, + "domain": false, + "labels": false, + "aria": false, + "maxExtent": 0, + "minExtent": 0, + "ticks": false, + "zindex": 0 + }, + { + "scale": "x", + "orient": "bottom", + "grid": false, + "title": "date", + "labelFlush": true, + "labelOverlap": true, + "tickCount": {"signal": "ceil(width/40)"}, + "zindex": 0 + }, + { + "scale": "y", + "orient": "left", + "grid": false, + "title": "price", + "labelOverlap": true, + "tickCount": {"signal": "ceil(height/40)"}, + "zindex": 0 + } + ] +} diff --git a/examples/specs/area_overlay_with_y2.vl.json b/examples/specs/area_overlay_with_y2.vl.json new file mode 100644 index 0000000000..4f4af3426d --- /dev/null +++ b/examples/specs/area_overlay_with_y2.vl.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://vega.github.io/schema/vega-lite/v5.json", + "description": "Google's stock price over time.", + "data": {"url": "data/stocks.csv"}, + "transform": [{"filter": "datum.symbol==='GOOG'"}], + "mark": {"type": "area", "line": true, "point": true}, + "encoding": { + "x": {"field": "date", "type": "temporal"}, + "y": {"field": "price", "type": "quantitative"}, + "y2": {"datum": 0, "type": "quantitative"} + } +} diff --git a/examples/specs/normalized/area_overlay_with_y2_normalized.vl.json b/examples/specs/normalized/area_overlay_with_y2_normalized.vl.json new file mode 100644 index 0000000000..9052e00bd0 --- /dev/null +++ b/examples/specs/normalized/area_overlay_with_y2_normalized.vl.json @@ -0,0 +1,30 @@ +{ + "$schema": "https://vega.github.io/schema/vega-lite/v5.json", + "description": "Google's stock price over time.", + "data": {"url": "data/stocks.csv"}, + "transform": [{"filter": "datum.symbol==='GOOG'"}], + "layer": [ + { + "mark": {"opacity": 0.7, "type": "area"}, + "encoding": { + "x": {"field": "date", "type": "temporal"}, + "y": {"field": "price", "type": "quantitative"}, + "y2": {"datum": 0, "type": "quantitative"} + } + }, + { + "mark": {"type": "line"}, + "encoding": { + "x": {"field": "date", "type": "temporal"}, + "y": {"field": "price", "type": "quantitative"} + } + }, + { + "mark": {"type": "point", "opacity": 1, "filled": true}, + "encoding": { + "x": {"field": "date", "type": "temporal"}, + "y": {"field": "price", "type": "quantitative"} + } + } + ] +} \ No newline at end of file diff --git a/src/normalize/pathoverlay.ts b/src/normalize/pathoverlay.ts index 333cd2b84c..33e988f7a0 100644 --- a/src/normalize/pathoverlay.ts +++ b/src/normalize/pathoverlay.ts @@ -143,6 +143,11 @@ export class PathOverlayNormalizer implements NonFacetUnitNormalizer { }); }); + it('correctly normalizes area using y2 with overlay line.', () => { + const spec: TopLevelSpec = { + data: {url: 'data/stocks.csv'}, + mark: 'area', + encoding: { + x: {field: 'date', type: 'temporal'}, + y: {field: 'price', type: 'quantitative'}, + y2: {value: 0, type: 'quantitative'} + }, + config: {area: {line: {}, point: {}}} + }; + const normalizedSpec = normalize(spec); + expect(normalizedSpec).toEqual({ + data: {url: 'data/stocks.csv'}, + layer: [ + { + mark: {type: 'area', opacity: 0.7}, + encoding: { + x: {field: 'date', type: 'temporal'}, + y: {field: 'price', type: 'quantitative'}, + y2: {value: 0, type: 'quantitative'} + } + }, + { + mark: {type: 'line'}, + encoding: { + x: {field: 'date', type: 'temporal'}, + y: {field: 'price', type: 'quantitative', stack: 'zero'} + } + }, + { + mark: {type: 'point', opacity: 1, filled: true}, + encoding: { + x: {field: 'date', type: 'temporal'}, + y: {field: 'price', type: 'quantitative', stack: 'zero'} + } + } + ], + config: {area: {line: {}, point: {}}} + }); + }); + it('correctly normalizes interpolated area with overlay line', () => { const spec: TopLevelSpec = { data: {url: 'data/stocks.csv'},