Skip to content

Commit 96cc57f

Browse files
committed
clean up editTypes/impliedEdits and formalize & document their schema definition
1 parent 7c38a4a commit 96cc57f

File tree

7 files changed

+110
-65
lines changed

7 files changed

+110
-65
lines changed

Diff for: src/lib/coerce.js

+7-7
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ var colorscaleNames = Object.keys(require('../components/colorscale/scales'));
1818
var nestedProperty = require('./nested_property');
1919
var counterRegex = require('./regex').counter;
2020

21-
exports.valObjects = {
21+
exports.valObjectMeta = {
2222
data_array: {
2323
// You can use *dflt=[] to force said array to exist though.
2424
description: [
@@ -283,7 +283,7 @@ exports.valObjects = {
283283
* Ensures that container[attribute] has a valid value.
284284
*
285285
* attributes[attribute] is an object with possible keys:
286-
* - valType: data_array, enumerated, boolean, ... as in valObjects
286+
* - valType: data_array, enumerated, boolean, ... as in valObjectMeta
287287
* - values: (enumerated only) array of allowed vals
288288
* - min, max: (number, integer only) inclusive bounds on allowed vals
289289
* either or both may be omitted
@@ -310,7 +310,7 @@ exports.coerce = function(containerIn, containerOut, attributes, attribute, dflt
310310
return v;
311311
}
312312

313-
exports.valObjects[opts.valType].coerceFunction(v, propOut, dflt, opts);
313+
exports.valObjectMeta[opts.valType].coerceFunction(v, propOut, dflt, opts);
314314

315315
return propOut.get();
316316
};
@@ -377,12 +377,12 @@ exports.coerceHoverinfo = function(traceIn, traceOut, layoutOut) {
377377
};
378378

379379
exports.validate = function(value, opts) {
380-
var valObject = exports.valObjects[opts.valType];
380+
var valObjectDef = exports.valObjectMeta[opts.valType];
381381

382382
if(opts.arrayOk && Array.isArray(value)) return true;
383383

384-
if(valObject.validateFunction) {
385-
return valObject.validateFunction(value, opts);
384+
if(valObjectDef.validateFunction) {
385+
return valObjectDef.validateFunction(value, opts);
386386
}
387387

388388
var failed = {},
@@ -391,6 +391,6 @@ exports.validate = function(value, opts) {
391391

392392
// 'failed' just something mutable that won't be === anything else
393393

394-
valObject.coerceFunction(value, propMock, failed, opts);
394+
valObjectDef.coerceFunction(value, propMock, failed, opts);
395395
return out !== failed;
396396
};

Diff for: src/lib/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ lib.relinkPrivateKeys = require('./relink_private');
2929
lib.ensureArray = require('./ensure_array');
3030

3131
var coerceModule = require('./coerce');
32-
lib.valObjects = coerceModule.valObjects;
32+
lib.valObjectMeta = coerceModule.valObjectMeta;
3333
lib.coerce = coerceModule.coerce;
3434
lib.coerce2 = coerceModule.coerce2;
3535
lib.coerceFont = coerceModule.coerceFont;

Diff for: src/plot_api/edit_types.js

+59-26
Original file line numberDiff line numberDiff line change
@@ -12,42 +12,69 @@ var Lib = require('../lib');
1212
var extendFlat = Lib.extendFlat;
1313
var isPlainObject = Lib.isPlainObject;
1414

15+
var traceOpts = {
16+
valType: 'flaglist',
17+
extras: ['none'],
18+
flags: ['calc', 'calcIfAutorange', 'plot', 'style', 'colorbars'],
19+
description: [
20+
'trace attributes should include an `editType` string matching this flaglist.',
21+
'*calc* is the most extensive: a full `Plotly.plot` starting by clearing `gd.calcdata`',
22+
'to force it to be regenerated',
23+
'*calcIfAutorange* does a full `Plotly.plot`, but only clears and redoes `gd.calcdata`',
24+
'if there is at least one autoranged axis.',
25+
'*plot* calls `Plotly.plot` but without first clearing `gd.calcdata`.',
26+
'*style* only calls `module.style` for all trace modules and redraws the legend.',
27+
'*colorbars* only redraws colorbars.'
28+
].join(' ')
29+
};
30+
31+
var layoutOpts = {
32+
valType: 'flaglist',
33+
extras: ['none'],
34+
flags: [
35+
'calc', 'calcIfAutorange', 'plot', 'legend', 'ticks',
36+
'layoutstyle', 'modebar', 'camera', 'arraydraw'
37+
],
38+
description: [
39+
'layout attributes should include an `editType` string matching this flaglist.',
40+
'*calc* is the most extensive: a full `Plotly.plot` starting by clearing `gd.calcdata`',
41+
'to force it to be regenerated',
42+
'*calcIfAutorange* does a full `Plotly.plot`, but only clears and redoes `gd.calcdata`',
43+
'if there is at least one autoranged axis.',
44+
'*plot* calls `Plotly.plot` but without first clearing `gd.calcdata`.',
45+
'*legend* only redraws the legend.',
46+
'*ticks* only redraws axis ticks, labels, and gridlines.',
47+
'*layoutstyle* reapplies global and SVG cartesian axis styles.',
48+
'*modebar* just updates the modebar.',
49+
'*camera* just updates the camera settings for gl3d scenes.',
50+
'*arraydraw* allows component arrays to invoke the redraw routines just for the',
51+
'component(s) that changed.'
52+
].join(' ')
53+
};
54+
55+
// flags for inside restyle/relayout include a few extras
56+
// that shouldn't be used in attributes, to deal with certain
57+
// combinations and conditionals efficiently
58+
var traceEditTypeFlags = traceOpts.flags.slice()
59+
.concat(['clearCalc', 'fullReplot']);
60+
61+
var layoutEditTypeFlags = layoutOpts.flags.slice()
62+
.concat('layoutReplot');
63+
1564
module.exports = {
65+
traces: traceOpts,
66+
layout: layoutOpts,
1667
/*
1768
* default (all false) edit flags for restyle (traces)
1869
* creates a new object each call, so the caller can mutate freely
1970
*/
20-
traces: function() {
21-
return {
22-
calc: false,
23-
calcIfAutorange: false,
24-
plot: false,
25-
style: false,
26-
colorbars: false,
27-
autorangeOn: false,
28-
clearCalc: false,
29-
fullReplot: false
30-
};
31-
},
71+
traceFlags: function() { return falseObj(traceEditTypeFlags); },
3272

3373
/*
3474
* default (all false) edit flags for relayout
3575
* creates a new object each call, so the caller can mutate freely
3676
*/
37-
layout: function() {
38-
return {
39-
legend: false,
40-
ticks: false,
41-
layoutstyle: false,
42-
plot: false,
43-
calc: false,
44-
calcIfAutorange: false,
45-
modebar: false,
46-
camera: false,
47-
arraydraw: false,
48-
layoutReplot: false
49-
};
50-
},
77+
layoutFlags: function() { return falseObj(layoutEditTypeFlags); },
5178

5279
/*
5380
* update `flags` with the `editType` values found in `attr`
@@ -65,6 +92,12 @@ module.exports = {
6592
overrideAll: overrideAll
6693
};
6794

95+
function falseObj(keys) {
96+
var out = {};
97+
for(var i = 0; i < keys.length; i++) out[keys[i]] = false;
98+
return out;
99+
}
100+
68101
/**
69102
* For attributes that are largely copied from elsewhere into a plot type that doesn't
70103
* support partial redraws - overrides the editType field of all attributes in the object

Diff for: src/plot_api/plot_api.js

+11-6
Original file line numberDiff line numberDiff line change
@@ -1331,7 +1331,7 @@ function _restyle(gd, aobj, _traces) {
13311331
var traces = helpers.coerceTraceIndices(gd, _traces);
13321332

13331333
// initialize flags
1334-
var flags = editTypes.traces();
1334+
var flags = editTypes.traceFlags();
13351335

13361336
// copies of the change (and previous values of anything affected)
13371337
// for the undo / redo queue
@@ -1585,9 +1585,14 @@ function _restyle(gd, aobj, _traces) {
15851585
}
15861586

15871587
// do we need to force a recalc?
1588-
Plotly.Axes.list(gd).forEach(function(ax) {
1589-
if(ax.autorange) flags.autorangeOn = true;
1590-
});
1588+
var autorangeOn = false;
1589+
var axList = Plotly.Axes.list(gd);
1590+
for(i = 0; i < axList.length; i++) {
1591+
if(axList[i].autorange) {
1592+
autorangeOn = true;
1593+
break;
1594+
}
1595+
}
15911596

15921597
// check axes we've flagged for possible deletion
15931598
// flagAxForDelete is a hash so we can make sure we only get each axis once
@@ -1610,7 +1615,7 @@ function _restyle(gd, aobj, _traces) {
16101615
}
16111616

16121617
// combine a few flags together;
1613-
if(flags.calc || (flags.calcIfAutorange && flags.autorangeOn)) {
1618+
if(flags.calc || (flags.calcIfAutorange && autorangeOn)) {
16141619
flags.clearCalc = true;
16151620
}
16161621
if(flags.calc || flags.plot || flags.calcIfAutorange) {
@@ -1735,7 +1740,7 @@ function _relayout(gd, aobj) {
17351740
}
17361741

17371742
// initialize flags
1738-
var flags = editTypes.layout();
1743+
var flags = editTypes.layoutFlags();
17391744

17401745
// copies of the change (and previous values of anything affected)
17411746
// for the undo / redo queue

Diff for: src/plot_api/plot_schema.js

+22-4
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,29 @@ exports.get = function() {
6363

6464
return {
6565
defs: {
66-
valObjects: Lib.valObjects,
66+
valObjects: Lib.valObjectMeta,
6767
metaKeys: UNDERSCORE_ATTRS.concat(['description', 'role']),
68-
editTypes: {
69-
traces: editTypes.traces(),
70-
layout: editTypes.layout()
68+
editType: {
69+
traces: editTypes.traces,
70+
layout: editTypes.layout
71+
},
72+
impliedEdits: {
73+
description: [
74+
'Sometimes when an attribute is changed, other attributes',
75+
'must be altered as well in order to achieve the intended',
76+
'result. For example, when `range` is specified, it is',
77+
'important to set `autorange` to `false` or the new `range`',
78+
'value would be lost in the redraw. `impliedEdits` is the',
79+
'mechanism to do this: `impliedEdits: {autorange: false}`.',
80+
'Each key is a relative paths to the attribute string to',
81+
'change, using *^* to ascend into the parent container,',
82+
'for example `range[0]` has `impliedEdits: {*^autorange*: false}`.',
83+
'A value of `undefined` means that the attribute will not be',
84+
'changed, but its previous value should be recorded in case',
85+
'we want to reverse this change later. For example, `autorange`',
86+
'has `impliedEdits: {*range[0]*: undefined, *range[1]*:undefined}',
87+
'because the range will likely be changed by redraw.'
88+
].join(' ')
7189
}
7290
},
7391

Diff for: test/jasmine/tests/plot_api_test.js

+7-7
Original file line numberDiff line numberDiff line change
@@ -2154,7 +2154,7 @@ describe('plot_api helpers', function() {
21542154

21552155
describe('plot_api edit_types', function() {
21562156
it('initializes flags with all false', function() {
2157-
['traces', 'layout'].forEach(function(container) {
2157+
['traceFlags', 'layoutFlags'].forEach(function(container) {
21582158
var initFlags = editTypes[container]();
21592159
Object.keys(initFlags).forEach(function(key) {
21602160
expect(initFlags[key]).toBe(false, container + '.' + key);
@@ -2163,35 +2163,35 @@ describe('plot_api edit_types', function() {
21632163
});
21642164

21652165
it('makes no changes if editType is not included', function() {
2166-
var flags = {docalc: false, dostyle: true};
2166+
var flags = {calc: false, style: true};
21672167

21682168
editTypes.update(flags, {
21692169
valType: 'boolean',
21702170
dflt: true,
21712171
role: 'style'
21722172
});
21732173

2174-
expect(flags).toEqual({docalc: false, dostyle: true});
2174+
expect(flags).toEqual({calc: false, style: true});
21752175

21762176
editTypes.update(flags, {
21772177
family: {valType: 'string', dflt: 'Comic sans'},
21782178
size: {valType: 'number', dflt: 96},
21792179
color: {valType: 'color', dflt: 'red'}
21802180
});
21812181

2182-
expect(flags).toEqual({docalc: false, dostyle: true});
2182+
expect(flags).toEqual({calc: false, style: true});
21832183
});
21842184

21852185
it('gets updates from the outer object and ignores nested items', function() {
2186-
var flags = {docalc: false, dolegend: true};
2186+
var flags = {calc: false, legend: true};
21872187

21882188
editTypes.update(flags, {
2189-
editType: 'docalc+dostyle',
2189+
editType: 'calc+style',
21902190
valType: 'number',
21912191
dflt: 1,
21922192
role: 'style'
21932193
});
21942194

2195-
expect(flags).toEqual({docalc: true, dolegend: true, dostyle: true});
2195+
expect(flags).toEqual({calc: true, legend: true, style: true});
21962196
});
21972197
});

Diff for: test/jasmine/tests/plotschema_test.js

+3-14
Original file line numberDiff line numberDiff line change
@@ -242,17 +242,6 @@ describe('plot schema', function() {
242242
});
243243

244244
it('has valid `editType` in all attributes and containers', function() {
245-
var traceEditTypeOpts = {
246-
valType: 'flaglist',
247-
extras: ['none'],
248-
flags: Object.keys(editTypes.traces)
249-
};
250-
var layoutEditTypeOpts = {
251-
valType: 'flaglist',
252-
extras: ['none'],
253-
flags: Object.keys(editTypes.layout)
254-
};
255-
256245
function shouldHaveEditType(attr, attrName) {
257246
// ensure any object (container or regular val object) has editType
258247
// array containers have extra nesting where editType would be redundant
@@ -262,21 +251,21 @@ describe('plot schema', function() {
262251

263252
assertTraceSchema(function(attr, attrName, attrs, level, attrString) {
264253
if(shouldHaveEditType(attr, attrName)) {
265-
expect(Lib.validate(attr.editType, traceEditTypeOpts))
254+
expect(Lib.validate(attr.editType, editTypes.traces))
266255
.toBe(true, attrString + ': ' + JSON.stringify(attr.editType));
267256
}
268257
});
269258

270259
assertTransformSchema(function(attr, attrName, attrs, level, attrString) {
271260
if(shouldHaveEditType(attr, attrName)) {
272-
expect(Lib.validate(attr.editType, traceEditTypeOpts))
261+
expect(Lib.validate(attr.editType, editTypes.traces))
273262
.toBe(true, attrString + ': ' + JSON.stringify(attr.editType));
274263
}
275264
});
276265

277266
assertLayoutSchema(function(attr, attrName, attrs, level, attrString) {
278267
if(shouldHaveEditType(attr, attrName)) {
279-
expect(Lib.validate(attr.editType, layoutEditTypeOpts))
268+
expect(Lib.validate(attr.editType, editTypes.layout))
280269
.toBe(true, attrString + ': ' + JSON.stringify(attr.editType));
281270
}
282271
});

0 commit comments

Comments
 (0)