From 97a8aeff72401a681f43165253124d6d1051742e Mon Sep 17 00:00:00 2001 From: archmoj Date: Wed, 10 Mar 2021 12:18:09 -0500 Subject: [PATCH 1/5] fix 5292 - only filter close points of the same trace when unified --- src/components/fx/hover.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/components/fx/hover.js b/src/components/fx/hover.js index bf0723787c5..361e89dc10e 100644 --- a/src/components/fx/hover.js +++ b/src/components/fx/hover.js @@ -980,13 +980,27 @@ function createHoverText(hoverData, opts, gd) { }); } + function unifiedFilterClosePoints(hoverData) { + var out = []; + var seen = []; + for(var i = 0; i < hoverData.length; i++) { + var d = hoverData[i]; + var id = d.cd[0].trace.index; + if(!seen[id]) { + seen[id] = 1; + out.push(d); + } + } + return out; + } + // Show a single hover label if(helpers.isUnifiedHover(hovermode)) { // Delete leftover hover labels from other hovermodes container.selectAll('g.hovertext').remove(); // similarly to compare mode, we remove the "close but not quite together" points - if((t0 !== undefined) && (c0.distance <= opts.hoverdistance)) hoverData = filterClosePoints(hoverData); + if((t0 !== undefined) && (c0.distance <= opts.hoverdistance)) hoverData = unifiedFilterClosePoints(hoverData); // Return early if nothing is hovered on if(hoverData.length === 0) return; From 73290c9f6ec83bed8b9ad32bac9f2eb0d635e41c Mon Sep 17 00:00:00 2001 From: archmoj Date: Wed, 10 Mar 2021 15:13:31 -0500 Subject: [PATCH 2/5] add test for unified hover filtering close points --- test/jasmine/tests/hover_label_test.js | 137 +++++++++++++++++++++++-- 1 file changed, 127 insertions(+), 10 deletions(-) diff --git a/test/jasmine/tests/hover_label_test.js b/test/jasmine/tests/hover_label_test.js index 22aee441ae6..f5e32081656 100644 --- a/test/jasmine/tests/hover_label_test.js +++ b/test/jasmine/tests/hover_label_test.js @@ -4728,7 +4728,7 @@ describe('hovermode: (x|y)unified', function() { .then(done, done.fail); }); - it('shares filtering logic with compare mode x', function(done) { + it('filtering logic for compare mode x', function(done) { var mock = require('@mocks/27.json'); var mockCopy = Lib.extendDeep({}, mock); @@ -4736,19 +4736,136 @@ describe('hovermode: (x|y)unified', function() { .then(function(gd) { _hover(gd, { xval: '2002' }); assertElementCount('g.hovertext', 2); - - return Plotly.relayout(gd, 'hovermode', 'x unified'); - }) - .then(function() { - _hover(gd, { xval: '2002' }); - assertLabel({title: '2002.042', items: [ - 'Market income : 0.5537845', - 'Market incom... : 0.4420997' - ]}); }) .then(done, done.fail); }); + it('x unified should include close points and filter multiple points from the same trace', function(done) { + Plotly.newPlot(gd, { + data: [ + { + type: 'bar', + name: 'bar', + x: [ + '2017-04', + '2017-07', + '2017-10', + '2018-01' + ], + xperiod: 'M3', + y: [10, 5, 10, 5] + }, + { + type: 'scatter', + name: 'scatter', + x: [ + '2017-01-01', + '2017-02-01', + '2017-03-01', + '2017-04-01', + '2017-05-01', + '2017-06-01', + '2017-07-01', + '2017-08-01', + '2017-09-01', + '2017-10-01', + '2017-11-01', + '2017-12-01' + ], + y: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] + } + ], + layout: { + hovermode: 'x unified', + showlegend: false, + width: 500, + height: 500, + margin: { + t: 50, + b: 50, + l: 50, + r: 50 + } + } + }) + .then(function(gd) { + _hover(gd, { xpx: 25, ypx: 250 }); + assertLabel({title: 'Jan 1, 2017', items: [ + 'scatter : 1' + ]}); + + _hover(gd, { xpx: 50, ypx: 250 }); + assertLabel({title: 'Feb 1, 2017', items: [ + 'scatter : 2' + ]}); + + _hover(gd, { xpx: 75, ypx: 250 }); + assertLabel({title: 'Mar 1, 2017', items: [ + 'scatter : 3' + ]}); + + _hover(gd, { xpx: 100, ypx: 250 }); + assertLabel({title: 'Apr 1, 2017', items: [ + 'bar : 10', + 'scatter : 4' + ]}); + + _hover(gd, { xpx: 125, ypx: 250 }); + assertLabel({title: 'May 1, 2017', items: [ + 'bar : (Apr 1, 2017, 10)', + 'scatter : 5' + ]}); + + _hover(gd, { xpx: 150, ypx: 250 }); + assertLabel({title: 'Jun 1, 2017', items: [ + 'bar : (Apr 1, 2017, 10)', + 'scatter : 6' + ]}); + + _hover(gd, { xpx: 175, ypx: 250 }); + assertLabel({title: 'Jul 1, 2017', items: [ + 'bar : 5', + 'scatter : 7' + ]}); + + _hover(gd, { xpx: 200, ypx: 250 }); + assertLabel({title: 'Aug 1, 2017', items: [ + 'bar : (Jul 1, 2017, 5)', + 'scatter : 8' + ]}); + + _hover(gd, { xpx: 225, ypx: 250 }); + assertLabel({title: 'Sep 1, 2017', items: [ + 'bar : (Jul 1, 2017, 5)', + 'scatter : 9' + ]}); + + _hover(gd, { xpx: 250, ypx: 250 }); + assertLabel({title: 'Oct 1, 2017', items: [ + 'bar : 10', + 'scatter : 10' + ]}); + + _hover(gd, { xpx: 275, ypx: 250 }); + assertLabel({title: 'Nov 1, 2017', items: [ + 'bar : (Oct 1, 2017, 10)', + 'scatter : 11' + ]}); + + _hover(gd, { xpx: 300, ypx: 250 }); + assertLabel({title: 'Dec 1, 2017', items: [ + 'bar : (Oct 1, 2017, 10)', + 'scatter : 12' + ]}); + + _hover(gd, { xpx: 350, ypx: 250 }); + assertLabel({title: 'Jan 1, 2018', items: [ + 'bar : 5' + ]}); + }) + .then(done, done.fail); + }); + it('should have the same traceorder as the legend', function(done) { var mock = require('@mocks/stacked_area.json'); var mockCopy = Lib.extendDeep({}, mock); From 1e59d895fb6cf0da6853d18c76d116f40776a8db Mon Sep 17 00:00:00 2001 From: archmoj Date: Wed, 24 Mar 2021 13:30:31 -0400 Subject: [PATCH 3/5] display period point in hover if the winning point located inside the period --- src/components/fx/hover.js | 43 ++++++++++++++------------ test/jasmine/tests/hover_label_test.js | 13 ++++++-- 2 files changed, 35 insertions(+), 21 deletions(-) diff --git a/src/components/fx/hover.js b/src/components/fx/hover.js index 6a365ff2359..a597e9da892 100644 --- a/src/components/fx/hover.js +++ b/src/components/fx/hover.js @@ -14,6 +14,7 @@ var Drawing = require('../drawing'); var Color = require('../color'); var dragElement = require('../dragelement'); var Axes = require('../../plots/cartesian/axes'); +var alignPeriod = require('../../plots/cartesian/align_period'); var Registry = require('../../registry'); var helpers = require('./helpers'); @@ -779,8 +780,9 @@ function createHoverText(hoverData, opts, gd) { var c0 = hoverData[0]; var xa = c0.xa; var ya = c0.ya; - var commonAttr = hovermode.charAt(0) === 'y' ? 'yLabel' : 'xLabel'; - var t0 = c0[commonAttr]; + var axLetter = hovermode.charAt(0); + var v0 = c0[axLetter + 'LabelVal']; + var t0 = c0[axLetter + 'Label']; var t00 = (String(t0) || '').split(' ')[0]; var outerContainerBB = outerContainer.node().getBoundingClientRect(); var outerTop = outerContainerBB.top; @@ -978,23 +980,26 @@ function createHoverText(hoverData, opts, gd) { function filterClosePoints(hoverData) { return hoverData.filter(function(d) { - return (d.zLabelVal !== undefined) || - (d[commonAttr] || '').split(' ')[0] === t00; - }); - } - - function unifiedFilterClosePoints(hoverData) { - var out = []; - var seen = []; - for(var i = 0; i < hoverData.length; i++) { - var d = hoverData[i]; - var id = d.cd[0].trace.index; - if(!seen[id]) { - seen[id] = 1; - out.push(d); + if(d.zLabelVal !== undefined) return true; + if((d[axLetter + 'Label'] || '').split(' ')[0] === t00) return true; + if(d.trace[axLetter + 'period']) { + var v = d[axLetter + 'LabelVal']; + var ax = d[axLetter + 'a']; + var trace = {}; + trace[axLetter + 'period'] = d.trace[axLetter + 'period']; + trace[axLetter + 'period0'] = d.trace[axLetter + 'period0']; + + trace[axLetter + 'periodalignment'] = 'start'; + + var start = alignPeriod(trace, ax, axLetter, [v])[0]; + trace[axLetter + 'periodalignment'] = 'end'; + var end = alignPeriod(trace, ax, axLetter, [v])[0]; + + if(v0 >= start && v0 <= end) return true; } - } - return out; + + return false; + }); } // Show a single hover label @@ -1003,7 +1008,7 @@ function createHoverText(hoverData, opts, gd) { container.selectAll('g.hovertext').remove(); // similarly to compare mode, we remove the "close but not quite together" points - if((t0 !== undefined) && (c0.distance <= opts.hoverdistance)) hoverData = unifiedFilterClosePoints(hoverData); + if((t0 !== undefined) && (c0.distance <= opts.hoverdistance)) hoverData = filterClosePoints(hoverData); // Return early if nothing is hovered on if(hoverData.length === 0) return; diff --git a/test/jasmine/tests/hover_label_test.js b/test/jasmine/tests/hover_label_test.js index f5e32081656..23e4e681d9c 100644 --- a/test/jasmine/tests/hover_label_test.js +++ b/test/jasmine/tests/hover_label_test.js @@ -4728,7 +4728,7 @@ describe('hovermode: (x|y)unified', function() { .then(done, done.fail); }); - it('filtering logic for compare mode x', function(done) { + it('shares filtering logic with compare mode x', function(done) { var mock = require('@mocks/27.json'); var mockCopy = Lib.extendDeep({}, mock); @@ -4736,11 +4736,20 @@ describe('hovermode: (x|y)unified', function() { .then(function(gd) { _hover(gd, { xval: '2002' }); assertElementCount('g.hovertext', 2); + + return Plotly.relayout(gd, 'hovermode', 'x unified'); + }) + .then(function() { + _hover(gd, { xval: '2002' }); + assertLabel({title: '2002.042', items: [ + 'Market income : 0.5537845', + 'Market incom... : 0.4420997' + ]}); }) .then(done, done.fail); }); - it('x unified should include close points and filter multiple points from the same trace', function(done) { + it('case of scatter points on period bars', function(done) { Plotly.newPlot(gd, { data: [ { From f8c019f727388fb0ac4baf321ca23e31bc17f6d8 Mon Sep 17 00:00:00 2001 From: archmoj Date: Wed, 24 Mar 2021 14:03:15 -0400 Subject: [PATCH 4/5] add hover test for M1 bars on M3 bars --- test/jasmine/tests/hover_label_test.js | 134 ++++++++++++++++++++++++- 1 file changed, 129 insertions(+), 5 deletions(-) diff --git a/test/jasmine/tests/hover_label_test.js b/test/jasmine/tests/hover_label_test.js index 23e4e681d9c..91db735a605 100644 --- a/test/jasmine/tests/hover_label_test.js +++ b/test/jasmine/tests/hover_label_test.js @@ -4798,11 +4798,6 @@ describe('hovermode: (x|y)unified', function() { } }) .then(function(gd) { - _hover(gd, { xpx: 25, ypx: 250 }); - assertLabel({title: 'Jan 1, 2017', items: [ - 'scatter : 1' - ]}); - _hover(gd, { xpx: 50, ypx: 250 }); assertLabel({title: 'Feb 1, 2017', items: [ 'scatter : 2' @@ -4875,6 +4870,135 @@ describe('hovermode: (x|y)unified', function() { .then(done, done.fail); }); + it('case of M1 period bars overlaid on M3 period bars', function(done) { + Plotly.newPlot(gd, { + data: [ + { + type: 'bar', + name: 'M3', + xperiod: 'M3', + x: [ + '2017-04', + '2017-07', + '2017-10', + '2018-01' + ], + y: [10, 5, 10, 5] + }, + { + type: 'bar', + name: 'M1', + xperiod: 'M1', + x: [ + '2017-01', + '2017-02', + '2017-03', + '2017-04', + '2017-05', + '2017-06', + '2017-07', + '2017-08', + '2017-09', + '2017-10', + '2017-11', + '2017-12' + ], + y: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] + } + ], + layout: { + barmode: 'overlay', + hovermode: 'x unified', + showlegend: false, + width: 500, + height: 500, + margin: { + t: 50, + b: 50, + l: 50, + r: 50 + } + } + }) + .then(function(gd) { + _hover(gd, { xpx: 25, ypx: 250 }); + assertLabel({title: 'Jan 1, 2017', items: [ + 'M1 : 1' + ]}); + + _hover(gd, { xpx: 50, ypx: 250 }); + assertLabel({title: 'Feb 1, 2017', items: [ + 'M1 : 2' + ]}); + + _hover(gd, { xpx: 75, ypx: 250 }); + assertLabel({title: 'Mar 1, 2017', items: [ + 'M1 : 3', + 'M1 : (Feb 1, 2017, 2)' + ]}); + + _hover(gd, { xpx: 100, ypx: 250 }); + assertLabel({title: 'Apr 1, 2017', items: [ + 'M3 : 10', + 'M1 : 4' + ]}); + + _hover(gd, { xpx: 125, ypx: 250 }); + assertLabel({title: 'May 1, 2017', items: [ + 'M3 : (Apr 1, 2017, 10)', + 'M1 : 5' + ]}); + + _hover(gd, { xpx: 150, ypx: 250 }); + assertLabel({title: 'Jun 1, 2017', items: [ + 'M3 : (Apr 1, 2017, 10)', + 'M1 : 6' + ]}); + + _hover(gd, { xpx: 175, ypx: 250 }); + assertLabel({title: 'Jul 1, 2017', items: [ + 'M3 : 5', + 'M1 : 7' + ]}); + + _hover(gd, { xpx: 200, ypx: 250 }); + assertLabel({title: 'Aug 1, 2017', items: [ + 'M3 : (Jul 1, 2017, 5)', + 'M1 : 8' + ]}); + + _hover(gd, { xpx: 225, ypx: 250 }); + assertLabel({title: 'Sep 1, 2017', items: [ + 'M3 : (Jul 1, 2017, 5)', + 'M1 : 9' + ]}); + + _hover(gd, { xpx: 250, ypx: 250 }); + assertLabel({title: 'Oct 1, 2017', items: [ + 'M3 : 10', + 'M1 : 10' + ]}); + + _hover(gd, { xpx: 275, ypx: 250 }); + assertLabel({title: 'Nov 1, 2017', items: [ + 'M3 : (Oct 1, 2017, 10)', + 'M1 : 11' + ]}); + + _hover(gd, { xpx: 300, ypx: 250 }); + assertLabel({title: 'Dec 1, 2017', items: [ + 'M3 : (Oct 1, 2017, 10)', + 'M1 : 12' + ]}); + + _hover(gd, { xpx: 350, ypx: 250 }); + assertLabel({title: 'Jan 1, 2018', items: [ + 'M3 : 5' + ]}); + }) + .then(done, done.fail); + }); + it('should have the same traceorder as the legend', function(done) { var mock = require('@mocks/stacked_area.json'); var mockCopy = Lib.extendDeep({}, mock); From 900ded3383380df68fd12abefc984aaace2bfb4d Mon Sep 17 00:00:00 2001 From: archmoj Date: Wed, 24 Mar 2021 14:21:14 -0400 Subject: [PATCH 5/5] correct end of period --- src/components/fx/hover.js | 4 ++-- test/jasmine/tests/hover_label_test.js | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/components/fx/hover.js b/src/components/fx/hover.js index a597e9da892..a4fa5a0834e 100644 --- a/src/components/fx/hover.js +++ b/src/components/fx/hover.js @@ -990,12 +990,12 @@ function createHoverText(hoverData, opts, gd) { trace[axLetter + 'period0'] = d.trace[axLetter + 'period0']; trace[axLetter + 'periodalignment'] = 'start'; - var start = alignPeriod(trace, ax, axLetter, [v])[0]; + trace[axLetter + 'periodalignment'] = 'end'; var end = alignPeriod(trace, ax, axLetter, [v])[0]; - if(v0 >= start && v0 <= end) return true; + if(v0 >= start && v0 < end) return true; } return false; diff --git a/test/jasmine/tests/hover_label_test.js b/test/jasmine/tests/hover_label_test.js index 91db735a605..164f0980e8e 100644 --- a/test/jasmine/tests/hover_label_test.js +++ b/test/jasmine/tests/hover_label_test.js @@ -4933,8 +4933,7 @@ describe('hovermode: (x|y)unified', function() { _hover(gd, { xpx: 75, ypx: 250 }); assertLabel({title: 'Mar 1, 2017', items: [ - 'M1 : 3', - 'M1 : (Feb 1, 2017, 2)' + 'M1 : 3' ]}); _hover(gd, { xpx: 100, ypx: 250 });