diff --git a/src/components/selections/select.js b/src/components/selections/select.js index 3e0455c2079..9155f5417cd 100644 --- a/src/components/selections/select.js +++ b/src/components/selections/select.js @@ -174,6 +174,8 @@ function prepSelect(evt, startX, startY, dragOptions, mode) { } if(selectionErased) { + gd._fullLayout._noEmitSelectedAtStart = true; + Registry.call('_guiRelayout', gd, { selections: list }); @@ -315,10 +317,10 @@ function prepSelect(evt, startX, startY, dragOptions, mode) { displayOutlines(convertPoly(mergedPolygons, isOpenMode), outlines, dragOptions); if(isSelectMode) { - var _res = reselect(gd); + var _res = reselect(gd, false); var extraPoints = _res.eventData ? _res.eventData.points.slice() : []; - _res = reselect(gd, selectionTesters, searchTraces, dragOptions); + _res = reselect(gd, false, selectionTesters, searchTraces, dragOptions); selectionTesters = _res.selectionTesters; eventData = _res.eventData; @@ -412,9 +414,13 @@ function prepSelect(evt, startX, startY, dragOptions, mode) { } } - Registry.call('_guiRelayout', gd, { - selections: subSelections - }); + if(subSelections.length < allSelections.length) { + gd._fullLayout._noEmitSelectedAtStart = true; + + Registry.call('_guiRelayout', gd, { + selections: subSelections + }); + } } } } else { @@ -733,6 +739,8 @@ function clearSelectionsCache(dragOptions, immediateSelect) { selections = newSelections(outlines, dragOptions); } if(selections) { + gd._fullLayout._noEmitSelectedAtStart = true; + Registry.call('_guiRelayout', gd, { selections: selections }).then(function() { @@ -1070,7 +1078,7 @@ function _doSelect(selectionTesters, searchTraces) { return allSelections; } -function reselect(gd, selectionTesters, searchTraces, dragOptions) { +function reselect(gd, mayEmitSelected, selectionTesters, searchTraces, dragOptions) { var hadSearchTraces = !!searchTraces; var plotinfo, xRef, yRef; if(dragOptions) { @@ -1193,15 +1201,15 @@ function reselect(gd, selectionTesters, searchTraces, dragOptions) { updateSelectedState(gd, allSearchTraces, eventData); var clickmode = fullLayout.clickmode; - var sendEvents = clickmode.indexOf('event') > -1; + var sendEvents = clickmode.indexOf('event') > -1 && mayEmitSelected; if( !plotinfo && // get called from plot_api & plots - fullLayout._reselect + mayEmitSelected ) { - if(sendEvents) { - var activePolygons = getLayoutPolygons(gd, true); + var activePolygons = getLayoutPolygons(gd, true); + if(activePolygons.length) { var xref = activePolygons[0].xref; var yref = activePolygons[0].yref; if(xref && yref) { @@ -1214,8 +1222,12 @@ function reselect(gd, selectionTesters, searchTraces, dragOptions) { fillRangeItems(eventData, poly); } + } - emitSelected(gd, eventData); + if(gd._fullLayout._noEmitSelectedAtStart) { + gd._fullLayout._noEmitSelectedAtStart = false; + } else { + if(sendEvents) emitSelected(gd, eventData); } fullLayout._reselect = false; @@ -1237,7 +1249,7 @@ function reselect(gd, selectionTesters, searchTraces, dragOptions) { if(eventData.points.length) { emitSelected(gd, eventData); } else { - gd.emit('plotly_deselect', null); + emitDeselect(gd); } } diff --git a/src/plots/plots.js b/src/plots/plots.js index d31fbc26cea..e7018f70647 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -3364,7 +3364,16 @@ plots.redrag = function(gd) { }; plots.reselect = function(gd) { - Registry.getComponentMethod('selections', 'reselect')(gd); + var fullLayout = gd._fullLayout; + + var A = (gd.layout || {}).selections; + var B = fullLayout._previousSelections; + fullLayout._previousSelections = A; + + var mayEmitSelected = fullLayout._reselect || + JSON.stringify(A) !== JSON.stringify(B); + + Registry.getComponentMethod('selections', 'reselect')(gd, mayEmitSelected); }; plots.generalUpdatePerTraceModule = function(gd, subplot, subplotCalcData, subplotLayout) { diff --git a/test/jasmine/tests/draw_newselection_test.js b/test/jasmine/tests/draw_newselection_test.js index 1ab1625cfbe..c400e376d50 100644 --- a/test/jasmine/tests/draw_newselection_test.js +++ b/test/jasmine/tests/draw_newselection_test.js @@ -425,6 +425,12 @@ describe('Activate and edit selections', function() { ['mouse'].forEach(function(device) { it('reactangle using ' + device, function(done) { + var range; + var lassoPoints; + var points; + var _selections; + var selectedCnt = 0; + var i = 0; // selection index Plotly.newPlot(gd, { @@ -432,6 +438,15 @@ describe('Activate and edit selections', function() { layout: fig.layout, config: fig.config }) + .then(function() { + gd.on('plotly_selected', function(d) { + range = d.range; + lassoPoints = d.lassoPoints; + points = d.points; + _selections = d.selections; + selectedCnt++; + }); + }) // selection between 175, 160 and 255, 230 .then(function() { click(210, 160); }) // activate selection @@ -454,6 +469,8 @@ describe('Activate and edit selections', function() { 'x1': 75, 'y1': 75 }); + + expect(selectedCnt).toEqual(0); }) .then(function() { drag([[255, 230], [300, 200]]); }) // move vertex .then(function() { @@ -475,6 +492,12 @@ describe('Activate and edit selections', function() { 'x1': 102.90852713178295, 'y1': 53.63323442136499 }); + + expect(selectedCnt).toEqual(1); + expect(points).not.toBeUndefined(); + expect(_selections).not.toBeUndefined(); + expect(range).not.toBeUndefined(); + expect(lassoPoints).toBeUndefined(); }) .then(function() { drag([[300, 200], [255, 230]]); }) // move vertex back .then(function() { @@ -496,6 +519,12 @@ describe('Activate and edit selections', function() { 'x1': 75, 'y1': 75 }); + + expect(selectedCnt).toEqual(2); + expect(points).not.toBeUndefined(); + expect(_selections).not.toBeUndefined(); + expect(range).not.toBeUndefined(); + expect(lassoPoints).toBeUndefined(); }) .then(function() { drag([[215, 195], [300, 200]]); }) // move selection .then(function() { @@ -517,6 +546,12 @@ describe('Activate and edit selections', function() { 'x1': 127.71472868217053, 'y1': 74.99821958456974 }); + + expect(selectedCnt).toEqual(3); + expect(points).not.toBeUndefined(); + expect(_selections).not.toBeUndefined(); + expect(range).not.toBeUndefined(); + expect(lassoPoints).toBeUndefined(); }) .then(function() { drag([[300, 200], [215, 195]]); }) // move selection back .then(function() { @@ -538,12 +573,24 @@ describe('Activate and edit selections', function() { 'x1': 75, 'y1': 75 }); + + expect(selectedCnt).toEqual(4); + expect(points).not.toBeUndefined(); + expect(_selections).not.toBeUndefined(); + expect(range).not.toBeUndefined(); + expect(lassoPoints).toBeUndefined(); }) .then(done, done.fail); }); it('closed-path using ' + device, function(done) { + var range; + var lassoPoints; + var points; + var _selections; + var selectedCnt = 0; + var i = 1; // selection index Plotly.newPlot(gd, { @@ -552,6 +599,16 @@ describe('Activate and edit selections', function() { config: fig.config }) + .then(function() { + gd.on('plotly_selected', function(d) { + range = d.range; + lassoPoints = d.lassoPoints; + points = d.points; + _selections = d.selections; + selectedCnt++; + }); + }) + // next selection .then(function() { click(500, 225); }) // activate selection .then(function() { @@ -562,6 +619,8 @@ describe('Activate and edit selections', function() { var obj = selections[id]._input; print(obj); assertPos(obj.path, 'M250,25L225,75L275,75Z'); + + expect(selectedCnt).toEqual(0); }) .then(function() { drag([[540, 160], [500, 120]]); }) // move vertex .then(function() { @@ -572,6 +631,12 @@ describe('Activate and edit selections', function() { var obj = selections[id]._input; print(obj); assertPos(obj.path, 'M225.1968992248062,-3.4896142433234463L225,75L275,75Z'); + + expect(selectedCnt).toEqual(1); + expect(points).not.toBeUndefined(); + expect(_selections).not.toBeUndefined(); + expect(lassoPoints).not.toBeUndefined(); + expect(range).toBeUndefined(); }) .then(function() { drag([[500, 120], [540, 160]]); }) // move vertex back .then(function() { @@ -582,6 +647,12 @@ describe('Activate and edit selections', function() { var obj = selections[id]._input; print(obj); assertPos(obj.path, 'M250,25L225,75L275,75Z'); + + expect(selectedCnt).toEqual(2); + expect(points).not.toBeUndefined(); + expect(_selections).not.toBeUndefined(); + expect(lassoPoints).not.toBeUndefined(); + expect(range).toBeUndefined(); }) .then(done, done.fail); diff --git a/test/jasmine/tests/selections_test.js b/test/jasmine/tests/selections_test.js index 066d6066b5e..0f69be88d7c 100644 --- a/test/jasmine/tests/selections_test.js +++ b/test/jasmine/tests/selections_test.js @@ -260,3 +260,64 @@ describe('Test selections:', function() { }); }); }); + +describe('Emit plotly_selected when plot a graph that has selections', function() { + 'use strict'; + + var gd; + var points; + var selections; + var selectedCnt = 0; + + beforeEach(function() { + gd = createGraphDiv(); + }); + + afterEach(destroyGraphDiv); + + it('emit plotly_selected on react calls', function(done) { + var data = [{y: [1, 2, 3]}]; + + Plotly.newPlot(gd, data, {}) + .then(function() { + gd.on('plotly_selected', function(d) { + points = d.points; + selections = d.selections; + selectedCnt++; + }); + }) + .then(function() { + return Plotly.react(gd, data, { + selections: [{ x0: 0.5, x1: 1.5, y0: 1.5, y1: 2.5}] + }); + }) + .then(function() { + expect(selectedCnt).toEqual(1); + expect(points).not.toBeUndefined(); + expect(selections).not.toBeUndefined(); + expect(selections.length).toEqual(1); + expect(selections[0].x0).toEqual(0.5); + }) + .then(function() { + return Plotly.react(gd, data, { + selections: [{ x0: 0.5, x1: 1.5, y0: 1.5, y1: 2.5}] // same selections + }); + }) + .then(function() { + expect(selectedCnt).toEqual(1); + }) + .then(function() { + return Plotly.react(gd, data, { + selections: [{ x0: 0.25, x1: 1.75, y0: 1.25, y1: 2.25}] // different selections + }); + }) + .then(function() { + expect(selectedCnt).toEqual(2); + expect(points).not.toBeUndefined(); + expect(selections).not.toBeUndefined(); + expect(selections.length).toEqual(1); + expect(selections[0].x0).toEqual(0.25); + }) + .then(done, done.fail); + }); +});