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/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/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/jasmine/tests/axes_test.js b/test/jasmine/tests/axes_test.js index 8730ed09d5f..cbf0c71eafe 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