diff --git a/src/plot_api/plot_schema.js b/src/plot_api/plot_schema.js index 54e2b5aab29..73aca869bc8 100644 --- a/src/plot_api/plot_schema.js +++ b/src/plot_api/plot_schema.js @@ -63,7 +63,7 @@ exports.get = function() { return { defs: { valObjects: valObjectMeta, - metaKeys: UNDERSCORE_ATTRS.concat(['description', 'editType', 'impliedEdits']), + metaKeys: UNDERSCORE_ATTRS.concat(['description', 'role', 'editType', 'impliedEdits']), editType: { traces: editTypes.traces, layout: editTypes.layout @@ -600,14 +600,14 @@ function getFramesAttributes() { } function formatAttributes(attrs) { - mergeValType(attrs); + mergeValTypeAndRole(attrs); formatArrayContainers(attrs); stringify(attrs); return attrs; } -function mergeValType(attrs) { +function mergeValTypeAndRole(attrs) { function makeSrcAttr(attrName) { return { valType: 'string', @@ -621,13 +621,13 @@ function mergeValType(attrs) { function callback(attr, attrName, attrs) { if(exports.isValObject(attr)) { - if(attr.valType === 'data_array') { - // all 'data_array' attrs have a corresponding 'src' attr - attrs[attrName + 'src'] = makeSrcAttr(attrName); - } else if(attr.arrayOk === true) { - // all 'arrayOk' attrs have a corresponding 'src' attr + if(attr.arrayOk === true || attr.valType === 'data_array') { + // all 'arrayOk' and 'data_array' attrs have a corresponding 'src' attr attrs[attrName + 'src'] = makeSrcAttr(attrName); } + } else if(isPlainObject(attr)) { + // all attrs container objects get role 'object' + attr.role = 'object'; } } @@ -646,6 +646,7 @@ function formatArrayContainers(attrs) { attrs[attrName] = { items: {} }; attrs[attrName].items[itemName] = attr; + attrs[attrName].role = 'object'; } exports.crawl(attrs, callback); diff --git a/tasks/compress_attributes.js b/tasks/compress_attributes.js index b2f43e7e5d4..aa1ae1df7d9 100644 --- a/tasks/compress_attributes.js +++ b/tasks/compress_attributes.js @@ -48,6 +48,7 @@ module.exports = function() { .replace(makeJoinedArrayRegex('description'), '') .replace(makeArrayRegex('requiredOpts'), '') .replace(makeArrayRegex('otherOpts'), '') + .replace(makeStringRegex('role'), '') .replace(makeStringRegex('hrName'), '') ); done(); diff --git a/test/jasmine/bundle_tests/plotschema_test.js b/test/jasmine/bundle_tests/plotschema_test.js index 3d749abe160..59cdf9ca0f1 100644 --- a/test/jasmine/bundle_tests/plotschema_test.js +++ b/test/jasmine/bundle_tests/plotschema_test.js @@ -25,6 +25,7 @@ describe('plot schema', function() { var isPlainObject = Lib.isPlainObject; var VALTYPES = Object.keys(valObjects); + var formerRoles = ['info', 'style', 'data']; var editType = plotSchema.defs.editType; function assertTraceSchema(callback) { @@ -72,6 +73,27 @@ describe('plot schema', function() { ); }); + it('all attributes should not have a former `role`', function() { + assertPlotSchema( + function(attr) { + if(isValObject(attr)) { + expect(formerRoles.indexOf(attr.role) === -1).toBe(true, attr); + expect(attr.role).toBeUndefined(attr); + } + } + ); + }); + + it('all nested objects should have the *object* `role`', function() { + assertPlotSchema( + function(attr, attrName) { + if(!isValObject(attr) && isPlainObject(attr) && attrName !== 'items') { + expect(attr.role === 'object').toBe(true); + } + } + ); + }); + it('all attributes should have the required options', function() { assertPlotSchema( function(attr) { @@ -94,7 +116,7 @@ describe('plot schema', function() { var opts = valObject.requiredOpts .concat(valObject.otherOpts) .concat([ - 'valType', 'description', + 'valType', 'description', 'role', 'editType', 'impliedEdits', 'anim', '_compareAsJSON', '_noTemplating' ]); @@ -164,8 +186,13 @@ describe('plot schema', function() { // N.B. the specs below must be satisfied for plotly.py expect(isPlainObject(itemsObj)).toBe(true); + expect(itemsObj.role).toBeUndefined(); expect(Object.keys(itemsObj).length).toEqual(1); expect(isPlainObject(itemObj)).toBe(true); + expect(itemObj.role).toBe('object'); + + var role = np.get().role; + expect(role).toEqual('object'); }); }); @@ -197,7 +224,7 @@ describe('plot schema', function() { ); }); - it('deprecated attributes should have a `valType`', function() { + it('deprecated attributes should have a `valType` and not any former roles', function() { var DEPRECATED = '_deprecated'; assertPlotSchema( @@ -206,8 +233,10 @@ describe('plot schema', function() { Object.keys(attr[DEPRECATED]).forEach(function(dAttrName) { var dAttr = attr[DEPRECATED][dAttrName]; - expect(VALTYPES.indexOf(dAttr.valType) !== -1) - .toBe(true, attrString + ': ' + dAttrName); + var msg = attrString + ': ' + dAttrName; + expect(VALTYPES.indexOf(dAttr.valType) !== -1).toBe(true, msg); + expect(formerRoles.indexOf(dAttr.role) === -1).toBe(true, msg); + expect(dAttr.role).toBeUndefined(msg); }); } } @@ -289,13 +318,15 @@ describe('plot schema', function() { expect(plotSchema.defs.metaKeys) .toEqual([ '_isSubplotObj', '_isLinkedToArray', '_arrayAttrRegexps', - '_deprecated', 'description', 'editType', 'impliedEdits' + '_deprecated', 'description', 'role', 'editType', 'impliedEdits' ]); }); it('should list the correct frame attributes', function() { expect(plotSchema.frames).toBeDefined(); + expect(plotSchema.frames.role).toEqual('object'); expect(plotSchema.frames.items.frames_entry).toBeDefined(); + expect(plotSchema.frames.items.frames_entry.role).toEqual('object'); }); it('should list config attributes', function() { @@ -439,7 +470,7 @@ describe('getTraceValObject', function() { // it still returns the attribute itself - but maybe we should only do this // for valType: any? (or data_array/arrayOk with just an index) [ - 'valType', 'dflt', 'description', 'arrayOk', + 'valType', 'dflt', 'role', 'description', 'arrayOk', 'editType', 'min', 'max', 'values' ].forEach(function(prop) { expect(getTraceValObject({}, ['x', prop]))