From 476c8b7dbe9ad986a000b74889cc0c63bbcf0497 Mon Sep 17 00:00:00 2001 From: Ricky Reusser Date: Wed, 28 Jun 2017 12:33:22 -0400 Subject: [PATCH 1/8] Add failing test for groupby colors --- test/jasmine/tests/transform_groupby_test.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/jasmine/tests/transform_groupby_test.js b/test/jasmine/tests/transform_groupby_test.js index 049a2856b5f..7361cb438da 100644 --- a/test/jasmine/tests/transform_groupby_test.js +++ b/test/jasmine/tests/transform_groupby_test.js @@ -63,6 +63,23 @@ describe('groupby', function() { }); }); + it('varies the color for each expanded trace', function () { + var uniqueColors = {}; + var dataOut = []; + var dataIn = [{ + y: [1, 2, 3], + transforms: [{type: 'groupby', groups: ['a', 'b', 'c']}] + }]; + + Plots.supplyDataDefaults(dataIn, dataOut, {}, {}); + + for (var i = 0; i < dataOut.length; i++) { + uniqueColors[dataOut[i].marker.color] = true; + } + + expect(Object.keys(uniqueColors).length).toEqual(3); + }); + it('Accepts deprecated object notation for styles', function(done) { var oldStyleMockData = [{ mode: 'markers', From 4398bd71440e243d643691b7502ee163e02ab905 Mon Sep 17 00:00:00 2001 From: Ricky Reusser Date: Fri, 30 Jun 2017 11:10:54 -0400 Subject: [PATCH 2/8] Clear expanded trace colors between supplydefault passes --- src/components/colorscale/color_attributes.js | 3 +- src/plots/plots.js | 35 ++++++++++++++++++ src/traces/scatter/attributes.js | 4 +- test/image/baselines/groupby_coloring.png | Bin 0 -> 24939 bytes test/image/mocks/groupby_coloring.json | 23 ++++++++++++ .../tests/transform_expanded_trace_test.js | 30 +++++++++++++++ test/jasmine/tests/transform_groupby_test.js | 17 --------- 7 files changed, 93 insertions(+), 19 deletions(-) create mode 100644 test/image/baselines/groupby_coloring.png create mode 100644 test/image/mocks/groupby_coloring.json create mode 100644 test/jasmine/tests/transform_expanded_trace_test.js diff --git a/src/components/colorscale/color_attributes.js b/src/components/colorscale/color_attributes.js index 9c8f6cdb065..557715374ef 100644 --- a/src/components/colorscale/color_attributes.js +++ b/src/components/colorscale/color_attributes.js @@ -23,7 +23,8 @@ module.exports = function makeColorScaleAttributes(context) { ' or an array of numbers that are mapped to the colorscale', ' relative to the max and min values of the array or relative to', ' `cmin` and `cmax` if set.' - ].join('') + ].join(''), + useExpandedTraceDefaultColor: true }, colorscale: extendDeep({}, colorScaleAttributes.colorscale, { description: [ diff --git a/src/plots/plots.js b/src/plots/plots.js index 38416678886..f63f20c2d3a 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -659,6 +659,39 @@ plots.linkSubplots = function(newFullData, newFullLayout, oldFullData, oldFullLa } }; +// This function clears defaults between the first and second pass of +// supplyDefaults. It exists because otherwise null attributes are +// supplyDefault'd and inherited as *colors* instead of an actual null +// attribute which needs to be supplydefaulted by the individual +// expanded traces. +plots.clearExpandedTraceDefaultColors = function(expandedTraces) { + var colorAttrs, path, trace, i, j; + + // A better check *might* be to explicitly check for a groupby transform + if(expandedTraces.length <= 1) return; + + function locateExpandedTraceAttrs(attr, attrName, attrs, level) { + path[level] = attrName; + path.length = level + 1; + if(attr.useExpandedTraceDefaultColor) { + colorAttrs.push(path.join('.')); + } + } + + for(i = 0; i < expandedTraces.length; i++) { + trace = expandedTraces[i]; + colorAttrs = []; + path = []; + + PlotSchema.crawl(trace._module.attributes, locateExpandedTraceAttrs); + + for(j = 0; j < colorAttrs.length; j++) { + Lib.nestedProperty(trace, colorAttrs[j]).set(null); + } + } +}; + + plots.supplyDataDefaults = function(dataIn, dataOut, layout, fullLayout) { var i, fullTrace, trace; var modules = fullLayout._modules = [], @@ -693,6 +726,8 @@ plots.supplyDataDefaults = function(dataIn, dataOut, layout, fullLayout) { if(fullTrace.transforms && fullTrace.transforms.length) { var expandedTraces = applyTransforms(fullTrace, dataOut, layout, fullLayout); + plots.clearExpandedTraceDefaultColors(expandedTraces); + for(var j = 0; j < expandedTraces.length; j++) { var expandedTrace = expandedTraces[j], fullExpandedTrace = plots.supplyTraceDefaults(expandedTrace, cnt, fullLayout, i); diff --git a/src/traces/scatter/attributes.js b/src/traces/scatter/attributes.js index 726bb94bfef..52325bb8d20 100644 --- a/src/traces/scatter/attributes.js +++ b/src/traces/scatter/attributes.js @@ -124,7 +124,8 @@ module.exports = { color: { valType: 'color', role: 'style', - description: 'Sets the line color.' + description: 'Sets the line color.', + useExpandedTraceDefaultColor: true }, width: { valType: 'number', @@ -203,6 +204,7 @@ module.exports = { fillcolor: { valType: 'color', role: 'style', + useExpandedTraceDefaultColor: true, description: [ 'Sets the fill color.', 'Defaults to a half-transparent variant of the line color,', diff --git a/test/image/baselines/groupby_coloring.png b/test/image/baselines/groupby_coloring.png new file mode 100644 index 0000000000000000000000000000000000000000..5f9e9be9455219e6e9c4aaa2feef24cae0defd1a GIT binary patch literal 24939 zcmeIbc{tQ<-#;!9C815ombBR_J0S^0QYm6Em5^nOtW%azw#rt7kSI~siLr!1jY`%D zS%+b=8_d|oIy1j>R99Wsec#9Zd!FxeJm2s09M>PN!{M4aKcCO}Ip5ps^?JX%f9|Y4 z_a?zjEG#VCr%#={z{0|cVPRP}$GHLgrO%w+$HF4Ua{8q9MTGTaGS^d)fi4E>^RTbQ zo|n(Jh89QaW@g+t{|fWkF|*L(f!alb69Z?m4#{jCIk5H7myU#U7aa&4#Hb6}PjR{m zv1UOZ_H9)?HN&tp!Fk2>PH5tK78>AQDP&(w&CX;}+0>FX8s|Uq%*PHd1FiHe&DUmO zy1!KadVT~aSO}M|0LL~=lMy%zVkLI*Ir-L&!h0^-n%SpGvWc1;p&7#yl<5Id|UlzllnSm zhU&NF(`MqUoD~%nm7Ln8eW+dbG0+D6-8WA@KP`?K!*7F}tFKkQ`RydBEjymp&!Kfn zi(E(cR}%`JO1lt&d901cW@&)!Ty3B{e4 zA~RjtTq>9B%BRXm;Hg)tV2z3$&NqD}^XHz_i1;+@GfI3cjeI+t=;S^GpDJJ8f0xU-UZ%wI`S2YMkte4f zlF9hR>4C{=9<4KW&uSuto!}Moah_y?#v_Xq&#AAOCIwIj%c|8OxyhxGloB(jUz_Ey za_5!xD@!<}3j|ga>o-%o|1_a`i+Y1)ouCWPF-kx(lD_0s&8>VnMbj^FkkTK>v#;dE zE8`RwjoWi?dcFI4w`k0Lxvtbx(np)}rhXEF?;V)vDRrMIiqfL12#kpMOkC9$_I$jw zx$h%C)H&Yzb|#I;gM5=|^2(yzYtosNTT%>N{l;G_ZRIeVE@_Rk?@HFjJ=1`#OqLkW zJdVWACVLZZDLH<9{gOkjYSj(fWj|(hh_+~mzst@Qoll#DBMlL`U|(yGhTI(ZB-tP& z>p&TBrJ^u14~pP@gi8863V)<#w@ro5!r55V_PMEYa(C_i6lb(O⁣uWJ#ZkRhh@> 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 Date: Fri, 30 Jun 2017 11:28:12 -0400 Subject: [PATCH 3/8] Add failsafe for clearExpandedTraceDefaultColors --- src/plots/plots.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plots/plots.js b/src/plots/plots.js index f63f20c2d3a..b9a2ae85940 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -683,6 +683,8 @@ plots.clearExpandedTraceDefaultColors = function(expandedTraces) { colorAttrs = []; path = []; + if (!trace || !trace._module) continue; + PlotSchema.crawl(trace._module.attributes, locateExpandedTraceAttrs); for(j = 0; j < colorAttrs.length; j++) { From 0877f4840512507e2edde66f06d9bd13c14c985f Mon Sep 17 00:00:00 2001 From: Ricky Reusser Date: Fri, 30 Jun 2017 11:42:09 -0400 Subject: [PATCH 4/8] Sigh linter --- src/plots/plots.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plots/plots.js b/src/plots/plots.js index b9a2ae85940..ec47e792d6e 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -683,7 +683,7 @@ plots.clearExpandedTraceDefaultColors = function(expandedTraces) { colorAttrs = []; path = []; - if (!trace || !trace._module) continue; + if(!trace || !trace._module) continue; PlotSchema.crawl(trace._module.attributes, locateExpandedTraceAttrs); From 5f346e4d676c8015026d6bdcf5b0ce49962b33f2 Mon Sep 17 00:00:00 2001 From: Ricky Reusser Date: Mon, 3 Jul 2017 10:16:02 -0500 Subject: [PATCH 5/8] Call clearTraceDefaultColors from groupby --- src/plots/plots.js | 41 +++++++++++--------- src/transforms/groupby.js | 3 ++ test/jasmine/tests/plotschema_test.js | 2 +- test/jasmine/tests/transform_groupby_test.js | 30 ++++++++++++++ 4 files changed, 57 insertions(+), 19 deletions(-) diff --git a/src/plots/plots.js b/src/plots/plots.js index ec47e792d6e..2b6c5e0d85f 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -664,13 +664,12 @@ plots.linkSubplots = function(newFullData, newFullLayout, oldFullData, oldFullLa // supplyDefault'd and inherited as *colors* instead of an actual null // attribute which needs to be supplydefaulted by the individual // expanded traces. -plots.clearExpandedTraceDefaultColors = function(expandedTraces) { - var colorAttrs, path, trace, i, j; +plots.clearTraceDefaultColors = function(trace) { + var colorAttrs, path, i; - // A better check *might* be to explicitly check for a groupby transform - if(expandedTraces.length <= 1) return; - - function locateExpandedTraceAttrs(attr, attrName, attrs, level) { + // This uses weird closure state in order to satisfy the linter rule + // that we can't create functions in a loop. + function locateColorAttrs(attr, attrName, attrs, level) { path[level] = attrName; path.length = level + 1; if(attr.useExpandedTraceDefaultColor) { @@ -678,17 +677,25 @@ plots.clearExpandedTraceDefaultColors = function(expandedTraces) { } } - for(i = 0; i < expandedTraces.length; i++) { - trace = expandedTraces[i]; - colorAttrs = []; - path = []; + path = []; - if(!trace || !trace._module) continue; + // Get the cached colorAttrs: + colorAttrs = trace._module._colorAttrs; - PlotSchema.crawl(trace._module.attributes, locateExpandedTraceAttrs); + // Or else compute and cache the colorAttrs on the module: + if(!colorAttrs) { + trace._module._colorAttrs = colorAttrs = []; + PlotSchema.crawl( + trace._module.attributes, + locateColorAttrs + ); + } - for(j = 0; j < colorAttrs.length; j++) { - Lib.nestedProperty(trace, colorAttrs[j]).set(null); + for(i = 0; i < colorAttrs.length; i++) { + var origprop = Lib.nestedProperty(trace, '_input.' + colorAttrs[i]); + + if(!origprop.get()) { + Lib.nestedProperty(trace, colorAttrs[i]).set(null); } } }; @@ -728,11 +735,9 @@ plots.supplyDataDefaults = function(dataIn, dataOut, layout, fullLayout) { if(fullTrace.transforms && fullTrace.transforms.length) { var expandedTraces = applyTransforms(fullTrace, dataOut, layout, fullLayout); - plots.clearExpandedTraceDefaultColors(expandedTraces); - for(var j = 0; j < expandedTraces.length; j++) { - var expandedTrace = expandedTraces[j], - fullExpandedTrace = plots.supplyTraceDefaults(expandedTrace, cnt, fullLayout, i); + var expandedTrace = expandedTraces[j]; + var fullExpandedTrace = plots.supplyTraceDefaults(expandedTrace, cnt, fullLayout, i); // mutate uid here using parent uid and expanded index // to promote consistency between update calls diff --git a/src/transforms/groupby.js b/src/transforms/groupby.js index ef1b78426b1..1e72fdd084d 100644 --- a/src/transforms/groupby.js +++ b/src/transforms/groupby.js @@ -10,6 +10,7 @@ var Lib = require('../lib'); var PlotSchema = require('../plot_api/plot_schema'); +var Plots = require('../plots/plots'); exports.moduleType = 'transform'; @@ -172,6 +173,8 @@ function transformOne(trace, state) { newTrace.name = groupName; + Plots.clearTraceDefaultColors(newTrace); + // there's no need to coerce styleLookup[groupName] here // as another round of supplyDefaults is done on the transformed traces newTrace = Lib.extendDeepNoArrays(newTrace, styleLookup[groupName] || {}); diff --git a/test/jasmine/tests/plotschema_test.js b/test/jasmine/tests/plotschema_test.js index eba5226cf31..e2adec25359 100644 --- a/test/jasmine/tests/plotschema_test.js +++ b/test/jasmine/tests/plotschema_test.js @@ -76,7 +76,7 @@ describe('plot schema', function() { var valObject = valObjects[attr.valType], opts = valObject.requiredOpts .concat(valObject.otherOpts) - .concat(['valType', 'description', 'role', 'editType']); + .concat(['valType', 'description', 'role', 'editType', 'useExpandedTraceDefaultColor']); Object.keys(attr).forEach(function(key) { expect(opts.indexOf(key) !== -1).toBe(true, key, attr); diff --git a/test/jasmine/tests/transform_groupby_test.js b/test/jasmine/tests/transform_groupby_test.js index 049a2856b5f..443ef77cf79 100644 --- a/test/jasmine/tests/transform_groupby_test.js +++ b/test/jasmine/tests/transform_groupby_test.js @@ -1,4 +1,5 @@ var Plotly = require('@lib/index'); +var Plots = require('@src/plots/plots'); var Lib = require('@src/lib'); var createGraphDiv = require('../assets/create_graph_div'); @@ -236,9 +237,38 @@ describe('groupby', function() { done(); }); }); + }); + + describe('many-to-many transforms', function() { + it('varies the color for each expanded trace', function() { + var uniqueColors = {}; + var dataOut = []; + var dataIn = [{ + y: [1, 2, 3], + transforms: [ + {type: 'filter', operation: '<', value: 4}, + {type: 'groupby', groups: ['a', 'b', 'c']} + ] + }, { + y: [4, 5, 6], + transforms: [ + {type: 'filter', operation: '<', value: 4}, + {type: 'groupby', groups: ['a', 'b', 'b']} + ] + }]; + Plots.supplyDataDefaults(dataIn, dataOut, {}, {}); + + for(var i = 0; i < dataOut.length; i++) { + uniqueColors[dataOut[i].marker.color] = true; + } + + // Confirm that five total colors exist: + expect(Object.keys(uniqueColors).length).toEqual(5); + }); }); + // these tests can be shortened, once the meaning of edge cases gets clarified describe('symmetry/degeneracy testing of one-to-many transforms on arbitrary arrays where there is no grouping (implicit 1):', function() { 'use strict'; From c8a26077ddeab6d5a3636dfab916f4db6340968c Mon Sep 17 00:00:00 2001 From: Ricky Reusser Date: Mon, 3 Jul 2017 10:22:41 -0500 Subject: [PATCH 6/8] Reorganize clearExpandedTraceDefaultColors code --- src/plots/plots.js | 15 ++++++---- src/transforms/groupby.js | 2 +- .../tests/transform_expanded_trace_test.js | 30 ------------------- test/jasmine/tests/transform_groupby_test.js | 28 +++++++++++++++++ 4 files changed, 38 insertions(+), 37 deletions(-) delete mode 100644 test/jasmine/tests/transform_expanded_trace_test.js diff --git a/src/plots/plots.js b/src/plots/plots.js index 2b6c5e0d85f..31358fe8b4b 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -659,12 +659,15 @@ plots.linkSubplots = function(newFullData, newFullLayout, oldFullData, oldFullLa } }; -// This function clears defaults between the first and second pass of -// supplyDefaults. It exists because otherwise null attributes are -// supplyDefault'd and inherited as *colors* instead of an actual null -// attribute which needs to be supplydefaulted by the individual -// expanded traces. -plots.clearTraceDefaultColors = function(trace) { +// This function clears any attributes with useExpandedTraceDefaultColor +// set to true in the plot schema. This is needed because groupby (which +// is the only transform for which this currently applies) supplies parent +// trace defaults, then expanded trace defaults. The result is that `null` +// colors are default-supplied and inherited as a color instead of a null. +// The result is that expanded trace default colors have no effect, with +// the final result that groups are indistinguishable. This function clears +// those colors so that individual groupby groups get unique colors. +plots.clearExpandedTraceDefaultColors = function(trace) { var colorAttrs, path, i; // This uses weird closure state in order to satisfy the linter rule diff --git a/src/transforms/groupby.js b/src/transforms/groupby.js index 1e72fdd084d..6590c428c26 100644 --- a/src/transforms/groupby.js +++ b/src/transforms/groupby.js @@ -173,7 +173,7 @@ function transformOne(trace, state) { newTrace.name = groupName; - Plots.clearTraceDefaultColors(newTrace); + Plots.clearExpandedTraceDefaultColors(newTrace); // there's no need to coerce styleLookup[groupName] here // as another round of supplyDefaults is done on the transformed traces diff --git a/test/jasmine/tests/transform_expanded_trace_test.js b/test/jasmine/tests/transform_expanded_trace_test.js deleted file mode 100644 index 3e0ea0fc930..00000000000 --- a/test/jasmine/tests/transform_expanded_trace_test.js +++ /dev/null @@ -1,30 +0,0 @@ -var Plots = require('@src/plots/plots'); - -describe('groupby', function() { - it('varies the color for each expanded trace', function() { - var uniqueColors = {}; - var dataOut = []; - var dataIn = [{ - y: [1, 2, 3], - transforms: [ - {type: 'filter', operation: '<', value: 4}, - {type: 'groupby', groups: ['a', 'b', 'c']} - ] - }, { - y: [4, 5, 6], - transforms: [ - {type: 'filter', operation: '<', value: 4}, - {type: 'groupby', groups: ['a', 'b', 'b']} - ] - }]; - - Plots.supplyDataDefaults(dataIn, dataOut, {}, {}); - - for(var i = 0; i < dataOut.length; i++) { - uniqueColors[dataOut[i].marker.color] = true; - } - - // Confirm that five total colors exist: - expect(Object.keys(uniqueColors).length).toEqual(5); - }); -}); diff --git a/test/jasmine/tests/transform_groupby_test.js b/test/jasmine/tests/transform_groupby_test.js index 443ef77cf79..94b3539ee5c 100644 --- a/test/jasmine/tests/transform_groupby_test.js +++ b/test/jasmine/tests/transform_groupby_test.js @@ -692,6 +692,34 @@ describe('groupby', function() { it('passes with no groups', test(mockData0)); it('passes with empty groups', test(mockData1)); it('passes with falsey groups', test(mockData2)); + }); + + describe('expanded trace coloring', function() { + it('assigns unique colors to each group', function() { + var uniqueColors = {}; + var dataOut = []; + var dataIn = [{ + y: [1, 2, 3], + transforms: [ + {type: 'filter', operation: '<', value: 4}, + {type: 'groupby', groups: ['a', 'b', 'c']} + ] + }, { + y: [4, 5, 6], + transforms: [ + {type: 'filter', operation: '<', value: 4}, + {type: 'groupby', groups: ['a', 'b', 'b']} + ] + }]; + Plots.supplyDataDefaults(dataIn, dataOut, {}, {}); + + for(var i = 0; i < dataOut.length; i++) { + uniqueColors[dataOut[i].marker.color] = true; + } + + // Confirm that five total colors exist: + expect(Object.keys(uniqueColors).length).toEqual(5); + }); }); }); From 2ceac06180bff32e886aad7f6ffc24afbcfcf211 Mon Sep 17 00:00:00 2001 From: Ricky Reusser Date: Thu, 6 Jul 2017 11:35:54 -0500 Subject: [PATCH 7/8] Make jasmine test more specific; remove mock --- test/image/baselines/groupby_coloring.png | Bin 24939 -> 0 bytes test/image/mocks/groupby_coloring.json | 23 ------------------- test/jasmine/tests/transform_groupby_test.js | 13 +++++++---- 3 files changed, 9 insertions(+), 27 deletions(-) delete mode 100644 test/image/baselines/groupby_coloring.png delete mode 100644 test/image/mocks/groupby_coloring.json diff --git a/test/image/baselines/groupby_coloring.png b/test/image/baselines/groupby_coloring.png deleted file mode 100644 index 5f9e9be9455219e6e9c4aaa2feef24cae0defd1a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24939 zcmeIbc{tQ<-#;!9C815ombBR_J0S^0QYm6Em5^nOtW%azw#rt7kSI~siLr!1jY`%D zS%+b=8_d|oIy1j>R99Wsec#9Zd!FxeJm2s09M>PN!{M4aKcCO}Ip5ps^?JX%f9|Y4 z_a?zjEG#VCr%#={z{0|cVPRP}$GHLgrO%w+$HF4Ua{8q9MTGTaGS^d)fi4E>^RTbQ zo|n(Jh89QaW@g+t{|fWkF|*L(f!alb69Z?m4#{jCIk5H7myU#U7aa&4#Hb6}PjR{m zv1UOZ_H9)?HN&tp!Fk2>PH5tK78>AQDP&(w&CX;}+0>FX8s|Uq%*PHd1FiHe&DUmO zy1!KadVT~aSO}M|0LL~=lMy%zVkLI*Ir-L&!h0^-n%SpGvWc1;p&7#yl<5Id|UlzllnSm zhU&NF(`MqUoD~%nm7Ln8eW+dbG0+D6-8WA@KP`?K!*7F}tFKkQ`RydBEjymp&!Kfn zi(E(cR}%`JO1lt&d901cW@&)!Ty3B{e4 zA~RjtTq>9B%BRXm;Hg)tV2z3$&NqD}^XHz_i1;+@GfI3cjeI+t=;S^GpDJJ8f0xU-UZ%wI`S2YMkte4f zlF9hR>4C{=9<4KW&uSuto!}Moah_y?#v_Xq&#AAOCIwIj%c|8OxyhxGloB(jUz_Ey za_5!xD@!<}3j|ga>o-%o|1_a`i+Y1)ouCWPF-kx(lD_0s&8>VnMbj^FkkTK>v#;dE zE8`RwjoWi?dcFI4w`k0Lxvtbx(np)}rhXEF?;V)vDRrMIiqfL12#kpMOkC9$_I$jw zx$h%C)H&Yzb|#I;gM5=|^2(yzYtosNTT%>N{l;G_ZRIeVE@_Rk?@HFjJ=1`#OqLkW zJdVWACVLZZDLH<9{gOkjYSj(fWj|(hh_+~mzst@Qoll#DBMlL`U|(yGhTI(ZB-tP& z>p&TBrJ^u14~pP@gi8863V)<#w@ro5!r55V_PMEYa(C_i6lb(O⁣uWJ#ZkRhh@> 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 Date: Fri, 7 Jul 2017 17:33:38 -0400 Subject: [PATCH 8/8] replace `useExpandedTraceDefaultColor` with smarter DRYer logic - now, Plots.clearExpandedTraceDefaultColors clears any trace attributes with valType: color and no set dflt. --- src/components/colorscale/color_attributes.js | 3 +-- src/plots/plots.js | 6 +++--- src/traces/scatter/attributes.js | 4 +--- test/jasmine/tests/plotschema_test.js | 2 +- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/components/colorscale/color_attributes.js b/src/components/colorscale/color_attributes.js index 557715374ef..9c8f6cdb065 100644 --- a/src/components/colorscale/color_attributes.js +++ b/src/components/colorscale/color_attributes.js @@ -23,8 +23,7 @@ module.exports = function makeColorScaleAttributes(context) { ' or an array of numbers that are mapped to the colorscale', ' relative to the max and min values of the array or relative to', ' `cmin` and `cmax` if set.' - ].join(''), - useExpandedTraceDefaultColor: true + ].join('') }, colorscale: extendDeep({}, colorScaleAttributes.colorscale, { description: [ diff --git a/src/plots/plots.js b/src/plots/plots.js index 31358fe8b4b..510b1752550 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -659,8 +659,8 @@ plots.linkSubplots = function(newFullData, newFullLayout, oldFullData, oldFullLa } }; -// This function clears any attributes with useExpandedTraceDefaultColor -// set to true in the plot schema. This is needed because groupby (which +// This function clears any trace attributes with valType: color and +// no set dflt filed in the plot schema. This is needed because groupby (which // is the only transform for which this currently applies) supplies parent // trace defaults, then expanded trace defaults. The result is that `null` // colors are default-supplied and inherited as a color instead of a null. @@ -675,7 +675,7 @@ plots.clearExpandedTraceDefaultColors = function(trace) { function locateColorAttrs(attr, attrName, attrs, level) { path[level] = attrName; path.length = level + 1; - if(attr.useExpandedTraceDefaultColor) { + if(attr.valType === 'color' && attr.dflt === undefined) { colorAttrs.push(path.join('.')); } } diff --git a/src/traces/scatter/attributes.js b/src/traces/scatter/attributes.js index 52325bb8d20..726bb94bfef 100644 --- a/src/traces/scatter/attributes.js +++ b/src/traces/scatter/attributes.js @@ -124,8 +124,7 @@ module.exports = { color: { valType: 'color', role: 'style', - description: 'Sets the line color.', - useExpandedTraceDefaultColor: true + description: 'Sets the line color.' }, width: { valType: 'number', @@ -204,7 +203,6 @@ module.exports = { fillcolor: { valType: 'color', role: 'style', - useExpandedTraceDefaultColor: true, description: [ 'Sets the fill color.', 'Defaults to a half-transparent variant of the line color,', diff --git a/test/jasmine/tests/plotschema_test.js b/test/jasmine/tests/plotschema_test.js index e2adec25359..eba5226cf31 100644 --- a/test/jasmine/tests/plotschema_test.js +++ b/test/jasmine/tests/plotschema_test.js @@ -76,7 +76,7 @@ describe('plot schema', function() { var valObject = valObjects[attr.valType], opts = valObject.requiredOpts .concat(valObject.otherOpts) - .concat(['valType', 'description', 'role', 'editType', 'useExpandedTraceDefaultColor']); + .concat(['valType', 'description', 'role', 'editType']); Object.keys(attr).forEach(function(key) { expect(opts.indexOf(key) !== -1).toBe(true, key, attr);