diff --git a/src/components/dragelement/unhover.js b/src/components/dragelement/unhover.js index 731b1470637..1eea8dde037 100644 --- a/src/components/dragelement/unhover.js +++ b/src/components/dragelement/unhover.js @@ -11,19 +11,19 @@ var Events = require('../../lib/events'); +var throttle = require('../../lib/throttle'); +var getGraphDiv = require('../../lib/get_graph_div'); +var hoverConstants = require('../fx/constants'); var unhover = module.exports = {}; unhover.wrapped = function(gd, evt, subplot) { - if(typeof gd === 'string') gd = document.getElementById(gd); + gd = getGraphDiv(gd); // Important, clear any queued hovers - if(gd._hoverTimer) { - clearTimeout(gd._hoverTimer); - gd._hoverTimer = undefined; - } + throttle.clear(gd._fullLayout._uid + hoverConstants.HOVERID); unhover.raw(gd, evt, subplot); }; diff --git a/src/components/fx/constants.js b/src/components/fx/constants.js index 37e21d014e0..12f46ef7764 100644 --- a/src/components/fx/constants.js +++ b/src/components/fx/constants.js @@ -26,5 +26,8 @@ module.exports = { HOVERFONT: 'Arial, sans-serif', // minimum time (msec) between hover calls - HOVERMINTIME: 50 + HOVERMINTIME: 50, + + // ID suffix (with fullLayout._uid) for hover events in the throttle cache + HOVERID: '-hover' }; diff --git a/src/components/fx/hover.js b/src/components/fx/hover.js index c9f9186009b..612ce0200c4 100644 --- a/src/components/fx/hover.js +++ b/src/components/fx/hover.js @@ -67,27 +67,13 @@ var HOVERTEXTPAD = constants.HOVERTEXTPAD; // We wrap the hovers in a timer, to limit their frequency. // The actual rendering is done by private function _hover. exports.hover = function hover(gd, evt, subplot, noHoverEvent) { - if(typeof gd === 'string') gd = document.getElementById(gd); - if(gd._lastHoverTime === undefined) gd._lastHoverTime = 0; + gd = Lib.getGraphDiv(gd); - // If we have an update queued, discard it now - if(gd._hoverTimer !== undefined) { - clearTimeout(gd._hoverTimer); - gd._hoverTimer = undefined; - } - // Is it more than 100ms since the last update? If so, force - // an update now (synchronously) and exit - if(Date.now() > gd._lastHoverTime + constants.HOVERMINTIME) { - _hover(gd, evt, subplot, noHoverEvent); - gd._lastHoverTime = Date.now(); - return; - } - // Queue up the next hover for 100ms from now (if no further events) - gd._hoverTimer = setTimeout(function() { - _hover(gd, evt, subplot, noHoverEvent); - gd._lastHoverTime = Date.now(); - gd._hoverTimer = undefined; - }, constants.HOVERMINTIME); + Lib.throttle( + gd._fullLayout._uid + constants.HOVERID, + constants.HOVERMINTIME, + function() { _hover(gd, evt, subplot, noHoverEvent); } + ); }; /* diff --git a/src/lib/get_graph_div.js b/src/lib/get_graph_div.js new file mode 100644 index 00000000000..aa96f651a6c --- /dev/null +++ b/src/lib/get_graph_div.js @@ -0,0 +1,36 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +/** + * Allow referencing a graph DOM element either directly + * or by its id string + * + * @param {HTMLDivElement|string} gd: a graph element or its id + * + * @returns {HTMLDivElement} the DOM element of the graph + */ +module.exports = function(gd) { + var gdElement; + + if(typeof gd === 'string') { + gdElement = document.getElementById(gd); + + if(gdElement === null) { + throw new Error('No DOM element with id \'' + gd + '\' exists on the page.'); + } + + return gdElement; + } + else if(gd === null || gd === undefined) { + throw new Error('DOM element provided is null or undefined'); + } + + return gd; // otherwise assume that gd is a DOM element +}; diff --git a/src/lib/index.js b/src/lib/index.js index 42620398469..8e0e2abe17f 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -97,6 +97,13 @@ lib.error = loggersModule.error; var regexModule = require('./regex'); lib.counterRegex = regexModule.counter; +var throttleModule = require('./throttle'); +lib.throttle = throttleModule.throttle; +lib.throttleDone = throttleModule.done; +lib.clearThrottle = throttleModule.clear; + +lib.getGraphDiv = require('./get_graph_div'); + lib.notifier = require('./notifier'); lib.filterUnique = require('./filter_unique'); diff --git a/src/lib/throttle.js b/src/lib/throttle.js new file mode 100644 index 00000000000..5f2960818dd --- /dev/null +++ b/src/lib/throttle.js @@ -0,0 +1,102 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var timerCache = {}; + +/** + * Throttle a callback. `callback` executes synchronously only if + * more than `minInterval` milliseconds have already elapsed since the latest + * call (if any). Otherwise we wait until `minInterval` is over and execute the + * last callback received while waiting. + * So the first and last events in a train are always executed (eventually) + * but some of the events in the middle can be dropped. + * + * @param {string} id: an identifier to mark events to throttle together + * @param {number} minInterval: minimum time, in milliseconds, between + * invocations of `callback` + * @param {function} callback: the function to throttle. `callback` itself + * should be a purely synchronous function. + */ +exports.throttle = function throttle(id, minInterval, callback) { + var cache = timerCache[id]; + var now = Date.now(); + + if(!cache) { + /* + * Throw out old items before making a new one, to prevent the cache + * getting overgrown, for example from old plots that have been replaced. + * 1 minute age is arbitrary. + */ + for(var idi in timerCache) { + if(timerCache[idi].ts < now - 60000) { + delete timerCache[idi]; + } + } + cache = timerCache[id] = {ts: 0, timer: null}; + } + + _clearTimeout(cache); + + function exec() { + callback(); + cache.ts = Date.now(); + if(cache.onDone) { + cache.onDone(); + cache.onDone = null; + } + } + + if(now > cache.ts + minInterval) { + exec(); + return; + } + + cache.timer = setTimeout(function() { + exec(); + cache.timer = null; + }, minInterval); +}; + +exports.done = function(id) { + var cache = timerCache[id]; + if(!cache || !cache.timer) return Promise.resolve(); + + return new Promise(function(resolve) { + var previousOnDone = cache.onDone; + cache.onDone = function onDone() { + if(previousOnDone) previousOnDone(); + resolve(); + cache.onDone = null; + }; + }); +}; + +/** + * Clear the throttle cache for one or all timers + * @param {optional string} id: + * if provided, clear just this timer + * if omitted, clear all timers (mainly useful for testing) + */ +exports.clear = function(id) { + if(id) { + _clearTimeout(timerCache[id]); + delete timerCache[id]; + } + else { + for(var idi in timerCache) exports.clear(idi); + } +}; + +function _clearTimeout(cache) { + if(cache && cache.timer !== null) { + clearTimeout(cache.timer); + cache.timer = null; + } +} diff --git a/src/plot_api/helpers.js b/src/plot_api/helpers.js index 322adec5f97..e27eff1cb9b 100644 --- a/src/plot_api/helpers.js +++ b/src/plot_api/helpers.js @@ -19,28 +19,6 @@ var Axes = require('../plots/cartesian/axes'); var Color = require('../components/color'); -// Get the container div: we store all variables for this plot as -// properties of this div -// some callers send this in by DOM element, others by id (string) -exports.getGraphDiv = function(gd) { - var gdElement; - - if(typeof gd === 'string') { - gdElement = document.getElementById(gd); - - if(gdElement === null) { - throw new Error('No DOM element with id \'' + gd + '\' exists on the page.'); - } - - return gdElement; - } - else if(gd === null || gd === undefined) { - throw new Error('DOM element provided is null or undefined'); - } - - return gd; // otherwise assume that gd is a DOM element -}; - // clear the promise queue if one of them got rejected exports.clearPromiseQueue = function(gd) { if(Array.isArray(gd._promises) && gd._promises.length > 0) { diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js index 486aea85e45..b12611ccbc7 100644 --- a/src/plot_api/plot_api.js +++ b/src/plot_api/plot_api.js @@ -60,7 +60,7 @@ var axisIds = require('../plots/cartesian/axis_ids'); Plotly.plot = function(gd, data, layout, config) { var frames; - gd = helpers.getGraphDiv(gd); + gd = Lib.getGraphDiv(gd); // Events.init is idempotent and bails early if gd has already been init'd Events.init(gd); @@ -581,7 +581,7 @@ function plotPolar(gd, data, layout) { // convenience function to force a full redraw, mostly for use by plotly.js Plotly.redraw = function(gd) { - gd = helpers.getGraphDiv(gd); + gd = Lib.getGraphDiv(gd); if(!Lib.isPlotDiv(gd)) { throw new Error('This element is not a Plotly plot: ' + gd); @@ -606,7 +606,7 @@ Plotly.redraw = function(gd) { * @param {Object} config */ Plotly.newPlot = function(gd, data, layout, config) { - gd = helpers.getGraphDiv(gd); + gd = Lib.getGraphDiv(gd); // remove gl contexts Plots.cleanPlot([], {}, gd._fullData || {}, gd._fullLayout || {}); @@ -959,7 +959,7 @@ function spliceTraces(gd, update, indices, maxPoints, lengthenArray, spliceArray * */ Plotly.extendTraces = function extendTraces(gd, update, indices, maxPoints) { - gd = helpers.getGraphDiv(gd); + gd = Lib.getGraphDiv(gd); var undo = spliceTraces(gd, update, indices, maxPoints, @@ -986,7 +986,7 @@ Plotly.extendTraces = function extendTraces(gd, update, indices, maxPoints) { }; Plotly.prependTraces = function prependTraces(gd, update, indices, maxPoints) { - gd = helpers.getGraphDiv(gd); + gd = Lib.getGraphDiv(gd); var undo = spliceTraces(gd, update, indices, maxPoints, @@ -1022,7 +1022,7 @@ Plotly.prependTraces = function prependTraces(gd, update, indices, maxPoints) { * */ Plotly.addTraces = function addTraces(gd, traces, newIndices) { - gd = helpers.getGraphDiv(gd); + gd = Lib.getGraphDiv(gd); var currentIndices = [], undoFunc = Plotly.deleteTraces, @@ -1099,7 +1099,7 @@ Plotly.addTraces = function addTraces(gd, traces, newIndices) { * @param {Number|Number[]} indices The indices */ Plotly.deleteTraces = function deleteTraces(gd, indices) { - gd = helpers.getGraphDiv(gd); + gd = Lib.getGraphDiv(gd); var traces = [], undoFunc = Plotly.addTraces, @@ -1165,7 +1165,7 @@ Plotly.deleteTraces = function deleteTraces(gd, indices) { * Plotly.moveTraces(gd, [b, d, e, a, c]) // same as 'move to end' */ Plotly.moveTraces = function moveTraces(gd, currentIndices, newIndices) { - gd = helpers.getGraphDiv(gd); + gd = Lib.getGraphDiv(gd); var newData = [], movingTraceMap = [], @@ -1262,7 +1262,7 @@ Plotly.moveTraces = function moveTraces(gd, currentIndices, newIndices) { * style files that want to specify cyclical default values). */ Plotly.restyle = function restyle(gd, astr, val, _traces) { - gd = helpers.getGraphDiv(gd); + gd = Lib.getGraphDiv(gd); helpers.clearPromiseQueue(gd); var aobj = {}; @@ -1649,7 +1649,7 @@ function _restyle(gd, aobj, traces) { * allows setting multiple attributes simultaneously */ Plotly.relayout = function relayout(gd, astr, val) { - gd = helpers.getGraphDiv(gd); + gd = Lib.getGraphDiv(gd); helpers.clearPromiseQueue(gd); if(gd.framework && gd.framework.isPolar) { @@ -2079,7 +2079,7 @@ function _relayout(gd, aobj) { * */ Plotly.update = function update(gd, traceUpdate, layoutUpdate, _traces) { - gd = helpers.getGraphDiv(gd); + gd = Lib.getGraphDiv(gd); helpers.clearPromiseQueue(gd); if(gd.framework && gd.framework.isPolar) { @@ -2185,7 +2185,7 @@ Plotly.update = function update(gd, traceUpdate, layoutUpdate, _traces) { * configuration for the animation */ Plotly.animate = function(gd, frameOrGroupNameOrFrameList, animationOpts) { - gd = helpers.getGraphDiv(gd); + gd = Lib.getGraphDiv(gd); if(!Lib.isPlotDiv(gd)) { throw new Error( @@ -2549,7 +2549,7 @@ Plotly.animate = function(gd, frameOrGroupNameOrFrameList, animationOpts) { * will be overwritten. */ Plotly.addFrames = function(gd, frameList, indices) { - gd = helpers.getGraphDiv(gd); + gd = Lib.getGraphDiv(gd); var numericNameWarningCount = 0; @@ -2673,7 +2673,7 @@ Plotly.addFrames = function(gd, frameList, indices) { * list of integer indices of frames to be deleted */ Plotly.deleteFrames = function(gd, frameList) { - gd = helpers.getGraphDiv(gd); + gd = Lib.getGraphDiv(gd); if(!Lib.isPlotDiv(gd)) { throw new Error('This element is not a Plotly plot: ' + gd); @@ -2717,7 +2717,7 @@ Plotly.deleteFrames = function(gd, frameList) { * the id or DOM element of the graph container div */ Plotly.purge = function purge(gd) { - gd = helpers.getGraphDiv(gd); + gd = Lib.getGraphDiv(gd); var fullLayout = gd._fullLayout || {}, fullData = gd._fullData || []; diff --git a/src/plot_api/to_image.js b/src/plot_api/to_image.js index 059cd973c50..3b01f4a4702 100644 --- a/src/plot_api/to_image.js +++ b/src/plot_api/to_image.js @@ -15,8 +15,6 @@ var helpers = require('../snapshot/helpers'); var toSVG = require('../snapshot/tosvg'); var svgToImg = require('../snapshot/svgtoimg'); -var getGraphDiv = require('./helpers').getGraphDiv; - var attrs = { format: { valType: 'enumerated', @@ -95,7 +93,7 @@ function toImage(gd, opts) { layout = gd.layout || {}; config = gd.config || {}; } else { - gd = getGraphDiv(gd); + gd = Lib.getGraphDiv(gd); data = Lib.extendDeep([], gd.data); layout = Lib.extendDeep({}, gd.layout); config = gd._context; diff --git a/src/plots/cartesian/constants.js b/src/plots/cartesian/constants.js index 93468b6b5f8..b44235855a2 100644 --- a/src/plots/cartesian/constants.js +++ b/src/plots/cartesian/constants.js @@ -47,6 +47,12 @@ module.exports = { // delay before a redraw (relayout) after smooth panning and zooming REDRAWDELAY: 50, + // throttling limit (ms) for selectPoints calls + SELECTDELAY: 100, + + // cache ID suffix for throttle + SELECTID: '-select', + // last resort axis ranges for x and y axes if we have no data DFLTRANGEX: [-1, 6], DFLTRANGEY: [-1, 4], diff --git a/src/plots/cartesian/select.js b/src/plots/cartesian/select.js index 50095155a99..70952c023dc 100644 --- a/src/plots/cartesian/select.js +++ b/src/plots/cartesian/select.js @@ -10,6 +10,7 @@ 'use strict'; var polygon = require('../../lib/polygon'); +var throttle = require('../../lib/throttle'); var color = require('../../components/color'); var appendArrayPointValue = require('../../components/fx/helpers').appendArrayPointValue; @@ -64,14 +65,11 @@ module.exports = function prepSelect(e, startX, startY, dragOptions, mode) { // find the traces to search for selection points - var searchTraces = [], - gd = dragOptions.gd, - i, - cd, - trace, - searchInfo, - selection = [], - eventData; + var searchTraces = []; + var gd = dragOptions.gd; + var throttleID = gd._fullLayout._uid + constants.SELECTID; + var selection = []; + var i, cd, trace, searchInfo, eventData; for(i = 0; i < gd.calcdata.length; i++) { cd = gd.calcdata[i]; @@ -185,34 +183,50 @@ module.exports = function prepSelect(e, startX, startY, dragOptions, mode) { outlines.attr('d', 'M' + pts.filtered.join('L') + 'Z'); } - selection = []; - for(i = 0; i < searchTraces.length; i++) { - searchInfo = searchTraces[i]; - [].push.apply(selection, fillSelectionItem( - searchInfo.selectPoints(searchInfo, poly), searchInfo - )); - } + throttle.throttle( + throttleID, + constants.SELECTDELAY, + function() { + selection = []; + for(i = 0; i < searchTraces.length; i++) { + searchInfo = searchTraces[i]; + var thisSelection = fillSelectionItem( + searchInfo.selectPoints(searchInfo, poly), searchInfo + ); + if(selection.length) { + for(var j = 0; j < thisSelection.length; j++) { + selection.push(thisSelection[j]); + } + } + else selection = thisSelection; + } - eventData = {points: selection}; - fillRangeItems(eventData, poly, pts); - dragOptions.gd.emit('plotly_selecting', eventData); + eventData = {points: selection}; + fillRangeItems(eventData, poly, pts); + dragOptions.gd.emit('plotly_selecting', eventData); + } + ); }; dragOptions.doneFn = function(dragged, numclicks) { corners.remove(); - if(!dragged && numclicks === 2) { - // clear selection on doubleclick - outlines.remove(); - for(i = 0; i < searchTraces.length; i++) { - searchInfo = searchTraces[i]; - searchInfo.selectPoints(searchInfo, false); - } + throttle.done(throttleID).then(function() { + throttle.clear(throttleID); + + if(!dragged && numclicks === 2) { + // clear selection on doubleclick + outlines.remove(); + for(i = 0; i < searchTraces.length; i++) { + searchInfo = searchTraces[i]; + searchInfo.selectPoints(searchInfo, false); + } - gd.emit('plotly_deselect', null); - } - else { - dragOptions.gd.emit('plotly_selected', eventData); - } + gd.emit('plotly_deselect', null); + } + else { + dragOptions.gd.emit('plotly_selected', eventData); + } + }); }; }; diff --git a/src/plots/plots.js b/src/plots/plots.js index 64bc8952617..d96643fa983 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -1327,8 +1327,6 @@ plots.purge = function(gd) { delete gd.hmlumcount; delete gd.hmpixcount; delete gd.numboxes; - delete gd._hoverTimer; - delete gd._lastHoverTime; delete gd._transitionData; delete gd._transitioning; delete gd._initialAutoSize; diff --git a/test/jasmine/tests/cartesian_interact_test.js b/test/jasmine/tests/cartesian_interact_test.js index 019cbeb519b..493c79a8e88 100644 --- a/test/jasmine/tests/cartesian_interact_test.js +++ b/test/jasmine/tests/cartesian_interact_test.js @@ -446,7 +446,7 @@ describe('Event data:', function() { function _hover(px, py) { return new Promise(function(resolve, reject) { gd.once('plotly_hover', function(d) { - delete gd._lastHoverTime; + Lib.clearThrottle(); resolve(d); }); diff --git a/test/jasmine/tests/geo_test.js b/test/jasmine/tests/geo_test.js index ab082395d20..444deb23de5 100644 --- a/test/jasmine/tests/geo_test.js +++ b/test/jasmine/tests/geo_test.js @@ -1218,7 +1218,7 @@ describe('Test geo interactions', function() { expect(d3.selectAll('g.hovertext').size()) .toBe(hoverLabelCnt, 'for ' + lonlat); - delete gd._lastHoverTime; + Lib.clearThrottle(); } Plotly.plot(gd, fig).then(function() { @@ -1301,7 +1301,7 @@ describe('Test geo interactions', function() { mouseEvent('mousemove', px[0], px[1]); expect(d3.selectAll('g.hovertext').size()).toBe(hoverLabelCnt, msg); - delete gd._lastHoverTime; + Lib.clearThrottle(); } Plotly.newPlot(gd, [{ @@ -1954,8 +1954,8 @@ describe('Test geo zoom/pan/drag interactions:', function() { var center = geoLayout.center; var scale = geoLayout.projection.scale; - expect(center.lon).toBeCloseTo(attr[0][0], 1, msg + 'center.lon'); - expect(center.lat).toBeCloseTo(attr[0][1], 1, msg + 'center.lat'); + expect(center.lon).toBeCloseTo(attr[0][0], 0.5, msg + 'center.lon'); + expect(center.lat).toBeCloseTo(attr[0][1], 0.5, msg + 'center.lat'); expect(scale).toBeCloseTo(attr[1], 1, msg + 'zoom'); // albersUsa projection does not have a center() method diff --git a/test/jasmine/tests/gl2d_click_test.js b/test/jasmine/tests/gl2d_click_test.js index 7ab364f11aa..bdca5e9d721 100644 --- a/test/jasmine/tests/gl2d_click_test.js +++ b/test/jasmine/tests/gl2d_click_test.js @@ -496,10 +496,12 @@ describe('@noCI Test gl2d lasso/select:', function() { function drag(path) { var len = path.length; + Lib.clearThrottle(); mouseEvent('mousemove', path[0][0], path[0][1]); mouseEvent('mousedown', path[0][0], path[0][1]); path.slice(1, len).forEach(function(pt) { + Lib.clearThrottle(); mouseEvent('mousemove', pt[0], pt[1]); }); diff --git a/test/jasmine/tests/hover_label_test.js b/test/jasmine/tests/hover_label_test.js index 2acd558e0f4..39f7e3618c4 100644 --- a/test/jasmine/tests/hover_label_test.js +++ b/test/jasmine/tests/hover_label_test.js @@ -470,7 +470,7 @@ describe('hover info', function() { describe('\'hover info for x/y/z traces', function() { function _hover(gd, xpx, ypx) { Fx.hover(gd, { xpx: xpx, ypx: ypx }, 'xy'); - delete gd._lastHoverTime; + Lib.clearThrottle(); } function _assert(nameLabel, lines) { @@ -811,7 +811,7 @@ describe('hover after resizing', function() { } function assertLabelCount(pos, cnt, msg) { - delete gd._lastHoverTime; + Lib.clearThrottle(); mouseEvent('mousemove', pos[0], pos[1]); var hoverText = d3.selectAll('g.hovertext'); @@ -1097,7 +1097,7 @@ describe('Test hover label custom styling:', function() { function _hover(gd, opts) { Fx.hover(gd, opts); - delete gd._lastHoverTime; + Lib.clearThrottle(); } it('should work for x/y cartesian traces', function(done) { diff --git a/test/jasmine/tests/hover_spikeline_test.js b/test/jasmine/tests/hover_spikeline_test.js index e983609b6ae..aca3c9f29d2 100644 --- a/test/jasmine/tests/hover_spikeline_test.js +++ b/test/jasmine/tests/hover_spikeline_test.js @@ -30,7 +30,7 @@ describe('spikeline', function() { function _hover(evt, subplot) { Fx.hover(gd, evt, subplot); - delete gd._lastHoverTime; + Lib.clearThrottle(); } function _assert(lineExpect, circleExpect) { diff --git a/test/jasmine/tests/pie_test.js b/test/jasmine/tests/pie_test.js index 957d1388e6c..a712a210c54 100644 --- a/test/jasmine/tests/pie_test.js +++ b/test/jasmine/tests/pie_test.js @@ -241,7 +241,7 @@ describe('pie hovering', function() { function _hover() { mouseEvent('mouseover', 223, 143); - delete gd._lastHoverTime; + Lib.clearThrottle(); } function assertLabel(content, style) { diff --git a/test/jasmine/tests/plots_test.js b/test/jasmine/tests/plots_test.js index b95b6482db0..efe5a92bf14 100644 --- a/test/jasmine/tests/plots_test.js +++ b/test/jasmine/tests/plots_test.js @@ -424,8 +424,7 @@ describe('Test Plots', function() { 'data', 'layout', '_fullData', '_fullLayout', 'calcdata', 'framework', 'empty', 'fid', 'undoqueue', 'undonum', 'autoplay', 'changed', '_promises', '_redrawTimer', 'firstscatter', 'hmlumcount', 'hmpixcount', - 'numboxes', '_hoverTimer', '_lastHoverTime', '_transitionData', - '_transitioning' + 'numboxes', '_transitionData', '_transitioning' ]; Plots.purge(gd); diff --git a/test/jasmine/tests/sankey_test.js b/test/jasmine/tests/sankey_test.js index 92f2197bbb9..9549826d058 100644 --- a/test/jasmine/tests/sankey_test.js +++ b/test/jasmine/tests/sankey_test.js @@ -382,7 +382,7 @@ describe('sankey tests', function() { function _hover(px, py) { mouseEvent('mousemove', px, py); mouseEvent('mouseover', px, py); - delete gd._lastHoverTime; + Lib.clearThrottle(); } Plotly.plot(gd, mockCopy).then(function() { @@ -454,7 +454,7 @@ describe('sankey tests', function() { function _hover(px, py) { mouseEvent('mousemove', px, py); mouseEvent('mouseover', px, py); - delete gd._lastHoverTime; + Lib.clearThrottle(); } Plotly.plot(gd, mockCopy) @@ -489,7 +489,7 @@ describe('sankey tests', function() { return function(elType) { return new Promise(function(resolve, reject) { gd.once(eventType, function(d) { - delete gd._lastHoverTime; + Lib.clearThrottle(); resolve(d); }); diff --git a/test/jasmine/tests/select_test.js b/test/jasmine/tests/select_test.js index d6c2dd4c0c5..67f6f8b2df0 100644 --- a/test/jasmine/tests/select_test.js +++ b/test/jasmine/tests/select_test.js @@ -17,10 +17,13 @@ function drag(path, options) { if(!options) options = {type: 'mouse'}; + Lib.clearThrottle(); + if(options.type === 'touch') { touchEvent('touchstart', path[0][0], path[0][1]); path.slice(1, len).forEach(function(pt) { + Lib.clearThrottle(); touchEvent('touchmove', pt[0], pt[1]); }); @@ -32,6 +35,7 @@ function drag(path, options) { mouseEvent('mousedown', path[0][0], path[0][1]); path.slice(1, len).forEach(function(pt) { + Lib.clearThrottle(); mouseEvent('mousemove', pt[0], pt[1]); }); @@ -45,9 +49,63 @@ function assertSelectionNodes(cornerCnt, outlineCnt) { .toBe(outlineCnt, 'selection outline count'); } +var selectingCnt, selectingData, selectedCnt, selectedData, deselectCnt, doubleClickData; +var selectedPromise, deselectPromise; + +function resetEvents(gd) { + selectingCnt = 0; + selectedCnt = 0; + deselectCnt = 0; + doubleClickData = null; + + gd.removeAllListeners(); + + selectedPromise = new Promise(function(resolve) { + gd.on('plotly_selecting', function(data) { + // note that since all of these events test node counts, + // and all of the other tests at some point check that each of + // these event handlers was called (via assertEventCounts), + // we no longer need separate tests that these nodes are created + // and this way *all* subplot variants get the test. + assertSelectionNodes(1, 2); + selectingCnt++; + selectingData = data; + }); + + gd.on('plotly_selected', function(data) { + assertSelectionNodes(0, 2); + selectedCnt++; + selectedData = data; + resolve(); + }); + }); + + deselectPromise = new Promise(function(resolve) { + gd.on('plotly_deselect', function(data) { + assertSelectionNodes(0, 0); + deselectCnt++; + doubleClickData = data; + resolve(); + }); + }); +} + +function assertEventCounts(selecting, selected, deselect, msg) { + expect(selectingCnt).toBe(selecting, 'plotly_selecting call count: ' + msg); + expect(selectedCnt).toBe(selected, 'plotly_selected call count: ' + msg); + expect(deselectCnt).toBe(deselect, 'plotly_deselect call count: ' + msg); +} + +// events for box or lasso select mouse moves then a doubleclick +var NOEVENTS = [0, 0, 0]; +// deselect gives an extra plotly_selected event on the first click +// event data is undefined +var BOXEVENTS = [1, 2, 1]; +// assumes 5 points in the lasso path +var LASSOEVENTS = [4, 2, 1]; + describe('Test select box and lasso in general:', function() { var mock = require('@mocks/14.json'); - var selectPath = [[93, 193], [143, 193]]; var lassoPath = [[316, 171], [318, 239], [335, 243], [328, 169]]; @@ -77,86 +135,6 @@ describe('Test select box and lasso in general:', function() { }); } - describe('select elements', function() { - var mockCopy = Lib.extendDeep({}, mock); - mockCopy.layout.dragmode = 'select'; - - var gd; - beforeEach(function(done) { - gd = createGraphDiv(); - - Plotly.plot(gd, mockCopy.data, mockCopy.layout) - .then(done); - }); - - it('should be appended to the zoom layer', function(done) { - var x0 = 100, - y0 = 200, - x1 = 150, - y1 = 250, - x2 = 50, - y2 = 50; - - gd.once('plotly_selecting', function() { - assertSelectionNodes(1, 2); - }); - - gd.once('plotly_selected', function() { - assertSelectionNodes(0, 2); - }); - - gd.once('plotly_deselect', function() { - assertSelectionNodes(0, 0); - }); - - mouseEvent('mousemove', x0, y0); - assertSelectionNodes(0, 0); - - drag([[x0, y0], [x1, y1]]); - doubleClick(x2, y2).then(done); - }); - }); - - describe('lasso elements', function() { - var mockCopy = Lib.extendDeep({}, mock); - mockCopy.layout.dragmode = 'lasso'; - - var gd; - beforeEach(function(done) { - gd = createGraphDiv(); - - Plotly.plot(gd, mockCopy.data, mockCopy.layout) - .then(done); - }); - - it('should be appended to the zoom layer', function(done) { - var x0 = 100, - y0 = 200, - x1 = 150, - y1 = 250, - x2 = 50, - y2 = 50; - - gd.once('plotly_selecting', function() { - assertSelectionNodes(1, 2); - }); - - gd.once('plotly_selected', function() { - assertSelectionNodes(0, 2); - }); - - gd.once('plotly_deselect', function() { - assertSelectionNodes(0, 0); - }); - - mouseEvent('mousemove', x0, y0); - assertSelectionNodes(0, 0); - - drag([[x0, y0], [x1, y1]]); - doubleClick(x2, y2).then(done); - }); - }); - describe('select events', function() { var mockCopy = Lib.extendDeep({}, mock); mockCopy.layout.dragmode = 'select'; @@ -174,72 +152,60 @@ describe('Test select box and lasso in general:', function() { }); it('should trigger selecting/selected/deselect events', function(done) { - var selectingCnt = 0, - selectingData; - gd.on('plotly_selecting', function(data) { - selectingCnt++; - selectingData = data; - }); - - var selectedCnt = 0, - selectedData; - gd.on('plotly_selected', function(data) { - selectedCnt++; - selectedData = data; - }); - - var doubleClickData; - gd.on('plotly_deselect', function(data) { - doubleClickData = data; - }); + resetEvents(gd); drag(selectPath); - expect(selectingCnt).toEqual(1, 'with the correct selecting count'); - assertEventData(selectingData.points, [{ - curveNumber: 0, - pointNumber: 0, - x: 0.002, - y: 16.25, - id: 'id-0.002', - customdata: 'customdata-16.25' - }, { - curveNumber: 0, - pointNumber: 1, - x: 0.004, - y: 12.5, - id: 'id-0.004', - customdata: 'customdata-12.5' - }], 'with the correct selecting points (1)'); - assertRange(selectingData.range, { - x: [0.002000, 0.0046236], - y: [0.10209191961595454, 24.512223978291406] - }, 'with the correct selecting range'); - expect(selectedCnt).toEqual(1, 'with the correct selected count'); - assertEventData(selectedData.points, [{ - curveNumber: 0, - pointNumber: 0, - x: 0.002, - y: 16.25, - id: 'id-0.002', - customdata: 'customdata-16.25' - }, { - curveNumber: 0, - pointNumber: 1, - x: 0.004, - y: 12.5, - id: 'id-0.004', - customdata: 'customdata-12.5' - }], 'with the correct selected points (2)'); - assertRange(selectedData.range, { - x: [0.002000, 0.0046236], - y: [0.10209191961595454, 24.512223978291406] - }, 'with the correct selected range'); - - doubleClick(250, 200).then(function() { + selectedPromise.then(function() { + expect(selectingCnt).toBe(1, 'with the correct selecting count'); + assertEventData(selectingData.points, [{ + curveNumber: 0, + pointNumber: 0, + x: 0.002, + y: 16.25, + id: 'id-0.002', + customdata: 'customdata-16.25' + }, { + curveNumber: 0, + pointNumber: 1, + x: 0.004, + y: 12.5, + id: 'id-0.004', + customdata: 'customdata-12.5' + }], 'with the correct selecting points (1)'); + assertRange(selectingData.range, { + x: [0.002000, 0.0046236], + y: [0.10209191961595454, 24.512223978291406] + }, 'with the correct selecting range'); + expect(selectedCnt).toBe(1, 'with the correct selected count'); + assertEventData(selectedData.points, [{ + curveNumber: 0, + pointNumber: 0, + x: 0.002, + y: 16.25, + id: 'id-0.002', + customdata: 'customdata-16.25' + }, { + curveNumber: 0, + pointNumber: 1, + x: 0.004, + y: 12.5, + id: 'id-0.004', + customdata: 'customdata-12.5' + }], 'with the correct selected points (2)'); + assertRange(selectedData.range, { + x: [0.002000, 0.0046236], + y: [0.10209191961595454, 24.512223978291406] + }, 'with the correct selected range'); + + return doubleClick(250, 200); + }) + .then(deselectPromise) + .then(function() { expect(doubleClickData).toBe(null, 'with the correct deselect data'); - done(); - }); + }) + .catch(fail) + .then(done); }); }); @@ -257,96 +223,72 @@ describe('Test select box and lasso in general:', function() { }); it('should trigger selecting/selected/deselect events', function(done) { - var selectingCnt = 0, - selectingData; - gd.on('plotly_selecting', function(data) { - selectingCnt++; - selectingData = data; - }); - - var selectedCnt = 0, - selectedData; - gd.on('plotly_selected', function(data) { - selectedCnt++; - selectedData = data; - }); - - var doubleClickData; - gd.on('plotly_deselect', function(data) { - doubleClickData = data; - }); + resetEvents(gd); drag(lassoPath); - expect(selectingCnt).toEqual(3, 'with the correct selecting count'); - assertEventData(selectingData.points, [{ - curveNumber: 0, - pointNumber: 10, - x: 0.099, - y: 2.75 - }], 'with the correct selecting points (1)'); - - expect(selectedCnt).toEqual(1, 'with the correct selected count'); - assertEventData(selectedData.points, [{ - curveNumber: 0, - pointNumber: 10, - x: 0.099, - y: 2.75, - }], 'with the correct selected points (2)'); - - expect(selectedData.lassoPoints.x).toBeCloseToArray( - [0.084, 0.087, 0.115, 0.103], 'lasso points x coords'); - expect(selectedData.lassoPoints.y).toBeCloseToArray( - [4.648, 1.342, 1.247, 4.821], 'lasso points y coords'); - - doubleClick(250, 200).then(function() { + selectedPromise.then(function() { + expect(selectingCnt).toBe(3, 'with the correct selecting count'); + assertEventData(selectingData.points, [{ + curveNumber: 0, + pointNumber: 10, + x: 0.099, + y: 2.75 + }], 'with the correct selecting points (1)'); + + expect(selectedCnt).toBe(1, 'with the correct selected count'); + assertEventData(selectedData.points, [{ + curveNumber: 0, + pointNumber: 10, + x: 0.099, + y: 2.75, + }], 'with the correct selected points (2)'); + + expect(selectedData.lassoPoints.x).toBeCloseToArray( + [0.084, 0.087, 0.115, 0.103], 'lasso points x coords'); + expect(selectedData.lassoPoints.y).toBeCloseToArray( + [4.648, 1.342, 1.247, 4.821], 'lasso points y coords'); + + return doubleClick(250, 200); + }) + .then(deselectPromise) + .then(function() { expect(doubleClickData).toBe(null, 'with the correct deselect data'); - done(); - }); + }) + .catch(fail) + .then(done); }); it('should trigger selecting/selected/deselect events for touches', function(done) { - var selectingCnt = 0, - selectingData; - gd.on('plotly_selecting', function(data) { - selectingCnt++; - selectingData = data; - }); - - var selectedCnt = 0, - selectedData; - gd.on('plotly_selected', function(data) { - selectedCnt++; - selectedData = data; - }); - - var doubleClickData; - gd.on('plotly_deselect', function(data) { - doubleClickData = data; - }); + resetEvents(gd); drag(lassoPath, {type: 'touch'}); - expect(selectingCnt).toEqual(3, 'with the correct selecting count'); - assertEventData(selectingData.points, [{ - curveNumber: 0, - pointNumber: 10, - x: 0.099, - y: 2.75 - }], 'with the correct selecting points (1)'); - - expect(selectedCnt).toEqual(1, 'with the correct selected count'); - assertEventData(selectedData.points, [{ - curveNumber: 0, - pointNumber: 10, - x: 0.099, - y: 2.75, - }], 'with the correct selected points (2)'); - - doubleClick(250, 200).then(function() { + selectedPromise.then(function() { + expect(selectingCnt).toBe(3, 'with the correct selecting count'); + assertEventData(selectingData.points, [{ + curveNumber: 0, + pointNumber: 10, + x: 0.099, + y: 2.75 + }], 'with the correct selecting points (1)'); + + expect(selectedCnt).toBe(1, 'with the correct selected count'); + assertEventData(selectedData.points, [{ + curveNumber: 0, + pointNumber: 10, + x: 0.099, + y: 2.75, + }], 'with the correct selected points (2)'); + + return doubleClick(250, 200); + }) + .then(deselectPromise) + .then(function() { expect(doubleClickData).toBe(null, 'with the correct deselect data'); - done(); - }); + }) + .catch(fail) + .then(done); }); }); @@ -355,43 +297,60 @@ describe('Test select box and lasso in general:', function() { mockCopy.layout.dragmode = 'select'; var gd = createGraphDiv(); - var selectedPtLength; - - Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(function() { - gd.on('plotly_selected', function(data) { - selectedPtLength = data.points.length; - }); + function resetAndSelect() { + resetEvents(gd); drag(selectPath); - expect(selectedPtLength).toEqual(2, '(case 0)'); + return selectedPromise; + } + + function resetAndLasso() { + resetEvents(gd); + drag(lassoPath); + return selectedPromise; + } + + function checkPointCount(cnt, msg) { + expect((selectedData.points || []).length).toBe(cnt, msg); + } + + Plotly.plot(gd, mockCopy.data, mockCopy.layout) + .then(resetAndSelect) + .then(function() { + checkPointCount(2, '(case 0)'); return Plotly.restyle(gd, 'visible', 'legendonly'); - }).then(function() { - drag(selectPath); - expect(selectedPtLength).toEqual(0, '(legendonly case)'); + }) + .then(resetAndSelect) + .then(function() { + checkPointCount(0, '(legendonly case)'); return Plotly.restyle(gd, 'visible', true); - }).then(function() { - drag(selectPath); - expect(selectedPtLength).toEqual(2, '(back to case 0)'); + }) + .then(resetAndSelect) + .then(function() { + checkPointCount(2, '(back to case 0)'); return Plotly.relayout(gd, 'dragmode', 'lasso'); - }).then(function() { - drag(lassoPath); - expect(selectedPtLength).toEqual(1, '(case 0 lasso)'); + }) + .then(resetAndLasso) + .then(function() { + checkPointCount(1, '(case 0 lasso)'); return Plotly.restyle(gd, 'visible', 'legendonly'); - }).then(function() { - drag(lassoPath); - expect(selectedPtLength).toEqual(0, '(lasso legendonly case)'); + }) + .then(resetAndSelect) + .then(function() { + checkPointCount(0, '(lasso legendonly case)'); return Plotly.restyle(gd, 'visible', true); - }).then(function() { - drag(lassoPath); - expect(selectedPtLength).toEqual(1, '(back to lasso case 0)'); - - done(); - }); + }) + .then(resetAndLasso) + .then(function() { + checkPointCount(1, '(back to lasso case 0)'); + }) + .catch(fail) + .then(done); }); it('should skip over BADNUM items', function(done) { @@ -406,33 +365,36 @@ describe('Test select box and lasso in general:', function() { heigth: 400, }; var gd = createGraphDiv(); - var pts; Plotly.plot(gd, data, layout).then(function() { - gd.on('plotly_selected', function(data) { - pts = data.points; - }); - + resetEvents(gd); drag([[100, 100], [300, 300]]); - expect(pts.length).toEqual(1); - expect(pts[0].x).toEqual(0); - expect(pts[0].y).toEqual(0); + return selectedPromise; + }) + .then(function() { + expect(selectedData.points.length).toBe(1); + expect(selectedData.points[0].x).toBe(0); + expect(selectedData.points[0].y).toBe(0); return Plotly.relayout(gd, 'dragmode', 'lasso'); }) .then(function() { + resetEvents(gd); drag([[100, 100], [100, 300], [300, 300], [300, 100], [100, 100]]); - expect(pts.length).toEqual(1); - expect(pts[0].x).toEqual(0); - expect(pts[0].y).toEqual(0); + return selectedPromise; + }) + .then(function() { + expect(selectedData.points.length).toBe(1); + expect(selectedData.points[0].x).toBe(0); + expect(selectedData.points[0].y).toBe(0); }) + .catch(fail) .then(done); }); }); describe('Test select box and lasso per trace:', function() { var gd; - var eventData; beforeEach(function() { gd = createGraphDiv(); @@ -445,7 +407,7 @@ describe('Test select box and lasso per trace:', function() { return function(expected) { var msg = '(call #' + callNumber + ') '; - var pts = (eventData || {}).points || []; + var pts = (selectedData || {}).points || []; expect(pts.length).toBe(expected.length, msg + 'selected points length'); @@ -467,7 +429,7 @@ describe('Test select box and lasso per trace:', function() { return function(expected) { var msg = '(call #' + callNumber + ') '; - var ranges = (eventData.range || {})[subplot] || []; + var ranges = (selectedData.range || {})[subplot] || []; expect(ranges) .toBeCloseTo2DArray(expected, tol, msg + 'select box range for ' + subplot); @@ -482,7 +444,7 @@ describe('Test select box and lasso per trace:', function() { return function(expected) { var msg = '(call #' + callNumber + ') '; - var lassoPoints = (eventData.lassoPoints || {})[subplot] || []; + var lassoPoints = (selectedData.lassoPoints || {})[subplot] || []; expect(lassoPoints) .toBeCloseTo2DArray(expected, tol, msg + 'lasso points for ' + subplot); @@ -491,70 +453,28 @@ describe('Test select box and lasso per trace:', function() { }; } - function _run(dragPath, afterDragFn, dblClickPos) { + function _run(dragPath, afterDragFn, dblClickPos, eventCounts, msg) { afterDragFn = afterDragFn || function() {}; dblClickPos = dblClickPos || [250, 200]; - var selectingCnt = 0; - var selectedCnt = 0; - var deselectCnt = 0; - - gd.once('plotly_selecting', function() { - assertSelectionNodes(1, 2); - selectingCnt++; - }); - - gd.once('plotly_selected', function(d) { - assertSelectionNodes(0, 2); - selectedCnt++; - eventData = d; - }); - - gd.once('plotly_deselect', function() { - deselectCnt++; - assertSelectionNodes(0, 0); - }); - - assertSelectionNodes(0, 0); - drag(dragPath); - afterDragFn(); - - return doubleClick(dblClickPos[0], dblClickPos[1]).then(function() { - expect(selectingCnt).toBe(1, 'plotly_selecting call count'); - expect(selectedCnt).toBe(1, 'plotly_selected call count'); - expect(deselectCnt).toBe(1, 'plotly_deselect call count'); - }); - } - - function _runNoSelect(dragPath, afterDragFn, dblClickPos) { - afterDragFn = afterDragFn || function() {}; - dblClickPos = dblClickPos || [250, 200]; - - var selectingCnt = 0; - var selectedCnt = 0; - var deselectCnt = 0; - - gd.once('plotly_selecting', function() { - selectingCnt++; - }); - - gd.once('plotly_selected', function() { - selectedCnt++; - }); - - gd.once('plotly_deselect', function() { - deselectCnt++; - }); + resetEvents(gd); assertSelectionNodes(0, 0); drag(dragPath); - afterDragFn(); - return doubleClick(dblClickPos[0], dblClickPos[1]).then(function() { - expect(selectingCnt).toBe(0, 'plotly_selecting call count'); - expect(selectedCnt).toBe(0, 'plotly_selected call count'); - expect(deselectCnt).toBe(0, 'plotly_deselect call count'); - }); + return (eventCounts[0] ? selectedPromise : Promise.resolve()) + .then(afterDragFn) + .then(function() { + // before dblclick we also have one less plotly_selected event + // because the first click of the dblclick emits plotly_selected + // even though it does so with undefined event data. + assertEventCounts(eventCounts[0], Math.max(0, eventCounts[1] - 1), 0, msg); + return doubleClick(dblClickPos[0], dblClickPos[1]); + }) + .then(eventCounts[2] ? deselectPromise : Promise.resolve()) + .then(function() { + assertEventCounts(eventCounts[0], eventCounts[1], eventCounts[2], msg); + }); } it('should work on scatterternary traces', function(done) { @@ -565,26 +485,37 @@ describe('Test select box and lasso per trace:', function() { fig.layout.dragmode = 'select'; Plotly.plot(gd, fig).then(function() { - return _run([[400, 200], [445, 235]], function() { - assertPoints([[0.5, 0.25, 0.25]]); - }, [380, 180]); + return _run( + [[400, 200], [445, 235]], + function() { + assertPoints([[0.5, 0.25, 0.25]]); + }, + [380, 180], + BOXEVENTS, 'scatterternary select' + ); }) .then(function() { return Plotly.relayout(gd, 'dragmode', 'lasso'); }) .then(function() { - return _run([[400, 200], [445, 200], [445, 235], [400, 235], [400, 200]], function() { - assertPoints([[0.5, 0.25, 0.25]]); - }, [380, 180]); + return _run( + [[400, 200], [445, 200], [445, 235], [400, 235], [400, 200]], + function() { assertPoints([[0.5, 0.25, 0.25]]); }, + [380, 180], + LASSOEVENTS, 'scatterternary lasso' + ); }) .then(function() { // should work after a relayout too return Plotly.relayout(gd, 'width', 400); }) .then(function() { - return _run([[200, 200], [230, 200], [230, 230], [200, 230], [200, 200]], function() { - assertPoints([[0.5, 0.25, 0.25]]); - }, [180, 180]); + return _run( + [[200, 200], [230, 200], [230, 230], [200, 230], [200, 200]], + function() { assertPoints([[0.5, 0.25, 0.25]]); }, + [180, 180], + LASSOEVENTS, 'scatterternary lasso after relayout' + ); }) .catch(fail) .then(done); @@ -597,17 +528,21 @@ describe('Test select box and lasso per trace:', function() { fig.layout.dragmode = 'select'; Plotly.plot(gd, fig).then(function() { - return _run([[300, 200], [400, 250]], function() { - assertPoints([[0.2, 1.5]]); - }); + return _run( + [[300, 200], [400, 250]], + function() { assertPoints([[0.2, 1.5]]); }, + null, BOXEVENTS, 'scattercarpet select' + ); }) .then(function() { return Plotly.relayout(gd, 'dragmode', 'lasso'); }) .then(function() { - return _run([[300, 200], [400, 200], [400, 250], [300, 250], [300, 200]], function() { - assertPoints([[0.2, 1.5]]); - }); + return _run( + [[300, 200], [400, 200], [400, 250], [300, 250], [300, 200]], + function() { assertPoints([[0.2, 1.5]]); }, + null, LASSOEVENTS, 'scattercarpet lasso' + ); }) .catch(fail) .then(done); @@ -625,28 +560,38 @@ describe('Test select box and lasso per trace:', function() { }; Plotly.plot(gd, fig).then(function() { - return _run([[370, 120], [500, 200]], function() { - assertPoints([[30, 30]]); - assertRanges([[21.99, 34.55], [38.14, 25.98]]); - }); + return _run( + [[370, 120], [500, 200]], + function() { + assertPoints([[30, 30]]); + assertRanges([[21.99, 34.55], [38.14, 25.98]]); + }, + null, BOXEVENTS, 'scattermapbox select' + ); }) .then(function() { return Plotly.relayout(gd, 'dragmode', 'lasso'); }) .then(function() { - return _run([[300, 200], [300, 300], [400, 300], [400, 200], [300, 200]], function() { - assertPoints([[20, 20]]); - assertLassoPoints([ - [13.28, 25.97], [13.28, 14.33], [25.71, 14.33], [25.71, 25.97], [13.28, 25.97] - ]); - }); + return _run( + [[300, 200], [300, 300], [400, 300], [400, 200], [300, 200]], + function() { + assertPoints([[20, 20]]); + assertLassoPoints([ + [13.28, 25.97], [13.28, 14.33], [25.71, 14.33], [25.71, 25.97], [13.28, 25.97] + ]); + }, + null, LASSOEVENTS, 'scattermapbox lasso' + ); }) .then(function() { // make selection handlers don't get called in 'pan' dragmode return Plotly.relayout(gd, 'dragmode', 'pan'); }) .then(function() { - return _runNoSelect([[370, 120], [500, 200]]); + return _run( + [[370, 120], [500, 200]], null, null, NOEVENTS, 'scattermapbox pan' + ); }) .catch(fail) .then(done); @@ -672,28 +617,41 @@ describe('Test select box and lasso per trace:', function() { height: 600 }) .then(function() { - return _run([[350, 200], [450, 400]], function() { - assertPoints([[10, 10], [20, 20], [-10, 10], [-20, 20]]); - assertRanges([[-28.13, 61.88], [28.13, -50.64]]); - }); + return _run( + [[350, 200], [450, 400]], + function() { + assertPoints([[10, 10], [20, 20], [-10, 10], [-20, 20]]); + assertRanges([[-28.13, 61.88], [28.13, -50.64]]); + }, + null, BOXEVENTS, 'scattergeo select' + ); }) .then(function() { return Plotly.relayout(gd, 'dragmode', 'lasso'); }) .then(function() { - return _run([[300, 200], [300, 300], [400, 300], [400, 200], [300, 200]], function() { - assertPoints([[-10, 10], [-20, 20], [-30, 30]]); - assertLassoPoints([ - [-56.25, 61.88], [-56.24, 5.63], [0, 5.63], [0, 61.88], [-56.25, 61.88] - ]); - }); + return _run( + [[300, 200], [300, 300], [400, 300], [400, 200], [300, 200]], + function() { + assertPoints([[-10, 10], [-20, 20], [-30, 30]]); + assertLassoPoints([ + [-56.25, 61.88], [-56.24, 5.63], [0, 5.63], [0, 61.88], [-56.25, 61.88] + ]); + }, + null, LASSOEVENTS, 'scattergeo lasso' + ); }) + // .then(deselectPromise) .then(function() { - // make selection handlers don't get called in 'pan' dragmode + // assertEventCounts(4, 2, 1, 'de-lasso'); + + // make sure selection handlers don't get called in 'pan' dragmode return Plotly.relayout(gd, 'dragmode', 'pan'); }) .then(function() { - return _runNoSelect([[370, 120], [500, 200]]); + return _run( + [[370, 120], [500, 200]], null, null, NOEVENTS, 'scattergeo pan' + ); }) .catch(fail) .then(done); @@ -712,28 +670,40 @@ describe('Test select box and lasso per trace:', function() { Plotly.plot(gd, fig) .then(function() { - return _run([[350, 200], [400, 250]], function() { - assertPoints([['GBR', 26.507354205352502], ['IRL', 86.4125147625692]]); - assertRanges([[-19.11, 63.06], [7.31, 53.72]]); - }, [280, 190]); + return _run( + [[350, 200], [400, 250]], + function() { + assertPoints([['GBR', 26.507354205352502], ['IRL', 86.4125147625692]]); + assertRanges([[-19.11, 63.06], [7.31, 53.72]]); + }, + [280, 190], + BOXEVENTS, 'choropleth select' + ); }) .then(function() { return Plotly.relayout(gd, 'dragmode', 'lasso'); }) .then(function() { - return _run([[350, 200], [400, 200], [400, 250], [350, 250], [350, 200]], function() { - assertPoints([['GBR', 26.507354205352502], ['IRL', 86.4125147625692]]); - assertLassoPoints([ - [-19.11, 63.06], [5.50, 65.25], [7.31, 53.72], [-12.90, 51.70], [-19.11, 63.06] - ]); - }, [280, 190]); + return _run( + [[350, 200], [400, 200], [400, 250], [350, 250], [350, 200]], + function() { + assertPoints([['GBR', 26.507354205352502], ['IRL', 86.4125147625692]]); + assertLassoPoints([ + [-19.11, 63.06], [5.50, 65.25], [7.31, 53.72], [-12.90, 51.70], [-19.11, 63.06] + ]); + }, + [280, 190], + LASSOEVENTS, 'choropleth lasso' + ); }) .then(function() { // make selection handlers don't get called in 'pan' dragmode return Plotly.relayout(gd, 'dragmode', 'pan'); }) .then(function() { - return _runNoSelect([[370, 120], [500, 200]]); + return _run( + [[370, 120], [500, 200]], null, [280, 190], NOEVENTS, 'choropleth pan' + ); }) .catch(fail) .then(done); diff --git a/test/jasmine/tests/ternary_test.js b/test/jasmine/tests/ternary_test.js index 5ba69ec71d1..325a31aeff9 100644 --- a/test/jasmine/tests/ternary_test.js +++ b/test/jasmine/tests/ternary_test.js @@ -122,7 +122,7 @@ describe('ternary plots', function() { 'hoverlabel.font.family': [['Gravitas', 'Arial', 'Roboto']] }) .then(function() { - delete gd._lastHoverTime; + Lib.clearThrottle(); mouseEvent('mousemove', pointPos[0], pointPos[1]); var path = d3.select('g.hovertext').select('path'); diff --git a/test/jasmine/tests/transform_sort_test.js b/test/jasmine/tests/transform_sort_test.js index c3d4e5774d0..108e04f7d56 100644 --- a/test/jasmine/tests/transform_sort_test.js +++ b/test/jasmine/tests/transform_sort_test.js @@ -275,7 +275,7 @@ describe('Test sort transform interactions:', function() { function hover(gd, id) { return new Promise(function(resolve, reject) { gd.once('plotly_hover', function(eventData) { - delete gd._lastHoverTime; + Lib.clearThrottle(); resolve(eventData); });