From 2bd8e75585073b1a567056aec701026160a02a41 Mon Sep 17 00:00:00 2001 From: Cindy Zhang Date: Wed, 1 Nov 2017 19:38:39 -0700 Subject: [PATCH 1/8] adding layout.colorway --- src/plot_api/helpers.js | 5 +++-- src/plot_api/plot_api.js | 6 ++--- src/plots/layout_attributes.js | 3 ++- src/plots/plots.js | 6 ++++- src/traces/pie/calc.js | 8 ++++--- test/image/mocks/layout-colorway.json | 32 +++++++++++++++++++++++++++ 6 files changed, 50 insertions(+), 10 deletions(-) create mode 100644 test/image/mocks/layout-colorway.json diff --git a/src/plot_api/helpers.js b/src/plot_api/helpers.js index e27eff1cb9b..3e9a1258388 100644 --- a/src/plot_api/helpers.js +++ b/src/plot_api/helpers.js @@ -192,7 +192,8 @@ function cleanAxRef(container, attr) { // Make a few changes to the data right away // before it gets used for anything -exports.cleanData = function(data, existingData) { +exports.cleanData = function(data, existingData, colorway) { + var defaultColors = colorway || Color.defaults; // Enforce unique IDs var suids = [], // seen uids --- so we can weed out incoming repeats uids = data.concat(Array.isArray(existingData) ? existingData : []) @@ -229,7 +230,7 @@ exports.cleanData = function(data, existingData) { // error_y.opacity is obsolete - merge into color if(trace.error_y && 'opacity' in trace.error_y) { - var dc = Color.defaults, + var dc = defaultColors, yeColor = trace.error_y.color || (Registry.traceIs(trace, 'bar') ? Color.defaultLine : dc[tracei % dc.length]); trace.error_y.color = Color.addOpacity( diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js index e4a1e83d9bb..eba184f61a9 100644 --- a/src/plot_api/plot_api.js +++ b/src/plot_api/plot_api.js @@ -115,7 +115,7 @@ Plotly.plot = function(gd, data, layout, config) { // if there is already data on the graph, append the new data // if you only want to redraw, pass a non-array for data if(Array.isArray(data)) { - helpers.cleanData(data, gd.data); + helpers.cleanData(data, gd.data, layout.colorway); if(graphWasEmpty) gd.data = data; else gd.data.push.apply(gd.data, data); @@ -587,7 +587,7 @@ Plotly.redraw = function(gd) { throw new Error('This element is not a Plotly plot: ' + gd); } - helpers.cleanData(gd.data, gd.data); + helpers.cleanData(gd.data, gd.data, gd.layout.colorway); helpers.cleanLayout(gd.layout); gd.calcdata = undefined; @@ -1045,7 +1045,7 @@ Plotly.addTraces = function addTraces(gd, traces, newIndices) { return Lib.extendFlat({}, trace); }); - helpers.cleanData(traces, gd.data); + helpers.cleanData(traces, gd.data, gd.layout.colorway); // add the traces to gd.data (no redrawing yet!) for(i = 0; i < traces.length; i++) { diff --git a/src/plots/layout_attributes.js b/src/plots/layout_attributes.js index c3ee025aa75..5437b51fe9a 100644 --- a/src/plots/layout_attributes.js +++ b/src/plots/layout_attributes.js @@ -175,5 +175,6 @@ module.exports = { role: 'info', editType: 'legend', description: 'Determines whether or not a legend is drawn.' - } + }, + colorway: colorAttrs.defaults, }; diff --git a/src/plots/plots.js b/src/plots/plots.js index 7e0973196f6..ce70531faf2 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -454,6 +454,9 @@ plots.supplyDefaults = function(gd) { newFullLayout._initialAutoSizeIsDone = true; + // pass along trace colors + newFullLayout._colorway = newLayout.colorway; + // keep track of how many traces are inputted newFullLayout._dataLength = newData.length; @@ -950,8 +953,9 @@ plots.supplyFrameDefaults = function(frameIn) { }; plots.supplyTraceDefaults = function(traceIn, traceOutIndex, layout, traceInIndex) { + var colorway = layout._colorway || Color.defaults; var traceOut = {}, - defaultColor = Color.defaults[traceOutIndex % Color.defaults.length]; + defaultColor = colorway[traceOutIndex % colorway.length]; function coerce(attr, dflt) { return Lib.coerce(traceIn, traceOut, plots.attributes, attr, dflt); diff --git a/src/traces/pie/calc.js b/src/traces/pie/calc.js index bb425b1a606..e85474be584 100644 --- a/src/traces/pie/calc.js +++ b/src/traces/pie/calc.js @@ -107,7 +107,9 @@ module.exports = function calc(gd, trace) { pt.color = colorMap[pt.label]; } else { - colorMap[pt.label] = pt.color = nextDefaultColor(fullLayout._piedefaultcolorcount); + colorMap[pt.label] = pt.color = nextDefaultColor( + fullLayout._piedefaultcolorcount, + fullLayout._colorway); fullLayout._piedefaultcolorcount++; } } @@ -148,10 +150,10 @@ module.exports = function calc(gd, trace) { */ var pieDefaultColors; -function nextDefaultColor(index) { +function nextDefaultColor(index, colorway) { if(!pieDefaultColors) { // generate this default set on demand (but then it gets saved in the module) - var mainDefaults = Color.defaults; + var mainDefaults = colorway || Color.defaults; pieDefaultColors = mainDefaults.slice(); var i; diff --git a/test/image/mocks/layout-colorway.json b/test/image/mocks/layout-colorway.json new file mode 100644 index 00000000000..6ebd8628331 --- /dev/null +++ b/test/image/mocks/layout-colorway.json @@ -0,0 +1,32 @@ +{ + "data": [ + {"y": [8, 7, 7, 6, 5, 4, 4, 3, 2, 2, 3]}, + {"y": [7, 7, 6, 5, 4, 4, 3, 2, 2, 3, 8]}, + {"y": [7, 6, 5, 4, 4, 3, 2, 2, 3, 8, 7]}, + {"y": [6, 5, 4, 4, 3, 2, 2, 3, 8, 7, 7]}, + {"y": [5, 4, 4, 3, 2, 2, 3, 8, 7, 7, 6]}, + { + "labels": ["a","b","c","c","c","a","d","e"], + "type": "pie", + "domain": {"x": [0, 0.4]}, + "xaxis": "x2", + "yaxis": "y2" + } + ], + "layout": { + "title": "Custom Trace Color Defaults", + "colorway": [ + "#DE5845", + "#E83898", + "#A83DD1", + "#5A38E8", + "#3C71DE" + ], + "xaxis": { + "domain": [0.4, 1] + }, + "yaxis": { + "anchor": "y2" + } + } +} From 4bba9fb1dc1e36ecc272482e8e0adde4841cfa46 Mon Sep 17 00:00:00 2001 From: Cindy Zhang Date: Thu, 2 Nov 2017 07:52:42 -0700 Subject: [PATCH 2/8] reverting changes to cleanData --- src/plot_api/helpers.js | 5 ++--- src/plot_api/plot_api.js | 6 +++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/plot_api/helpers.js b/src/plot_api/helpers.js index 3e9a1258388..e27eff1cb9b 100644 --- a/src/plot_api/helpers.js +++ b/src/plot_api/helpers.js @@ -192,8 +192,7 @@ function cleanAxRef(container, attr) { // Make a few changes to the data right away // before it gets used for anything -exports.cleanData = function(data, existingData, colorway) { - var defaultColors = colorway || Color.defaults; +exports.cleanData = function(data, existingData) { // Enforce unique IDs var suids = [], // seen uids --- so we can weed out incoming repeats uids = data.concat(Array.isArray(existingData) ? existingData : []) @@ -230,7 +229,7 @@ exports.cleanData = function(data, existingData, colorway) { // error_y.opacity is obsolete - merge into color if(trace.error_y && 'opacity' in trace.error_y) { - var dc = defaultColors, + var dc = Color.defaults, yeColor = trace.error_y.color || (Registry.traceIs(trace, 'bar') ? Color.defaultLine : dc[tracei % dc.length]); trace.error_y.color = Color.addOpacity( diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js index eba184f61a9..e4a1e83d9bb 100644 --- a/src/plot_api/plot_api.js +++ b/src/plot_api/plot_api.js @@ -115,7 +115,7 @@ Plotly.plot = function(gd, data, layout, config) { // if there is already data on the graph, append the new data // if you only want to redraw, pass a non-array for data if(Array.isArray(data)) { - helpers.cleanData(data, gd.data, layout.colorway); + helpers.cleanData(data, gd.data); if(graphWasEmpty) gd.data = data; else gd.data.push.apply(gd.data, data); @@ -587,7 +587,7 @@ Plotly.redraw = function(gd) { throw new Error('This element is not a Plotly plot: ' + gd); } - helpers.cleanData(gd.data, gd.data, gd.layout.colorway); + helpers.cleanData(gd.data, gd.data); helpers.cleanLayout(gd.layout); gd.calcdata = undefined; @@ -1045,7 +1045,7 @@ Plotly.addTraces = function addTraces(gd, traces, newIndices) { return Lib.extendFlat({}, trace); }); - helpers.cleanData(traces, gd.data, gd.layout.colorway); + helpers.cleanData(traces, gd.data); // add the traces to gd.data (no redrawing yet!) for(i = 0; i < traces.length; i++) { From 0a5b3a3bbd684930f592f90b6518e69f4044f669 Mon Sep 17 00:00:00 2001 From: Cindy Zhang Date: Thu, 2 Nov 2017 07:55:22 -0700 Subject: [PATCH 3/8] updating color attribute description --- src/plots/layout_attributes.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/plots/layout_attributes.js b/src/plots/layout_attributes.js index 5437b51fe9a..e441fd3e217 100644 --- a/src/plots/layout_attributes.js +++ b/src/plots/layout_attributes.js @@ -176,5 +176,11 @@ module.exports = { editType: 'legend', description: 'Determines whether or not a legend is drawn.' }, - colorway: colorAttrs.defaults, + colorway: { + valType: 'any', + dflt: colorAttrs.defaults, + role: 'style', + editType: 'plot', + description: 'Sets the default trace colors.' + }, }; From 7c7a5a7c38faac29df1c56902b8e3c41ce25ff48 Mon Sep 17 00:00:00 2001 From: Cindy Zhang Date: Thu, 2 Nov 2017 20:12:59 -0700 Subject: [PATCH 4/8] coercing colorway --- src/lib/coerce.js | 16 ++++++++++++++++ src/plots/layout_attributes.js | 2 +- src/plots/plots.js | 7 +++---- src/traces/pie/calc.js | 2 +- 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/lib/coerce.js b/src/lib/coerce.js index 5810311ed99..45f9ea360cf 100644 --- a/src/lib/coerce.js +++ b/src/lib/coerce.js @@ -141,6 +141,22 @@ exports.valObjectMeta = { else propOut.set(dflt); } }, + colorlist: { + description: [ + 'A list of colors.', + 'Must be an {array} containing valid colors.', + ].join(' '), + requiredOpts: [], + otherOpts: ['dflt'], + coerceFunction: function(v, propOut, dflt) { + function isColor(color) { + return tinycolor(color).isValid(); + } + if(!Array.isArray(v)) propOut.set(dflt); + else if(v.every(isColor)) propOut.set(v); + else propOut.set(dflt); + } + }, colorscale: { description: [ 'A Plotly colorscale either picked by a name:', diff --git a/src/plots/layout_attributes.js b/src/plots/layout_attributes.js index e441fd3e217..a3fbb0ff239 100644 --- a/src/plots/layout_attributes.js +++ b/src/plots/layout_attributes.js @@ -177,7 +177,7 @@ module.exports = { description: 'Determines whether or not a legend is drawn.' }, colorway: { - valType: 'any', + valType: 'colorlist', dflt: colorAttrs.defaults, role: 'style', editType: 'plot', diff --git a/src/plots/plots.js b/src/plots/plots.js index ce70531faf2..c9298432864 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -454,9 +454,6 @@ plots.supplyDefaults = function(gd) { newFullLayout._initialAutoSizeIsDone = true; - // pass along trace colors - newFullLayout._colorway = newLayout.colorway; - // keep track of how many traces are inputted newFullLayout._dataLength = newData.length; @@ -953,7 +950,7 @@ plots.supplyFrameDefaults = function(frameIn) { }; plots.supplyTraceDefaults = function(traceIn, traceOutIndex, layout, traceInIndex) { - var colorway = layout._colorway || Color.defaults; + var colorway = layout.colorway || Color.defaults; var traceOut = {}, defaultColor = colorway[traceOutIndex % colorway.length]; @@ -1142,6 +1139,8 @@ plots.supplyLayoutGlobalDefaults = function(layoutIn, layoutOut) { coerce('separators'); coerce('hidesources'); + coerce('colorway'); + Registry.getComponentMethod( 'calendars', 'handleDefaults' diff --git a/src/traces/pie/calc.js b/src/traces/pie/calc.js index e85474be584..fb7ed222cb9 100644 --- a/src/traces/pie/calc.js +++ b/src/traces/pie/calc.js @@ -109,7 +109,7 @@ module.exports = function calc(gd, trace) { else { colorMap[pt.label] = pt.color = nextDefaultColor( fullLayout._piedefaultcolorcount, - fullLayout._colorway); + fullLayout.colorway); fullLayout._piedefaultcolorcount++; } } From 6de8f2e116e121e951352d6d371d31bdc380a343 Mon Sep 17 00:00:00 2001 From: Cindy Zhang Date: Thu, 2 Nov 2017 21:21:12 -0700 Subject: [PATCH 5/8] fixing issue where setting colorway affected all subsequent pie charts --- src/plots/plots.js | 1 + src/traces/pie/calc.js | 36 +++++++++++++++++++-------- test/image/mocks/layout-colorway.json | 2 +- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/plots/plots.js b/src/plots/plots.js index c9298432864..b11384b869f 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -2174,6 +2174,7 @@ plots.doCalcdata = function(gd, traces) { // for sharing colors across pies (and for legend) fullLayout._piecolormap = {}; + fullLayout._piecolorway = []; fullLayout._piedefaultcolorcount = 0; // If traces were specified and this trace was not included, diff --git a/src/traces/pie/calc.js b/src/traces/pie/calc.js index fb7ed222cb9..6fdd5ea2213 100644 --- a/src/traces/pie/calc.js +++ b/src/traces/pie/calc.js @@ -21,13 +21,20 @@ module.exports = function calc(gd, trace) { var colors = trace.marker.colors; var cd = []; var fullLayout = gd._fullLayout; + var colorWay = fullLayout.colorway; var colorMap = fullLayout._piecolormap; + var pieColorWay = fullLayout._piecolorway; var allThisTraceLabels = {}; var vTotal = 0; var hiddenLabels = fullLayout.hiddenlabels || []; var i, v, label, hidden, pt; + if(!pieColorWay.length && colorWay !== Color.defaults) { + pieColorWay.push.apply(pieColorWay, colorWay); + generateDefaultColors(colorWay, pieColorWay); + } + if(trace.dlabel) { labels = new Array(vals.length); for(i = 0; i < vals.length; i++) { @@ -109,7 +116,8 @@ module.exports = function calc(gd, trace) { else { colorMap[pt.label] = pt.color = nextDefaultColor( fullLayout._piedefaultcolorcount, - fullLayout.colorway); + pieColorWay + ); fullLayout._piedefaultcolorcount++; } } @@ -150,22 +158,28 @@ module.exports = function calc(gd, trace) { */ var pieDefaultColors; -function nextDefaultColor(index, colorway) { +function nextDefaultColor(index, pieColorWay) { if(!pieDefaultColors) { // generate this default set on demand (but then it gets saved in the module) - var mainDefaults = colorway || Color.defaults; + var mainDefaults = Color.defaults; pieDefaultColors = mainDefaults.slice(); + generateDefaultColors(mainDefaults, pieDefaultColors); + } + + var pieColors = pieColorWay.length ? pieColorWay : pieDefaultColors; + return pieColors[index % pieColors.length]; +} - var i; +function generateDefaultColors(colorList, pieColors) { + var i; - for(i = 0; i < mainDefaults.length; i++) { - pieDefaultColors.push(tinycolor(mainDefaults[i]).lighten(20).toHexString()); - } + for(i = 0; i < colorList.length; i++) { + pieColors.push(tinycolor(colorList[i]).lighten(20).toHexString()); + } - for(i = 0; i < Color.defaults.length; i++) { - pieDefaultColors.push(tinycolor(mainDefaults[i]).darken(20).toHexString()); - } + for(i = 0; i < colorList.length; i++) { + pieColors.push(tinycolor(colorList[i]).darken(20).toHexString()); } - return pieDefaultColors[index % pieDefaultColors.length]; + return pieColors; } diff --git a/test/image/mocks/layout-colorway.json b/test/image/mocks/layout-colorway.json index 6ebd8628331..d6a16d389e7 100644 --- a/test/image/mocks/layout-colorway.json +++ b/test/image/mocks/layout-colorway.json @@ -6,7 +6,7 @@ {"y": [6, 5, 4, 4, 3, 2, 2, 3, 8, 7, 7]}, {"y": [5, 4, 4, 3, 2, 2, 3, 8, 7, 7, 6]}, { - "labels": ["a","b","c","c","c","a","d","e"], + "labels": ["a","b","c","c","c","a","d","e","f","f","g","h"], "type": "pie", "domain": {"x": [0, 0.4]}, "xaxis": "x2", From 36665dbce6a65dfd2bc328c917ab210996ba2e22 Mon Sep 17 00:00:00 2001 From: Cindy Zhang Date: Thu, 2 Nov 2017 21:30:08 -0700 Subject: [PATCH 6/8] cleaner checks with null initial piecolorway --- src/plots/plots.js | 2 +- src/traces/pie/calc.js | 17 ++++++++--------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/plots/plots.js b/src/plots/plots.js index b11384b869f..145f08c4f4f 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -2174,7 +2174,7 @@ plots.doCalcdata = function(gd, traces) { // for sharing colors across pies (and for legend) fullLayout._piecolormap = {}; - fullLayout._piecolorway = []; + fullLayout._piecolorway = null; fullLayout._piedefaultcolorcount = 0; // If traces were specified and this trace was not included, diff --git a/src/traces/pie/calc.js b/src/traces/pie/calc.js index 6fdd5ea2213..8e31767d917 100644 --- a/src/traces/pie/calc.js +++ b/src/traces/pie/calc.js @@ -23,16 +23,14 @@ module.exports = function calc(gd, trace) { var fullLayout = gd._fullLayout; var colorWay = fullLayout.colorway; var colorMap = fullLayout._piecolormap; - var pieColorWay = fullLayout._piecolorway; var allThisTraceLabels = {}; var vTotal = 0; var hiddenLabels = fullLayout.hiddenlabels || []; var i, v, label, hidden, pt; - if(!pieColorWay.length && colorWay !== Color.defaults) { - pieColorWay.push.apply(pieColorWay, colorWay); - generateDefaultColors(colorWay, pieColorWay); + if(!fullLayout._piecolorway && colorWay !== Color.defaults) { + fullLayout._piecolorway = generateDefaultColors(colorWay); } if(trace.dlabel) { @@ -116,7 +114,7 @@ module.exports = function calc(gd, trace) { else { colorMap[pt.label] = pt.color = nextDefaultColor( fullLayout._piedefaultcolorcount, - pieColorWay + fullLayout._piecolorway ); fullLayout._piedefaultcolorcount++; } @@ -162,17 +160,18 @@ function nextDefaultColor(index, pieColorWay) { if(!pieDefaultColors) { // generate this default set on demand (but then it gets saved in the module) var mainDefaults = Color.defaults; - pieDefaultColors = mainDefaults.slice(); - generateDefaultColors(mainDefaults, pieDefaultColors); + pieDefaultColors = generateDefaultColors(mainDefaults); } - var pieColors = pieColorWay.length ? pieColorWay : pieDefaultColors; + var pieColors = pieColorWay || pieDefaultColors; return pieColors[index % pieColors.length]; } -function generateDefaultColors(colorList, pieColors) { +function generateDefaultColors(colorList) { var i; + var pieColors = colorList.slice(); + for(i = 0; i < colorList.length; i++) { pieColors.push(tinycolor(colorList[i]).lighten(20).toHexString()); } From a4b828f7c36ea16017ebff36d2a644086f24f011 Mon Sep 17 00:00:00 2001 From: Cindy Zhang Date: Thu, 2 Nov 2017 21:50:39 -0700 Subject: [PATCH 7/8] pie colors are updated during colorway relayout --- src/plots/layout_attributes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plots/layout_attributes.js b/src/plots/layout_attributes.js index a3fbb0ff239..7c23ffd77e8 100644 --- a/src/plots/layout_attributes.js +++ b/src/plots/layout_attributes.js @@ -180,7 +180,7 @@ module.exports = { valType: 'colorlist', dflt: colorAttrs.defaults, role: 'style', - editType: 'plot', + editType: 'calc', description: 'Sets the default trace colors.' }, }; From 9219ee9741b7c21be00caba53260881c1c0ef7d5 Mon Sep 17 00:00:00 2001 From: Cindy Zhang Date: Thu, 2 Nov 2017 22:09:51 -0700 Subject: [PATCH 8/8] adding test for pie relayout colorway --- test/jasmine/tests/pie_test.js | 41 ++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/test/jasmine/tests/pie_test.js b/test/jasmine/tests/pie_test.js index 1f4e109a18f..a1e688e2467 100644 --- a/test/jasmine/tests/pie_test.js +++ b/test/jasmine/tests/pie_test.js @@ -546,3 +546,44 @@ describe('Test event data of interactions on a pie plot:', function() { }); }); }); + +describe('pie relayout', function() { + var gd; + + beforeEach(function() { gd = createGraphDiv(); }); + + afterEach(destroyGraphDiv); + + it('will update colors when colorway is updated', function(done) { + var originalColors = [ + 'rgb(255,0,0)', + 'rgb(0,255,0)', + 'rgb(0,0,255)', + ]; + + var relayoutColors = [ + 'rgb(255,255,0)', + 'rgb(0,255,255)', + 'rgb(255,0,255)', + ]; + + function checkRelayoutColor(d, i) { + expect(this.style.fill.replace(/\s/g, '')).toBe(relayoutColors[i]); + } + + Plotly.newPlot(gd, [{ + labels: ['a', 'b', 'c', 'a', 'b', 'a'], + type: 'pie' + }], { + colorway: originalColors + }) + .then(function() { + return Plotly.relayout(gd, 'colorway', relayoutColors); + }) + .then(function() { + var slices = d3.selectAll('.slice path'); + slices.each(checkRelayoutColor); + }) + .then(done); + }); +});