From 0eaec35add44f533b565b6d5188c3c1f43eb725e Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Fri, 12 Apr 2024 10:53:33 -0400 Subject: [PATCH 1/4] fix (x|y) spike position and ensure main subplot hover points are sorted first --- src/components/fx/hover.js | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/src/components/fx/hover.js b/src/components/fx/hover.js index 883bf8c9194..e854c457759 100644 --- a/src/components/fx/hover.js +++ b/src/components/fx/hover.js @@ -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 @@ -270,6 +274,9 @@ 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++) { @@ -277,8 +284,11 @@ function _hover(gd, evt, subplot, noHoverEvent, eventTarget) { 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) { @@ -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; @@ -700,9 +713,23 @@ 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; }); - } + 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); From 1ab4e1a8cbb99d8b689146f288383603e84b17ab Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Fri, 12 Apr 2024 16:00:44 -0400 Subject: [PATCH 2/4] jasmine test --- test/jasmine/tests/hover_label_test.js | 56 ++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/test/jasmine/tests/hover_label_test.js b/test/jasmine/tests/hover_label_test.js index e666526651e..e7082860ba4 100644 --- a/test/jasmine/tests/hover_label_test.js +++ b/test/jasmine/tests/hover_label_test.js @@ -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'; @@ -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'], @@ -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'], @@ -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'], @@ -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'); }); }); @@ -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'], @@ -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'], @@ -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'], @@ -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'); }); }); @@ -2641,6 +2667,9 @@ describe('splom hover on subplots when hoversubplots is set to *axis* and (x|y) Lib.clearThrottle(); Plotly.Fx.hover(gd, {x: 200, y: 200}, 'xy'); expect(gd._hoverdata.length).toBe(3); + + assertFirstPointOn(gd, 'x', 'y'); + assertHoverLabelContent({ nums: ['100', '100k'], name: ['', ''], @@ -2651,11 +2680,17 @@ describe('splom hover on subplots when hoversubplots is set to *axis* and (x|y) Lib.clearThrottle(); Plotly.Fx.hover(gd, {x: 200, y: 200}, 'xy'); + + assertFirstPointOn(gd, 'x', 'y'); + expect(gd._hoverdata.length).toBe(3); 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(3); }); }); @@ -2697,6 +2732,9 @@ describe('splom hover *axis* hoversubplots splom points on same position should Lib.clearThrottle(); Plotly.Fx.hover(gd, {}, 'xy'); expect(gd._hoverdata.length).toBe(5); + + assertFirstPointOn(gd, 'x', 'y'); + assertHoverLabelContent({ nums: ['1', '1', '1', '1'], name: ['', '', '', ''], @@ -2706,6 +2744,9 @@ describe('splom hover *axis* hoversubplots splom points on same position should Lib.clearThrottle(); Plotly.Fx.hover(gd, {}, 'xy2'); expect(gd._hoverdata.length).toBe(4); + + assertFirstPointOn(gd, 'x', 'y2'); + assertHoverLabelContent({ nums: ['1', '2', '2'], name: ['', '', ''], @@ -2715,6 +2756,9 @@ describe('splom hover *axis* hoversubplots splom points on same position should Lib.clearThrottle(); Plotly.Fx.hover(gd, {}, 'xy3'); expect(gd._hoverdata.length).toBe(4); + + assertFirstPointOn(gd, 'x', 'y3'); + assertHoverLabelContent({ nums: ['1', '2', '2'], name: ['', '', ''], @@ -2724,6 +2768,9 @@ describe('splom hover *axis* hoversubplots splom points on same position should Lib.clearThrottle(); Plotly.Fx.hover(gd, {}, 'xy4'); expect(gd._hoverdata.length).toBe(5); + + assertFirstPointOn(gd, 'x', 'y4'); + assertHoverLabelContent({ nums: ['1', '3', '3', '3'], name: ['', '', '', ''], @@ -2733,6 +2780,9 @@ describe('splom hover *axis* hoversubplots splom points on same position should Lib.clearThrottle(); Plotly.Fx.hover(gd, {}, 'x2y'); expect(gd._hoverdata.length).toBe(5); + + assertFirstPointOn(gd, 'x2', 'y'); + assertHoverLabelContent({ nums: ['1', '3', '3', '3'], name: ['', '', '', ''], @@ -2742,6 +2792,9 @@ describe('splom hover *axis* hoversubplots splom points on same position should Lib.clearThrottle(); Plotly.Fx.hover(gd, {}, 'x3y'); expect(gd._hoverdata.length).toBe(5); + + assertFirstPointOn(gd, 'x3', 'y'); + assertHoverLabelContent({ nums: ['1', '3', '3', '3'], name: ['', '', '', ''], @@ -2751,6 +2804,9 @@ describe('splom hover *axis* hoversubplots splom points on same position should Lib.clearThrottle(); Plotly.Fx.hover(gd, {}, 'x4y'); expect(gd._hoverdata.length).toBe(5); + + assertFirstPointOn(gd, 'x4', 'y'); + assertHoverLabelContent({ nums: ['1', '3', '3', '3'], name: ['', '', '', ''], From 337cda0580859be8dd9187d78b5aaf2fc111acf6 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Fri, 12 Apr 2024 17:31:46 -0400 Subject: [PATCH 3/4] draftlog --- draftlogs/6963_fix.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 draftlogs/6963_fix.md diff --git a/draftlogs/6963_fix.md b/draftlogs/6963_fix.md new file mode 100644 index 00000000000..0637298dc65 --- /dev/null +++ b/draftlogs/6963_fix.md @@ -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)] From 5b6998dd34bb3f850c1556621b8131ffe1ee3bc1 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Mon, 15 Apr 2024 14:06:34 -0400 Subject: [PATCH 4/4] comment --- src/components/fx/hover.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/fx/hover.js b/src/components/fx/hover.js index e854c457759..9c256b6650b 100644 --- a/src/components/fx/hover.js +++ b/src/components/fx/hover.js @@ -713,6 +713,9 @@ function _hover(gd, evt, subplot, noHoverEvent, eventTarget) { gd._spikepoints = newspikepoints; var sortHoverData = function() { + // 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) &&