Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add legend click interaction attrs #3862

Merged
merged 4 commits into from
May 21, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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: {
antoinerg marked this conversation as resolved.
Show resolved Hide resolved
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