diff --git a/src/lib/index.js b/src/lib/index.js index 1639ba06e07..9ec4313587e 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -989,11 +989,14 @@ lib.templateString = function(string, obj) { var getterCache = {}; return string.replace(lib.TEMPLATE_STRING_REGEX, function(dummy, key) { + var v; if(SIMPLE_PROPERTY_REGEX.test(key)) { - return obj[key] || ''; + v = obj[key]; + } else { + getterCache[key] = getterCache[key] || lib.nestedProperty(obj, key).get; + v = getterCache[key](); } - getterCache[key] = getterCache[key] || lib.nestedProperty(obj, key).get; - return getterCache[key]() || ''; + return lib.isValidTextValue(v) ? v : ''; }); }; diff --git a/src/plot_api/validate.js b/src/plot_api/validate.js index 0fccac8c55c..f24994ea235 100644 --- a/src/plot_api/validate.js +++ b/src/plot_api/validate.js @@ -161,13 +161,14 @@ function crawl(objIn, objOut, schema, list, base, path) { var valOut = objOut[k]; var nestedSchema = getNestedSchema(schema, k); - var isInfoArray = (nestedSchema || {}).valType === 'info_array'; - var isColorscale = (nestedSchema || {}).valType === 'colorscale'; + var nestedValType = (nestedSchema || {}).valType; + var isInfoArray = nestedValType === 'info_array'; + var isColorscale = nestedValType === 'colorscale'; var items = (nestedSchema || {}).items; if(!isInSchema(schema, k)) { list.push(format('schema', base, p)); - } else if(isPlainObject(valIn) && isPlainObject(valOut)) { + } else if(isPlainObject(valIn) && isPlainObject(valOut) && nestedValType !== 'any') { crawl(valIn, valOut, nestedSchema, list, base, p); } else if(isInfoArray && isArray(valIn)) { if(valIn.length > valOut.length) { diff --git a/test/jasmine/tests/lib_test.js b/test/jasmine/tests/lib_test.js index 34d5d2c43a6..4105e5455ea 100644 --- a/test/jasmine/tests/lib_test.js +++ b/test/jasmine/tests/lib_test.js @@ -2229,6 +2229,14 @@ describe('Test lib.js:', function() { it('replaces empty key with empty string', function() { expect(Lib.templateString('foo %{} %{}', {})).toEqual('foo '); }); + + it('should work with the number *0*', function() { + expect(Lib.templateString('%{group}', {group: 0})).toEqual('0'); + }); + + it('should work with the number *0* (nested case)', function() { + expect(Lib.templateString('%{x.y}', {'x': {y: 0}})).toEqual('0'); + }); }); describe('hovertemplateString', function() { @@ -2249,6 +2257,14 @@ describe('Test lib.js:', function() { expect(Lib.hovertemplateString('foo %{bar[0].baz}', {}, locale, {bar: [{baz: 'asdf'}]})).toEqual('foo asdf'); }); + it('should work with the number *0*', function() { + expect(Lib.hovertemplateString('%{group}', {}, locale, {group: 0})).toEqual('0'); + }); + + it('should work with the number *0* (nested case)', function() { + expect(Lib.hovertemplateString('%{x.y}', {}, locale, {'x': {y: 0}})).toEqual('0'); + }); + it('subtitutes multiple matches', function() { expect(Lib.hovertemplateString('foo %{group} %{trace}', {}, locale, {group: 'asdf', trace: 'jkl;'})).toEqual('foo asdf jkl;'); }); diff --git a/test/jasmine/tests/validate_test.js b/test/jasmine/tests/validate_test.js index 46de7d5cb0e..22ae7841f98 100644 --- a/test/jasmine/tests/validate_test.js +++ b/test/jasmine/tests/validate_test.js @@ -640,4 +640,27 @@ describe('Plotly.validate', function() { }]); expect(out).toBeUndefined(); }); + + it('should not attempt to crawl into nested objects of valType: \'any\' attributes', function() { + var out = Plotly.validate([{ + mode: 'markers', + x: ['a', 'b', 'c', 'a', 'b', 'c'], + y: [1, 2, 3, 4, 5, 6], + transforms: [{ + type: 'groupby', + groups: ['a', 'b', 'c'], + styles: [{ + target: 'a', + value: {marker: {color: 'blue'}} + }, { + target: 'b', + value: {marker: {color: 'red'}} + }, { + target: 'c', + value: {marker: {color: 'black'}} + }] + }] + }]); + expect(out).toBeUndefined(); + }); });