diff --git a/src/traces/parcoords/attributes.js b/src/traces/parcoords/attributes.js index d89456a07ab..00bdb452f51 100644 --- a/src/traces/parcoords/attributes.js +++ b/src/traces/parcoords/attributes.js @@ -141,6 +141,23 @@ module.exports = { description: 'The dimensions (variables) of the parallel coordinates chart. 2..60 dimensions are supported.' }), + unselected: { + line: { + color: { + valType: 'color', + dflt: '#777', + role: 'style', + editType: 'plot', + description: [ + 'Sets the color of lines in the background', + 'i.e. unselected lines.' + ].join(' ') + }, + editType: 'plot' + }, + editType: 'plot' + }, + line: extendFlat({editType: 'calc'}, colorScaleAttrs('line', { // the default autocolorscale isn't quite usable for parcoords due to context ambiguity around 0 (grey, off-white) diff --git a/src/traces/parcoords/constants.js b/src/traces/parcoords/constants.js index 3467ac057ec..bda7fbccb09 100644 --- a/src/traces/parcoords/constants.js +++ b/src/traces/parcoords/constants.js @@ -19,7 +19,6 @@ module.exports = { layers: ['contextLineLayer', 'focusLineLayer', 'pickLineLayer'], axisTitleOffset: 28, axisExtentOffset: 10, - deselectedLineColor: '#777', bar: { width: 4, // Visible width of the filter bar captureWidth: 10, // Mouse-sensitive width for interaction (Fitts law) diff --git a/src/traces/parcoords/defaults.js b/src/traces/parcoords/defaults.js index 33e769d2a86..5f63661ff30 100644 --- a/src/traces/parcoords/defaults.js +++ b/src/traces/parcoords/defaults.js @@ -115,4 +115,5 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout coerce('labelangle'); coerce('labelside'); + coerce('unselected.line.color'); }; diff --git a/src/traces/parcoords/parcoords.js b/src/traces/parcoords/parcoords.js index d314f9ad4c8..407f47b39f4 100644 --- a/src/traces/parcoords/parcoords.js +++ b/src/traces/parcoords/parcoords.js @@ -154,7 +154,7 @@ function model(layout, d, i) { var trace = cd0.trace; var lineColor = helpers.convertTypedArray(cd0.lineColor); var line = trace.line; - var deselectedLines = {color: rgba(c.deselectedLineColor)}; + var deselectedLines = {color: rgba(trace.unselected.line.color)}; var cOpts = Colorscale.extractOpts(line); var cscale = cOpts.reversescale ? Colorscale.flipScale(cd0.cscale) : cd0.cscale; var domain = trace.domain; diff --git a/test/jasmine/tests/parcoords_test.js b/test/jasmine/tests/parcoords_test.js index 7718ba64f2c..072eb8f5868 100644 --- a/test/jasmine/tests/parcoords_test.js +++ b/test/jasmine/tests/parcoords_test.js @@ -856,6 +856,12 @@ describe('parcoords Lifecycle methods', function() { }], line: {color: 'blue'} }], { + margin: { + t: 0, + b: 0, + l: 0, + r: 0 + }, width: 300, height: 200 }) @@ -888,6 +894,12 @@ describe('parcoords Lifecycle methods', function() { }], line: {color: 'blue'} }], { + margin: { + t: 0, + b: 0, + l: 0, + r: 0 + }, width: 300, height: 200 }) @@ -910,6 +922,130 @@ describe('parcoords Lifecycle methods', function() { .catch(failTest) .then(done); }); + + it('@gl unselected.line.color `Plotly.restyle` should change context layer line.color', function(done) { + var testLayer = '.gl-canvas-context'; + + var list1 = []; + var list2 = []; + for(var i = 0; i <= 100; i++) { + list1[i] = i; + list2[i] = 100 - i; + } + + Plotly.plot(gd, [{ + type: 'parcoords', + dimensions: [{ + constraintrange: [1, 10], + values: list1 + }, { + values: list2 + }], + line: {color: '#0F0'}, + unselected: {line: {color: '#F00'}} + }], { + margin: { + t: 0, + b: 0, + l: 0, + r: 0 + }, + width: 300, + height: 200 + }) + .then(function() { + var rgb = getAvgPixelByChannel(testLayer); + expect(rgb[0]).not.toBe(0, 'red'); + expect(rgb[1]).toBe(0, 'no green'); + expect(rgb[2]).toBe(0, 'no blue'); + + return Plotly.restyle(gd, 'unselected.line.color', '#00F'); + }) + .then(function() { + var rgb = getAvgPixelByChannel(testLayer); + expect(rgb[0]).toBe(0, 'no red'); + expect(rgb[1]).toBe(0, 'no green'); + expect(rgb[2]).not.toBe(0, 'blue'); + + return Plotly.restyle(gd, 'unselected.line.color', 'rgba(0,0,0,0)'); + }) + .then(function() { + var rgb = getAvgPixelByChannel(testLayer); + expect(rgb[0]).toBe(0, 'no red'); + expect(rgb[1]).toBe(0, 'no green'); + expect(rgb[2]).toBe(0, 'no blue'); + }) + .catch(failTest) + .then(done); + }); + + it('@gl unselected.line.color `Plotly.react` should change line.color and unselected.line.color', function(done) { + var unselectedLayer = '.gl-canvas-context'; + var selectedLayer = '.gl-canvas-focus'; + + var list1 = []; + var list2 = []; + for(var i = 0; i <= 100; i++) { + list1[i] = i; + list2[i] = 100 - i; + } + + var fig = { + data: [{ + type: 'parcoords', + dimensions: [{ + constraintrange: [1, 10], + values: list1 + }, { + values: list2 + }], + line: {color: '#0F0'}, + unselected: {line: {color: '#F00'}} + }], + layout: { + margin: { + t: 0, + b: 0, + l: 0, + r: 0 + }, + width: 300, + height: 200 + } + }; + + var rgb; + + Plotly.newPlot(gd, fig) + .then(function() { + rgb = getAvgPixelByChannel(unselectedLayer); + expect(rgb[0]).not.toBe(0, 'red'); + expect(rgb[1]).toBe(0, 'no green'); + expect(rgb[2]).toBe(0, 'no blue'); + + rgb = getAvgPixelByChannel(selectedLayer); + expect(rgb[0]).toBe(0, 'no red'); + expect(rgb[1]).not.toBe(0, 'green'); + expect(rgb[2]).toBe(0, 'no blue'); + + fig.data[0].line.color = '#FF0'; + fig.data[0].unselected.line.color = '#00F'; + return Plotly.react(gd, fig); + }) + .then(function() { + rgb = getAvgPixelByChannel(selectedLayer); + expect(rgb[0]).not.toBe(0, 'red'); + expect(rgb[1]).not.toBe(0, 'green'); + expect(rgb[2]).toBe(0, 'no blue'); + + rgb = getAvgPixelByChannel(unselectedLayer); + expect(rgb[0]).toBe(0, 'no red'); + expect(rgb[1]).toBe(0, 'no green'); + expect(rgb[2]).not.toBe(0, 'blue'); + }) + .catch(failTest) + .then(done); + }); }); describe('parcoords basic use', function() {