From a03e4e619ad8244ff700ff844811f196f9bbd1a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Tue, 14 May 2019 11:28:04 -0400 Subject: [PATCH 1/4] add legend itemclick and itemdoubleclick attrs - both enumerated with values 'toggle', 'focus', false --- src/components/legend/attributes.js | 27 ++++++++++++ src/components/legend/defaults.js | 3 ++ src/components/legend/handle_click.js | 38 ++++++++++------ test/jasmine/tests/legend_test.js | 63 ++++++++++++++++++++++++++- 4 files changed, 117 insertions(+), 14 deletions(-) diff --git a/src/components/legend/attributes.js b/src/components/legend/attributes.js index 1a11eda29c9..dde8946dc01 100644 --- a/src/components/legend/attributes.js +++ b/src/components/legend/attributes.js @@ -90,6 +90,33 @@ module.exports = { ].join(' ') }, + itemclick: { + valType: 'enumerated', + values: ['toggle', 'focus', false], + dflt: 'toggle', + role: 'info', + editType: 'legend', + description: [ + 'Determines the behavior on legend item click.', + '*toggle* toggles the visible of the item clicked on the graph.', + '*focus* makes the clicked item the sole visible item on the graph.', + '*false* disable legend item click interactions.' + ].join(' ') + }, + itemdoubleclick: { + valType: 'enumerated', + values: ['toggle', 'focus', false], + dflt: 'focus', + role: 'info', + editType: 'legend', + description: [ + 'Determines the behavior on legend item double-click.', + '*toggle* toggles the visible of the item clicked on the graph.', + '*focus* makes the clicked item the sole visible item on the graph.', + '*false* disable legend item double-click interactions.' + ].join(' ') + }, + x: { valType: 'number', min: -2, diff --git a/src/components/legend/defaults.js b/src/components/legend/defaults.js index c56da87077a..c08fdb2ec8d 100644 --- a/src/components/legend/defaults.js +++ b/src/components/legend/defaults.js @@ -104,6 +104,9 @@ module.exports = function legendDefaults(layoutIn, layoutOut, fullData) { coerce('itemsizing'); + coerce('itemclick'); + coerce('itemdoubleclick'); + coerce('x', defaultX); coerce('xanchor', defaultXAnchor); coerce('y', defaultY); diff --git a/src/components/legend/handle_click.js b/src/components/legend/handle_click.js index 45fefc951d7..780cc1d8e2f 100644 --- a/src/components/legend/handle_click.js +++ b/src/components/legend/handle_click.js @@ -14,10 +14,29 @@ var Registry = require('../../registry'); var SHOWISOLATETIP = true; module.exports = function handleClick(g, gd, numClicks) { + var fullLayout = gd._fullLayout; + if(gd._dragged || gd._editing) return; - var hiddenSlices = gd._fullLayout.hiddenlabels ? - gd._fullLayout.hiddenlabels.slice() : + var itemClick = fullLayout.legend.itemclick; + var itemDoubleClick = fullLayout.legend.itemdoubleclick; + + if(numClicks === 1 && itemClick === 'toggle' && itemDoubleClick === 'focus' && + SHOWISOLATETIP && gd.data && gd._context.showTips + ) { + Lib.notifier(Lib._(gd, 'Double-click on legend to isolate one trace'), 'long'); + SHOWISOLATETIP = false; + } else { + SHOWISOLATETIP = false; + } + + var mode; + if(numClicks === 1) mode = itemClick; + else if(numClicks === 2) mode = itemDoubleClick; + if(!mode) return; + + var hiddenSlices = fullLayout.hiddenlabels ? + fullLayout.hiddenlabels.slice() : []; var legendItem = g.data()[0][0]; @@ -85,21 +104,14 @@ module.exports = function handleClick(g, gd, numClicks) { } } - if(numClicks === 1 && SHOWISOLATETIP && gd.data && gd._context.showTips) { - Lib.notifier(Lib._(gd, 'Double-click on legend to isolate one trace'), 'long'); - SHOWISOLATETIP = false; - } else { - SHOWISOLATETIP = false; - } - if(Registry.traceIs(fullTrace, 'pie')) { var thisLabel = legendItem.label; var thisLabelIndex = hiddenSlices.indexOf(thisLabel); - if(numClicks === 1) { + if(mode === 'toggle') { if(thisLabelIndex === -1) hiddenSlices.push(thisLabel); else hiddenSlices.splice(thisLabelIndex, 1); - } else if(numClicks === 2) { + } else if(mode === 'focus') { hiddenSlices = []; gd.calcdata[0].forEach(function(d) { if(thisLabel !== d.label) { @@ -126,7 +138,7 @@ module.exports = function handleClick(g, gd, numClicks) { } } - if(numClicks === 1) { + if(mode === 'toggle') { var nextVisibility; switch(fullTrace.visible) { @@ -150,7 +162,7 @@ module.exports = function handleClick(g, gd, numClicks) { } else { setVisibility(fullTrace, nextVisibility); } - } else if(numClicks === 2) { + } else if(mode === 'focus') { // Compute the clicked index. expandedIndex does what we want for expanded traces // but also culls hidden traces. That means we have some work to do. var isClicked, isInGroup, otherState; diff --git a/test/jasmine/tests/legend_test.js b/test/jasmine/tests/legend_test.js index bb1af39da6c..5ed9ddd73b5 100644 --- a/test/jasmine/tests/legend_test.js +++ b/test/jasmine/tests/legend_test.js @@ -1067,7 +1067,6 @@ describe('legend interaction', function() { }); }); - describe('editable mode interactions', function() { var gd; @@ -1667,6 +1666,68 @@ describe('legend interaction', function() { .then(done); }); }); + + describe('should honor *itemclick* and *itemdoubleclick* settings', function() { + var _assert; + + function run() { + return Promise.resolve() + .then(click(0, 1)).then(_assert(['legendonly', true])) + .then(click(0, 1)).then(_assert([true, true])) + .then(click(0, 2)).then(_assert([true, 'legendonly'])) + .then(click(0, 2)).then(_assert([true, true])) + .then(function() { + return Plotly.relayout(gd, { + 'legend.itemclick': false, + 'legend.itemdoubleclick': false + }); + }) + .then(click(0, 1)).then(_assert([true, true])) + .then(click(0, 2)).then(_assert([true, true])) + .then(function() { + return Plotly.relayout(gd, { + 'legend.itemclick': 'focus', + 'legend.itemdoubleclick': 'toggle' + }); + }) + .then(click(0, 1)).then(_assert([true, 'legendonly'])) + .then(click(0, 1)).then(_assert([true, true])) + .then(click(0, 2)).then(_assert(['legendonly', true])) + .then(click(0, 2)).then(_assert([true, true])); + } + + it('- regular trace case', function(done) { + _assert = assertVisible; + + Plotly.plot(gd, [ + { y: [1, 2, 1] }, + { y: [2, 1, 2] } + ]) + .then(run) + .catch(failTest) + .then(done); + }); + + it('- pie case', function(done) { + _assert = function(exp) { + return function() { + var actual = []; + if(exp[0] === 'legendonly') actual.push('B'); + if(exp[1] === 'legendonly') actual.push('A'); + expect(actual).toEqual(gd._fullLayout.hiddenlabels || []); + }; + }; + + Plotly.plot(gd, [{ + type: 'pie', + labels: ['A', 'B'], + values: [1, 2] + }]) + .then(run) + .catch(failTest) + .then(done); + }); + }); }); }); From 88e566a8abb03cb3971f4af73d7a07b2a2a11ed5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Wed, 15 May 2019 16:35:01 -0400 Subject: [PATCH 2/4] fix typo in itemclick, itemdoubleclick attr descriptions --- src/components/legend/attributes.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/legend/attributes.js b/src/components/legend/attributes.js index dde8946dc01..371cad9c158 100644 --- a/src/components/legend/attributes.js +++ b/src/components/legend/attributes.js @@ -98,7 +98,7 @@ module.exports = { editType: 'legend', description: [ 'Determines the behavior on legend item click.', - '*toggle* toggles the visible of the item clicked on the graph.', + '*toggle* toggles the visibility of the item clicked on the graph.', '*focus* makes the clicked item the sole visible item on the graph.', '*false* disable legend item click interactions.' ].join(' ') @@ -111,7 +111,7 @@ module.exports = { editType: 'legend', description: [ 'Determines the behavior on legend item double-click.', - '*toggle* toggles the visible of the item clicked on the graph.', + '*toggle* toggles the visibility of the item clicked on the graph.', '*focus* makes the clicked item the sole visible item on the graph.', '*false* disable legend item double-click interactions.' ].join(' ') From 81907381b6615488751e4e01131d1899e04948c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Wed, 15 May 2019 16:46:34 -0400 Subject: [PATCH 3/4] improve itemclick / itemdoubleclick tests --- test/jasmine/tests/legend_test.js | 38 ++++++++++++++++--------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/test/jasmine/tests/legend_test.js b/test/jasmine/tests/legend_test.js index 5ed9ddd73b5..6036dafdc58 100644 --- a/test/jasmine/tests/legend_test.js +++ b/test/jasmine/tests/legend_test.js @@ -1672,28 +1672,28 @@ describe('legend interaction', function() { function run() { return Promise.resolve() - .then(click(0, 1)).then(_assert(['legendonly', true])) - .then(click(0, 1)).then(_assert([true, true])) - .then(click(0, 2)).then(_assert([true, 'legendonly'])) - .then(click(0, 2)).then(_assert([true, true])) + .then(click(0, 1)).then(_assert(['legendonly', true, true])) + .then(click(0, 1)).then(_assert([true, true, true])) + .then(click(0, 2)).then(_assert([true, 'legendonly', 'legendonly'])) + .then(click(0, 2)).then(_assert([true, true, true])) .then(function() { return Plotly.relayout(gd, { 'legend.itemclick': false, 'legend.itemdoubleclick': false }); }) - .then(click(0, 1)).then(_assert([true, true])) - .then(click(0, 2)).then(_assert([true, true])) + .then(click(0, 1)).then(_assert([true, true, true])) + .then(click(0, 2)).then(_assert([true, true, true])) .then(function() { return Plotly.relayout(gd, { 'legend.itemclick': 'focus', 'legend.itemdoubleclick': 'toggle' }); }) - .then(click(0, 1)).then(_assert([true, 'legendonly'])) - .then(click(0, 1)).then(_assert([true, true])) - .then(click(0, 2)).then(_assert(['legendonly', true])) - .then(click(0, 2)).then(_assert([true, true])); + .then(click(0, 1)).then(_assert([true, 'legendonly', 'legendonly'])) + .then(click(0, 1)).then(_assert([true, true, true])) + .then(click(0, 2)).then(_assert(['legendonly', true, true])) + .then(click(0, 2)).then(_assert([true, true, true])); } it('- regular trace case', function(done) { @@ -1701,7 +1701,8 @@ describe('legend interaction', function() { Plotly.plot(gd, [ { y: [1, 2, 1] }, - { y: [2, 1, 2] } + { y: [2, 1, 2] }, + { y: [3, 5, 0] } ]) .then(run) .catch(failTest) @@ -1709,19 +1710,20 @@ describe('legend interaction', function() { }); it('- pie case', function(done) { - _assert = function(exp) { + _assert = function(_exp) { return function() { - var actual = []; - if(exp[0] === 'legendonly') actual.push('B'); - if(exp[1] === 'legendonly') actual.push('A'); - expect(actual).toEqual(gd._fullLayout.hiddenlabels || []); + var exp = []; + if(_exp[0] === 'legendonly') exp.push('C'); + if(_exp[1] === 'legendonly') exp.push('B'); + if(_exp[2] === 'legendonly') exp.push('A'); + expect(gd._fullLayout.hiddenlabels || []).toEqual(exp); }; }; Plotly.plot(gd, [{ type: 'pie', - labels: ['A', 'B'], - values: [1, 2] + labels: ['A', 'B', 'C'], + values: [1, 2, 3] }]) .then(run) .catch(failTest) From bde19eea4206211b34cef30eb39734f051992a9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Fri, 17 May 2019 17:48:48 -0400 Subject: [PATCH 4/4] replace 'focus' -> 'toggleothers' --- src/components/legend/attributes.js | 10 +++++----- src/components/legend/handle_click.js | 6 +++--- test/jasmine/tests/legend_test.js | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/components/legend/attributes.js b/src/components/legend/attributes.js index 371cad9c158..2eee7aa05cb 100644 --- a/src/components/legend/attributes.js +++ b/src/components/legend/attributes.js @@ -92,27 +92,27 @@ module.exports = { itemclick: { valType: 'enumerated', - values: ['toggle', 'focus', false], + values: ['toggle', 'toggleothers', false], dflt: 'toggle', role: 'info', editType: 'legend', description: [ 'Determines the behavior on legend item click.', '*toggle* toggles the visibility of the item clicked on the graph.', - '*focus* makes the clicked item the sole visible item on the graph.', + '*toggleothers* makes the clicked item the sole visible item on the graph.', '*false* disable legend item click interactions.' ].join(' ') }, itemdoubleclick: { valType: 'enumerated', - values: ['toggle', 'focus', false], - dflt: 'focus', + values: ['toggle', 'toggleothers', false], + dflt: 'toggleothers', role: 'info', editType: 'legend', description: [ 'Determines the behavior on legend item double-click.', '*toggle* toggles the visibility of the item clicked on the graph.', - '*focus* makes the clicked item the sole visible item on the graph.', + '*toggleothers* makes the clicked item the sole visible item on the graph.', '*false* disable legend item double-click interactions.' ].join(' ') }, diff --git a/src/components/legend/handle_click.js b/src/components/legend/handle_click.js index 780cc1d8e2f..6bc98641108 100644 --- a/src/components/legend/handle_click.js +++ b/src/components/legend/handle_click.js @@ -21,7 +21,7 @@ module.exports = function handleClick(g, gd, numClicks) { var itemClick = fullLayout.legend.itemclick; var itemDoubleClick = fullLayout.legend.itemdoubleclick; - if(numClicks === 1 && itemClick === 'toggle' && itemDoubleClick === 'focus' && + if(numClicks === 1 && itemClick === 'toggle' && itemDoubleClick === 'toggleothers' && SHOWISOLATETIP && gd.data && gd._context.showTips ) { Lib.notifier(Lib._(gd, 'Double-click on legend to isolate one trace'), 'long'); @@ -111,7 +111,7 @@ module.exports = function handleClick(g, gd, numClicks) { if(mode === 'toggle') { if(thisLabelIndex === -1) hiddenSlices.push(thisLabel); else hiddenSlices.splice(thisLabelIndex, 1); - } else if(mode === 'focus') { + } else if(mode === 'toggleothers') { hiddenSlices = []; gd.calcdata[0].forEach(function(d) { if(thisLabel !== d.label) { @@ -162,7 +162,7 @@ module.exports = function handleClick(g, gd, numClicks) { } else { setVisibility(fullTrace, nextVisibility); } - } else if(mode === 'focus') { + } else if(mode === 'toggleothers') { // Compute the clicked index. expandedIndex does what we want for expanded traces // but also culls hidden traces. That means we have some work to do. var isClicked, isInGroup, otherState; diff --git a/test/jasmine/tests/legend_test.js b/test/jasmine/tests/legend_test.js index 6036dafdc58..f4b5cf5d7b7 100644 --- a/test/jasmine/tests/legend_test.js +++ b/test/jasmine/tests/legend_test.js @@ -1686,7 +1686,7 @@ describe('legend interaction', function() { .then(click(0, 2)).then(_assert([true, true, true])) .then(function() { return Plotly.relayout(gd, { - 'legend.itemclick': 'focus', + 'legend.itemclick': 'toggleothers', 'legend.itemdoubleclick': 'toggle' }); })