Skip to content
Open
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
1 change: 1 addition & 0 deletions draftlogs/7997_add.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Add `legend.groupdoubleclick` to set the group behavior for a legend double-click [[#7997](https://github.com/plotly/plotly.js/pull/7997)]
11 changes: 11 additions & 0 deletions src/components/legend/attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,17 @@ module.exports = {
'*togglegroup* toggles the visibility of all items in the same legendgroup as the item clicked on the graph.'
].join(' ')
},
groupdoubleclick: {
valType: 'enumerated',
values: ['toggleitem', 'togglegroup'],
editType: 'legend',
description: [
'Determines the behavior on legend group item double-click.',
'*toggleitem* toggles the visibility of the individual item clicked on the graph.',
'*togglegroup* toggles the visibility of all items in the same legendgroup as the item clicked on the graph.',
'Defaults to the value of `groupclick`.'
].join(' ')
},
titleclick: {
valType: 'enumerated',
values: ['toggle', 'toggleothers', false],
Expand Down
3 changes: 2 additions & 1 deletion src/components/legend/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,8 @@ function groupDefaults(legendId, layoutIn, layoutOut, fullData, legendCount) {

coerce('itemclick');
coerce('itemdoubleclick');
coerce('groupclick');
const groupClick = coerce('groupclick');
coerce('groupdoubleclick', groupClick);

coerce('xanchor', defaultXAnchor);
coerce('yanchor', defaultYAnchor);
Expand Down
4 changes: 2 additions & 2 deletions src/components/legend/draw.js
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,7 @@ function clickOrDoubleClick(gd, legend, legendItem, numClicks, evt) {
if(clickVal === false) return;
legend._clickTimeout = setTimeout(function() {
if(!gd._fullLayout) return;
if(itemClick) handleItemClick(legendItem, gd, legend, itemClick);
if(itemClick) handleItemClick(legendItem, gd, legend, itemClick, numClicks);
}, gd._context.doubleClickDelay);
} else if(numClicks === 2) {
if(legend._clickTimeout) clearTimeout(legend._clickTimeout);
Expand All @@ -553,7 +553,7 @@ function clickOrDoubleClick(gd, legend, legendItem, numClicks, evt) {
var dblClickVal = Events.triggerHandler(gd, 'plotly_legenddoubleclick', evtData);
// Activate default double click behaviour only when both single click and double click values are not false
if(dblClickVal !== false && clickVal !== false && itemDoubleClick) {
handleItemClick(legendItem, gd, legend, itemDoubleClick);
handleItemClick(legendItem, gd, legend, itemDoubleClick, numClicks);
}
}
}
Expand Down
14 changes: 9 additions & 5 deletions src/components/legend/handle_click.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,20 @@ var SHOWISOLATETIP = true;
* @param {object} gd graph div
* @param {object} legendObj the legend object from fullLayout
* @param {string} mode toggle mode for the current action: 'toggle' | 'toggleothers'
* - 'toggle': Toggle visibility of this item (or group if groupclick is 'togglegroup')
* - 'toggle': Toggle visibility of this item (or group if the group behavior is 'togglegroup')
* - 'toggleothers': Show only this item, hide all others (isolation mode)
* @param {number} numClicks 1 for a single click, 2 for a double click. Selects `groupclick`
* or `groupdoubleclick` as the group behavior.
*/
exports.handleItemClick = function handleItemClick(g, gd, legendObj, mode) {
exports.handleItemClick = function handleItemClick(g, gd, legendObj, mode, numClicks) {
var fullLayout = gd._fullLayout;

if (gd._dragged || gd._editing) return;

var legendItem = g.data()[0][0];
if (legendItem.groupTitle && legendItem.noClick) return;

var groupClick = legendObj.groupclick;
const groupClick = numClicks === 2 ? legendObj.groupdoubleclick : legendObj.groupclick;

// Show isolate tip on first single click when default behavior is active
if (
Expand Down Expand Up @@ -196,13 +198,15 @@ exports.handleItemClick = function handleItemClick(g, gd, legendObj, mode) {
// but also culls hidden traces. That means we have some work to do.
var isClicked, isInGroup, notInLegend, otherState, _item;
var isIsolated = true;
// 'toggleitem' isolates the clicked trace alone, so its group peers hide with the rest.
const isolateGroup = hasLegendgroup && toggleGroup;
for (i = 0; i < allLegendItems.length; i++) {
_item = allLegendItems[i];
isClicked = _item === fullTrace;
notInLegend = _item.showlegend !== true;
if (isClicked || notInLegend) continue;

isInGroup = hasLegendgroup && _item.legendgroup === legendgroup;
isInGroup = isolateGroup && _item.legendgroup === legendgroup;

if (
!isInGroup &&
Expand Down Expand Up @@ -234,7 +238,7 @@ exports.handleItemClick = function handleItemClick(g, gd, legendObj, mode) {
isClicked = _item === fullTrace;
// N.B. consider traces that have a set legendgroup as toggleable
notInLegend = _item.showlegend !== true && !_item.legendgroup;
isInGroup = isClicked || (hasLegendgroup && _item.legendgroup === legendgroup);
isInGroup = isClicked || (isolateGroup && _item.legendgroup === legendgroup);
setVisibility(_item, isInGroup || notInLegend ? true : otherState);
break;
}
Expand Down
2 changes: 2 additions & 0 deletions src/types/generated/schema.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12727,6 +12727,8 @@ export interface Legend {
* @default 'togglegroup'
*/
groupclick?: 'toggleitem' | 'togglegroup';
/** Determines the behavior on legend group item double-click. *toggleitem* toggles the visibility of the individual item clicked on the graph. *togglegroup* toggles the visibility of all items in the same legendgroup as the item clicked on the graph. Defaults to the value of `groupclick`. */
groupdoubleclick?: 'toggleitem' | 'togglegroup';
/** Sets the font for group titles in legend. Defaults to `legend.font` with its size increased about 10%. */
grouptitlefont?: Font;
/**
Expand Down
74 changes: 74 additions & 0 deletions test/jasmine/tests/legend_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,28 @@ describe('legend defaults', function () {
expect(layoutOut.legend.title.side).toEqual('left');
});

it('should default `groupdoubleclick` to the `groupclick` value', function () {
fullData = allShown([{ type: 'scatter' }, { type: 'scatter' }]);

supplyLayoutDefaults(layoutIn, layoutOut, fullData);
expect(layoutOut.legend.groupdoubleclick).toBe('togglegroup');

layoutIn.legend = { groupclick: 'toggleitem' };

supplyLayoutDefaults(layoutIn, layoutOut, fullData);
expect(layoutOut.legend.groupdoubleclick).toBe('toggleitem');
});

it('should coerce `groupdoubleclick` independently of `groupclick`', function () {
fullData = allShown([{ type: 'scatter' }, { type: 'scatter' }]);

layoutIn.legend = { groupdoubleclick: 'toggleitem' };

supplyLayoutDefaults(layoutIn, layoutOut, fullData);
expect(layoutOut.legend.groupclick).toBe('togglegroup');
expect(layoutOut.legend.groupdoubleclick).toBe('toggleitem');
});

describe('for horizontal legends', function () {
var layoutInForHorizontalLegends;

Expand Down Expand Up @@ -2482,6 +2504,58 @@ describe('legend interaction', function () {
});
});

describe('legendgroup visibility case of groupdoubleclick: "toggleitem"', function () {
beforeEach(function (done) {
Plotly.newPlot(
gd,
[
{
x: [1, 2],
y: [3, 4],
visible: false
},
{
x: [1, 2, 3, 4],
y: [0, 1, 2, 3],
legendgroup: 'foo'
},
{
x: [1, 2, 3, 4],
y: [1, 3, 2, 4]
},
{
x: [1, 2, 3, 4],
y: [1, 3, 2, 4],
legendgroup: 'foo'
}
],
{
legend: {
groupdoubleclick: 'toggleitem'
}
}
).then(done);
});

it('isolates the clicked item instead of its legendgroup', function (done) {
Promise.resolve()
.then(click(1, 2))
.then(assertVisible([false, 'legendonly', 'legendonly', true]))
.then(click(1, 2))
.then(assertVisible([false, true, true, true]))
.then(done, done.fail);
});

it('leaves the single click behavior to groupclick', function (done) {
Promise.resolve()
.then(click(1))
.then(assertVisible([false, 'legendonly', true, 'legendonly']))
.then(click(1))
.then(assertVisible([false, true, true, true]))
.then(done, done.fail);
});
});

describe('legend visibility with *showlegend:false* traces', function () {
beforeEach(function (done) {
Plotly.newPlot(gd, [
Expand Down
9 changes: 9 additions & 0 deletions test/plot-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -3312,6 +3312,15 @@
"togglegroup"
]
},
"groupdoubleclick": {
"description": "Determines the behavior on legend group item double-click. *toggleitem* toggles the visibility of the individual item clicked on the graph. *togglegroup* toggles the visibility of all items in the same legendgroup as the item clicked on the graph. Defaults to the value of `groupclick`.",
"editType": "legend",
"valType": "enumerated",
"values": [
"toggleitem",
"togglegroup"
]
},
"grouptitlefont": {
"color": {
"editType": "legend",
Expand Down