Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit b2df371

Browse filesBrowse files
authored
Merge pull request #6189 from plotly/improve-box-violin-hoverlabels
revise `box` & `violin` hover labels - improve order & handle duplicates
2 parents 1ec75d5 + 3e25576 commit b2df371
Copy full SHA for b2df371

File tree

Expand file treeCollapse file tree

6 files changed

+174
-88
lines changed
Filter options
Expand file treeCollapse file tree

6 files changed

+174
-88
lines changed

‎draftlogs/6189_fix.md

Copy file name to clipboard
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Fix undesirable missing hover labels of `box` & `violin` traces [[#6189](https://github.com/plotly/plotly.js/pull/6189)]

‎src/traces/box/hover.js

Copy file name to clipboardExpand all lines: src/traces/box/hover.js
+33-16Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ function hoverOnBoxes(pointData, xval, yval, hovermode) {
4444
var trace = cd[0].trace;
4545
var t = cd[0].t;
4646
var isViolin = trace.type === 'violin';
47-
var closeBoxData = [];
4847

4948
var pLetter, vLetter, pAxis, vAxis, vVal, pVal, dx, dy, dPos,
5049
hoverPseudoDistance, spikePseudoDistance;
@@ -141,22 +140,30 @@ function hoverOnBoxes(pointData, xval, yval, hovermode) {
141140
pointData.spikeDistance = dxy(di) * spikePseudoDistance / hoverPseudoDistance;
142141
pointData[spikePosAttr] = pAxis.c2p(di.pos, true);
143142

144-
// box plots: each "point" gets many labels
145-
var usedVals = {};
146-
var attrs = ['med', 'q1', 'q3', 'min', 'max'];
143+
var hasMean = trace.boxmean || (trace.meanline || {}).visible;
144+
var hasFences = trace.boxpoints || trace.points;
147145

148-
if(trace.boxmean || (trace.meanline || {}).visible) {
149-
attrs.push('mean');
150-
}
151-
if(trace.boxpoints || trace.points) {
152-
attrs.push('lf', 'uf');
146+
// labels with equal values (e.g. when min === q1) should still be presented in the order they have when they're unequal
147+
var attrs =
148+
(hasFences && hasMean) ? ['max', 'uf', 'q3', 'med', 'mean', 'q1', 'lf', 'min'] :
149+
(hasFences && !hasMean) ? ['max', 'uf', 'q3', 'med', 'q1', 'lf', 'min'] :
150+
(!hasFences && hasMean) ? ['max', 'q3', 'med', 'mean', 'q1', 'min'] :
151+
['max', 'q3', 'med', 'q1', 'min'];
152+
153+
var rev = vAxis.range[1] < vAxis.range[0];
154+
155+
if(trace.orientation === (rev ? 'v' : 'h')) {
156+
attrs.reverse();
153157
}
154158

159+
var spikeDistance = pointData.spikeDistance;
160+
var spikePosition = pointData[spikePosAttr];
161+
162+
var closeBoxData = [];
155163
for(var i = 0; i < attrs.length; i++) {
156164
var attr = attrs[i];
157165

158-
if(!(attr in di) || (di[attr] in usedVals)) continue;
159-
usedVals[di[attr]] = true;
166+
if(!(attr in di)) continue;
160167

161168
// copy out to a new object for each value to label
162169
var val = di[attr];
@@ -176,17 +183,27 @@ function hoverOnBoxes(pointData, xval, yval, hovermode) {
176183
pointData2[vLetter + 'err'] = di.sd;
177184
}
178185

179-
// only keep name and spikes on the first item (median)
180-
pointData.name = '';
181-
pointData.spikeDistance = undefined;
182-
pointData[spikePosAttr] = undefined;
183-
184186
// no hovertemplate support yet
185187
pointData2.hovertemplate = false;
186188

187189
closeBoxData.push(pointData2);
188190
}
189191

192+
// only keep name and spikes on the median
193+
pointData.name = '';
194+
pointData.spikeDistance = undefined;
195+
pointData[spikePosAttr] = undefined;
196+
for(var k = 0; k < closeBoxData.length; k++) {
197+
if(closeBoxData[k].attr !== 'med') {
198+
closeBoxData[k].name = '';
199+
closeBoxData[k].spikeDistance = undefined;
200+
closeBoxData[k][spikePosAttr] = undefined;
201+
} else {
202+
closeBoxData[k].spikeDistance = spikeDistance;
203+
closeBoxData[k][spikePosAttr] = spikePosition;
204+
}
205+
}
206+
190207
return closeBoxData;
191208
}
192209

‎src/traces/violin/hover.js

Copy file name to clipboardExpand all lines: src/traces/violin/hover.js
+12-4Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,19 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode, opts) {
5656
kdePointData[vLetter + 'Label'] = vLetter + ': ' + Axes.hoverLabelText(vAxis, vVal, trace[vLetter + 'hoverformat']) + ', ' + cd[0].t.labels.kde + ' ' + kdeVal.toFixed(3);
5757

5858
// move the spike to the KDE point
59-
kdePointData.spikeDistance = closeBoxData[0].spikeDistance;
59+
var medId = 0;
60+
for(var k = 0; k < closeBoxData.length; k++) {
61+
if(closeBoxData[k].attr === 'med') {
62+
medId = k;
63+
break;
64+
}
65+
}
66+
67+
kdePointData.spikeDistance = closeBoxData[medId].spikeDistance;
6068
var spikePosAttr = pLetter + 'Spike';
61-
kdePointData[spikePosAttr] = closeBoxData[0][spikePosAttr];
62-
closeBoxData[0].spikeDistance = undefined;
63-
closeBoxData[0][spikePosAttr] = undefined;
69+
kdePointData[spikePosAttr] = closeBoxData[medId][spikePosAttr];
70+
closeBoxData[medId].spikeDistance = undefined;
71+
closeBoxData[medId][spikePosAttr] = undefined;
6472

6573
// no hovertemplate support yet
6674
kdePointData.hovertemplate = false;

‎test/jasmine/tests/box_test.js

Copy file name to clipboardExpand all lines: test/jasmine/tests/box_test.js
+51-28Lines changed: 51 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -743,8 +743,8 @@ describe('Test box hover:', function() {
743743
fig.layout.hovermode = 'x';
744744
return fig;
745745
},
746-
nums: ['median: 0.55', 'min: 0', 'q1: 0.3', 'q3: 0.6', 'max: 0.7'],
747-
name: ['radishes', '', '', '', ''],
746+
nums: ['median: 0.55', 'min: 0', 'lower fence: 0', 'q1: 0.3', 'q3: 0.6', 'upper fence: 0.7', 'max: 0.7'],
747+
name: ['radishes', '', '', '', '', '', ''],
748748
axis: 'day 1'
749749
}, {
750750
desc: 'with mean',
@@ -755,8 +755,8 @@ describe('Test box hover:', function() {
755755
fig.layout.hovermode = 'x';
756756
return fig;
757757
},
758-
nums: ['median: 0.55', 'min: 0', 'q1: 0.3', 'q3: 0.6', 'max: 0.7', 'mean: 0.45'],
759-
name: ['radishes', '', '', '', '', ''],
758+
nums: ['median: 0.55', 'min: 0', 'lower fence: 0', 'q1: 0.3', 'q3: 0.6', 'upper fence: 0.7', 'max: 0.7', 'mean: 0.45'],
759+
name: ['radishes', '', '', '', '', '', '', ''],
760760
axis: 'day 1'
761761
}, {
762762
desc: 'with sd',
@@ -768,10 +768,10 @@ describe('Test box hover:', function() {
768768
return fig;
769769
},
770770
nums: [
771-
'median: 0.55', 'min: 0', 'q1: 0.3', 'q3: 0.6', 'max: 0.7',
771+
'median: 0.55', 'min: 0', 'lower fence: 0', 'q1: 0.3', 'q3: 0.6', 'upper fence: 0.7', 'max: 0.7',
772772
'mean ± σ: 0.45 ± 0.2362908'
773773
],
774-
name: ['radishes', '', '', '', '', ''],
774+
name: ['radishes', '', '', '', '', '', '', ''],
775775
axis: 'day 1'
776776
}, {
777777
desc: 'with boxpoints fences',
@@ -782,10 +782,11 @@ describe('Test box hover:', function() {
782782
},
783783
pos: [350, 200],
784784
nums: [
785+
'23.25',
785786
'median: 8.15', 'min: 0.75', 'q1: 6.8',
786787
'q3: 10.25', 'max: 23.25', 'lower fence: 5.25', 'upper fence: 12'
787788
],
788-
name: ['', '', '', '', '', '', ''],
789+
name: ['', '', '', '', '', '', '', ''],
789790
axis: 'trace 0'
790791
}, {
791792
desc: 'with overlaid boxes',
@@ -795,12 +796,22 @@ describe('Test box hover:', function() {
795796
return fig;
796797
},
797798
nums: [
798-
'q1: 0.3', 'median: 0.45', 'q3: 0.6', 'max: 1', 'median: 0.55', 'min: 0', 'q1: 0.1',
799-
'q3: 0.6', 'max: 0.7', 'median: 0.45', 'q1: 0.2', 'q3: 0.6', 'max: 0.9'
799+
'median: 0.45', 'median: 0.45', 'median: 0.55',
800+
'min: 0', 'min: 0.1', 'min: 0.2',
801+
'lower fence: 0', 'lower fence: 0.1', 'lower fence: 0.2',
802+
'q1: 0.1', 'q1: 0.2', 'q1: 0.3',
803+
'q3: 0.6', 'q3: 0.6', 'q3: 0.6',
804+
'upper fence: 0.7', 'upper fence: 0.9', 'upper fence: 1',
805+
'max: 0.7', 'max: 0.9', 'max: 1'
800806
],
801807
name: [
802-
'', 'kale', '', '', 'radishes', '', '',
803-
'', '', 'carrots', '', '', ''
808+
'carrots', 'kale', 'radishes',
809+
'', '', '',
810+
'', '', '',
811+
'', '', '',
812+
'', '', '',
813+
'', '', '',
814+
'', '', ''
804815
],
805816
axis: 'day 1'
806817
}, {
@@ -841,8 +852,8 @@ describe('Test box hover:', function() {
841852
return fig;
842853
},
843854
pos: [215, 200],
844-
nums: ['median: 0.55', 'min: 0', 'q1: 0.3', 'q3: 0.6', 'max: 0.7'],
845-
name: ['radishes', '', '', '', ''],
855+
nums: ['median: 0.55', 'min: 0', 'q1: 0.3', 'q3: 0.6', 'max: 0.7', 'lower fence: 0', 'upper fence: 0.7'],
856+
name: ['radishes', '', '', '', '', '', ''],
846857
axis: 'day 1'
847858
}, {
848859
desc: 'hoveron boxes+points | hovermode x (box AND closest point)',
@@ -855,8 +866,8 @@ describe('Test box hover:', function() {
855866
fig.layout.hovermode = 'x';
856867
return fig;
857868
},
858-
nums: ['0.6', 'median: 0.55', 'min: 0', 'q1: 0.3', 'q3: 0.6', 'max: 0.7'],
859-
name: ['radishes', 'radishes', '', '', '', ''],
869+
nums: ['0.6', 'median: 0.55', 'min: 0', 'q1: 0.3', 'q3: 0.6', 'max: 0.7', 'lower fence: 0', 'upper fence: 0.7'],
870+
name: ['radishes', 'radishes', '', '', '', '', '', ''],
860871
axis: 'day 1'
861872
}, {
862873
desc: 'text items on hover',
@@ -909,20 +920,32 @@ describe('Test box hover:', function() {
909920
},
910921
pos: [430, 130],
911922
nums: [
912-
'max: 1', 'mean ± σ: 0.6833333 ± 0.2409472', 'min: 0.3',
913-
'q1: 0.5', 'q3: 0.9', 'median: 0.7'],
914-
name: ['', '', '', '', '', 'carrots'],
915-
axis: 'day 2',
916-
hOrder: [0, 4, 5, 1, 3, 2]
923+
'median: 0.7',
924+
'min: 0.3',
925+
'q1: 0.5',
926+
'q3: 0.9',
927+
'max: 1',
928+
'lower fence: 0.3',
929+
'upper fence: 1',
930+
'mean ± σ: 0.6833333 ± 0.2409472',
931+
],
932+
name: ['carrots', '', '', '', '', '', '', ''],
933+
axis: 'day 2'
917934
}, {
918935
desc: 'orientation:h | hovermode:closest',
919936
mock: require('@mocks/box_grouped_horz.json'),
920937
pos: [430, 130],
921938
nums: [
922-
'(max: 1, day 2)', '(mean ± σ: 0.6833333 ± 0.2409472, day 2)', '(min: 0.3, day 2)',
923-
'(q1: 0.5, day 2)', '(q3: 0.9, day 2)', '(median: 0.7, day 2)'],
924-
name: ['', '', '', '', '', 'carrots'],
925-
hOrder: [0, 4, 5, 1, 3, 2]
939+
'(median: 0.7, day 2)',
940+
'(min: 0.3, day 2)',
941+
'(q1: 0.5, day 2)',
942+
'(q3: 0.9, day 2)',
943+
'(max: 1, day 2)',
944+
'(lower fence: 0.3, day 2)',
945+
'(upper fence: 1, day 2)',
946+
'(mean ± σ: 0.6833333 ± 0.2409472, day 2)'
947+
],
948+
name: ['carrots', '', '', '', '', '', '', ''],
926949
}, {
927950
desc: 'on boxpoints with numeric positions | hovermode:closest',
928951
mock: {
@@ -967,8 +990,8 @@ describe('Test box hover:', function() {
967990
}
968991
},
969992
pos: [200, 200],
970-
nums: ['median: 2', 'q1: 1.5', 'q3: 2.5', 'max: 3', 'min: 1'],
971-
name: ['', '', '', '', ''],
993+
nums: ['median: 2', 'q1: 1.5', 'q3: 2.5', 'max: 3', 'min: 1', 'lower fence: 1', 'upper fence: 3'],
994+
name: ['', '', '', '', '', '', ''],
972995
axis: 'trace 0'
973996
}, {
974997
desc: 'q1/median/q3 signature on boxes',
@@ -987,8 +1010,8 @@ describe('Test box hover:', function() {
9871010
}
9881011
},
9891012
pos: [200, 200],
990-
nums: ['median: 2', 'q1: 1', 'q3: 3'],
991-
name: ['', '', ''],
1013+
nums: ['median: 2', 'min: 1', 'q1: 1', 'q3: 3', 'max: 3', 'lower fence: 1', 'upper fence: 3'],
1014+
name: ['', '', '', '', '', '', ''],
9921015
axis: 'A'
9931016
}, {
9941017
desc: 'q1/median/q3 signature on points',

‎test/jasmine/tests/hover_label_test.js

Copy file name to clipboardExpand all lines: test/jasmine/tests/hover_label_test.js
+27-6Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2833,21 +2833,25 @@ describe('hover on traces with (x|y)period positioning', function() {
28332833
.then(function() { _hover(385, 355); })
28342834
.then(function() {
28352835
assertHoverLabelContent({
2836-
name: ['', '', '', 'box (v)', ''],
2836+
name: ['', '', '', 'box (v)', '', '', ''],
28372837
nums: [
28382838
'(Jan 2001, min: 2)',
2839+
'(Jan 2001, lower fence: 2)',
28392840
'(Jan 2001, q1: 4)',
2840-
'(Jan 2001, q3: 8)',
28412841
'(Jan 2001, median: 6)',
2842-
'(Jan 2001, max: 10)',
2842+
'(Jan 2001, q3: 8)',
2843+
'(Jan 2001, upper fence: 10)',
2844+
'(Jan 2001, max: 10)'
28432845
]
28442846
});
28452847
})
28462848
.then(function() { _hover(475, 120); })
28472849
.then(function() {
28482850
assertHoverLabelContent({
2849-
name: ['', '', '', '', 'box (h)'],
2851+
name: ['', '', '', '', '', '', 'box (h)'],
28502852
nums: [
2853+
'(upper fence: 8, Jan 2004)',
2854+
'(lower fence: 0, Jan 2004)',
28512855
'(max: 8, Jan 2004)',
28522856
'(min: 0, Jan 2004)',
28532857
'(q1: 2, Jan 2004)',
@@ -4673,7 +4677,13 @@ describe('hovermode: (x|y)unified', function() {
46734677

46744678
assertLabel({title: '3', items: [
46754679
'trace 0 : 2',
4680+
'min: 1',
4681+
'lower fence: 1',
4682+
'q1: 1',
46764683
'trace 1 : median: 1',
4684+
'q3: 1',
4685+
'upper fence: 1',
4686+
'max: 1',
46774687
'trace 3 : 2',
46784688
'trace 2 : 2',
46794689
'trace 5 : 2',
@@ -6262,7 +6272,18 @@ describe('hover on traces with (x|y)hoverformat', function() {
62626272
{type: 'scattergl', nums: '(02/01/2000, 1.00)'},
62636273
{type: 'histogram', nums: '(02/01/2000, 1.00)'},
62646274
{type: 'bar', nums: '(02/01/2000, 1.00)'},
6265-
{type: 'box', nums: '(02/01/2000, median: 1.00)'},
6275+
{type: 'box',
6276+
name: ['', '', '', '', '', '', ''],
6277+
nums: [
6278+
'(02/01/2000, median: 1.00)',
6279+
'(02/01/2000, max: 1.00)',
6280+
'(02/01/2000, upper fence: 1.00)',
6281+
'(02/01/2000, q3: 1.00)',
6282+
'(02/01/2000, q1: 1.00)',
6283+
'(02/01/2000, lower fence: 1.00)',
6284+
'(02/01/2000, min: 1.00)'
6285+
]
6286+
},
62666287
{type: 'ohlc', nums: '02/01/2000\nopen: 4.00\nhigh: 5.00\nlow: 2.00\nclose: 3.00 ▼'},
62676288
{type: 'candlestick', nums: '02/01/2000\nopen: 4.00\nhigh: 5.00\nlow: 2.00\nclose: 3.00 ▼'},
62686289
{type: 'waterfall', nums: '(02/01/2000, 1.00)\n1.00 ▲\nInitial: 0.00'},
@@ -6286,7 +6307,7 @@ describe('hover on traces with (x|y)hoverformat', function() {
62866307
.then(function() { _hover(200, 200); })
62876308
.then(function() {
62886309
assertHoverLabelContent({
6289-
name: '',
6310+
name: t.name ? t.name : '',
62906311
nums: t.nums
62916312
});
62926313
})

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.