From 580d5fe5ef2fdc140551dd8982411d3699700662 Mon Sep 17 00:00:00 2001 From: Nicolas Riesco Date: Wed, 15 Mar 2017 12:23:38 +0000 Subject: [PATCH 01/27] events: plotly_click, plotly_hover, plotly_unhover * Include the mouse event in the event data returned by `plotly_click`, `plotly_hover` and `plotly_unhover`. --- src/components/dragelement/index.js | 3 +-- src/components/dragelement/unhover.js | 5 ++++- src/plots/cartesian/graph_interact.js | 8 ++++++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/components/dragelement/index.js b/src/components/dragelement/index.js index 156382b6c5d..7a0e9afa62c 100644 --- a/src/components/dragelement/index.js +++ b/src/components/dragelement/index.js @@ -140,8 +140,7 @@ dragElement.init = function init(options) { if(options.doneFn) options.doneFn(gd._dragged, numClicks, e); if(!gd._dragged) { - var e2 = document.createEvent('MouseEvents'); - e2.initEvent('click', true, true); + var e2 = new MouseEvent('click', e); initialTarget.dispatchEvent(e2); } diff --git a/src/components/dragelement/unhover.js b/src/components/dragelement/unhover.js index 4caf14ce456..1905cdf6825 100644 --- a/src/components/dragelement/unhover.js +++ b/src/components/dragelement/unhover.js @@ -44,6 +44,9 @@ unhover.raw = function unhoverRaw(gd, evt) { gd._hoverdata = undefined; if(evt.target && oldhoverdata) { - gd.emit('plotly_unhover', {points: oldhoverdata}); + gd.emit('plotly_unhover', { + event: evt, + points: oldhoverdata + }); } }; diff --git a/src/plots/cartesian/graph_interact.js b/src/plots/cartesian/graph_interact.js index 85f0130a5e8..7016656f8e9 100644 --- a/src/plots/cartesian/graph_interact.js +++ b/src/plots/cartesian/graph_interact.js @@ -624,10 +624,14 @@ function hover(gd, evt, subplot) { if(!hoverChanged(gd, evt, oldhoverdata)) return; if(oldhoverdata) { - gd.emit('plotly_unhover', { points: oldhoverdata }); + gd.emit('plotly_unhover', { + event: evt, + points: oldhoverdata + }); } gd.emit('plotly_hover', { + event: evt, points: gd._hoverdata, xaxes: xaArray, yaxes: yaArray, @@ -1350,7 +1354,7 @@ function hoverChanged(gd, evt, oldhoverdata) { fx.click = function(gd, evt) { var annotationsDone = Registry.getComponentMethod('annotations', 'onClick')(gd, gd._hoverdata); - function emitClick() { gd.emit('plotly_click', {points: gd._hoverdata}); } + function emitClick() { gd.emit('plotly_click', {points: gd._hoverdata, event: evt}); } if(gd._hoverdata && evt && evt.target) { if(annotationsDone && annotationsDone.then) { From fe4239bb4cb9190ae500d55ad1eaa87815875e3a Mon Sep 17 00:00:00 2001 From: Nicolas Riesco Date: Wed, 15 Mar 2017 15:04:32 +0000 Subject: [PATCH 02/27] events: update tests * Test event property in `plotly_click`, `plotly_hover` and `plotly_unhover`. --- test/jasmine/tests/click_test.js | 55 ++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/test/jasmine/tests/click_test.js b/test/jasmine/tests/click_test.js index 9e13b9ffd12..4776f39b87f 100644 --- a/test/jasmine/tests/click_test.js +++ b/test/jasmine/tests/click_test.js @@ -39,6 +39,17 @@ describe('Test click interactions:', function() { afterEach(destroyGraphDiv); + function move(fromX, fromY, toX, toY, delay) { + return new Promise(function(resolve) { + mouseEvent('mousemove', fromX, fromY); + + setTimeout(function() { + mouseEvent('mousemove', toX, toY); + resolve(); + }, delay || DBLCLICKDELAY / 4); + }); + } + function drag(fromX, fromY, toX, toY, delay) { return new Promise(function(resolve) { mouseEvent('mousemove', fromX, fromY); @@ -87,6 +98,10 @@ describe('Test click interactions:', function() { expect(pt.pointNumber).toEqual(11); expect(pt.x).toEqual(0.125); expect(pt.y).toEqual(2.125); + + var evt = futureData.event; + expect(evt.clientX).toEqual(pointPos[0]); + expect(evt.clientY).toEqual(pointPos[1]); }); }); @@ -191,6 +206,46 @@ describe('Test click interactions:', function() { expect(pt.pointNumber).toEqual(11); expect(pt.x).toEqual(0.125); expect(pt.y).toEqual(2.125); + + var evt = futureData.event; + expect(evt.clientX).toEqual(pointPos[0]); + expect(evt.clientY).toEqual(pointPos[1]); + }); + }); + + describe('plotly_unhover event with hoverinfo set to none', function() { + var futureData; + + beforeEach(function(done) { + + var modifiedMockCopy = Lib.extendDeep({}, mockCopy); + modifiedMockCopy.data[0].hoverinfo = 'none'; + Plotly.plot(gd, modifiedMockCopy.data, modifiedMockCopy.layout) + .then(done); + + gd.on('plotly_unhover', function(data) { + futureData = data; + }); + }); + + it('should contain the correct fields despite hoverinfo: "none"', function(done) { + move(pointPos[0], pointPos[1], blankPos[0], blankPos[1]).then(function() { + expect(futureData.points.length).toEqual(1); + + var pt = futureData.points[0]; + expect(Object.keys(pt)).toEqual([ + 'data', 'fullData', 'curveNumber', 'pointNumber', + 'x', 'y', 'xaxis', 'yaxis' + ]); + expect(pt.curveNumber).toEqual(0); + expect(pt.pointNumber).toEqual(11); + expect(pt.x).toEqual(0.125); + expect(pt.y).toEqual(2.125); + + var evt = futureData.event; + expect(evt.clientX).toEqual(blankPos[0]); + expect(evt.clientY).toEqual(blankPos[1]); + }).then(done); }); }); From 7a90972ad9af46ec4ec902d84f9ebfe98d15eb34 Mon Sep 17 00:00:00 2001 From: Nicolas Riesco Date: Thu, 16 Mar 2017 12:28:12 +0000 Subject: [PATCH 03/27] events: test modified clicks --- test/jasmine/assets/click.js | 8 +++--- test/jasmine/assets/mouse_event.js | 12 ++++++++ test/jasmine/tests/click_test.js | 45 ++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 4 deletions(-) diff --git a/test/jasmine/assets/click.js b/test/jasmine/assets/click.js index e2cc43e444b..30d25ece655 100644 --- a/test/jasmine/assets/click.js +++ b/test/jasmine/assets/click.js @@ -1,7 +1,7 @@ var mouseEvent = require('./mouse_event'); -module.exports = function click(x, y) { - mouseEvent('mousemove', x, y); - mouseEvent('mousedown', x, y); - mouseEvent('mouseup', x, y); +module.exports = function click(x, y, opts) { + mouseEvent('mousemove', x, y, opts); + mouseEvent('mousedown', x, y, opts); + mouseEvent('mouseup', x, y, opts); }; diff --git a/test/jasmine/assets/mouse_event.js b/test/jasmine/assets/mouse_event.js index 153314c5abd..776524a84a1 100644 --- a/test/jasmine/assets/mouse_event.js +++ b/test/jasmine/assets/mouse_event.js @@ -9,6 +9,18 @@ module.exports = function(type, x, y, opts) { if(opts && opts.buttons) { fullOpts.buttons = opts.buttons; } + if(opts && opts.altKey) { + fullOpts.altKey = opts.altKey; + } + if(opts && opts.ctrlKey) { + fullOpts.ctrlKey = opts.ctrlKey; + } + if(opts && opts.metaKey) { + fullOpts.metaKey = opts.metaKey; + } + if(opts && opts.shiftKey) { + fullOpts.shiftKey = opts.shiftKey; + } var el = (opts && opts.element) || document.elementFromPoint(x, y), ev; diff --git a/test/jasmine/tests/click_test.js b/test/jasmine/tests/click_test.js index 4776f39b87f..fba26587b68 100644 --- a/test/jasmine/tests/click_test.js +++ b/test/jasmine/tests/click_test.js @@ -105,6 +105,51 @@ describe('Test click interactions:', function() { }); }); + describe('modified click events', function() { + var clickOpts = { + altKey: true, + ctrlKey: true, + metaKey: true, + shiftKey: true + }, + futureData; + + beforeEach(function(done) { + Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); + + gd.on('plotly_click', function(data) { + futureData = data; + }); + }); + + it('should not be trigged when not on data points', function() { + click(blankPos[0], blankPos[1], clickOpts); + expect(futureData).toBe(undefined); + }); + + it('should contain the correct fields', function() { + click(pointPos[0], pointPos[1], clickOpts); + expect(futureData.points.length).toEqual(1); + + var pt = futureData.points[0]; + expect(Object.keys(pt)).toEqual([ + 'data', 'fullData', 'curveNumber', 'pointNumber', + 'x', 'y', 'xaxis', 'yaxis' + ]); + expect(pt.curveNumber).toEqual(0); + expect(pt.pointNumber).toEqual(11); + expect(pt.x).toEqual(0.125); + expect(pt.y).toEqual(2.125); + + var evt = futureData.event; + expect(evt.clientX).toEqual(pointPos[0]); + expect(evt.clientY).toEqual(pointPos[1]); + Object.getOwnPropertyNames(clickOpts).forEach(function(opt) { + expect(evt[opt]).toEqual(clickOpts[opt], opt); + }); + }); + }); + describe('click event with hoverinfo set to skip - plotly_click', function() { var futureData = null; From aea866efd5e93765e6f1fc876b11fa7d941e1dde Mon Sep 17 00:00:00 2001 From: Nicolas Riesco Date: Thu, 16 Mar 2017 12:44:11 +0000 Subject: [PATCH 04/27] events: do not use `new MouseEvent(evt)` * Keep compatibility with IE9. --- src/components/dragelement/index.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/components/dragelement/index.js b/src/components/dragelement/index.js index 7a0e9afa62c..ce63dad9b10 100644 --- a/src/components/dragelement/index.js +++ b/src/components/dragelement/index.js @@ -140,7 +140,13 @@ dragElement.init = function init(options) { if(options.doneFn) options.doneFn(gd._dragged, numClicks, e); if(!gd._dragged) { - var e2 = new MouseEvent('click', e); + var e2 = document.createEvent('MouseEvents'); + e2.initMouseEvent('click', true, true, + e.view, e.detail, + e.screenX, e.screenY, + e.clientX, e.clientY, + e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, + e.button, e.relatedTarget); initialTarget.dispatchEvent(e2); } From e809b23c178508917e83734d52db7f73416f6783 Mon Sep 17 00:00:00 2001 From: Nicolas Riesco Date: Thu, 16 Mar 2017 14:52:29 +0000 Subject: [PATCH 05/27] events: try-catch `new MouseEvent(type, evt)` * Since `initMouseEvent` is deprecated, we try `new MouseEvent()` first. --- src/components/dragelement/index.js | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/components/dragelement/index.js b/src/components/dragelement/index.js index ce63dad9b10..a748a37310d 100644 --- a/src/components/dragelement/index.js +++ b/src/components/dragelement/index.js @@ -140,13 +140,22 @@ dragElement.init = function init(options) { if(options.doneFn) options.doneFn(gd._dragged, numClicks, e); if(!gd._dragged) { - var e2 = document.createEvent('MouseEvents'); - e2.initMouseEvent('click', true, true, - e.view, e.detail, - e.screenX, e.screenY, - e.clientX, e.clientY, - e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, - e.button, e.relatedTarget); + var e2; + + try { + e2 = new MouseEvent('click', e); + } + catch(err) { + e2 = document.createEvent('MouseEvents'); + e2.initMouseEvent('click', + e.bubbles, e.cancelable, + e.view, e.detail, + e.screenX, e.screenY, + e.clientX, e.clientY, + e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, + e.button, e.relatedTarget); + } + initialTarget.dispatchEvent(e2); } From 2e6fb16d79cb67f1fefdec35822faf48703b3d85 Mon Sep 17 00:00:00 2001 From: Nicolas Riesco Date: Fri, 17 Mar 2017 15:15:56 +0000 Subject: [PATCH 06/27] test: update helper function click * Added click event after mouseup, otherwise click events in pie plots don't get triggered. --- test/jasmine/assets/click.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/jasmine/assets/click.js b/test/jasmine/assets/click.js index 30d25ece655..a1dff1f1d6f 100644 --- a/test/jasmine/assets/click.js +++ b/test/jasmine/assets/click.js @@ -4,4 +4,5 @@ module.exports = function click(x, y, opts) { mouseEvent('mousemove', x, y, opts); mouseEvent('mousedown', x, y, opts); mouseEvent('mouseup', x, y, opts); + mouseEvent('click', x, y, opts); }; From 550a257e71c776e26a33d865b6f35e9ad6c5dee3 Mon Sep 17 00:00:00 2001 From: Nicolas Riesco Date: Fri, 17 Mar 2017 15:19:22 +0000 Subject: [PATCH 07/27] events: test plotly_click in a pie plot --- test/jasmine/tests/click_test.js | 201 +++++++++++++++++++++++++++---- 1 file changed, 177 insertions(+), 24 deletions(-) diff --git a/test/jasmine/tests/click_test.js b/test/jasmine/tests/click_test.js index fba26587b68..fa0e9191d8a 100644 --- a/test/jasmine/tests/click_test.js +++ b/test/jasmine/tests/click_test.js @@ -17,6 +17,31 @@ var click = require('../assets/click'); var doubleClickRaw = require('../assets/double_click'); +function move(fromX, fromY, toX, toY, delay) { + return new Promise(function(resolve) { + mouseEvent('mousemove', fromX, fromY); + + setTimeout(function() { + mouseEvent('mousemove', toX, toY); + resolve(); + }, delay || DBLCLICKDELAY / 4); + }); +} + +function drag(fromX, fromY, toX, toY, delay) { + return new Promise(function(resolve) { + mouseEvent('mousemove', fromX, fromY); + mouseEvent('mousedown', fromX, fromY); + mouseEvent('mousemove', toX, toY); + + setTimeout(function() { + mouseEvent('mouseup', toX, toY); + resolve(); + }, delay || DBLCLICKDELAY / 4); + }); +} + + describe('Test click interactions:', function() { var mock = require('@mocks/14.json'); @@ -39,30 +64,6 @@ describe('Test click interactions:', function() { afterEach(destroyGraphDiv); - function move(fromX, fromY, toX, toY, delay) { - return new Promise(function(resolve) { - mouseEvent('mousemove', fromX, fromY); - - setTimeout(function() { - mouseEvent('mousemove', toX, toY); - resolve(); - }, delay || DBLCLICKDELAY / 4); - }); - } - - function drag(fromX, fromY, toX, toY, delay) { - return new Promise(function(resolve) { - mouseEvent('mousemove', fromX, fromY); - mouseEvent('mousedown', fromX, fromY); - mouseEvent('mousemove', toX, toY); - - setTimeout(function() { - mouseEvent('mouseup', toX, toY); - resolve(); - }, delay || DBLCLICKDELAY / 4); - }); - } - function doubleClick(x, y) { return doubleClickRaw(x, y).then(function() { return Plotly.Plots.previousPromises(gd); @@ -917,6 +918,158 @@ describe('Test click interactions:', function() { }); }); + +describe('Test click interactions on a pie plot:', function() { + var mock = require('@mocks/pie_simple.json'); + + var mockCopy, gd; + + var blankPos = [10, 10], + pointPos; + + function getPos(selector, index) { + index = index || 0; + var selection = document.querySelectorAll(selector), + clientPos = selection[index].getBoundingClientRect(), + x = Math.floor((clientPos.left + clientPos.right) / 2), + y = Math.floor((clientPos.top + clientPos.bottom) / 2); + return [x, y]; + } + + beforeAll(function(done) { + jasmine.addMatchers(customMatchers); + + gd = createGraphDiv(); + mockCopy = Lib.extendDeep({}, mock); + Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(function() { + pointPos = getPos('g.slicetext'); + destroyGraphDiv(); + done(); + }); + }); + + beforeEach(function() { + gd = createGraphDiv(); + mockCopy = Lib.extendDeep({}, mock); + }); + + afterEach(destroyGraphDiv); + + describe('click events', function() { + var futureData; + + beforeEach(function(done) { + Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); + + gd.on('plotly_click', function(data) { + futureData = data; + }); + }); + + it('should not be trigged when not on data points', function() { + click(blankPos[0], blankPos[1]); + expect(futureData).toBe(undefined); + }); + + it('should contain the correct fields', function() { + click(pointPos[0], pointPos[1]); + expect(futureData.points.length).toEqual(1); + + var pt = futureData.points[0]; + expect(Object.keys(pt)).toEqual([ + 'v', 'label', 'color', 'i', 'hidden', 'vTotal', 'text', 't', + 'trace', 'r', 'cx', 'cy', 'px1', 'pxmid', 'midangle', 'px0', + 'largeArc', 'cxFinal', 'cyFinal' + ]); + expect(typeof pt.color).toEqual(typeof '#1f77b4', 'points[0].color'); + expect(pt.cx).toEqual(200, 'points[0].cx'); + expect(pt.cxFinal).toEqual(200, 'points[0].cxFinal'); + expect(pt.cy).toEqual(160, 'points[0].cy'); + expect(pt.cyFinal).toEqual(160, 'points[0].cyFinal'); + expect(pt.hidden).toEqual(false, 'points[0].hidden'); + expect(pt.i).toEqual(4, 'points[0].i'); + expect(pt.label).toEqual('4', 'points[0].label'); + expect(pt.largeArc).toEqual(0, 'points[0].largeArc'); + expect(pt.midangle).toEqual(1.0471975511965976, 'points[0].midangle'); + expect(pt.px0).toEqual([0, -60], 'points[0].px0'); + expect(pt.px1).toEqual([51.96152422706632, 29.999999999999986], 'points[0].px1'); + expect(pt.pxmid).toEqual([51.96152422706631, -30.000000000000007], 'points[0].pxmid'); + expect(pt.r).toEqual(60, 'points[0].r'); + expect(typeof pt.t).toEqual(typeof {}, 'points[0].t'); + expect(pt.text).toEqual('33.3%', 'points[0].text'); + expect(typeof pt.trace).toEqual(typeof {}, 'points[0].trace'); + expect(pt.v).toEqual(5, 'points[0].v'); + expect(pt.vTotal).toEqual(15, 'points[0].vTotal'); + + var evt = futureData.event; + expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); + expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); + }); + }); + + describe('modified click events', function() { + var clickOpts = { + altKey: true, + ctrlKey: true, + metaKey: true, + shiftKey: true + }, + futureData; + + beforeEach(function(done) { + Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); + + gd.on('plotly_click', function(data) { + futureData = data; + }); + }); + + it('should not be trigged when not on data points', function() { + click(blankPos[0], blankPos[1], clickOpts); + expect(futureData).toBe(undefined); + }); + + it('should contain the correct fields', function() { + click(pointPos[0], pointPos[1], clickOpts); + expect(futureData.points.length).toEqual(1); + + var pt = futureData.points[0]; + expect(Object.keys(pt)).toEqual([ + 'v', 'label', 'color', 'i', 'hidden', 'vTotal', 'text', 't', + 'trace', 'r', 'cx', 'cy', 'px1', 'pxmid', 'midangle', 'px0', + 'largeArc', 'cxFinal', 'cyFinal' + ]); + expect(typeof pt.color).toEqual(typeof '#1f77b4', 'points[0].color'); + expect(pt.cx).toEqual(200, 'points[0].cx'); + expect(pt.cxFinal).toEqual(200, 'points[0].cxFinal'); + expect(pt.cy).toEqual(160, 'points[0].cy'); + expect(pt.cyFinal).toEqual(160, 'points[0].cyFinal'); + expect(pt.hidden).toEqual(false, 'points[0].hidden'); + expect(pt.i).toEqual(4, 'points[0].i'); + expect(pt.label).toEqual('4', 'points[0].label'); + expect(pt.largeArc).toEqual(0, 'points[0].largeArc'); + expect(pt.midangle).toEqual(1.0471975511965976, 'points[0].midangle'); + expect(pt.px0).toEqual([0, -60], 'points[0].px0'); + expect(pt.px1).toEqual([51.96152422706632, 29.999999999999986], 'points[0].px1'); + expect(pt.pxmid).toEqual([51.96152422706631, -30.000000000000007], 'points[0].pxmid'); + expect(pt.r).toEqual(60, 'points[0].r'); + expect(typeof pt.t).toEqual(typeof {}, 'points[0].t'); + expect(pt.text).toEqual('33.3%', 'points[0].text'); + expect(typeof pt.trace).toEqual(typeof {}, 'points[0].trace'); + expect(pt.v).toEqual(5, 'points[0].v'); + expect(pt.vTotal).toEqual(15, 'points[0].vTotal'); + + var evt = futureData.event; + expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); + expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); + Object.getOwnPropertyNames(clickOpts).forEach(function(opt) { + expect(evt[opt]).toEqual(clickOpts[opt], 'event.' + opt); + }); + }); + }); +}); + + describe('dragbox', function() { afterEach(destroyGraphDiv); From d7e56cf8d7a8139bf132d7bc9d57d699ebec26b9 Mon Sep 17 00:00:00 2001 From: Nicolas Riesco Date: Fri, 17 Mar 2017 15:20:10 +0000 Subject: [PATCH 08/27] events: fix plotly_click in pie plots --- src/traces/pie/plot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/traces/pie/plot.js b/src/traces/pie/plot.js index 1d58dd2d9b4..a2c8b3905fb 100644 --- a/src/traces/pie/plot.js +++ b/src/traces/pie/plot.js @@ -145,7 +145,7 @@ module.exports = function plot(gd, cdpie) { function handleClick() { gd._hoverdata = [pt]; gd._hoverdata.trace = cd.trace; - Fx.click(gd, { target: true }); + Fx.click(gd, d3.event); } slicePath.enter().append('path') From 40dc52292375f9499e2719d880f63e07c3a7c727 Mon Sep 17 00:00:00 2001 From: Nicolas Riesco Date: Fri, 17 Mar 2017 16:47:24 +0000 Subject: [PATCH 09/27] events: test plotly_hover in pie plots --- test/jasmine/tests/click_test.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test/jasmine/tests/click_test.js b/test/jasmine/tests/click_test.js index fa0e9191d8a..31f3c2d3b55 100644 --- a/test/jasmine/tests/click_test.js +++ b/test/jasmine/tests/click_test.js @@ -1067,6 +1067,28 @@ describe('Test click interactions on a pie plot:', function() { }); }); }); + + describe('hover events', function() { + var futureData; + + beforeEach(function(done) { + Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); + + gd.on('plotly_hover', function(data) { + futureData = data; + }); + }); + + it('should contain the correct fields', function() { + mouseEvent('mouseover', pointPos[0], pointPos[1]); + + var point0 = futureData.points[0], + evt = futureData.event; + expect(point0).toEqual(evt, 'points'); + expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); + expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); + }); + }); }); From 85f72fffce6e0f8ead618b2e02bb12804adb8454 Mon Sep 17 00:00:00 2001 From: Nicolas Riesco Date: Fri, 17 Mar 2017 16:47:59 +0000 Subject: [PATCH 10/27] events: add event to plotly_hover --- src/plots/cartesian/graph_interact.js | 1 + src/traces/pie/plot.js | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/plots/cartesian/graph_interact.js b/src/plots/cartesian/graph_interact.js index 7016656f8e9..ae359e83037 100644 --- a/src/plots/cartesian/graph_interact.js +++ b/src/plots/cartesian/graph_interact.js @@ -305,6 +305,7 @@ fx.hover = function(gd, evt, subplot) { function hover(gd, evt, subplot) { if(subplot === 'pie') { gd.emit('plotly_hover', { + event: evt, points: [evt] }); return; diff --git a/src/traces/pie/plot.js b/src/traces/pie/plot.js index a2c8b3905fb..13703451464 100644 --- a/src/traces/pie/plot.js +++ b/src/traces/pie/plot.js @@ -85,7 +85,9 @@ module.exports = function plot(gd, cdpie) { slicePath = sliceTop.selectAll('path.surface').data([pt]), hasHoverData = false; - function handleMouseOver(evt) { + function handleMouseOver() { + var evt = d3.event; + // in case fullLayout or fullData has changed without a replot var fullLayout2 = gd._fullLayout, trace2 = gd._fullData[trace.index], From 5f5bb9eeba244b44c152e3a82c2dffa41f90a617 Mon Sep 17 00:00:00 2001 From: Nicolas Riesco Date: Fri, 17 Mar 2017 17:23:07 +0000 Subject: [PATCH 11/27] events: test plotly_unhover in pie plots --- test/jasmine/tests/click_test.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test/jasmine/tests/click_test.js b/test/jasmine/tests/click_test.js index 31f3c2d3b55..f961d27aa90 100644 --- a/test/jasmine/tests/click_test.js +++ b/test/jasmine/tests/click_test.js @@ -1089,6 +1089,28 @@ describe('Test click interactions on a pie plot:', function() { expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); }); }); + + describe('unhover events', function() { + var futureData; + + beforeEach(function(done) { + Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); + + gd.on('plotly_unhover', function(data) { + futureData = data; + }); + }); + + it('should contain the correct fields', function() { + mouseEvent('mouseout', pointPos[0], pointPos[1]); + + var point0 = futureData.points[0], + evt = futureData.event; + expect(point0).toEqual(evt, 'points'); + expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); + expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); + }); + }); }); From 8d5697b853bbfeafa4767c111091c951e9b4cddd Mon Sep 17 00:00:00 2001 From: Nicolas Riesco Date: Fri, 17 Mar 2017 17:23:45 +0000 Subject: [PATCH 12/27] events: add event to plotly_unhover --- src/traces/pie/plot.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/traces/pie/plot.js b/src/traces/pie/plot.js index 13703451464..1e07c0af264 100644 --- a/src/traces/pie/plot.js +++ b/src/traces/pie/plot.js @@ -133,8 +133,11 @@ module.exports = function plot(gd, cdpie) { hasHoverData = true; } - function handleMouseOut(evt) { + function handleMouseOut() { + var evt = d3.event; + gd.emit('plotly_unhover', { + event: evt, points: [evt] }); From 9cf50c20d9d9ebf1f650cb1d904566fdca968286 Mon Sep 17 00:00:00 2001 From: Nicolas Riesco Date: Fri, 17 Mar 2017 19:28:14 +0000 Subject: [PATCH 13/27] events: test plotly events in ternary plots --- test/jasmine/tests/click_test.js | 224 +++++++++++++++++++++++++++++-- 1 file changed, 214 insertions(+), 10 deletions(-) diff --git a/test/jasmine/tests/click_test.js b/test/jasmine/tests/click_test.js index f961d27aa90..0e09c66795c 100644 --- a/test/jasmine/tests/click_test.js +++ b/test/jasmine/tests/click_test.js @@ -17,6 +17,15 @@ var click = require('../assets/click'); var doubleClickRaw = require('../assets/double_click'); +function getClientPosition(selector, index) { + index = index || 0; + var selection = document.querySelectorAll(selector), + clientPos = selection[index].getBoundingClientRect(), + x = Math.floor((clientPos.left + clientPos.right) / 2), + y = Math.floor((clientPos.top + clientPos.bottom) / 2); + return [x, y]; +} + function move(fromX, fromY, toX, toY, delay) { return new Promise(function(resolve) { mouseEvent('mousemove', fromX, fromY); @@ -927,22 +936,13 @@ describe('Test click interactions on a pie plot:', function() { var blankPos = [10, 10], pointPos; - function getPos(selector, index) { - index = index || 0; - var selection = document.querySelectorAll(selector), - clientPos = selection[index].getBoundingClientRect(), - x = Math.floor((clientPos.left + clientPos.right) / 2), - y = Math.floor((clientPos.top + clientPos.bottom) / 2); - return [x, y]; - } - beforeAll(function(done) { jasmine.addMatchers(customMatchers); gd = createGraphDiv(); mockCopy = Lib.extendDeep({}, mock); Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(function() { - pointPos = getPos('g.slicetext'); + pointPos = getClientPosition('g.slicetext'); destroyGraphDiv(); done(); }); @@ -1114,6 +1114,210 @@ describe('Test click interactions on a pie plot:', function() { }); +describe('Test click interactions on a ternary plot:', function() { + var mock = require('@mocks/ternary_simple.json'); + + var mockCopy, gd; + + var blankPos = [10, 10], + pointPos; + + beforeAll(function(done) { + jasmine.addMatchers(customMatchers); + + gd = createGraphDiv(); + mockCopy = Lib.extendDeep({}, mock); + Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(function() { + pointPos = getClientPosition('path.point'); + destroyGraphDiv(); + done(); + }); + }); + + beforeEach(function() { + gd = createGraphDiv(); + mockCopy = Lib.extendDeep({}, mock); + }); + + afterEach(destroyGraphDiv); + + describe('click events', function() { + var futureData; + + beforeEach(function(done) { + Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); + + gd.on('plotly_click', function(data) { + futureData = data; + }); + }); + + it('should not be trigged when not on data points', function() { + click(blankPos[0], blankPos[1]); + expect(futureData).toBe(undefined); + }); + + it('should contain the correct fields', function() { + click(pointPos[0], pointPos[1]); + + var pt = futureData.points[0], + evt = futureData.event; + + expect(Object.keys(pt)).toEqual([ + 'data', 'fullData', 'curveNumber', 'pointNumber', 'x', 'y', + 'xaxis', 'yaxis' + ]); + + expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); + expect(typeof pt.data).toEqual(typeof {}, 'points[0].data'); + expect(typeof pt.fullData).toEqual(typeof {}, 'points[0].fullData'); + expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); + expect(pt.x).toEqual(undefined, 'points[0].x'); + expect(pt.y).toEqual(undefined, 'points[0].y'); + expect(typeof pt.xaxis).toEqual(typeof {}, 'points[0].xaxis'); + expect(typeof pt.yaxis).toEqual(typeof {}, 'points[0].yaxis'); + + expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); + expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); + }); + }); + + describe('modified click events', function() { + var clickOpts = { + altKey: true, + ctrlKey: true, + metaKey: true, + shiftKey: true + }, + futureData; + + beforeEach(function(done) { + Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); + + gd.on('plotly_click', function(data) { + futureData = data; + }); + }); + + it('should not be trigged when not on data points', function() { + click(blankPos[0], blankPos[1], clickOpts); + expect(futureData).toBe(undefined); + }); + + it('should contain the correct fields', function() { + click(pointPos[0], pointPos[1], clickOpts); + + var pt = futureData.points[0], + evt = futureData.event; + + expect(Object.keys(pt)).toEqual([ + 'data', 'fullData', 'curveNumber', 'pointNumber', 'x', 'y', + 'xaxis', 'yaxis' + ]); + + expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); + expect(typeof pt.data).toEqual(typeof {}, 'points[0].data'); + expect(typeof pt.fullData).toEqual(typeof {}, 'points[0].fullData'); + expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); + expect(pt.x).toEqual(undefined, 'points[0].x'); + expect(pt.y).toEqual(undefined, 'points[0].y'); + expect(typeof pt.xaxis).toEqual(typeof {}, 'points[0].xaxis'); + expect(typeof pt.yaxis).toEqual(typeof {}, 'points[0].yaxis'); + + expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); + expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); + Object.getOwnPropertyNames(clickOpts).forEach(function(opt) { + expect(evt[opt]).toEqual(clickOpts[opt], 'event.' + opt); + }); + }); + }); + + describe('hover events', function() { + var futureData; + + beforeEach(function(done) { + Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); + + gd.on('plotly_hover', function(data) { + futureData = data; + }); + }); + + it('should contain the correct fields', function() { + mouseEvent('mousemove', blankPos[0], blankPos[1]); + mouseEvent('mousemove', pointPos[0], pointPos[1]); + + var pt = futureData.points[0], + evt = futureData.event, + xaxes0 = futureData.xaxes[0], + xvals0 = futureData.xvals[0], + yaxes0 = futureData.yaxes[0], + yvals0 = futureData.yvals[0]; + + expect(Object.keys(pt)).toEqual([ + 'data', 'fullData', 'curveNumber', 'pointNumber', 'x', 'y', + 'xaxis', 'yaxis' + ]); + + expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); + expect(typeof pt.data).toEqual(typeof {}, 'points[0].data'); + expect(typeof pt.fullData).toEqual(typeof {}, 'points[0].fullData'); + expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); + expect(pt.x).toEqual(undefined, 'points[0].x'); + expect(pt.y).toEqual(undefined, 'points[0].y'); + expect(typeof pt.xaxis).toEqual(typeof {}, 'points[0].xaxis'); + expect(typeof pt.yaxis).toEqual(typeof {}, 'points[0].yaxis'); + + expect(xaxes0).toEqual(pt.xaxis, 'xaxes[0]'); + expect(xvals0).toEqual(-0.0016654247744483342, 'xaxes[0]'); + expect(yaxes0).toEqual(pt.yaxis, 'yaxes[0]'); + expect(yvals0).toEqual(0.5013, 'xaxes[0]'); + + expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); + expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); + }); + }); + + describe('unhover events', function() { + var futureData; + + beforeEach(function(done) { + Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); + + gd.on('plotly_unhover', function(data) { + futureData = data; + }); + }); + + it('should contain the correct fields', function() { + mouseEvent('mousemove', blankPos[0], blankPos[1]); + mouseEvent('mousemove', pointPos[0], pointPos[1]); + mouseEvent('mouseout', pointPos[0], pointPos[1]); + + var pt = futureData.points[0], + evt = futureData.event; + + expect(Object.keys(pt)).toEqual([ + 'data', 'fullData', 'curveNumber', 'pointNumber', 'x', 'y', + 'xaxis', 'yaxis' + ]); + + expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); + expect(typeof pt.data).toEqual(typeof {}, 'points[0].data'); + expect(typeof pt.fullData).toEqual(typeof {}, 'points[0].fullData'); + expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); + expect(pt.x).toEqual(undefined, 'points[0].x'); + expect(pt.y).toEqual(undefined, 'points[0].y'); + expect(typeof pt.xaxis).toEqual(typeof {}, 'points[0].xaxis'); + expect(typeof pt.yaxis).toEqual(typeof {}, 'points[0].yaxis'); + + expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); + expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); + }); + }); +}); + + describe('dragbox', function() { afterEach(destroyGraphDiv); From 27eb8c390d360b5c5b823feefe69085ea27dd6a9 Mon Sep 17 00:00:00 2001 From: Nicolas Riesco Date: Fri, 17 Mar 2017 23:51:48 +0000 Subject: [PATCH 14/27] events: fix plotly events in geo plots --- src/plots/cartesian/graph_interact.js | 17 +++++++---------- src/plots/geo/geo.js | 10 ++++------ 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/src/plots/cartesian/graph_interact.js b/src/plots/cartesian/graph_interact.js index ae359e83037..2eac531091a 100644 --- a/src/plots/cartesian/graph_interact.js +++ b/src/plots/cartesian/graph_interact.js @@ -416,10 +416,14 @@ function hover(gd, evt, subplot) { // on the currently selected plot area var xpx, ypx; - // mouse event? ie is there a target element with - // clientX and clientY values? - if(evt.target && ('clientX' in evt) && ('clientY' in evt)) { + if('xpx' in evt || 'ypx' in evt) { + if('xpx' in evt) xpx = evt.xpx; + else xpx = xaArray[0]._length / 2; + if('ypx' in evt) ypx = evt.ypx; + else ypx = yaArray[0]._length / 2; + } + else { // fire the beforehover event and quit if it returns false // note that we're only calling this on real mouse events, so // manual calls to fx.hover will always run. @@ -438,13 +442,6 @@ function hover(gd, evt, subplot) { return dragElement.unhoverRaw(gd, evt); } } - else { - if('xpx' in evt) xpx = evt.xpx; - else xpx = xaArray[0]._length / 2; - - if('ypx' in evt) ypx = evt.ypx; - else ypx = yaArray[0]._length / 2; - } if('xval' in evt) xvalArray = flat(subplots, evt.xval); else xvalArray = p2c(xaArray, xpx); diff --git a/src/plots/geo/geo.js b/src/plots/geo/geo.js index 56dd3ce42cc..16d760872ae 100644 --- a/src/plots/geo/geo.js +++ b/src/plots/geo/geo.js @@ -91,11 +91,9 @@ proto.plot = function(geoCalcData, fullLayout, promises) { if(!lonlat || isNaN(lonlat[0]) || isNaN(lonlat[1])) return; - var evt = { - target: true, - xpx: mouse[0], - ypx: mouse[1] - }; + var evt = d3.event; + evt.xpx = mouse[0]; + evt.ypx = mouse[1]; _this.xaxis.c2p = function() { return mouse[0]; }; _this.xaxis.p2c = function() { return lonlat[0]; }; @@ -110,7 +108,7 @@ proto.plot = function(geoCalcData, fullLayout, promises) { }); _this.framework.on('click', function() { - Fx.click(_this.graphDiv, { target: true }); + Fx.click(_this.graphDiv, d3.event); }); topojsonNameNew = topojsonUtils.getTopojsonName(geoLayout); From 3584bbad29d1c23fbd450769176c5b664cd33f9e Mon Sep 17 00:00:00 2001 From: Nicolas Riesco Date: Fri, 17 Mar 2017 23:53:30 +0000 Subject: [PATCH 15/27] events: test plotly events in geo plots --- test/jasmine/tests/click_test.js | 192 +++++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) diff --git a/test/jasmine/tests/click_test.js b/test/jasmine/tests/click_test.js index 0e09c66795c..432723b275d 100644 --- a/test/jasmine/tests/click_test.js +++ b/test/jasmine/tests/click_test.js @@ -2,6 +2,7 @@ var Plotly = require('@lib/index'); var Lib = require('@src/lib'); var Drawing = require('@src/components/drawing'); var DBLCLICKDELAY = require('@src/constants/interactions').DBLCLICKDELAY; +var HOVERMINTIME = 50; var d3 = require('d3'); var createGraphDiv = require('../assets/create_graph_div'); @@ -1318,6 +1319,197 @@ describe('Test click interactions on a ternary plot:', function() { }); +describe('Test click interactions on a geo plot:', function() { + var mock = require('@mocks/geo_scattergeo-locations.json'); + + var mockCopy, gd; + + var blankPos = [10, 10], + pointPos, + nearPos; + + beforeAll(function(done) { + jasmine.addMatchers(customMatchers); + + gd = createGraphDiv(); + mockCopy = Lib.extendDeep({}, mock); + Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(function() { + pointPos = getClientPosition('path.point'); + nearPos = [pointPos[0] - 30, pointPos[1] - 30]; + destroyGraphDiv(); + done(); + }); + }); + + beforeEach(function() { + gd = createGraphDiv(); + mockCopy = Lib.extendDeep({}, mock); + }); + + afterEach(destroyGraphDiv); + + describe('click events', function() { + var futureData; + + beforeEach(function(done) { + Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); + + gd.on('plotly_click', function(data) { + futureData = data; + }); + }); + + it('should not be trigged when not on data points', function() { + click(blankPos[0], blankPos[1]); + expect(futureData).toBe(undefined); + }); + + it('should contain the correct fields', function() { + click(pointPos[0], pointPos[1]); + + var pt = futureData.points[0], + evt = futureData.event; + + expect(Object.keys(pt)).toEqual([ + 'data', 'fullData', 'curveNumber', 'pointNumber', 'lon', 'lat', + 'location' + ]); + + expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); + expect(typeof pt.data).toEqual(typeof {}, 'points[0].data'); + expect(typeof pt.fullData).toEqual(typeof {}, 'points[0].fullData'); + expect(pt.lat).toEqual(-101.57, 'points[0].lat'); + expect(pt.lon).toEqual(57.75, 'points[0].lon'); + expect(pt.location).toEqual(57.75, 'points[0].location'); + expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); + + expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); + expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); + }); + }); + + describe('modified click events', function() { + var clickOpts = { + altKey: true, + ctrlKey: true, + metaKey: true, + shiftKey: true + }, + futureData; + + beforeEach(function(done) { + Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); + + gd.on('plotly_click', function(data) { + futureData = data; + }); + }); + + it('should not be trigged when not on data points', function() { + click(blankPos[0], blankPos[1], clickOpts); + expect(futureData).toBe(undefined); + }); + + it('should contain the correct fields', function() { + click(pointPos[0], pointPos[1], clickOpts); + + var pt = futureData.points[0], + evt = futureData.event; + + expect(Object.keys(pt)).toEqual([ + 'data', 'fullData', 'curveNumber', 'pointNumber', 'lon', 'lat', + 'location' + ]); + + expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); + expect(typeof pt.data).toEqual(typeof {}, 'points[0].data'); + expect(typeof pt.fullData).toEqual(typeof {}, 'points[0].fullData'); + expect(pt.lat).toEqual(-101.57, 'points[0].lat'); + expect(pt.lon).toEqual(57.75, 'points[0].lon'); + expect(pt.location).toEqual(57.75, 'points[0].location'); + expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); + + expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); + expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); + Object.getOwnPropertyNames(clickOpts).forEach(function(opt) { + expect(evt[opt]).toEqual(clickOpts[opt], 'event.' + opt); + }); + }); + }); + + describe('hover events', function() { + var futureData; + + beforeEach(function(done) { + Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); + + gd.on('plotly_hover', function(data) { + futureData = data; + }); + }); + + it('should contain the correct fields', function() { + mouseEvent('mousemove', blankPos[0], blankPos[1]); + mouseEvent('mousemove', pointPos[0], pointPos[1]); + + var pt = futureData.points[0], + evt = futureData.event; + + expect(Object.keys(pt)).toEqual([ + 'data', 'fullData', 'curveNumber', 'pointNumber', 'lon', 'lat', + 'location' + ]); + + expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); + expect(typeof pt.data).toEqual(typeof {}, 'points[0].data'); + expect(typeof pt.fullData).toEqual(typeof {}, 'points[0].fullData'); + expect(pt.lat).toEqual(-101.57, 'points[0].lat'); + expect(pt.lon).toEqual(57.75, 'points[0].lon'); + expect(pt.location).toEqual(57.75, 'points[0].location'); + expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); + + expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); + expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); + }); + }); + + describe('unhover events', function() { + var futureData; + + beforeEach(function(done) { + Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); + + gd.on('plotly_unhover', function(data) { + futureData = data; + }); + }); + + it('should contain the correct fields', function(done) { + move(pointPos[0], pointPos[1], nearPos[0], nearPos[1], HOVERMINTIME + 10).then(function() { + var pt = futureData.points[0], + evt = futureData.event; + + expect(Object.keys(pt)).toEqual([ + 'data', 'fullData', 'curveNumber', 'pointNumber', 'lon', 'lat', + 'location' + ]); + + expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); + expect(typeof pt.data).toEqual(typeof {}, 'points[0].data'); + expect(typeof pt.fullData).toEqual(typeof {}, 'points[0].fullData'); + expect(pt.lat).toEqual(-101.57, 'points[0].lat'); + expect(pt.lon).toEqual(57.75, 'points[0].lon'); + expect(pt.location).toEqual(57.75, 'points[0].location'); + expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); + + expect(evt.clientX).toEqual(nearPos[0], 'event.clientX'); + expect(evt.clientY).toEqual(nearPos[1], 'event.clientY'); + }).then(done); + }); + }); +}); + + describe('dragbox', function() { afterEach(destroyGraphDiv); From 60ee15359301994cea9d112953d136ab544f22bf Mon Sep 17 00:00:00 2001 From: Nicolas Riesco Date: Mon, 20 Mar 2017 10:19:21 +0000 Subject: [PATCH 16/27] events: fix plotly events in mapbox plots --- src/plots/mapbox/mapbox.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plots/mapbox/mapbox.js b/src/plots/mapbox/mapbox.js index 522cf66b8a6..3c3185b1c44 100644 --- a/src/plots/mapbox/mapbox.js +++ b/src/plots/mapbox/mapbox.js @@ -166,8 +166,8 @@ proto.createMap = function(calcData, fullLayout, resolve, reject) { Fx.hover(gd, evt, self.id); }); - map.on('click', function() { - Fx.click(gd, { target: true }); + map.on('click', function(evt) { + Fx.click(gd, evt.originalEvent); }); function unhover() { From dcdda6fbdc637de8a7f21d2b0a356d7a34088191 Mon Sep 17 00:00:00 2001 From: Nicolas Riesco Date: Mon, 20 Mar 2017 10:19:59 +0000 Subject: [PATCH 17/27] events: test plotly events in mapbox plots --- test/jasmine/tests/scattermapbox_test.js | 216 +++++++++++++++++++++++ 1 file changed, 216 insertions(+) diff --git a/test/jasmine/tests/scattermapbox_test.js b/test/jasmine/tests/scattermapbox_test.js index 716af5581a3..7c19c91d9ac 100644 --- a/test/jasmine/tests/scattermapbox_test.js +++ b/test/jasmine/tests/scattermapbox_test.js @@ -9,6 +9,21 @@ var createGraphDiv = require('../assets/create_graph_div'); var destroyGraphDiv = require('../assets/destroy_graph_div'); var customMatchers = require('../assets/custom_matchers'); +var mouseEvent = require('../assets/mouse_event'); +var click = require('../assets/click'); +var HOVERMINTIME = 50; + +function move(fromX, fromY, toX, toY, delay) { + return new Promise(function(resolve) { + mouseEvent('mousemove', fromX, fromY); + + setTimeout(function() { + mouseEvent('mousemove', toX, toY); + resolve(); + }, delay || HOVERMINTIME + 10); + }); +} + Plotly.setPlotConfig({ mapboxAccessToken: require('@build/credentials.json').MAPBOX_ACCESS_TOKEN }); @@ -600,3 +615,204 @@ describe('@noCI scattermapbox hover', function() { }); }); }); + + +describe('@noCI Test plotly events on a scattermapbox plot:', function() { + var mock = require('@mocks/mapbox_0.json'); + + var mockCopy, gd; + + var blankPos = [10, 10], + pointPos, + nearPos; + + function getPointData(gd) { + var cd = gd.calcdata, + mapbox = gd._fullLayout.mapbox._subplot; + + return { + index: false, + distance: 20, + cd: cd[0], + trace: cd[0][0].trace, + xa: mapbox.xaxis, + ya: mapbox.yaxis + }; + } + + beforeAll(function(done) { + jasmine.addMatchers(customMatchers); + + gd = createGraphDiv(); + mockCopy = Lib.extendDeep({}, mock); + + Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(function() { + var bb = gd._fullLayout.mapbox._subplot.div.getBoundingClientRect(), + xval = 10, + yval = 10, + point = ScatterMapbox.hoverPoints(getPointData(gd), xval, yval)[0]; + pointPos = [Math.floor(bb.left + (point.x0 + point.x1) / 2), + Math.floor(bb.top + (point.y0 + point.y1) / 2)]; + nearPos = [pointPos[0] - 30, pointPos[1] - 30]; + }).then(destroyGraphDiv).then(done); + }); + + beforeEach(function() { + gd = createGraphDiv(); + mockCopy = Lib.extendDeep({}, mock); + }); + + afterEach(destroyGraphDiv); + + describe('click events', function() { + var futureData; + + beforeEach(function(done) { + Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); + + gd.on('plotly_click', function(data) { + futureData = data; + }); + }); + + it('should not be trigged when not on data points', function() { + click(blankPos[0], blankPos[1]); + expect(futureData).toBe(undefined); + }); + + it('should contain the correct fields', function() { + click(pointPos[0], pointPos[1]); + + var pt = futureData.points[0], + evt = futureData.event; + + expect(Object.keys(pt)).toEqual([ + 'data', 'fullData', 'curveNumber', 'pointNumber', 'lon', 'lat' + ]); + + expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); + expect(typeof pt.data).toEqual(typeof {}, 'points[0].data'); + expect(typeof pt.fullData).toEqual(typeof {}, 'points[0].fullData'); + expect(pt.lat).toEqual(undefined, 'points[0].lat'); + expect(pt.lon).toEqual(undefined, 'points[0].lon'); + expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); + + expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); + expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); + }); + }); + + describe('modified click events', function() { + var clickOpts = { + altKey: true, + ctrlKey: true, + metaKey: true, + shiftKey: true + }, + futureData; + + beforeEach(function(done) { + Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); + + gd.on('plotly_click', function(data) { + futureData = data; + }); + }); + + it('should not be trigged when not on data points', function() { + click(blankPos[0], blankPos[1], clickOpts); + expect(futureData).toBe(undefined); + }); + + it('should contain the correct fields', function() { + click(pointPos[0], pointPos[1], clickOpts); + + var pt = futureData.points[0], + evt = futureData.event; + + expect(Object.keys(pt)).toEqual([ + 'data', 'fullData', 'curveNumber', 'pointNumber', 'lon', 'lat' + ]); + + expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); + expect(typeof pt.data).toEqual(typeof {}, 'points[0].data'); + expect(typeof pt.fullData).toEqual(typeof {}, 'points[0].fullData'); + expect(pt.lat).toEqual(undefined, 'points[0].lat'); + expect(pt.lon).toEqual(undefined, 'points[0].lon'); + expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); + + expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); + expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); + Object.getOwnPropertyNames(clickOpts).forEach(function(opt) { + expect(evt[opt]).toEqual(clickOpts[opt], 'event.' + opt); + }); + }); + }); + + describe('hover events', function() { + var futureData; + + beforeEach(function(done) { + Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); + + gd.on('plotly_hover', function(data) { + futureData = data; + }); + }); + + it('should contain the correct fields', function() { + mouseEvent('mousemove', blankPos[0], blankPos[1]); + mouseEvent('mousemove', pointPos[0], pointPos[1]); + + var pt = futureData.points[0], + evt = futureData.event; + + expect(Object.keys(pt)).toEqual([ + 'data', 'fullData', 'curveNumber', 'pointNumber', 'lon', 'lat' + ]); + + expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); + expect(typeof pt.data).toEqual(typeof {}, 'points[0].data'); + expect(typeof pt.fullData).toEqual(typeof {}, 'points[0].fullData'); + expect(pt.lat).toEqual(undefined, 'points[0].lat'); + expect(pt.lon).toEqual(undefined, 'points[0].lon'); + expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); + + expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); + expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); + }); + }); + + describe('unhover events', function() { + var futureData; + + beforeEach(function(done) { + Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); + + gd.on('plotly_unhover', function(data) { + futureData = data; + }); + }); + + it('should contain the correct fields', function(done) { + move(pointPos[0], pointPos[1], nearPos[0], nearPos[1], HOVERMINTIME + 10).then(function() { + var pt = futureData.points[0], + evt = futureData.event; + + expect(Object.keys(pt)).toEqual([ + 'data', 'fullData', 'curveNumber', 'pointNumber', 'lon', 'lat' + ]); + + expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); + expect(typeof pt.data).toEqual(typeof {}, 'points[0].data'); + expect(typeof pt.fullData).toEqual(typeof {}, 'points[0].fullData'); + expect(pt.lat).toEqual(undefined, 'points[0].lat'); + expect(pt.lon).toEqual(undefined, 'points[0].lon'); + expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); + + expect(evt.clientX).toEqual(nearPos[0], 'event.clientX'); + expect(evt.clientY).toEqual(nearPos[1], 'event.clientY'); + }).then(done); + }); + }); +}); From 194243d88c63dfe0422d107d53b7f539e62e56bf Mon Sep 17 00:00:00 2001 From: Nicolas Riesco Date: Mon, 20 Mar 2017 15:32:03 +0000 Subject: [PATCH 18/27] events: define variable hasUserCalledHover * To document the need for an if-block. --- src/plots/cartesian/graph_interact.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/plots/cartesian/graph_interact.js b/src/plots/cartesian/graph_interact.js index 2eac531091a..2db0ea29798 100644 --- a/src/plots/cartesian/graph_interact.js +++ b/src/plots/cartesian/graph_interact.js @@ -414,9 +414,10 @@ function hover(gd, evt, subplot) { // [x|y]px: the pixels (from top left) of the mouse location // on the currently selected plot area - var xpx, ypx; + var hasUserCalledHover = ('xpx' in evt || 'ypx' in evt), + xpx, ypx; - if('xpx' in evt || 'ypx' in evt) { + if(hasUserCalledHover) { if('xpx' in evt) xpx = evt.xpx; else xpx = xaArray[0]._length / 2; From 20ff1a0a593fb64bf251f4f74adf83700648a250 Mon Sep 17 00:00:00 2001 From: Nicolas Riesco Date: Mon, 20 Mar 2017 15:41:02 +0000 Subject: [PATCH 19/27] events: do not hard-code HOVERMINTIME --- test/jasmine/tests/click_test.js | 2 +- test/jasmine/tests/scattermapbox_test.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/jasmine/tests/click_test.js b/test/jasmine/tests/click_test.js index 432723b275d..52ac6677266 100644 --- a/test/jasmine/tests/click_test.js +++ b/test/jasmine/tests/click_test.js @@ -2,7 +2,7 @@ var Plotly = require('@lib/index'); var Lib = require('@src/lib'); var Drawing = require('@src/components/drawing'); var DBLCLICKDELAY = require('@src/constants/interactions').DBLCLICKDELAY; -var HOVERMINTIME = 50; +var HOVERMINTIME = require('@src/plots/cartesian/constants').HOVERMINTIME; var d3 = require('d3'); var createGraphDiv = require('../assets/create_graph_div'); diff --git a/test/jasmine/tests/scattermapbox_test.js b/test/jasmine/tests/scattermapbox_test.js index 7c19c91d9ac..88aa8adfdb0 100644 --- a/test/jasmine/tests/scattermapbox_test.js +++ b/test/jasmine/tests/scattermapbox_test.js @@ -11,7 +11,7 @@ var customMatchers = require('../assets/custom_matchers'); var mouseEvent = require('../assets/mouse_event'); var click = require('../assets/click'); -var HOVERMINTIME = 50; +var HOVERMINTIME = require('@src/plots/cartesian/constants').HOVERMINTIME; function move(fromX, fromY, toX, toY, delay) { return new Promise(function(resolve) { From 3c4196161ec1ee5105b91f96b21a2202c090b0bd Mon Sep 17 00:00:00 2001 From: Nicolas Riesco Date: Mon, 20 Mar 2017 15:52:21 +0000 Subject: [PATCH 20/27] events: move geo tests to get_test.js --- test/jasmine/tests/click_test.js | 192 --------------------------- test/jasmine/tests/geo_test.js | 215 +++++++++++++++++++++++++++++++ 2 files changed, 215 insertions(+), 192 deletions(-) diff --git a/test/jasmine/tests/click_test.js b/test/jasmine/tests/click_test.js index 52ac6677266..0e09c66795c 100644 --- a/test/jasmine/tests/click_test.js +++ b/test/jasmine/tests/click_test.js @@ -2,7 +2,6 @@ var Plotly = require('@lib/index'); var Lib = require('@src/lib'); var Drawing = require('@src/components/drawing'); var DBLCLICKDELAY = require('@src/constants/interactions').DBLCLICKDELAY; -var HOVERMINTIME = require('@src/plots/cartesian/constants').HOVERMINTIME; var d3 = require('d3'); var createGraphDiv = require('../assets/create_graph_div'); @@ -1319,197 +1318,6 @@ describe('Test click interactions on a ternary plot:', function() { }); -describe('Test click interactions on a geo plot:', function() { - var mock = require('@mocks/geo_scattergeo-locations.json'); - - var mockCopy, gd; - - var blankPos = [10, 10], - pointPos, - nearPos; - - beforeAll(function(done) { - jasmine.addMatchers(customMatchers); - - gd = createGraphDiv(); - mockCopy = Lib.extendDeep({}, mock); - Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(function() { - pointPos = getClientPosition('path.point'); - nearPos = [pointPos[0] - 30, pointPos[1] - 30]; - destroyGraphDiv(); - done(); - }); - }); - - beforeEach(function() { - gd = createGraphDiv(); - mockCopy = Lib.extendDeep({}, mock); - }); - - afterEach(destroyGraphDiv); - - describe('click events', function() { - var futureData; - - beforeEach(function(done) { - Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); - - gd.on('plotly_click', function(data) { - futureData = data; - }); - }); - - it('should not be trigged when not on data points', function() { - click(blankPos[0], blankPos[1]); - expect(futureData).toBe(undefined); - }); - - it('should contain the correct fields', function() { - click(pointPos[0], pointPos[1]); - - var pt = futureData.points[0], - evt = futureData.event; - - expect(Object.keys(pt)).toEqual([ - 'data', 'fullData', 'curveNumber', 'pointNumber', 'lon', 'lat', - 'location' - ]); - - expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); - expect(typeof pt.data).toEqual(typeof {}, 'points[0].data'); - expect(typeof pt.fullData).toEqual(typeof {}, 'points[0].fullData'); - expect(pt.lat).toEqual(-101.57, 'points[0].lat'); - expect(pt.lon).toEqual(57.75, 'points[0].lon'); - expect(pt.location).toEqual(57.75, 'points[0].location'); - expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); - - expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); - expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); - }); - }); - - describe('modified click events', function() { - var clickOpts = { - altKey: true, - ctrlKey: true, - metaKey: true, - shiftKey: true - }, - futureData; - - beforeEach(function(done) { - Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); - - gd.on('plotly_click', function(data) { - futureData = data; - }); - }); - - it('should not be trigged when not on data points', function() { - click(blankPos[0], blankPos[1], clickOpts); - expect(futureData).toBe(undefined); - }); - - it('should contain the correct fields', function() { - click(pointPos[0], pointPos[1], clickOpts); - - var pt = futureData.points[0], - evt = futureData.event; - - expect(Object.keys(pt)).toEqual([ - 'data', 'fullData', 'curveNumber', 'pointNumber', 'lon', 'lat', - 'location' - ]); - - expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); - expect(typeof pt.data).toEqual(typeof {}, 'points[0].data'); - expect(typeof pt.fullData).toEqual(typeof {}, 'points[0].fullData'); - expect(pt.lat).toEqual(-101.57, 'points[0].lat'); - expect(pt.lon).toEqual(57.75, 'points[0].lon'); - expect(pt.location).toEqual(57.75, 'points[0].location'); - expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); - - expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); - expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); - Object.getOwnPropertyNames(clickOpts).forEach(function(opt) { - expect(evt[opt]).toEqual(clickOpts[opt], 'event.' + opt); - }); - }); - }); - - describe('hover events', function() { - var futureData; - - beforeEach(function(done) { - Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); - - gd.on('plotly_hover', function(data) { - futureData = data; - }); - }); - - it('should contain the correct fields', function() { - mouseEvent('mousemove', blankPos[0], blankPos[1]); - mouseEvent('mousemove', pointPos[0], pointPos[1]); - - var pt = futureData.points[0], - evt = futureData.event; - - expect(Object.keys(pt)).toEqual([ - 'data', 'fullData', 'curveNumber', 'pointNumber', 'lon', 'lat', - 'location' - ]); - - expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); - expect(typeof pt.data).toEqual(typeof {}, 'points[0].data'); - expect(typeof pt.fullData).toEqual(typeof {}, 'points[0].fullData'); - expect(pt.lat).toEqual(-101.57, 'points[0].lat'); - expect(pt.lon).toEqual(57.75, 'points[0].lon'); - expect(pt.location).toEqual(57.75, 'points[0].location'); - expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); - - expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); - expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); - }); - }); - - describe('unhover events', function() { - var futureData; - - beforeEach(function(done) { - Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); - - gd.on('plotly_unhover', function(data) { - futureData = data; - }); - }); - - it('should contain the correct fields', function(done) { - move(pointPos[0], pointPos[1], nearPos[0], nearPos[1], HOVERMINTIME + 10).then(function() { - var pt = futureData.points[0], - evt = futureData.event; - - expect(Object.keys(pt)).toEqual([ - 'data', 'fullData', 'curveNumber', 'pointNumber', 'lon', 'lat', - 'location' - ]); - - expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); - expect(typeof pt.data).toEqual(typeof {}, 'points[0].data'); - expect(typeof pt.fullData).toEqual(typeof {}, 'points[0].fullData'); - expect(pt.lat).toEqual(-101.57, 'points[0].lat'); - expect(pt.lon).toEqual(57.75, 'points[0].lon'); - expect(pt.location).toEqual(57.75, 'points[0].location'); - expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); - - expect(evt.clientX).toEqual(nearPos[0], 'event.clientX'); - expect(evt.clientY).toEqual(nearPos[1], 'event.clientY'); - }).then(done); - }); - }); -}); - - describe('dragbox', function() { afterEach(destroyGraphDiv); diff --git a/test/jasmine/tests/geo_test.js b/test/jasmine/tests/geo_test.js index 2922c8f978c..f604e59f154 100644 --- a/test/jasmine/tests/geo_test.js +++ b/test/jasmine/tests/geo_test.js @@ -11,11 +11,35 @@ var topojsonUtils = require('@src/lib/topojson_utils'); var d3 = require('d3'); var createGraphDiv = require('../assets/create_graph_div'); var destroyGraphDiv = require('../assets/destroy_graph_div'); +var customMatchers = require('../assets/custom_matchers'); var mouseEvent = require('../assets/mouse_event'); +var click = require('../assets/click'); +var DBLCLICKDELAY = require('@src/constants/interactions').DBLCLICKDELAY; var HOVERMINTIME = require('@src/plots/cartesian/constants').HOVERMINTIME; +function getClientPosition(selector, index) { + index = index || 0; + var selection = document.querySelectorAll(selector), + clientPos = selection[index].getBoundingClientRect(), + x = Math.floor((clientPos.left + clientPos.right) / 2), + y = Math.floor((clientPos.top + clientPos.bottom) / 2); + return [x, y]; +} + +function move(fromX, fromY, toX, toY, delay) { + return new Promise(function(resolve) { + mouseEvent('mousemove', fromX, fromY); + + setTimeout(function() { + mouseEvent('mousemove', toX, toY); + resolve(); + }, delay || DBLCLICKDELAY / 4); + }); +} + + describe('Test geoaxes', function() { 'use strict'; @@ -1015,3 +1039,194 @@ describe('Test geo interactions', function() { }); }); }); + + +describe('Test event property of interactions on a geo plot:', function() { + var mock = require('@mocks/geo_scattergeo-locations.json'); + + var mockCopy, gd; + + var blankPos = [10, 10], + pointPos, + nearPos; + + beforeAll(function(done) { + jasmine.addMatchers(customMatchers); + + gd = createGraphDiv(); + mockCopy = Lib.extendDeep({}, mock); + Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(function() { + pointPos = getClientPosition('path.point'); + nearPos = [pointPos[0] - 30, pointPos[1] - 30]; + destroyGraphDiv(); + done(); + }); + }); + + beforeEach(function() { + gd = createGraphDiv(); + mockCopy = Lib.extendDeep({}, mock); + }); + + afterEach(destroyGraphDiv); + + describe('click events', function() { + var futureData; + + beforeEach(function(done) { + Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); + + gd.on('plotly_click', function(data) { + futureData = data; + }); + }); + + it('should not be trigged when not on data points', function() { + click(blankPos[0], blankPos[1]); + expect(futureData).toBe(undefined); + }); + + it('should contain the correct fields', function() { + click(pointPos[0], pointPos[1]); + + var pt = futureData.points[0], + evt = futureData.event; + + expect(Object.keys(pt)).toEqual([ + 'data', 'fullData', 'curveNumber', 'pointNumber', 'lon', 'lat', + 'location' + ]); + + expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); + expect(typeof pt.data).toEqual(typeof {}, 'points[0].data'); + expect(typeof pt.fullData).toEqual(typeof {}, 'points[0].fullData'); + expect(pt.lat).toEqual(-101.57, 'points[0].lat'); + expect(pt.lon).toEqual(57.75, 'points[0].lon'); + expect(pt.location).toEqual(57.75, 'points[0].location'); + expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); + + expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); + expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); + }); + }); + + describe('modified click events', function() { + var clickOpts = { + altKey: true, + ctrlKey: true, + metaKey: true, + shiftKey: true + }, + futureData; + + beforeEach(function(done) { + Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); + + gd.on('plotly_click', function(data) { + futureData = data; + }); + }); + + it('should not be trigged when not on data points', function() { + click(blankPos[0], blankPos[1], clickOpts); + expect(futureData).toBe(undefined); + }); + + it('should contain the correct fields', function() { + click(pointPos[0], pointPos[1], clickOpts); + + var pt = futureData.points[0], + evt = futureData.event; + + expect(Object.keys(pt)).toEqual([ + 'data', 'fullData', 'curveNumber', 'pointNumber', 'lon', 'lat', + 'location' + ]); + + expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); + expect(typeof pt.data).toEqual(typeof {}, 'points[0].data'); + expect(typeof pt.fullData).toEqual(typeof {}, 'points[0].fullData'); + expect(pt.lat).toEqual(-101.57, 'points[0].lat'); + expect(pt.lon).toEqual(57.75, 'points[0].lon'); + expect(pt.location).toEqual(57.75, 'points[0].location'); + expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); + + expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); + expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); + Object.getOwnPropertyNames(clickOpts).forEach(function(opt) { + expect(evt[opt]).toEqual(clickOpts[opt], 'event.' + opt); + }); + }); + }); + + describe('hover events', function() { + var futureData; + + beforeEach(function(done) { + Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); + + gd.on('plotly_hover', function(data) { + futureData = data; + }); + }); + + it('should contain the correct fields', function() { + mouseEvent('mousemove', blankPos[0], blankPos[1]); + mouseEvent('mousemove', pointPos[0], pointPos[1]); + + var pt = futureData.points[0], + evt = futureData.event; + + expect(Object.keys(pt)).toEqual([ + 'data', 'fullData', 'curveNumber', 'pointNumber', 'lon', 'lat', + 'location' + ]); + + expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); + expect(typeof pt.data).toEqual(typeof {}, 'points[0].data'); + expect(typeof pt.fullData).toEqual(typeof {}, 'points[0].fullData'); + expect(pt.lat).toEqual(-101.57, 'points[0].lat'); + expect(pt.lon).toEqual(57.75, 'points[0].lon'); + expect(pt.location).toEqual(57.75, 'points[0].location'); + expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); + + expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); + expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); + }); + }); + + describe('unhover events', function() { + var futureData; + + beforeEach(function(done) { + Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); + + gd.on('plotly_unhover', function(data) { + futureData = data; + }); + }); + + it('should contain the correct fields', function(done) { + move(pointPos[0], pointPos[1], nearPos[0], nearPos[1], HOVERMINTIME + 10).then(function() { + var pt = futureData.points[0], + evt = futureData.event; + + expect(Object.keys(pt)).toEqual([ + 'data', 'fullData', 'curveNumber', 'pointNumber', 'lon', 'lat', + 'location' + ]); + + expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); + expect(typeof pt.data).toEqual(typeof {}, 'points[0].data'); + expect(typeof pt.fullData).toEqual(typeof {}, 'points[0].fullData'); + expect(pt.lat).toEqual(-101.57, 'points[0].lat'); + expect(pt.lon).toEqual(57.75, 'points[0].lon'); + expect(pt.location).toEqual(57.75, 'points[0].location'); + expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); + + expect(evt.clientX).toEqual(nearPos[0], 'event.clientX'); + expect(evt.clientY).toEqual(nearPos[1], 'event.clientY'); + }).then(done); + }); + }); +}); From 00baba0e75e5530e8b578833191dfe189ead28cd Mon Sep 17 00:00:00 2001 From: Nicolas Riesco Date: Mon, 20 Mar 2017 16:48:02 +0000 Subject: [PATCH 21/27] event: fix regression in pie interactions --- src/plots/cartesian/graph_interact.js | 2 +- src/traces/pie/plot.js | 12 +++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/plots/cartesian/graph_interact.js b/src/plots/cartesian/graph_interact.js index 2db0ea29798..f3e4b1e3085 100644 --- a/src/plots/cartesian/graph_interact.js +++ b/src/plots/cartesian/graph_interact.js @@ -305,7 +305,7 @@ fx.hover = function(gd, evt, subplot) { function hover(gd, evt, subplot) { if(subplot === 'pie') { gd.emit('plotly_hover', { - event: evt, + event: evt.originalEvent, points: [evt] }); return; diff --git a/src/traces/pie/plot.js b/src/traces/pie/plot.js index 1e07c0af264..e1fdbf94753 100644 --- a/src/traces/pie/plot.js +++ b/src/traces/pie/plot.js @@ -85,9 +85,7 @@ module.exports = function plot(gd, cdpie) { slicePath = sliceTop.selectAll('path.surface').data([pt]), hasHoverData = false; - function handleMouseOver() { - var evt = d3.event; - + function handleMouseOver(evt) { // in case fullLayout or fullData has changed without a replot var fullLayout2 = gd._fullLayout, trace2 = gd._fullData[trace.index], @@ -128,16 +126,16 @@ module.exports = function plot(gd, cdpie) { outerContainer: fullLayout2._paper.node() }); + evt.originalEvent = d3.event; Fx.hover(gd, evt, 'pie'); hasHoverData = true; } - function handleMouseOut() { - var evt = d3.event; - + function handleMouseOut(evt) { + evt.originalEvent = d3.event; gd.emit('plotly_unhover', { - event: evt, + event: d3.event, points: [evt] }); From 5cdf0092e154712f3d12e3d3727ef3c250d39531 Mon Sep 17 00:00:00 2001 From: Nicolas Riesco Date: Mon, 20 Mar 2017 16:49:25 +0000 Subject: [PATCH 22/27] events: move pie tests to hover_pie_test.js --- test/jasmine/tests/click_test.js | 186 ------------------------ test/jasmine/tests/hover_pie_test.js | 207 ++++++++++++++++++++++++++- 2 files changed, 205 insertions(+), 188 deletions(-) diff --git a/test/jasmine/tests/click_test.js b/test/jasmine/tests/click_test.js index 0e09c66795c..811ab24bbb8 100644 --- a/test/jasmine/tests/click_test.js +++ b/test/jasmine/tests/click_test.js @@ -928,192 +928,6 @@ describe('Test click interactions:', function() { }); -describe('Test click interactions on a pie plot:', function() { - var mock = require('@mocks/pie_simple.json'); - - var mockCopy, gd; - - var blankPos = [10, 10], - pointPos; - - beforeAll(function(done) { - jasmine.addMatchers(customMatchers); - - gd = createGraphDiv(); - mockCopy = Lib.extendDeep({}, mock); - Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(function() { - pointPos = getClientPosition('g.slicetext'); - destroyGraphDiv(); - done(); - }); - }); - - beforeEach(function() { - gd = createGraphDiv(); - mockCopy = Lib.extendDeep({}, mock); - }); - - afterEach(destroyGraphDiv); - - describe('click events', function() { - var futureData; - - beforeEach(function(done) { - Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); - - gd.on('plotly_click', function(data) { - futureData = data; - }); - }); - - it('should not be trigged when not on data points', function() { - click(blankPos[0], blankPos[1]); - expect(futureData).toBe(undefined); - }); - - it('should contain the correct fields', function() { - click(pointPos[0], pointPos[1]); - expect(futureData.points.length).toEqual(1); - - var pt = futureData.points[0]; - expect(Object.keys(pt)).toEqual([ - 'v', 'label', 'color', 'i', 'hidden', 'vTotal', 'text', 't', - 'trace', 'r', 'cx', 'cy', 'px1', 'pxmid', 'midangle', 'px0', - 'largeArc', 'cxFinal', 'cyFinal' - ]); - expect(typeof pt.color).toEqual(typeof '#1f77b4', 'points[0].color'); - expect(pt.cx).toEqual(200, 'points[0].cx'); - expect(pt.cxFinal).toEqual(200, 'points[0].cxFinal'); - expect(pt.cy).toEqual(160, 'points[0].cy'); - expect(pt.cyFinal).toEqual(160, 'points[0].cyFinal'); - expect(pt.hidden).toEqual(false, 'points[0].hidden'); - expect(pt.i).toEqual(4, 'points[0].i'); - expect(pt.label).toEqual('4', 'points[0].label'); - expect(pt.largeArc).toEqual(0, 'points[0].largeArc'); - expect(pt.midangle).toEqual(1.0471975511965976, 'points[0].midangle'); - expect(pt.px0).toEqual([0, -60], 'points[0].px0'); - expect(pt.px1).toEqual([51.96152422706632, 29.999999999999986], 'points[0].px1'); - expect(pt.pxmid).toEqual([51.96152422706631, -30.000000000000007], 'points[0].pxmid'); - expect(pt.r).toEqual(60, 'points[0].r'); - expect(typeof pt.t).toEqual(typeof {}, 'points[0].t'); - expect(pt.text).toEqual('33.3%', 'points[0].text'); - expect(typeof pt.trace).toEqual(typeof {}, 'points[0].trace'); - expect(pt.v).toEqual(5, 'points[0].v'); - expect(pt.vTotal).toEqual(15, 'points[0].vTotal'); - - var evt = futureData.event; - expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); - expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); - }); - }); - - describe('modified click events', function() { - var clickOpts = { - altKey: true, - ctrlKey: true, - metaKey: true, - shiftKey: true - }, - futureData; - - beforeEach(function(done) { - Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); - - gd.on('plotly_click', function(data) { - futureData = data; - }); - }); - - it('should not be trigged when not on data points', function() { - click(blankPos[0], blankPos[1], clickOpts); - expect(futureData).toBe(undefined); - }); - - it('should contain the correct fields', function() { - click(pointPos[0], pointPos[1], clickOpts); - expect(futureData.points.length).toEqual(1); - - var pt = futureData.points[0]; - expect(Object.keys(pt)).toEqual([ - 'v', 'label', 'color', 'i', 'hidden', 'vTotal', 'text', 't', - 'trace', 'r', 'cx', 'cy', 'px1', 'pxmid', 'midangle', 'px0', - 'largeArc', 'cxFinal', 'cyFinal' - ]); - expect(typeof pt.color).toEqual(typeof '#1f77b4', 'points[0].color'); - expect(pt.cx).toEqual(200, 'points[0].cx'); - expect(pt.cxFinal).toEqual(200, 'points[0].cxFinal'); - expect(pt.cy).toEqual(160, 'points[0].cy'); - expect(pt.cyFinal).toEqual(160, 'points[0].cyFinal'); - expect(pt.hidden).toEqual(false, 'points[0].hidden'); - expect(pt.i).toEqual(4, 'points[0].i'); - expect(pt.label).toEqual('4', 'points[0].label'); - expect(pt.largeArc).toEqual(0, 'points[0].largeArc'); - expect(pt.midangle).toEqual(1.0471975511965976, 'points[0].midangle'); - expect(pt.px0).toEqual([0, -60], 'points[0].px0'); - expect(pt.px1).toEqual([51.96152422706632, 29.999999999999986], 'points[0].px1'); - expect(pt.pxmid).toEqual([51.96152422706631, -30.000000000000007], 'points[0].pxmid'); - expect(pt.r).toEqual(60, 'points[0].r'); - expect(typeof pt.t).toEqual(typeof {}, 'points[0].t'); - expect(pt.text).toEqual('33.3%', 'points[0].text'); - expect(typeof pt.trace).toEqual(typeof {}, 'points[0].trace'); - expect(pt.v).toEqual(5, 'points[0].v'); - expect(pt.vTotal).toEqual(15, 'points[0].vTotal'); - - var evt = futureData.event; - expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); - expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); - Object.getOwnPropertyNames(clickOpts).forEach(function(opt) { - expect(evt[opt]).toEqual(clickOpts[opt], 'event.' + opt); - }); - }); - }); - - describe('hover events', function() { - var futureData; - - beforeEach(function(done) { - Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); - - gd.on('plotly_hover', function(data) { - futureData = data; - }); - }); - - it('should contain the correct fields', function() { - mouseEvent('mouseover', pointPos[0], pointPos[1]); - - var point0 = futureData.points[0], - evt = futureData.event; - expect(point0).toEqual(evt, 'points'); - expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); - expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); - }); - }); - - describe('unhover events', function() { - var futureData; - - beforeEach(function(done) { - Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); - - gd.on('plotly_unhover', function(data) { - futureData = data; - }); - }); - - it('should contain the correct fields', function() { - mouseEvent('mouseout', pointPos[0], pointPos[1]); - - var point0 = futureData.points[0], - evt = futureData.event; - expect(point0).toEqual(evt, 'points'); - expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); - expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); - }); - }); -}); - - describe('Test click interactions on a ternary plot:', function() { var mock = require('@mocks/ternary_simple.json'); diff --git a/test/jasmine/tests/hover_pie_test.js b/test/jasmine/tests/hover_pie_test.js index 75610e36834..7f89756b624 100644 --- a/test/jasmine/tests/hover_pie_test.js +++ b/test/jasmine/tests/hover_pie_test.js @@ -1,10 +1,25 @@ var Plotly = require('@lib/index'); var Lib = require('@src/lib'); +var customMatchers = require('../assets/custom_matchers'); + var createGraphDiv = require('../assets/create_graph_div'); var destroyGraphDiv = require('../assets/destroy_graph_div'); + +var click = require('../assets/click'); var mouseEvent = require('../assets/mouse_event'); + +function getClientPosition(selector, index) { + index = index || 0; + var selection = document.querySelectorAll(selector), + clientPos = selection[index].getBoundingClientRect(), + x = Math.floor((clientPos.left + clientPos.right) / 2), + y = Math.floor((clientPos.top + clientPos.bottom) / 2); + return [x, y]; +} + + describe('pie hovering', function() { var mock = require('@mocks/pie_simple.json'); @@ -39,7 +54,8 @@ describe('pie hovering', function() { * px0: [-59.67131372209641,6.2717077960592], * largeArc: 0, * cxFinal: 200, - * cyFinal: 160 + * cyFinal: 160, + * originalEvent: MouseEvent * }]; */ var hoverData, @@ -63,7 +79,8 @@ describe('pie hovering', function() { var fields = [ 'v', 'label', 'color', 'i', 'hidden', 'text', 'px1', 'pxmid', 'midangle', - 'px0', 'largeArc', 'cxFinal', 'cyFinal' + 'px0', 'largeArc', 'cxFinal', 'cyFinal', + 'originalEvent' ]; expect(Object.keys(hoverData.points[0])).toEqual(fields); @@ -165,3 +182,189 @@ describe('pie hovering', function() { }); }); }); + + +describe('Test event property of interactions on a pie plot:', function() { + var mock = require('@mocks/pie_simple.json'); + + var mockCopy, gd; + + var blankPos = [10, 10], + pointPos; + + beforeAll(function(done) { + jasmine.addMatchers(customMatchers); + + gd = createGraphDiv(); + mockCopy = Lib.extendDeep({}, mock); + Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(function() { + pointPos = getClientPosition('g.slicetext'); + destroyGraphDiv(); + done(); + }); + }); + + beforeEach(function() { + gd = createGraphDiv(); + mockCopy = Lib.extendDeep({}, mock); + }); + + afterEach(destroyGraphDiv); + + describe('click events', function() { + var futureData; + + beforeEach(function(done) { + Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); + + gd.on('plotly_click', function(data) { + futureData = data; + }); + }); + + it('should not be trigged when not on data points', function() { + click(blankPos[0], blankPos[1]); + expect(futureData).toBe(undefined); + }); + + it('should contain the correct fields', function() { + click(pointPos[0], pointPos[1]); + expect(futureData.points.length).toEqual(1); + + var pt = futureData.points[0]; + expect(Object.keys(pt)).toEqual([ + 'v', 'label', 'color', 'i', 'hidden', 'vTotal', 'text', 't', + 'trace', 'r', 'cx', 'cy', 'px1', 'pxmid', 'midangle', 'px0', + 'largeArc', 'cxFinal', 'cyFinal' + ]); + expect(typeof pt.color).toEqual(typeof '#1f77b4', 'points[0].color'); + expect(pt.cx).toEqual(200, 'points[0].cx'); + expect(pt.cxFinal).toEqual(200, 'points[0].cxFinal'); + expect(pt.cy).toEqual(160, 'points[0].cy'); + expect(pt.cyFinal).toEqual(160, 'points[0].cyFinal'); + expect(pt.hidden).toEqual(false, 'points[0].hidden'); + expect(pt.i).toEqual(4, 'points[0].i'); + expect(pt.label).toEqual('4', 'points[0].label'); + expect(pt.largeArc).toEqual(0, 'points[0].largeArc'); + expect(pt.midangle).toEqual(1.0471975511965976, 'points[0].midangle'); + expect(pt.px0).toEqual([0, -60], 'points[0].px0'); + expect(pt.px1).toEqual([51.96152422706632, 29.999999999999986], 'points[0].px1'); + expect(pt.pxmid).toEqual([51.96152422706631, -30.000000000000007], 'points[0].pxmid'); + expect(pt.r).toEqual(60, 'points[0].r'); + expect(typeof pt.t).toEqual(typeof {}, 'points[0].t'); + expect(pt.text).toEqual('33.3%', 'points[0].text'); + expect(typeof pt.trace).toEqual(typeof {}, 'points[0].trace'); + expect(pt.v).toEqual(5, 'points[0].v'); + expect(pt.vTotal).toEqual(15, 'points[0].vTotal'); + + var evt = futureData.event; + expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); + expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); + }); + }); + + describe('modified click events', function() { + var clickOpts = { + altKey: true, + ctrlKey: true, + metaKey: true, + shiftKey: true + }, + futureData; + + beforeEach(function(done) { + Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); + + gd.on('plotly_click', function(data) { + futureData = data; + }); + }); + + it('should not be trigged when not on data points', function() { + click(blankPos[0], blankPos[1], clickOpts); + expect(futureData).toBe(undefined); + }); + + it('should contain the correct fields', function() { + click(pointPos[0], pointPos[1], clickOpts); + expect(futureData.points.length).toEqual(1); + + var pt = futureData.points[0]; + expect(Object.keys(pt)).toEqual([ + 'v', 'label', 'color', 'i', 'hidden', 'vTotal', 'text', 't', + 'trace', 'r', 'cx', 'cy', 'px1', 'pxmid', 'midangle', 'px0', + 'largeArc', 'cxFinal', 'cyFinal' + ]); + expect(typeof pt.color).toEqual(typeof '#1f77b4', 'points[0].color'); + expect(pt.cx).toEqual(200, 'points[0].cx'); + expect(pt.cxFinal).toEqual(200, 'points[0].cxFinal'); + expect(pt.cy).toEqual(160, 'points[0].cy'); + expect(pt.cyFinal).toEqual(160, 'points[0].cyFinal'); + expect(pt.hidden).toEqual(false, 'points[0].hidden'); + expect(pt.i).toEqual(4, 'points[0].i'); + expect(pt.label).toEqual('4', 'points[0].label'); + expect(pt.largeArc).toEqual(0, 'points[0].largeArc'); + expect(pt.midangle).toEqual(1.0471975511965976, 'points[0].midangle'); + expect(pt.px0).toEqual([0, -60], 'points[0].px0'); + expect(pt.px1).toEqual([51.96152422706632, 29.999999999999986], 'points[0].px1'); + expect(pt.pxmid).toEqual([51.96152422706631, -30.000000000000007], 'points[0].pxmid'); + expect(pt.r).toEqual(60, 'points[0].r'); + expect(typeof pt.t).toEqual(typeof {}, 'points[0].t'); + expect(pt.text).toEqual('33.3%', 'points[0].text'); + expect(typeof pt.trace).toEqual(typeof {}, 'points[0].trace'); + expect(pt.v).toEqual(5, 'points[0].v'); + expect(pt.vTotal).toEqual(15, 'points[0].vTotal'); + + var evt = futureData.event; + expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); + expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); + Object.getOwnPropertyNames(clickOpts).forEach(function(opt) { + expect(evt[opt]).toEqual(clickOpts[opt], 'event.' + opt); + }); + }); + }); + + describe('hover events', function() { + var futureData; + + beforeEach(function(done) { + Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); + + gd.on('plotly_hover', function(data) { + futureData = data; + }); + }); + + it('should contain the correct fields', function() { + mouseEvent('mouseover', pointPos[0], pointPos[1]); + + var point0 = futureData.points[0], + evt = futureData.event; + expect(point0.originalEvent).toEqual(evt, 'points'); + expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); + expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); + }); + }); + + describe('unhover events', function() { + var futureData; + + beforeEach(function(done) { + Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); + + gd.on('plotly_unhover', function(data) { + futureData = data; + }); + }); + + it('should contain the correct fields', function() { + mouseEvent('mouseout', pointPos[0], pointPos[1]); + + var point0 = futureData.points[0], + evt = futureData.event; + expect(point0.originalEvent).toEqual(evt, 'points'); + expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); + expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); + }); + }); +}); From 76e382ed5935a08249e1011e29e777a0da01704c Mon Sep 17 00:00:00 2001 From: Nicolas Riesco Date: Mon, 20 Mar 2017 17:10:17 +0000 Subject: [PATCH 23/27] events: move ternary tests to ternary_test.js --- test/jasmine/tests/click_test.js | 214 ----------------------------- test/jasmine/tests/ternary_test.js | 214 +++++++++++++++++++++++++++++ 2 files changed, 214 insertions(+), 214 deletions(-) diff --git a/test/jasmine/tests/click_test.js b/test/jasmine/tests/click_test.js index 811ab24bbb8..bfc06419e1a 100644 --- a/test/jasmine/tests/click_test.js +++ b/test/jasmine/tests/click_test.js @@ -16,16 +16,6 @@ var customMatchers = require('../assets/custom_matchers'); var click = require('../assets/click'); var doubleClickRaw = require('../assets/double_click'); - -function getClientPosition(selector, index) { - index = index || 0; - var selection = document.querySelectorAll(selector), - clientPos = selection[index].getBoundingClientRect(), - x = Math.floor((clientPos.left + clientPos.right) / 2), - y = Math.floor((clientPos.top + clientPos.bottom) / 2); - return [x, y]; -} - function move(fromX, fromY, toX, toY, delay) { return new Promise(function(resolve) { mouseEvent('mousemove', fromX, fromY); @@ -928,210 +918,6 @@ describe('Test click interactions:', function() { }); -describe('Test click interactions on a ternary plot:', function() { - var mock = require('@mocks/ternary_simple.json'); - - var mockCopy, gd; - - var blankPos = [10, 10], - pointPos; - - beforeAll(function(done) { - jasmine.addMatchers(customMatchers); - - gd = createGraphDiv(); - mockCopy = Lib.extendDeep({}, mock); - Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(function() { - pointPos = getClientPosition('path.point'); - destroyGraphDiv(); - done(); - }); - }); - - beforeEach(function() { - gd = createGraphDiv(); - mockCopy = Lib.extendDeep({}, mock); - }); - - afterEach(destroyGraphDiv); - - describe('click events', function() { - var futureData; - - beforeEach(function(done) { - Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); - - gd.on('plotly_click', function(data) { - futureData = data; - }); - }); - - it('should not be trigged when not on data points', function() { - click(blankPos[0], blankPos[1]); - expect(futureData).toBe(undefined); - }); - - it('should contain the correct fields', function() { - click(pointPos[0], pointPos[1]); - - var pt = futureData.points[0], - evt = futureData.event; - - expect(Object.keys(pt)).toEqual([ - 'data', 'fullData', 'curveNumber', 'pointNumber', 'x', 'y', - 'xaxis', 'yaxis' - ]); - - expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); - expect(typeof pt.data).toEqual(typeof {}, 'points[0].data'); - expect(typeof pt.fullData).toEqual(typeof {}, 'points[0].fullData'); - expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); - expect(pt.x).toEqual(undefined, 'points[0].x'); - expect(pt.y).toEqual(undefined, 'points[0].y'); - expect(typeof pt.xaxis).toEqual(typeof {}, 'points[0].xaxis'); - expect(typeof pt.yaxis).toEqual(typeof {}, 'points[0].yaxis'); - - expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); - expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); - }); - }); - - describe('modified click events', function() { - var clickOpts = { - altKey: true, - ctrlKey: true, - metaKey: true, - shiftKey: true - }, - futureData; - - beforeEach(function(done) { - Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); - - gd.on('plotly_click', function(data) { - futureData = data; - }); - }); - - it('should not be trigged when not on data points', function() { - click(blankPos[0], blankPos[1], clickOpts); - expect(futureData).toBe(undefined); - }); - - it('should contain the correct fields', function() { - click(pointPos[0], pointPos[1], clickOpts); - - var pt = futureData.points[0], - evt = futureData.event; - - expect(Object.keys(pt)).toEqual([ - 'data', 'fullData', 'curveNumber', 'pointNumber', 'x', 'y', - 'xaxis', 'yaxis' - ]); - - expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); - expect(typeof pt.data).toEqual(typeof {}, 'points[0].data'); - expect(typeof pt.fullData).toEqual(typeof {}, 'points[0].fullData'); - expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); - expect(pt.x).toEqual(undefined, 'points[0].x'); - expect(pt.y).toEqual(undefined, 'points[0].y'); - expect(typeof pt.xaxis).toEqual(typeof {}, 'points[0].xaxis'); - expect(typeof pt.yaxis).toEqual(typeof {}, 'points[0].yaxis'); - - expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); - expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); - Object.getOwnPropertyNames(clickOpts).forEach(function(opt) { - expect(evt[opt]).toEqual(clickOpts[opt], 'event.' + opt); - }); - }); - }); - - describe('hover events', function() { - var futureData; - - beforeEach(function(done) { - Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); - - gd.on('plotly_hover', function(data) { - futureData = data; - }); - }); - - it('should contain the correct fields', function() { - mouseEvent('mousemove', blankPos[0], blankPos[1]); - mouseEvent('mousemove', pointPos[0], pointPos[1]); - - var pt = futureData.points[0], - evt = futureData.event, - xaxes0 = futureData.xaxes[0], - xvals0 = futureData.xvals[0], - yaxes0 = futureData.yaxes[0], - yvals0 = futureData.yvals[0]; - - expect(Object.keys(pt)).toEqual([ - 'data', 'fullData', 'curveNumber', 'pointNumber', 'x', 'y', - 'xaxis', 'yaxis' - ]); - - expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); - expect(typeof pt.data).toEqual(typeof {}, 'points[0].data'); - expect(typeof pt.fullData).toEqual(typeof {}, 'points[0].fullData'); - expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); - expect(pt.x).toEqual(undefined, 'points[0].x'); - expect(pt.y).toEqual(undefined, 'points[0].y'); - expect(typeof pt.xaxis).toEqual(typeof {}, 'points[0].xaxis'); - expect(typeof pt.yaxis).toEqual(typeof {}, 'points[0].yaxis'); - - expect(xaxes0).toEqual(pt.xaxis, 'xaxes[0]'); - expect(xvals0).toEqual(-0.0016654247744483342, 'xaxes[0]'); - expect(yaxes0).toEqual(pt.yaxis, 'yaxes[0]'); - expect(yvals0).toEqual(0.5013, 'xaxes[0]'); - - expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); - expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); - }); - }); - - describe('unhover events', function() { - var futureData; - - beforeEach(function(done) { - Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); - - gd.on('plotly_unhover', function(data) { - futureData = data; - }); - }); - - it('should contain the correct fields', function() { - mouseEvent('mousemove', blankPos[0], blankPos[1]); - mouseEvent('mousemove', pointPos[0], pointPos[1]); - mouseEvent('mouseout', pointPos[0], pointPos[1]); - - var pt = futureData.points[0], - evt = futureData.event; - - expect(Object.keys(pt)).toEqual([ - 'data', 'fullData', 'curveNumber', 'pointNumber', 'x', 'y', - 'xaxis', 'yaxis' - ]); - - expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); - expect(typeof pt.data).toEqual(typeof {}, 'points[0].data'); - expect(typeof pt.fullData).toEqual(typeof {}, 'points[0].fullData'); - expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); - expect(pt.x).toEqual(undefined, 'points[0].x'); - expect(pt.y).toEqual(undefined, 'points[0].y'); - expect(typeof pt.xaxis).toEqual(typeof {}, 'points[0].xaxis'); - expect(typeof pt.yaxis).toEqual(typeof {}, 'points[0].yaxis'); - - expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); - expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); - }); - }); -}); - - describe('dragbox', function() { afterEach(destroyGraphDiv); diff --git a/test/jasmine/tests/ternary_test.js b/test/jasmine/tests/ternary_test.js index c563a500b1b..2e0070218f1 100644 --- a/test/jasmine/tests/ternary_test.js +++ b/test/jasmine/tests/ternary_test.js @@ -12,6 +12,16 @@ var doubleClick = require('../assets/double_click'); var customMatchers = require('../assets/custom_matchers'); +function getClientPosition(selector, index) { + index = index || 0; + var selection = document.querySelectorAll(selector), + clientPos = selection[index].getBoundingClientRect(), + x = Math.floor((clientPos.left + clientPos.right) / 2), + y = Math.floor((clientPos.top + clientPos.bottom) / 2); + return [x, y]; +} + + describe('ternary plots', function() { 'use strict'; @@ -334,3 +344,207 @@ describe('ternary defaults', function() { expect(layoutOut.ternary.caxis.gridcolor).toEqual('black'); }); }); + + +describe('Test event property of interactions on a ternary plot:', function() { + var mock = require('@mocks/ternary_simple.json'); + + var mockCopy, gd; + + var blankPos = [10, 10], + pointPos; + + beforeAll(function(done) { + jasmine.addMatchers(customMatchers); + + gd = createGraphDiv(); + mockCopy = Lib.extendDeep({}, mock); + Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(function() { + pointPos = getClientPosition('path.point'); + destroyGraphDiv(); + done(); + }); + }); + + beforeEach(function() { + gd = createGraphDiv(); + mockCopy = Lib.extendDeep({}, mock); + }); + + afterEach(destroyGraphDiv); + + describe('click events', function() { + var futureData; + + beforeEach(function(done) { + Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); + + gd.on('plotly_click', function(data) { + futureData = data; + }); + }); + + it('should not be trigged when not on data points', function() { + click(blankPos[0], blankPos[1]); + expect(futureData).toBe(undefined); + }); + + it('should contain the correct fields', function() { + click(pointPos[0], pointPos[1]); + + var pt = futureData.points[0], + evt = futureData.event; + + expect(Object.keys(pt)).toEqual([ + 'data', 'fullData', 'curveNumber', 'pointNumber', 'x', 'y', + 'xaxis', 'yaxis' + ]); + + expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); + expect(typeof pt.data).toEqual(typeof {}, 'points[0].data'); + expect(typeof pt.fullData).toEqual(typeof {}, 'points[0].fullData'); + expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); + expect(pt.x).toEqual(undefined, 'points[0].x'); + expect(pt.y).toEqual(undefined, 'points[0].y'); + expect(typeof pt.xaxis).toEqual(typeof {}, 'points[0].xaxis'); + expect(typeof pt.yaxis).toEqual(typeof {}, 'points[0].yaxis'); + + expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); + expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); + }); + }); + + describe('modified click events', function() { + var clickOpts = { + altKey: true, + ctrlKey: true, + metaKey: true, + shiftKey: true + }, + futureData; + + beforeEach(function(done) { + Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); + + gd.on('plotly_click', function(data) { + futureData = data; + }); + }); + + it('should not be trigged when not on data points', function() { + click(blankPos[0], blankPos[1], clickOpts); + expect(futureData).toBe(undefined); + }); + + it('should contain the correct fields', function() { + click(pointPos[0], pointPos[1], clickOpts); + + var pt = futureData.points[0], + evt = futureData.event; + + expect(Object.keys(pt)).toEqual([ + 'data', 'fullData', 'curveNumber', 'pointNumber', 'x', 'y', + 'xaxis', 'yaxis' + ]); + + expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); + expect(typeof pt.data).toEqual(typeof {}, 'points[0].data'); + expect(typeof pt.fullData).toEqual(typeof {}, 'points[0].fullData'); + expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); + expect(pt.x).toEqual(undefined, 'points[0].x'); + expect(pt.y).toEqual(undefined, 'points[0].y'); + expect(typeof pt.xaxis).toEqual(typeof {}, 'points[0].xaxis'); + expect(typeof pt.yaxis).toEqual(typeof {}, 'points[0].yaxis'); + + expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); + expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); + Object.getOwnPropertyNames(clickOpts).forEach(function(opt) { + expect(evt[opt]).toEqual(clickOpts[opt], 'event.' + opt); + }); + }); + }); + + describe('hover events', function() { + var futureData; + + beforeEach(function(done) { + Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); + + gd.on('plotly_hover', function(data) { + futureData = data; + }); + }); + + it('should contain the correct fields', function() { + mouseEvent('mousemove', blankPos[0], blankPos[1]); + mouseEvent('mousemove', pointPos[0], pointPos[1]); + + var pt = futureData.points[0], + evt = futureData.event, + xaxes0 = futureData.xaxes[0], + xvals0 = futureData.xvals[0], + yaxes0 = futureData.yaxes[0], + yvals0 = futureData.yvals[0]; + + expect(Object.keys(pt)).toEqual([ + 'data', 'fullData', 'curveNumber', 'pointNumber', 'x', 'y', + 'xaxis', 'yaxis' + ]); + + expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); + expect(typeof pt.data).toEqual(typeof {}, 'points[0].data'); + expect(typeof pt.fullData).toEqual(typeof {}, 'points[0].fullData'); + expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); + expect(pt.x).toEqual(undefined, 'points[0].x'); + expect(pt.y).toEqual(undefined, 'points[0].y'); + expect(typeof pt.xaxis).toEqual(typeof {}, 'points[0].xaxis'); + expect(typeof pt.yaxis).toEqual(typeof {}, 'points[0].yaxis'); + + expect(xaxes0).toEqual(pt.xaxis, 'xaxes[0]'); + expect(xvals0).toEqual(-0.0016654247744483342, 'xaxes[0]'); + expect(yaxes0).toEqual(pt.yaxis, 'yaxes[0]'); + expect(yvals0).toEqual(0.5013, 'xaxes[0]'); + + expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); + expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); + }); + }); + + describe('unhover events', function() { + var futureData; + + beforeEach(function(done) { + Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); + + gd.on('plotly_unhover', function(data) { + futureData = data; + }); + }); + + it('should contain the correct fields', function() { + mouseEvent('mousemove', blankPos[0], blankPos[1]); + mouseEvent('mousemove', pointPos[0], pointPos[1]); + mouseEvent('mouseout', pointPos[0], pointPos[1]); + + var pt = futureData.points[0], + evt = futureData.event; + + expect(Object.keys(pt)).toEqual([ + 'data', 'fullData', 'curveNumber', 'pointNumber', 'x', 'y', + 'xaxis', 'yaxis' + ]); + + expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber'); + expect(typeof pt.data).toEqual(typeof {}, 'points[0].data'); + expect(typeof pt.fullData).toEqual(typeof {}, 'points[0].fullData'); + expect(pt.pointNumber).toEqual(0, 'points[0].pointNumber'); + expect(pt.x).toEqual(undefined, 'points[0].x'); + expect(pt.y).toEqual(undefined, 'points[0].y'); + expect(typeof pt.xaxis).toEqual(typeof {}, 'points[0].xaxis'); + expect(typeof pt.yaxis).toEqual(typeof {}, 'points[0].yaxis'); + + expect(evt.clientX).toEqual(pointPos[0], 'event.clientX'); + expect(evt.clientY).toEqual(pointPos[1], 'event.clientY'); + }); + }); +}); From f679bbf176fd07e9a9c59e180be76b2a9f083507 Mon Sep 17 00:00:00 2001 From: Nicolas Riesco Date: Tue, 21 Mar 2017 13:30:35 +0000 Subject: [PATCH 24/27] pie: test issue #902 * Test that 'plotly_hover' is triggered when `hoverinfo` is set to `"none"`. --- test/jasmine/tests/hover_pie_test.js | 54 ++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/test/jasmine/tests/hover_pie_test.js b/test/jasmine/tests/hover_pie_test.js index 7f89756b624..b49f4a53bac 100644 --- a/test/jasmine/tests/hover_pie_test.js +++ b/test/jasmine/tests/hover_pie_test.js @@ -23,6 +23,60 @@ function getClientPosition(selector, index) { describe('pie hovering', function() { var mock = require('@mocks/pie_simple.json'); + describe('with hoverinfo set to none', function() { + var mockCopy = Lib.extendDeep({}, mock), + gd; + + mockCopy.data[0].hoverinfo = 'none'; + + beforeEach(function(done) { + gd = createGraphDiv(); + + Plotly.plot(gd, mockCopy.data, mockCopy.layout) + .then(done); + }); + + afterEach(destroyGraphDiv); + + it('should fire hover event when moving from one slice to another', function(done) { + var count = 0, + hoverData = []; + + gd.on('plotly_hover', function(data) { + count++; + hoverData.push(data); + }); + + mouseEvent('mouseover', 173, 133); + setTimeout(function() { + mouseEvent('mouseover', 233, 193); + expect(count).toEqual(2); + expect(hoverData[0]).not.toEqual(hoverData[1]); + done(); + }, 100); + }); + + it('should fire unhover event when the mouse moves off the graph', function(done) { + var count = 0, + unhoverData = []; + + gd.on('plotly_unhover', function(data) { + count++; + unhoverData.push(data); + }); + + mouseEvent('mouseover', 173, 133); + mouseEvent('mouseout', 173, 133); + setTimeout(function() { + mouseEvent('mouseover', 233, 193); + mouseEvent('mouseout', 233, 193); + expect(count).toEqual(2); + expect(unhoverData[0]).not.toEqual(unhoverData[1]); + done(); + }, 100); + }); + }); + describe('event data', function() { var mockCopy = Lib.extendDeep({}, mock), width = mockCopy.layout.width, From 91cc175024fa6283167494fbc8c5ded85409fe3b Mon Sep 17 00:00:00 2001 From: Nicolas Riesco Date: Tue, 21 Mar 2017 13:31:48 +0000 Subject: [PATCH 25/27] pie: emit `plotly_hover` even if hoverinfo is none Fixes https://github.com/plotly/plotly.js/issues/902 --- src/traces/pie/plot.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/traces/pie/plot.js b/src/traces/pie/plot.js index e1fdbf94753..0b0893afdd0 100644 --- a/src/traces/pie/plot.js +++ b/src/traces/pie/plot.js @@ -86,6 +86,8 @@ module.exports = function plot(gd, cdpie) { hasHoverData = false; function handleMouseOver(evt) { + evt.originalEvent = d3.event; + // in case fullLayout or fullData has changed without a replot var fullLayout2 = gd._fullLayout, trace2 = gd._fullData[trace.index], @@ -97,6 +99,7 @@ module.exports = function plot(gd, cdpie) { // or if hover is turned off if(gd._dragging || fullLayout2.hovermode === false || hoverinfo === 'none' || hoverinfo === 'skip' || !hoverinfo) { + Fx.hover(gd, evt, 'pie'); return; } @@ -126,7 +129,6 @@ module.exports = function plot(gd, cdpie) { outerContainer: fullLayout2._paper.node() }); - evt.originalEvent = d3.event; Fx.hover(gd, evt, 'pie'); hasHoverData = true; From 499c393773467811d2288b1aed1e27874c466aac Mon Sep 17 00:00:00 2001 From: Nicolas Riesco Date: Tue, 21 Mar 2017 14:36:32 +0000 Subject: [PATCH 26/27] pie: test issue #1456 * Test that 'plotly_click' doesn't emit an undefined trace. --- test/jasmine/tests/hover_pie_test.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/jasmine/tests/hover_pie_test.js b/test/jasmine/tests/hover_pie_test.js index b49f4a53bac..ef20b62f022 100644 --- a/test/jasmine/tests/hover_pie_test.js +++ b/test/jasmine/tests/hover_pie_test.js @@ -285,6 +285,9 @@ describe('Test event property of interactions on a pie plot:', function() { click(pointPos[0], pointPos[1]); expect(futureData.points.length).toEqual(1); + var trace = futureData.points.trace; + expect(typeof trace).toEqual(typeof {}, 'points.trace'); + var pt = futureData.points[0]; expect(Object.keys(pt)).toEqual([ 'v', 'label', 'color', 'i', 'hidden', 'vTotal', 'text', 't', @@ -343,6 +346,9 @@ describe('Test event property of interactions on a pie plot:', function() { click(pointPos[0], pointPos[1], clickOpts); expect(futureData.points.length).toEqual(1); + var trace = futureData.points.trace; + expect(typeof trace).toEqual(typeof {}, 'points.trace'); + var pt = futureData.points[0]; expect(Object.keys(pt)).toEqual([ 'v', 'label', 'color', 'i', 'hidden', 'vTotal', 'text', 't', From 9e10119141a724161a1cfba4e1b68e341c1b150c Mon Sep 17 00:00:00 2001 From: Nicolas Riesco Date: Tue, 21 Mar 2017 14:38:18 +0000 Subject: [PATCH 27/27] pie: Fix undefined trace in 'plotly_click' Fixes https://github.com/plotly/plotly.js/issues/1456 --- src/traces/pie/plot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/traces/pie/plot.js b/src/traces/pie/plot.js index 0b0893afdd0..8539093101f 100644 --- a/src/traces/pie/plot.js +++ b/src/traces/pie/plot.js @@ -149,7 +149,7 @@ module.exports = function plot(gd, cdpie) { function handleClick() { gd._hoverdata = [pt]; - gd._hoverdata.trace = cd.trace; + gd._hoverdata.trace = cd0.trace; Fx.click(gd, d3.event); }