From bc10cad004057ecdf2f98faccab0dc3145893a82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Mon, 6 Mar 2017 17:29:09 -0500 Subject: [PATCH] clear gd._hoverdata before emitting plotly_unhover - so that in the case where a plotly_unhover handler calls a Plotly method (restyle, relayout and update which in turn call Plots.rehover), an infinite isn't triggered! - add test case showing the current infinite loop code path --- src/components/dragelement/unhover.js | 8 ++-- test/jasmine/tests/hover_label_test.js | 58 ++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 4 deletions(-) diff --git a/src/components/dragelement/unhover.js b/src/components/dragelement/unhover.js index 61b602ac6e6..4caf14ce456 100644 --- a/src/components/dragelement/unhover.js +++ b/src/components/dragelement/unhover.js @@ -32,6 +32,7 @@ unhover.wrapped = function(gd, evt, subplot) { // remove hover effects on mouse out, and emit unhover event unhover.raw = function unhoverRaw(gd, evt) { var fullLayout = gd._fullLayout; + var oldhoverdata = gd._hoverdata; if(!evt) evt = {}; if(evt.target && @@ -40,10 +41,9 @@ unhover.raw = function unhoverRaw(gd, evt) { } fullLayout._hoverlayer.selectAll('g').remove(); + gd._hoverdata = undefined; - if(evt.target && gd._hoverdata) { - gd.emit('plotly_unhover', {points: gd._hoverdata}); + if(evt.target && oldhoverdata) { + gd.emit('plotly_unhover', {points: oldhoverdata}); } - - gd._hoverdata = undefined; }; diff --git a/test/jasmine/tests/hover_label_test.js b/test/jasmine/tests/hover_label_test.js index 3561e6d6082..ebfd193ce8c 100644 --- a/test/jasmine/tests/hover_label_test.js +++ b/test/jasmine/tests/hover_label_test.js @@ -886,4 +886,62 @@ describe('hover updates', function() { return assertLabelsCorrect(null, [103, 100], 'trace 10.5'); }).catch(fail).then(done); }); + + it('should not trigger infinite loop of plotly_unhover events', function(done) { + var gd = createGraphDiv(); + var colors0 = ['#00000', '#00000', '#00000', '#00000', '#00000', '#00000', '#00000']; + + function unhover() { + return new Promise(function(resolve) { + mouseEvent('mousemove', 394, 285); + setTimeout(function() { + resolve(); + }, constants.HOVERMINTIME); + }); + } + + var hoverCnt = 0; + var unHoverCnt = 0; + + Plotly.plot(gd, [{ + mode: 'markers', + x: [1, 2, 3, 4, 5, 6, 7], + y: [1, 2, 3, 2, 3, 4, 3], + marker: { + size: 16, + colors: colors0.slice() + } + }]) + .then(function() { + + gd.on('plotly_hover', function(eventData) { + hoverCnt++; + + var pt = eventData.points[0]; + Plotly.restyle(gd, 'marker.color[' + pt.pointNumber + ']', 'red'); + }); + + gd.on('plotly_unhover', function() { + unHoverCnt++; + + Plotly.restyle(gd, 'marker.color', [colors0.slice()]); + }); + + return assertLabelsCorrect([351, 251], [358, 272], '2'); + }) + .then(unhover) + .then(function() { + expect(hoverCnt).toEqual(1); + expect(unHoverCnt).toEqual(1); + + return assertLabelsCorrect([400, 200], [435, 198], '3'); + }) + .then(unhover) + .then(function() { + expect(hoverCnt).toEqual(2); + expect(unHoverCnt).toEqual(2); + }) + .then(done); + + }); });