Skip to content

Commit

Permalink
Merge pull request #6963 from plotly/fix6960-hoversubplots
Browse files Browse the repository at this point in the history
Ensure winning points of hover are listed first when `hoversubplots` is set to "axis" and sorting by distance
  • Loading branch information
archmoj authored Apr 15, 2024
2 parents f673ce6 + 5b6998d commit 3b9de53
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 5 deletions.
1 change: 1 addition & 0 deletions draftlogs/6963_fix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Ensure winning points of hover are listed first when hoversubplots is set to "axis" and sorting by distance [[#6963](https://github.com/plotly/plotly.js/pull/6963)]
38 changes: 34 additions & 4 deletions src/components/fx/hover.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ var cartesianScatterPoints = {
splom: true
};

function distanceSort(a, b) {
return a.distance - b.distance;
}

// fx.hover: highlight data on hover
// evt can be a mousemove event, or an object with data about what points
// to hover on
Expand Down Expand Up @@ -270,15 +274,21 @@ function _hover(gd, evt, subplot, noHoverEvent, eventTarget) {
var hovermodeHasX = (hovermode || '').charAt(0) === 'x';
var hovermodeHasY = (hovermode || '').charAt(0) === 'y';

var firstXaxis;
var firstYaxis;

if(hasCartesian && (hovermodeHasX || hovermodeHasY) && hoversubplots === 'axis') {
var subplotsLength = subplots.length;
for(var p = 0; p < subplotsLength; p++) {
spId = subplots[p];
if(plots[spId]) {
// 'cartesian' case

firstXaxis = Axes.getFromId(gd, spId, 'x');
firstYaxis = Axes.getFromId(gd, spId, 'y');

var subplotsWith = (
Axes.getFromId(gd, spId, hovermodeHasX ? 'x' : 'y')
hovermodeHasX ? firstXaxis : firstYaxis
)._subplotsWith;

if(subplotsWith && subplotsWith.length) {
Expand Down Expand Up @@ -661,6 +671,9 @@ function _hover(gd, evt, subplot, noHoverEvent, eventTarget) {
var thisSpikeDistance;

for(var i = 0; i < pointsData.length; i++) {
if(firstXaxis && firstXaxis._id !== pointsData[i].xa._id) continue;
if(firstYaxis && firstYaxis._id !== pointsData[i].ya._id) continue;

thisSpikeDistance = pointsData[i].spikeDistance;
if(spikeOnWinning && i === 0) thisSpikeDistance = -Infinity;

Expand Down Expand Up @@ -700,9 +713,26 @@ function _hover(gd, evt, subplot, noHoverEvent, eventTarget) {
gd._spikepoints = newspikepoints;

var sortHoverData = function() {
if(hoversubplots !== 'axis') {
hoverData.sort(function(d1, d2) { return d1.distance - d2.distance; });
}
// When sorting keep the points in the main subplot at the top
// then add points in other subplots

var hoverDataInSubplot = hoverData.filter(function(a) {
return (
(firstXaxis && firstXaxis._id === a.xa._id) &&
(firstYaxis && firstYaxis._id === a.ya._id)
);
});

var hoverDataOutSubplot = hoverData.filter(function(a) {
return !(
(firstXaxis && firstXaxis._id === a.xa._id) &&
(firstYaxis && firstYaxis._id === a.ya._id)
);
});

hoverDataInSubplot.sort(distanceSort);
hoverDataOutSubplot.sort(distanceSort);
hoverData = hoverDataInSubplot.concat(hoverDataOutSubplot);

// move period positioned points and box/bar-like traces to the end of the list
hoverData = orderRangePoints(hoverData, hovermode);
Expand Down
37 changes: 36 additions & 1 deletion test/jasmine/tests/hover_label_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2445,6 +2445,12 @@ describe('hover on subplots when hoversubplots is set to *single* and x hovermod
});
});

function assertFirstPointOn(gd, xaxisId, yaxisId) {
// first point should be on the main subplot
expect(gd._hoverdata[0].xaxis._id).toBe(xaxisId);
expect(gd._hoverdata[0].yaxis._id).toBe(yaxisId);
}

describe('hover on subplots when hoversubplots is set to *axis* and x hovermodes', function() {
'use strict';

Expand Down Expand Up @@ -2494,6 +2500,9 @@ describe('hover on subplots when hoversubplots is set to *axis* and x hovermodes
Lib.clearThrottle();
Plotly.Fx.hover(gd, {xval: pos}, subplot);
expect(gd._hoverdata.length).toBe(3);

assertFirstPointOn(gd, 'x', 'y');

assertHoverLabelContent({
nums: ['1', '10', '100'],
name: ['trace 0', 'trace 1', 'trace 2'],
Expand All @@ -2505,6 +2514,8 @@ describe('hover on subplots when hoversubplots is set to *axis* and x hovermodes
Lib.clearThrottle();
Plotly.Fx.hover(gd, {xval: pos}, subplot);

assertFirstPointOn(gd, 'x', 'y2');

expect(gd._hoverdata.length).toBe(3);
assertHoverLabelContent({
nums: ['2', '20', '200'],
Expand All @@ -2517,6 +2528,8 @@ describe('hover on subplots when hoversubplots is set to *axis* and x hovermodes
Lib.clearThrottle();
Plotly.Fx.hover(gd, {xval: pos}, subplot);

assertFirstPointOn(gd, 'x', 'y3');

expect(gd._hoverdata.length).toBe(3);
assertHoverLabelContent({
nums: ['3', '30', '300'],
Expand All @@ -2530,6 +2543,8 @@ describe('hover on subplots when hoversubplots is set to *axis* and x hovermodes
Lib.clearThrottle();
Plotly.Fx.hover(gd, {xval: pos}, subplot);
expect(gd._hoverdata.length).toBe(3);

assertFirstPointOn(gd, 'x', 'y');
});
});

Expand Down Expand Up @@ -2582,6 +2597,9 @@ describe('hover on subplots when hoversubplots is set to *axis* and y hovermodes
Lib.clearThrottle();
Plotly.Fx.hover(gd, {yval: pos}, subplot);
expect(gd._hoverdata.length).toBe(3);

assertFirstPointOn(gd, 'x', 'y');

assertHoverLabelContent({
nums: ['1', '10', '100'],
name: ['trace 0', 'trace 1', 'trace 2'],
Expand All @@ -2594,6 +2612,9 @@ describe('hover on subplots when hoversubplots is set to *axis* and y hovermodes
Plotly.Fx.hover(gd, {yval: pos}, subplot);

expect(gd._hoverdata.length).toBe(3);

assertFirstPointOn(gd, 'x2', 'y');

assertHoverLabelContent({
nums: ['2', '20', '200'],
name: ['trace 0', 'trace 1', 'trace 2'],
Expand All @@ -2606,6 +2627,9 @@ describe('hover on subplots when hoversubplots is set to *axis* and y hovermodes
Plotly.Fx.hover(gd, {yval: pos}, subplot);

expect(gd._hoverdata.length).toBe(3);

assertFirstPointOn(gd, 'x3', 'y');

assertHoverLabelContent({
nums: ['3', '30', '300'],
name: ['trace 0', 'trace 1', 'trace 2'],
Expand All @@ -2618,6 +2642,8 @@ describe('hover on subplots when hoversubplots is set to *axis* and y hovermodes
Lib.clearThrottle();
Plotly.Fx.hover(gd, {yval: pos}, subplot);
expect(gd._hoverdata.length).toBe(3);

assertFirstPointOn(gd, 'x', 'y');
});
});

Expand All @@ -2640,6 +2666,7 @@ describe('splom hover on subplots when hoversubplots is set to *axis* and (x|y)
it('splom hoversubplots: *axis*', function() {
Lib.clearThrottle();
Plotly.Fx.hover(gd, {x: 200, y: 200}, 'xy');
assertFirstPointOn(gd, 'x', 'y');
expect(gd._hoverdata.length).toBe(2);
assertHoverLabelContent({
nums: ['100', '100k'],
Expand All @@ -2648,14 +2675,15 @@ describe('splom hover on subplots when hoversubplots is set to *axis* and (x|y)
});

Plotly.relayout(gd, 'hovermode', 'x unified');

Lib.clearThrottle();
Plotly.Fx.hover(gd, {x: 200, y: 200}, 'xy');
assertFirstPointOn(gd, 'x', 'y');
expect(gd._hoverdata.length).toBe(2);

Plotly.relayout(gd, 'hovermode', 'y unified');
Lib.clearThrottle();
Plotly.Fx.hover(gd, {x: 200, y: 200}, 'xy');
assertFirstPointOn(gd, 'x', 'y');
expect(gd._hoverdata.length).toBe(2);
});
});
Expand Down Expand Up @@ -2696,6 +2724,7 @@ describe('splom hover *axis* hoversubplots splom points on same position should
it('splom *axis* hoversubplots', function() {
Lib.clearThrottle();
Plotly.Fx.hover(gd, {}, 'xy');
assertFirstPointOn(gd, 'x', 'y');
expect(gd._hoverdata.length).toBe(4);
assertHoverLabelContent({
nums: ['1', '1', '1', '1'],
Expand All @@ -2705,6 +2734,7 @@ describe('splom hover *axis* hoversubplots splom points on same position should

Lib.clearThrottle();
Plotly.Fx.hover(gd, {}, 'xy2');
assertFirstPointOn(gd, 'x', 'y2');
expect(gd._hoverdata.length).toBe(3);
assertHoverLabelContent({
nums: ['1', '2', '2'],
Expand All @@ -2714,6 +2744,7 @@ describe('splom hover *axis* hoversubplots splom points on same position should

Lib.clearThrottle();
Plotly.Fx.hover(gd, {}, 'xy3');
assertFirstPointOn(gd, 'x', 'y3');
expect(gd._hoverdata.length).toBe(3);
assertHoverLabelContent({
nums: ['1', '2', '2'],
Expand All @@ -2723,6 +2754,7 @@ describe('splom hover *axis* hoversubplots splom points on same position should

Lib.clearThrottle();
Plotly.Fx.hover(gd, {}, 'xy4');
assertFirstPointOn(gd, 'x', 'y4');
expect(gd._hoverdata.length).toBe(4);
assertHoverLabelContent({
nums: ['1', '3', '3', '3'],
Expand All @@ -2732,6 +2764,7 @@ describe('splom hover *axis* hoversubplots splom points on same position should

Lib.clearThrottle();
Plotly.Fx.hover(gd, {}, 'x2y');
assertFirstPointOn(gd, 'x2', 'y');
expect(gd._hoverdata.length).toBe(4);
assertHoverLabelContent({
nums: ['1', '3', '3', '3'],
Expand All @@ -2741,6 +2774,7 @@ describe('splom hover *axis* hoversubplots splom points on same position should

Lib.clearThrottle();
Plotly.Fx.hover(gd, {}, 'x3y');
assertFirstPointOn(gd, 'x3', 'y');
expect(gd._hoverdata.length).toBe(4);
assertHoverLabelContent({
nums: ['1', '3', '3', '3'],
Expand All @@ -2750,6 +2784,7 @@ describe('splom hover *axis* hoversubplots splom points on same position should

Lib.clearThrottle();
Plotly.Fx.hover(gd, {}, 'x4y');
assertFirstPointOn(gd, 'x4', 'y');
expect(gd._hoverdata.length).toBe(4);
assertHoverLabelContent({
nums: ['1', '3', '3', '3'],
Expand Down

0 comments on commit 3b9de53

Please sign in to comment.