From 2d732328f6afda14c0429292d8be33bf3eded31e Mon Sep 17 00:00:00 2001 From: Younghoon Kim Date: Tue, 16 Aug 2022 11:25:22 -0700 Subject: [PATCH] feat: Allow config to set default scale.zero per marktype (#8354) * Add examples * chore: update examples [CI] * Add config.scale.zero * chore: update examples [CI] * update doc * Minor refactor: - Undo encodingHasRangeChannels - Set `true` for continuous size scale regardless to config - Fix doc * Update the doc and fix the typo Co-authored-by: GitHub Actions Bot --- build/vega-lite-schema.json | 4 + examples/compiled/bar_config_no_zero.png | Bin 0 -> 4954 bytes examples/compiled/bar_config_no_zero.svg | 1 + examples/compiled/bar_config_no_zero.vg.json | 122 +++++++++++++++++ .../compiled/bar_gantt_config_no_zero.png | Bin 0 -> 3991 bytes .../compiled/bar_gantt_config_no_zero.svg | 5 + .../compiled/bar_gantt_config_no_zero.vg.json | 107 +++++++++++++++ examples/compiled/point_2d_config_no_zero.png | Bin 0 -> 28538 bytes examples/compiled/point_2d_config_no_zero.svg | 5 + .../compiled/point_2d_config_no_zero.vg.json | 110 +++++++++++++++ examples/specs/bar_config_no_zero.vl.json | 17 +++ .../specs/bar_gantt_config_no_zero.vl.json | 18 +++ .../specs/point_2d_config_no_zero.vl.json | 11 ++ site/docs/encoding/scale.md | 2 +- src/compile/scale/properties.ts | 23 +++- src/scale.ts | 12 +- test/compile/scale/properties.test.ts | 128 ++++++++++++++++-- 17 files changed, 545 insertions(+), 20 deletions(-) create mode 100644 examples/compiled/bar_config_no_zero.png create mode 100644 examples/compiled/bar_config_no_zero.svg create mode 100644 examples/compiled/bar_config_no_zero.vg.json create mode 100644 examples/compiled/bar_gantt_config_no_zero.png create mode 100644 examples/compiled/bar_gantt_config_no_zero.svg create mode 100644 examples/compiled/bar_gantt_config_no_zero.vg.json create mode 100644 examples/compiled/point_2d_config_no_zero.png create mode 100644 examples/compiled/point_2d_config_no_zero.svg create mode 100644 examples/compiled/point_2d_config_no_zero.vg.json create mode 100644 examples/specs/bar_config_no_zero.vl.json create mode 100644 examples/specs/bar_gantt_config_no_zero.vl.json create mode 100644 examples/specs/point_2d_config_no_zero.vl.json diff --git a/build/vega-lite-schema.json b/build/vega-lite-schema.json index 4a36f8bb6b4..e8fb892c217 100644 --- a/build/vega-lite-schema.json +++ b/build/vega-lite-schema.json @@ -21632,6 +21632,10 @@ } ], "description": "Reverse x-scale by default (useful for right-to-left charts)." + }, + "zero": { + "description": "Default `scale.zero` for [`continuous`](https://vega.github.io/vega-lite/docs/scale.html#continuous) scales except for (1) x/y-scales of non-ranged bar or area charts and (2) size scales.\n\n__Default value:__ `true`", + "type": "boolean" } }, "type": "object" diff --git a/examples/compiled/bar_config_no_zero.png b/examples/compiled/bar_config_no_zero.png new file mode 100644 index 0000000000000000000000000000000000000000..01a1312af20c77cbb9d733c46118feb17e20ea22 GIT binary patch literal 4954 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 Mwz3AiMA0nZFYc2|b^rhX literal 0 HcmV?d00001 diff --git a/examples/compiled/bar_config_no_zero.svg b/examples/compiled/bar_config_no_zero.svg new file mode 100644 index 00000000000..9e458ba5253 --- /dev/null +++ b/examples/compiled/bar_config_no_zero.svg @@ -0,0 +1 @@ +ABCDEFGHIa020406080100b \ No newline at end of file diff --git a/examples/compiled/bar_config_no_zero.vg.json b/examples/compiled/bar_config_no_zero.vg.json new file mode 100644 index 00000000000..a7403201ada --- /dev/null +++ b/examples/compiled/bar_config_no_zero.vg.json @@ -0,0 +1,122 @@ +{ + "$schema": "https://vega.github.io/schema/vega/v5.json", + "description": "A simple bar chart with embedded data.", + "background": "white", + "padding": 5, + "height": 200, + "style": "cell", + "data": [ + { + "name": "source_0", + "values": [ + {"a": "A", "b": 28}, + {"a": "B", "b": 55}, + {"a": "C", "b": 43}, + {"a": "D", "b": 91}, + {"a": "E", "b": 81}, + {"a": "F", "b": 53}, + {"a": "G", "b": 19}, + {"a": "H", "b": 87}, + {"a": "I", "b": 52} + ] + }, + { + "name": "data_0", + "source": "source_0", + "transform": [ + { + "type": "stack", + "groupby": ["a"], + "field": "b", + "sort": {"field": [], "order": []}, + "as": ["b_start", "b_end"], + "offset": "zero" + }, + { + "type": "filter", + "expr": "isValid(datum[\"b\"]) && isFinite(+datum[\"b\"])" + } + ] + } + ], + "signals": [ + {"name": "x_step", "value": 20}, + { + "name": "width", + "update": "bandspace(domain('x').length, 0.1, 0.05) * x_step" + } + ], + "marks": [ + { + "name": "marks", + "type": "rect", + "style": ["bar"], + "from": {"data": "data_0"}, + "encode": { + "update": { + "fill": {"value": "#4c78a8"}, + "ariaRoleDescription": {"value": "bar"}, + "description": { + "signal": "\"a: \" + (isValid(datum[\"a\"]) ? datum[\"a\"] : \"\"+datum[\"a\"]) + \"; b: \" + (format(datum[\"b\"], \"\"))" + }, + "x": {"scale": "x", "field": "a"}, + "width": {"signal": "max(0.25, bandwidth('x'))"}, + "y": {"scale": "y", "field": "b_end"}, + "y2": {"scale": "y", "field": "b_start"} + } + } + } + ], + "scales": [ + { + "name": "x", + "type": "band", + "domain": {"data": "data_0", "field": "a", "sort": true}, + "range": {"step": {"signal": "x_step"}}, + "paddingInner": 0.1, + "paddingOuter": 0.05 + }, + { + "name": "y", + "type": "linear", + "domain": {"data": "data_0", "fields": ["b_start", "b_end"]}, + "range": [{"signal": "height"}, 0], + "nice": true, + "zero": true + } + ], + "axes": [ + { + "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": "a", + "labelAngle": 0, + "labelBaseline": "top", + "zindex": 0 + }, + { + "scale": "y", + "orient": "left", + "grid": false, + "title": "b", + "labelOverlap": true, + "tickCount": {"signal": "ceil(height/40)"}, + "zindex": 0 + } + ] +} diff --git a/examples/compiled/bar_gantt_config_no_zero.png b/examples/compiled/bar_gantt_config_no_zero.png new file mode 100644 index 0000000000000000000000000000000000000000..96b20dd9409168b2547ad67c8f6b641bc4f22c9a GIT binary patch literal 3991 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 AL;wH) literal 0 HcmV?d00001 diff --git a/examples/compiled/bar_gantt_config_no_zero.svg b/examples/compiled/bar_gantt_config_no_zero.svg new file mode 100644 index 00000000000..d31602a5377 --- /dev/null +++ b/examples/compiled/bar_gantt_config_no_zero.svg @@ -0,0 +1,5 @@ +<<<<<<< Updated upstream +0246810start, endABCtask +======= +246810start, endABCtask +>>>>>>> Stashed changes diff --git a/examples/compiled/bar_gantt_config_no_zero.vg.json b/examples/compiled/bar_gantt_config_no_zero.vg.json new file mode 100644 index 00000000000..47a431f9b68 --- /dev/null +++ b/examples/compiled/bar_gantt_config_no_zero.vg.json @@ -0,0 +1,107 @@ +{ + "$schema": "https://vega.github.io/schema/vega/v5.json", + "description": "A simple bar chart with ranged data (aka Gantt Chart).", + "background": "white", + "padding": 5, + "width": 200, + "style": "cell", + "data": [ + { + "name": "source_0", + "values": [ + {"task": "A", "start": 1, "end": 3}, + {"task": "B", "start": 3, "end": 8}, + {"task": "C", "start": 8, "end": 10} + ] + }, + { + "name": "data_0", + "source": "source_0", + "transform": [ + { + "type": "filter", + "expr": "isValid(datum[\"start\"]) && isFinite(+datum[\"start\"])" + } + ] + } + ], + "signals": [ + {"name": "y_step", "value": 20}, + { + "name": "height", + "update": "bandspace(domain('y').length, 0.1, 0.05) * y_step" + } + ], + "marks": [ + { + "name": "marks", + "type": "rect", + "style": ["bar"], + "from": {"data": "data_0"}, + "encode": { + "update": { + "fill": {"value": "#4c78a8"}, + "ariaRoleDescription": {"value": "bar"}, + "description": { + "signal": "\"start: \" + (format(datum[\"start\"], \"\")) + \"; task: \" + (isValid(datum[\"task\"]) ? datum[\"task\"] : \"\"+datum[\"task\"]) + \"; end: \" + (format(datum[\"end\"], \"\"))" + }, + "x": {"scale": "x", "field": "start"}, + "x2": {"scale": "x", "field": "end"}, + "y": {"scale": "y", "field": "task"}, + "height": {"signal": "max(0.25, bandwidth('y'))"} + } + } + } + ], + "scales": [ + { + "name": "x", + "type": "linear", + "domain": {"data": "data_0", "fields": ["start", "end"]}, + "range": [0, {"signal": "width"}], + "nice": true, + "zero": false + }, + { + "name": "y", + "type": "band", + "domain": {"data": "data_0", "field": "task", "sort": true}, + "range": {"step": {"signal": "y_step"}}, + "paddingInner": 0.1, + "paddingOuter": 0.05 + } + ], + "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": "x", + "orient": "bottom", + "grid": false, + "title": "start, end", + "labelFlush": true, + "labelOverlap": true, + "tickCount": {"signal": "ceil(width/40)"}, + "zindex": 0 + }, + { + "scale": "y", + "orient": "left", + "grid": false, + "title": "task", + "zindex": 0 + } + ] +} diff --git a/examples/compiled/point_2d_config_no_zero.png b/examples/compiled/point_2d_config_no_zero.png new file mode 100644 index 0000000000000000000000000000000000000000..868a9d1b5c9f8155690f1beb0d49b47cf04ea944 GIT binary patch literal 28538 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

?d4MhplNLR;;ElI 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 literal 0 HcmV?d00001 diff --git a/examples/compiled/point_2d_config_no_zero.svg b/examples/compiled/point_2d_config_no_zero.svg new file mode 100644 index 00000000000..80ccd7f99b4 --- /dev/null +++ b/examples/compiled/point_2d_config_no_zero.svg @@ -0,0 +1,5 @@ +<<<<<<< Updated upstream +050100150200Horsepower01020304050Miles_per_Gallon +======= +50100150200Horsepower1020304050Miles_per_Gallon +>>>>>>> Stashed changes diff --git a/examples/compiled/point_2d_config_no_zero.vg.json b/examples/compiled/point_2d_config_no_zero.vg.json new file mode 100644 index 00000000000..b6de1de0d23 --- /dev/null +++ b/examples/compiled/point_2d_config_no_zero.vg.json @@ -0,0 +1,110 @@ +{ + "$schema": "https://vega.github.io/schema/vega/v5.json", + "description": "A scatterplot showing horsepower and miles per gallons for various cars.", + "background": "white", + "padding": 5, + "width": 200, + "height": 200, + "style": "cell", + "data": [ + { + "name": "source_0", + "url": "data/cars.json", + "format": {"type": "json"}, + "transform": [ + { + "type": "filter", + "expr": "isValid(datum[\"Horsepower\"]) && isFinite(+datum[\"Horsepower\"]) && isValid(datum[\"Miles_per_Gallon\"]) && isFinite(+datum[\"Miles_per_Gallon\"])" + } + ] + } + ], + "marks": [ + { + "name": "marks", + "type": "symbol", + "style": ["point"], + "from": {"data": "source_0"}, + "encode": { + "update": { + "opacity": {"value": 0.7}, + "fill": {"value": "transparent"}, + "stroke": {"value": "#4c78a8"}, + "ariaRoleDescription": {"value": "point"}, + "description": { + "signal": "\"Horsepower: \" + (format(datum[\"Horsepower\"], \"\")) + \"; Miles_per_Gallon: \" + (format(datum[\"Miles_per_Gallon\"], \"\"))" + }, + "x": {"scale": "x", "field": "Horsepower"}, + "y": {"scale": "y", "field": "Miles_per_Gallon"} + } + } + } + ], + "scales": [ + { + "name": "x", + "type": "linear", + "domain": {"data": "source_0", "field": "Horsepower"}, + "range": [0, {"signal": "width"}], + "nice": true, + "zero": false + }, + { + "name": "y", + "type": "linear", + "domain": {"data": "source_0", "field": "Miles_per_Gallon"}, + "range": [{"signal": "height"}, 0], + "nice": true, + "zero": false + } + ], + "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": "Horsepower", + "labelFlush": true, + "labelOverlap": true, + "tickCount": {"signal": "ceil(width/40)"}, + "zindex": 0 + }, + { + "scale": "y", + "orient": "left", + "grid": false, + "title": "Miles_per_Gallon", + "labelOverlap": true, + "tickCount": {"signal": "ceil(height/40)"}, + "zindex": 0 + } + ] +} diff --git a/examples/specs/bar_config_no_zero.vl.json b/examples/specs/bar_config_no_zero.vl.json new file mode 100644 index 00000000000..4a155b00d7c --- /dev/null +++ b/examples/specs/bar_config_no_zero.vl.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://vega.github.io/schema/vega-lite/v5.json", + "description": "A simple bar chart with embedded data.", + "data": { + "values": [ + {"a": "A", "b": 28}, {"a": "B", "b": 55}, {"a": "C", "b": 43}, + {"a": "D", "b": 91}, {"a": "E", "b": 81}, {"a": "F", "b": 53}, + {"a": "G", "b": 19}, {"a": "H", "b": 87}, {"a": "I", "b": 52} + ] + }, + "mark": "bar", + "encoding": { + "x": {"field": "a", "type": "nominal", "axis": {"labelAngle": 0}}, + "y": {"field": "b", "type": "quantitative"} + }, + "config": { "scale": { "zero": false } } +} diff --git a/examples/specs/bar_gantt_config_no_zero.vl.json b/examples/specs/bar_gantt_config_no_zero.vl.json new file mode 100644 index 00000000000..66a0b32e138 --- /dev/null +++ b/examples/specs/bar_gantt_config_no_zero.vl.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://vega.github.io/schema/vega-lite/v5.json", + "description": "A simple bar chart with ranged data (aka Gantt Chart).", + "data": { + "values": [ + {"task": "A", "start": 1, "end": 3}, + {"task": "B", "start": 3, "end": 8}, + {"task": "C", "start": 8, "end": 10} + ] + }, + "mark": "bar", + "encoding": { + "y": {"field": "task", "type": "ordinal"}, + "x": {"field": "start", "type": "quantitative"}, + "x2": {"field": "end"} + }, + "config": { "scale": { "zero": false } } +} diff --git a/examples/specs/point_2d_config_no_zero.vl.json b/examples/specs/point_2d_config_no_zero.vl.json new file mode 100644 index 00000000000..977c8553c71 --- /dev/null +++ b/examples/specs/point_2d_config_no_zero.vl.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://vega.github.io/schema/vega-lite/v5.json", + "description": "A scatterplot showing horsepower and miles per gallons for various cars.", + "data": { "url": "data/cars.json" }, + "mark": "point", + "encoding": { + "x": { "field": "Horsepower", "type": "quantitative" }, + "y": { "field": "Miles_per_Gallon", "type": "quantitative" } + }, + "config": { "scale": { "zero": false } } +} diff --git a/site/docs/encoding/scale.md b/site/docs/encoding/scale.md index 847a2d6316b..1b12563b277 100644 --- a/site/docs/encoding/scale.md +++ b/site/docs/encoding/scale.md @@ -452,7 +452,7 @@ To provide themes for all scales, the scale config (`config: {scale: {...}}`) ca #### Other -{% include table.html props="clamp,round,xReverse,useUnaggregatedDomain" source="ScaleConfig" %} +{% include table.html props="clamp,round,xReverse,useUnaggregatedDomain,zero" source="ScaleConfig" %} {:#range-config} diff --git a/src/compile/scale/properties.ts b/src/compile/scale/properties.ts index 501e9dfe3ff..e4463fdd34c 100644 --- a/src/compile/scale/properties.ts +++ b/src/compile/scale/properties.ts @@ -4,6 +4,7 @@ import {isBinned, isBinning, isBinParams} from '../../bin'; import { COLOR, FILL, + getSecondaryRangeChannel, isXorY, isXorYOffset, POLAR_POSITION_SCALE_CHANNELS, @@ -120,7 +121,8 @@ function parseUnitScaleProperty(model: UnitModel, property: Exclude; config: Config; + hasSecondaryRangeChannel: boolean; } export const scaleRules: { @@ -169,8 +172,8 @@ export const scaleRules: { const sort = isFieldDef(fieldOrDatumDef) ? fieldOrDatumDef.sort : undefined; return reverse(scaleType, sort, channel, config.scale); }, - zero: ({channel, fieldOrDatumDef, domain, markDef, scaleType}) => - zero(channel, fieldOrDatumDef, domain, markDef, scaleType) + zero: ({channel, fieldOrDatumDef, domain, markDef, scaleType, config, hasSecondaryRangeChannel}) => + zero(channel, fieldOrDatumDef, domain, markDef, scaleType, config.scale, hasSecondaryRangeChannel) }; // This method is here rather than in range.ts to avoid circular dependency. @@ -399,7 +402,9 @@ export function zero( fieldDef: TypedFieldDef | ScaleDatumDef, specifiedDomain: Domain, markDef: MarkDef, - scaleType: ScaleType + scaleType: ScaleType, + scaleConfig: ScaleConfig, + hasSecondaryRangeChannel: boolean ) { // If users explicitly provide a domain, we should not augment zero as that will be unexpected. const hasCustomDomain = !!specifiedDomain && specifiedDomain !== 'unaggregated'; @@ -418,7 +423,7 @@ export function zero( } } - // If there is no custom domain, return true only for the following cases: + // If there is no custom domain, return configZero value (=`true` as default) only for the following cases: // 1) using quantitative field with size // While this can be either ratio or interval fields, our assumption is that @@ -430,6 +435,7 @@ export function zero( // 2) non-binned, quantitative x-scale or y-scale // (For binning, we should not include zero by default because binning are calculated without zero.) + // (For area/bar charts with ratio scale chart, we should always include zero.) if ( !(isFieldDef(fieldDef) && fieldDef.bin) && util.contains([...POSITION_SCALE_CHANNELS, ...POLAR_POSITION_SCALE_CHANNELS], channel) @@ -441,7 +447,12 @@ export function zero( } } - return true; + if (contains(['bar', 'area'], type) && !hasSecondaryRangeChannel) { + return true; + } + + return scaleConfig?.zero; } + return false; } diff --git a/src/scale.ts b/src/scale.ts index a9bfd382f19..c90e7b13421 100644 --- a/src/scale.ts +++ b/src/scale.ts @@ -415,6 +415,14 @@ export interface ScaleConfig { * Reverse x-scale by default (useful for right-to-left charts). */ xReverse?: boolean | ES; + + /** + * Default `scale.zero` for [`continuous`](https://vega.github.io/vega-lite/docs/scale.html#continuous) scales except for (1) x/y-scales of non-ranged bar or area charts and (2) size scales. + * + * __Default value:__ `true` + * + */ + zero?: boolean; } export const defaultScaleConfig: ScaleConfig = { @@ -439,7 +447,9 @@ export const defaultScaleConfig: ScaleConfig = { minStrokeWidth: 1, maxStrokeWidth: 4, quantileCount: 4, - quantizeCount: 4 + quantizeCount: 4, + + zero: true }; export interface SchemeParams { diff --git a/test/compile/scale/properties.test.ts b/test/compile/scale/properties.test.ts index 82676906fc7..1129cfc0fa7 100644 --- a/test/compile/scale/properties.test.ts +++ b/test/compile/scale/properties.test.ts @@ -199,35 +199,63 @@ describe('compile/scale', () => { }); describe('zero', () => { - it('should return true when mapping a quantitative field to x with scale.domain = "unaggregated"', () => { + it('should return default (undefined) when mapping a quantitative field to x with scale.domain = "unaggregated"', () => { expect( - rules.zero('x', {field: 'a', type: 'quantitative'}, 'unaggregated', {type: 'point'}, 'linear') - ).toBeTruthy(); + rules.zero('x', {field: 'a', type: 'quantitative'}, 'unaggregated', {type: 'point'}, 'linear', undefined, false) + ).toBeUndefined(); }); it('should return true when mapping a quantitative field to size', () => { - expect(rules.zero('size', {field: 'a', type: 'quantitative'}, undefined, {type: 'point'}, 'linear')).toBeTruthy(); + expect( + rules.zero('size', {field: 'a', type: 'quantitative'}, undefined, {type: 'point'}, 'linear', undefined, false) + ).toBeTruthy(); }); it('should return false when mapping a ordinal field to size', () => { - expect(!rules.zero('size', {field: 'a', type: 'ordinal'}, undefined, {type: 'point'}, 'linear')).toBeTruthy(); + expect( + !rules.zero('size', {field: 'a', type: 'ordinal'}, undefined, {type: 'point'}, 'linear', undefined, false) + ).toBeTruthy(); }); - it('should return true when mapping a non-binned quantitative field to x/y of point', () => { + it('should return default (undefined) when mapping a non-binned quantitative field to x/y of point', () => { for (const channel of ['x', 'y'] as const) { expect( - rules.zero(channel, {field: 'a', type: 'quantitative'}, undefined, {type: 'point'}, 'linear') - ).toBeTruthy(); + rules.zero( + channel, + {field: 'a', type: 'quantitative'}, + undefined, + {type: 'point'}, + 'linear', + undefined, + false + ) + ).toBeUndefined(); } }); it('should return false when mapping a quantitative field to dimension axis of bar, line, and area', () => { for (const mark of [BAR, AREA, LINE]) { expect( - rules.zero('x', {field: 'a', type: 'quantitative'}, undefined, {type: mark, orient: 'vertical'}, 'linear') + rules.zero( + 'x', + {field: 'a', type: 'quantitative'}, + undefined, + {type: mark, orient: 'vertical'}, + 'linear', + undefined, + false + ) ).toBe(false); expect( - rules.zero('y', {field: 'a', type: 'quantitative'}, undefined, {type: mark, orient: 'horizontal'}, 'linear') + rules.zero( + 'y', + {field: 'a', type: 'quantitative'}, + undefined, + {type: mark, orient: 'horizontal'}, + 'linear', + undefined, + false + ) ).toBe(false); } }); @@ -235,7 +263,15 @@ describe('compile/scale', () => { it('should return false when mapping a binned quantitative field to x/y', () => { for (const channel of ['x', 'y'] as const) { expect( - !rules.zero(channel, {bin: true, field: 'a', type: 'quantitative'}, undefined, {type: 'point'}, 'linear') + !rules.zero( + channel, + {bin: true, field: 'a', type: 'quantitative'}, + undefined, + {type: 'point'}, + 'linear', + undefined, + false + ) ).toBeTruthy(); } }); @@ -252,10 +288,78 @@ describe('compile/scale', () => { }, [3, 5], {type: 'point'}, - 'linear' + 'linear', + undefined, + false ) ).toBeTruthy(); } }); + + it(`should return config.scale.zero instead of true if it is specified`, () => { + const configZero = false; + for (const channel of ['x', 'y'] as const) { + expect( + rules.zero( + channel, + {field: 'a', type: 'quantitative'}, + undefined, + {type: 'point'}, + 'linear', + {zero: configZero}, + false + ) + ).toBe(configZero); + } + + expect( + rules.zero( + 'size', + {field: 'a', type: 'ordinal'}, + undefined, + {type: 'point'}, + 'linear', + {zero: configZero}, + false + ) + ).toBe(configZero); + + // ranged bar/area should take default configZero + expect( + rules.zero('x', {field: 'a', type: 'quantitative'}, undefined, {type: BAR}, 'linear', {zero: configZero}, true) + ).toBe(configZero); + }); + + it(`should return true for x/y scales of the non-ranged area/bar charts regardless to config`, () => { + for (const mark of [BAR, AREA]) { + for (const channel of ['x', 'y'] as const) { + expect( + rules.zero( + channel, + {field: 'a', type: 'quantitative'}, + undefined, + {type: mark}, + 'linear', + {zero: false}, + false + ) + ).toBe(true); + } + } + }); + + it(`should return true for the continuous & quantitative size scale regardless to config`, () => { + expect( + rules.zero( + 'size', + {field: 'a', type: 'quantitative'}, + undefined, + {type: 'point'}, + 'linear', + {zero: false}, + false + ) + ).toBe(true); + }); }); });