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