diff --git a/src/lib/coerce.js b/src/lib/coerce.js index 108370cfe40..b36cc1c1b95 100644 --- a/src/lib/coerce.js +++ b/src/lib/coerce.js @@ -361,58 +361,81 @@ exports.valObjectMeta = { * as a convenience, returns the value it finally set */ exports.coerce = function(containerIn, containerOut, attributes, attribute, dflt) { - var opts = nestedProperty(attributes, attribute).get(); + return _coerce(containerIn, containerOut, attributes, attribute, dflt).val; +}; + +function _coerce(containerIn, containerOut, attributes, attribute, dflt, opts) { + var shouldValidate = (opts || {}).shouldValidate; + + var attr = nestedProperty(attributes, attribute).get(); + if(dflt === undefined) dflt = attr.dflt; + var src = false; + var propIn = nestedProperty(containerIn, attribute); var propOut = nestedProperty(containerOut, attribute); - var v = propIn.get(); + var valIn = propIn.get(); var template = containerOut._template; - if(v === undefined && template) { - v = nestedProperty(template, attribute).get(); + if(valIn === undefined && template) { + valIn = nestedProperty(template, attribute).get(); + src = (valIn !== undefined); + // already used the template value, so short-circuit the second check template = 0; } - if(dflt === undefined) dflt = opts.dflt; - /** * arrayOk: value MAY be an array, then we do no value checking * at this point, because it can be more complicated than the * individual form (eg. some array vals can be numbers, even if the * single values must be color strings) */ - if(opts.arrayOk && isArrayOrTypedArray(v)) { - propOut.set(v); - return v; + if(attr.arrayOk && isArrayOrTypedArray(valIn)) { + propOut.set(valIn); + return { + inp: valIn, + val: valIn, + src: true + }; } - var coerceFunction = exports.valObjectMeta[opts.valType].coerceFunction; - coerceFunction(v, propOut, dflt, opts); + var coerceFunction = exports.valObjectMeta[attr.valType].coerceFunction; + coerceFunction(valIn, propOut, dflt, attr); + + var valOut = propOut.get(); + src = (valOut !== undefined) && shouldValidate && validate(valIn, attr); - var out = propOut.get(); // in case v was provided but invalid, try the template again so it still // overrides the regular default - if(template && out === dflt && !validate(v, opts)) { - v = nestedProperty(template, attribute).get(); - coerceFunction(v, propOut, dflt, opts); - out = propOut.get(); + if(template && valOut === dflt && !validate(valIn, attr)) { + valIn = nestedProperty(template, attribute).get(); + coerceFunction(valIn, propOut, dflt, attr); + valOut = propOut.get(); + + src = (valOut !== undefined) && shouldValidate && validate(valIn, attr); } - return out; -}; + + return { + inp: valIn, + val: valOut, + src: src + }; +} /** * Variation on coerce + * useful when setting an attribute to a valid value + * can change the default for another attribute. * * Uses coerce to get attribute value if user input is valid, * returns attribute default if user input it not valid or * returns false if there is no user input. */ exports.coerce2 = function(containerIn, containerOut, attributes, attribute, dflt) { - var propIn = nestedProperty(containerIn, attribute); - var propOut = exports.coerce(containerIn, containerOut, attributes, attribute, dflt); - var valIn = propIn.get(); - - return (valIn !== undefined && valIn !== null) ? propOut : false; + var out = _coerce(containerIn, containerOut, attributes, attribute, dflt, { + shouldValidate: true + }); + return (out.src && out.inp !== undefined) ? out.val : false; }; /* diff --git a/src/plots/cartesian/axis_defaults.js b/src/plots/cartesian/axis_defaults.js index c1fbf92f9ea..5dec11fec88 100644 --- a/src/plots/cartesian/axis_defaults.js +++ b/src/plots/cartesian/axis_defaults.js @@ -99,6 +99,7 @@ module.exports = function handleAxisDefaults(containerIn, containerOut, coerce, dfltColor: dfltColor, bgColor: options.bgColor, showGrid: options.showGrid, + hideGrid: options.hideGrid, attributes: layoutAttributes }); diff --git a/src/plots/cartesian/layout_defaults.js b/src/plots/cartesian/layout_defaults.js index 3e411ae0574..515b30fa138 100644 --- a/src/plots/cartesian/layout_defaults.js +++ b/src/plots/cartesian/layout_defaults.js @@ -239,6 +239,7 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) { letter: axLetter, font: layoutOut.font, outerTicks: outerTicks[axName], + hideGrid: noGrids[axName], showGrid: !noGrids[axName], data: ax2traces[axName] || [], bgColor: bgColor, @@ -303,6 +304,7 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) { letter: axLetter, font: layoutOut.font, outerTicks: outerTicks[axName], + hideGrid: noGrids[axName], showGrid: !noGrids[axName], data: [], bgColor: bgColor, diff --git a/src/plots/cartesian/line_grid_defaults.js b/src/plots/cartesian/line_grid_defaults.js index 18fd9974134..2ed89aff7c4 100644 --- a/src/plots/cartesian/line_grid_defaults.js +++ b/src/plots/cartesian/line_grid_defaults.js @@ -31,6 +31,18 @@ module.exports = function handleLineGridDefaults(containerIn, containerOut, coer return Lib.coerce2(containerIn, containerOut, opts.attributes, attr, dflt); } + function _coerce(attr, dflt) { + var val = coerce2(attr, dflt); + + if( + val && opts.hideGrid && + containerOut[attr] !== containerIn[attr] && + containerOut[attr] === containerOut._template[attr] + ) return false; + + return val; + } + var lineColor = coerce2('linecolor', dfltColor); var lineWidth = coerce2('linewidth'); var showLine = coerce('showline', opts.showLine || !!lineColor || !!lineWidth); @@ -41,8 +53,8 @@ module.exports = function handleLineGridDefaults(containerIn, containerOut, coer } var gridColorDflt = colorMix(dfltColor, opts.bgColor, opts.blend || lightFraction).toRgbString(); - var gridColor = coerce2('gridcolor', gridColorDflt); - var gridWidth = coerce2('gridwidth'); + var gridColor = _coerce('gridcolor', gridColorDflt); + var gridWidth = _coerce('gridwidth'); var showGridLines = coerce('showgrid', opts.showGrid || !!gridColor || !!gridWidth); if(!showGridLines) { @@ -51,8 +63,8 @@ module.exports = function handleLineGridDefaults(containerIn, containerOut, coer } if(!opts.noZeroLine) { - var zeroLineColor = coerce2('zerolinecolor', dfltColor); - var zeroLineWidth = coerce2('zerolinewidth'); + var zeroLineColor = _coerce('zerolinecolor', dfltColor); + var zeroLineWidth = _coerce('zerolinewidth'); var showZeroLine = coerce('zeroline', opts.showGrid || !!zeroLineColor || !!zeroLineWidth); if(!showZeroLine) { diff --git a/test/image/baselines/axes_breaks-round-weekdays.png b/test/image/baselines/axes_breaks-round-weekdays.png index 9f20ece5dfa..4f3b0c6ffdd 100644 Binary files a/test/image/baselines/axes_breaks-round-weekdays.png and b/test/image/baselines/axes_breaks-round-weekdays.png differ diff --git a/test/image/baselines/hide_gridline-template_color.png b/test/image/baselines/hide_gridline-template_color.png new file mode 100644 index 00000000000..49023fb0667 Binary files /dev/null and b/test/image/baselines/hide_gridline-template_color.png differ diff --git a/test/image/mocks/axes_custom-ticks_log-date.json b/test/image/mocks/axes_custom-ticks_log-date.json index 261015f32d1..3adb12d7b7a 100644 --- a/test/image/mocks/axes_custom-ticks_log-date.json +++ b/test/image/mocks/axes_custom-ticks_log-date.json @@ -15,44 +15,48 @@ "layout": { "width": 500, "height": 300, - "title": { - "text": "custom ticks on date & log axes" - }, - "paper_bgcolor": "lightblue", - "plot_bgcolor": "#ddd", - "yaxis": { - "type": "log", - "tickmode": "array", - "tickvals": [ - 1, - 10, - 100 - ], - "ticktext": [ - "one", - "ten", - "one
hundred" - ], - "gridwidth": 2, - "tickwidth": 15, - "tickcolor": "green", - "gridcolor": "green" - }, - "xaxis": { - "type": "date", - "tickmode": "array", - "tickvals": [ - "2010-01-16", - "2010-02-14" - ], - "ticktext": [ - "Jan 16", - "Feb 14" - ], - "gridwidth": 10, - "tickwidth": 50, - "tickcolor": "rgba(255,0,0,0.75)", - "gridcolor": "rgba(255,0,0,0.25)" + "template": { + "layout": { + "title": { + "text": "custom ticks on date & log axes" + }, + "paper_bgcolor": "lightblue", + "plot_bgcolor": "#ddd", + "yaxis": { + "type": "log", + "tickmode": "array", + "tickvals": [ + 1, + 10, + 100 + ], + "ticktext": [ + "one", + "ten", + "one
hundred" + ], + "gridwidth": 2, + "tickwidth": 15, + "tickcolor": "green", + "gridcolor": "green" + }, + "xaxis": { + "type": "date", + "tickmode": "array", + "tickvals": [ + "2010-01-16", + "2010-02-14" + ], + "ticktext": [ + "Jan 16", + "Feb 14" + ], + "gridwidth": 10, + "tickwidth": 50, + "tickcolor": "rgba(255,0,0,0.75)", + "gridcolor": "rgba(255,0,0,0.25)" + } + } } } } diff --git a/test/image/mocks/hide_gridline-template_color.json b/test/image/mocks/hide_gridline-template_color.json new file mode 100644 index 00000000000..a412a1ded05 --- /dev/null +++ b/test/image/mocks/hide_gridline-template_color.json @@ -0,0 +1,70 @@ +{ + "data": [ + { + "type": "scatter", + "marker": { "color": "blue" }, + "x": [-1, 1], + "y": [-1, 1] + }, + { + "type": "bar", + "marker": { "color": "rgba(127,127,127,0.5)" }, + "y": [-1, 1], + "xaxis": "x2", + "yaxis": "y2" + }, + { + "type": "bar", + "marker": { "color": "rgba(127,127,127,0.5)" }, + "x": [-1, 1], + "xaxis": "x3", + "yaxis": "y3" + }, + { + "type": "bar", + "marker": { "color": "rgba(127,127,127,0.5)" }, + "y": [-1, 1], + "xaxis": "x4", + "yaxis": "y4" + }, + { + "type": "scatter", + "marker": { "color": "blue" }, + "x": [-1, 1], + "y": [-1, 1], + "xaxis": "x4", + "yaxis": "y4" + } + ], + "layout": { + "title": { + "text": "hide grid lines for oriented traces" + }, + "showlegend": false, + "width": 600, + "height": 600, + "grid": { + "rows": 2, + "columns": 2, + "xgap": 0.2, + "ygap": 0.2, + "pattern": "independent" + }, + "template": { + "layout": { + "xaxis": { + "gridcolor": "red", + "gridwidth": 2, + "zerolinecolor": "orange", + "zerolinewidth": 5 + }, + "yaxis": { + "gridcolor": "red", + "gridwidth": 2, + "zerolinecolor": "orange", + "zerolinewidth": 5 + } + } + } + } +} diff --git a/test/jasmine/tests/axes_test.js b/test/jasmine/tests/axes_test.js index 5c19984ce9e..eb4d6ab44dc 100644 --- a/test/jasmine/tests/axes_test.js +++ b/test/jasmine/tests/axes_test.js @@ -1803,10 +1803,10 @@ describe('Test axes', function() { Plotly.plot(gd, data, layout); var yaxis = gd._fullLayout.yaxis; - expect(yaxis.ticklen).toBe(5); - expect(yaxis.tickwidth).toBe(1); - expect(yaxis.tickcolor).toBe('#444'); - expect(yaxis.ticks).toBe('outside'); + expect(yaxis.ticklen).toBe(undefined); + expect(yaxis.tickwidth).toBe(undefined); + expect(yaxis.tickcolor).toBe(undefined); + expect(yaxis.ticks).toBe(''); expect(yaxis.showticklabels).toBe(true); expect(yaxis.tickfont).toEqual({ family: '"Open Sans", verdana, arial, sans-serif', size: 12, color: '#444' }); expect(yaxis.tickangle).toBe('auto'); diff --git a/test/jasmine/tests/lib_test.js b/test/jasmine/tests/lib_test.js index 4105e5455ea..b9fa49649e0 100644 --- a/test/jasmine/tests/lib_test.js +++ b/test/jasmine/tests/lib_test.js @@ -778,7 +778,7 @@ describe('Test lib.js:', function() { expect(sizeOut).toBe(outObj.testMarker.testSize); }); - it('should set and return the default if the user input is not valid', function() { + it('should set the default and return false if the user input is not valid', function() { var colVal = 'r'; var sizeVal = 'aaaaah!'; var attrs = { @@ -792,12 +792,80 @@ describe('Test lib.js:', function() { var colOut = coerce2(obj, outObj, attrs, 'testMarker.testColor'); var sizeOut = coerce2(obj, outObj, attrs, 'testMarker.testSize'); - expect(colOut).toBe('rgba(0, 0, 0, 0)'); + expect(colOut).toBe(false); + expect(outObj.testMarker.testColor).toBe('rgba(0, 0, 0, 0)'); + expect(sizeOut).toBe(false); + expect(outObj.testMarker.testSize).toBe(20); + }); + + it('should set the user input', function() { + var colVal = 'red'; + var sizeVal = '1e2'; + var attrs = { + testMarker: { + testColor: {valType: 'color', dflt: 'rgba(0, 0, 0, 0)'}, + testSize: {valType: 'number', dflt: 20} + } + }; + var obj = {testMarker: {testColor: colVal, testSize: sizeVal}}; + var outObj = {}; + var colOut = coerce2(obj, outObj, attrs, 'testMarker.testColor'); + var sizeOut = coerce2(obj, outObj, attrs, 'testMarker.testSize'); + + expect(colOut).toBe('red'); + expect(colOut).toBe(outObj.testMarker.testColor); + expect(sizeOut).toBe(100); expect(sizeOut).toBe(outObj.testMarker.testSize); - expect(sizeOut).toBe(20); + }); + + it('should set to template if the container input is not valid', function() { + var attrs = { + testMarker: { + testColor: {valType: 'color', dflt: 'rgba(0, 0, 0, 0)'}, + testSize: {valType: 'number', dflt: 20} + } + }; + var obj = { + testMarker: {testColor: 'invalid', testSize: 'invalid'} + }; + var outObj = { + _template: { + testMarker: {testColor: 'red', testSize: '1e2'} + } + }; + var colOut = coerce2(obj, outObj, attrs, 'testMarker.testColor'); + var sizeOut = coerce2(obj, outObj, attrs, 'testMarker.testSize'); + + expect(colOut).toBe('red'); + expect(colOut).toBe(outObj.testMarker.testColor); + expect(sizeOut).toBe(100); expect(sizeOut).toBe(outObj.testMarker.testSize); }); + it('should set to default and return false if the both container and template inputs are not valid', function() { + var attrs = { + testMarker: { + testColor: {valType: 'color', dflt: 'rgba(0, 0, 0, 0)'}, + testSize: {valType: 'number', dflt: 20} + } + }; + var obj = { + testMarker: {testColor: 'invalid', testSize: 'invalid'} + }; + var outObj = { + _template: { + testMarker: {testColor: 'invalid', testSize: 'invalid'} + } + }; + var colOut = coerce2(obj, outObj, attrs, 'testMarker.testColor'); + var sizeOut = coerce2(obj, outObj, attrs, 'testMarker.testSize'); + + expect(colOut).toBe(false); + expect(outObj.testMarker.testColor).toBe('rgba(0, 0, 0, 0)'); + expect(sizeOut).toBe(false); + expect(outObj.testMarker.testSize).toBe(20); + }); + it('should return false if there is no user input', function() { var colVal = null; var sizeVal; // undefined diff --git a/test/jasmine/tests/mock_test.js b/test/jasmine/tests/mock_test.js index eb7abbbfa29..169323a3c52 100644 --- a/test/jasmine/tests/mock_test.js +++ b/test/jasmine/tests/mock_test.js @@ -596,6 +596,7 @@ var list = [ 'heatmap_xyz-gaps-on-sides', 'heatmap-reverse-autocolorscale', 'heatmap-with-zero-category', + 'hide_gridline-template_color', 'hist_0_to_093', 'hist_0_to_1_midpoints', 'hist_003_to_093', @@ -1639,6 +1640,7 @@ figs['heatmap_xyz-dates-and-categories'] = require('@mocks/heatmap_xyz-dates-and figs['heatmap_xyz-gaps-on-sides'] = require('@mocks/heatmap_xyz-gaps-on-sides'); figs['heatmap-reverse-autocolorscale'] = require('@mocks/heatmap-reverse-autocolorscale'); figs['heatmap-with-zero-category'] = require('@mocks/heatmap-with-zero-category'); +figs['hide_gridline-template_color'] = require('@mocks/hide_gridline-template_color'); figs['hist_0_to_093'] = require('@mocks/hist_0_to_093'); figs['hist_0_to_1_midpoints'] = require('@mocks/hist_0_to_1_midpoints'); figs['hist_003_to_093'] = require('@mocks/hist_003_to_093');