Skip to content

Commit

Permalink
Merge pull request #3862 from plotly/legend-trace-toggle
Browse files Browse the repository at this point in the history
Add legend click interaction attrs
  • Loading branch information
etpinard authored May 21, 2019
2 parents 08e2d7e + bde19ee commit 425900e
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 14 deletions.
27 changes: 27 additions & 0 deletions src/components/legend/attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,33 @@ module.exports = {
].join(' ')
},

itemclick: {
valType: 'enumerated',
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.',
'*toggleothers* makes the clicked item the sole visible item on the graph.',
'*false* disable legend item click interactions.'
].join(' ')
},
itemdoubleclick: {
valType: 'enumerated',
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.',
'*toggleothers* makes the clicked item the sole visible item on the graph.',
'*false* disable legend item double-click interactions.'
].join(' ')
},

x: {
valType: 'number',
min: -2,
Expand Down
3 changes: 3 additions & 0 deletions src/components/legend/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
38 changes: 25 additions & 13 deletions src/components/legend/handle_click.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 === 'toggleothers' &&
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];
Expand Down Expand Up @@ -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 === 'toggleothers') {
hiddenSlices = [];
gd.calcdata[0].forEach(function(d) {
if(thisLabel !== d.label) {
Expand All @@ -126,7 +138,7 @@ module.exports = function handleClick(g, gd, numClicks) {
}
}

if(numClicks === 1) {
if(mode === 'toggle') {
var nextVisibility;

switch(fullTrace.visible) {
Expand All @@ -150,7 +162,7 @@ module.exports = function handleClick(g, gd, numClicks) {
} else {
setVisibility(fullTrace, nextVisibility);
}
} else if(numClicks === 2) {
} 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;
Expand Down
65 changes: 64 additions & 1 deletion test/jasmine/tests/legend_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1067,7 +1067,6 @@ describe('legend interaction', function() {
});
});


describe('editable mode interactions', function() {
var gd;

Expand Down Expand Up @@ -1667,6 +1666,70 @@ 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, 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, true]))
.then(click(0, 2)).then(_assert([true, true, true]))
.then(function() {
return Plotly.relayout(gd, {
'legend.itemclick': 'toggleothers',
'legend.itemdoubleclick': 'toggle'
});
})
.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) {
_assert = assertVisible;

Plotly.plot(gd, [
{ y: [1, 2, 1] },
{ y: [2, 1, 2] },
{ y: [3, 5, 0] }
])
.then(run)
.catch(failTest)
.then(done);
});

it('- pie case', function(done) {
_assert = function(_exp) {
return function() {
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', 'C'],
values: [1, 2, 3]
}])
.then(run)
.catch(failTest)
.then(done);
});
});
});
});

Expand Down

0 comments on commit 425900e

Please sign in to comment.