diff --git a/src/components/dragelement/index.js b/src/components/dragelement/index.js index 477edbb99de..a64e1dbf69c 100644 --- a/src/components/dragelement/index.js +++ b/src/components/dragelement/index.js @@ -14,7 +14,6 @@ var supportsPassive = require('has-passive-events'); var removeElement = require('../../lib').removeElement; var constants = require('../../plots/cartesian/constants'); -var interactConstants = require('../../constants/interactions'); var dragElement = module.exports = {}; @@ -82,7 +81,7 @@ dragElement.unhoverRaw = unhover.raw; dragElement.init = function init(options) { var gd = options.gd; var numClicks = 1; - var DBLCLICKDELAY = interactConstants.DBLCLICKDELAY; + var doubleClickDelay = gd._context.doubleClickDelay; var element = options.element; var startX, @@ -137,7 +136,7 @@ dragElement.init = function init(options) { } newMouseDownTime = (new Date()).getTime(); - if(newMouseDownTime - gd._mouseDownTime < DBLCLICKDELAY) { + if(newMouseDownTime - gd._mouseDownTime < doubleClickDelay) { // in a click train numClicks += 1; } else { @@ -223,7 +222,7 @@ dragElement.init = function init(options) { // don't count as a dblClick unless the mouseUp is also within // the dblclick delay - if((new Date()).getTime() - gd._mouseDownTime > DBLCLICKDELAY) { + if((new Date()).getTime() - gd._mouseDownTime > doubleClickDelay) { numClicks = Math.max(numClicks - 1, 1); } diff --git a/src/components/legend/draw.js b/src/components/legend/draw.js index eb97fee7a87..159536a7230 100644 --- a/src/components/legend/draw.js +++ b/src/components/legend/draw.js @@ -21,7 +21,6 @@ var svgTextUtils = require('../../lib/svg_text_utils'); var handleClick = require('./handle_click'); var constants = require('./constants'); -var interactConstants = require('../../constants/interactions'); var alignmentConstants = require('../../constants/alignment'); var LINE_SPACING = alignmentConstants.LINE_SPACING; var FROM_TL = alignmentConstants.FROM_TL; @@ -31,8 +30,6 @@ var getLegendData = require('./get_legend_data'); var style = require('./style'); var helpers = require('./helpers'); -var DBLCLICKDELAY = interactConstants.DBLCLICKDELAY; - module.exports = function draw(gd) { var fullLayout = gd._fullLayout; var clipId = 'legend' + fullLayout._uid; @@ -358,7 +355,6 @@ module.exports = function draw(gd) { function clickOrDoubleClick(gd, legend, legendItem, numClicks, evt) { var trace = legendItem.data()[0][0].trace; - var evtData = { event: evt, node: legendItem.node(), @@ -385,7 +381,7 @@ function clickOrDoubleClick(gd, legend, legendItem, numClicks, evt) { if(numClicks === 1) { legend._clickTimeout = setTimeout(function() { handleClick(legendItem, gd, numClicks); - }, DBLCLICKDELAY); + }, gd._context.doubleClickDelay); } else if(numClicks === 2) { if(legend._clickTimeout) clearTimeout(legend._clickTimeout); gd._legendMouseDownTime = 0; @@ -469,6 +465,7 @@ function ensureLength(str, maxLength) { } function setupTraceToggle(g, gd) { + var doubleClickDelay = gd._context.doubleClickDelay; var newMouseDownTime; var numClicks = 1; @@ -480,7 +477,7 @@ function setupTraceToggle(g, gd) { traceToggle.on('mousedown', function() { newMouseDownTime = (new Date()).getTime(); - if(newMouseDownTime - gd._legendMouseDownTime < DBLCLICKDELAY) { + if(newMouseDownTime - gd._legendMouseDownTime < doubleClickDelay) { // in a click train numClicks += 1; } else { @@ -493,7 +490,7 @@ function setupTraceToggle(g, gd) { if(gd._dragged || gd._editing) return; var legend = gd._fullLayout.legend; - if((new Date()).getTime() - gd._legendMouseDownTime > DBLCLICKDELAY) { + if((new Date()).getTime() - gd._legendMouseDownTime > doubleClickDelay) { numClicks = Math.max(numClicks - 1, 1); } diff --git a/src/constants/interactions.js b/src/constants/interactions.js index 17745ee4160..624fb5a4f24 100644 --- a/src/constants/interactions.js +++ b/src/constants/interactions.js @@ -16,10 +16,6 @@ module.exports = { SHOW_PLACEHOLDER: 100, HIDE_PLACEHOLDER: 1000, - // ms between first mousedown and 2nd mouseup to constitute dblclick... - // we don't seem to have access to the system setting - DBLCLICKDELAY: 300, - // opacity dimming fraction for points that are not in selection DESELECTDIM: 0.2 }; diff --git a/src/plot_api/plot_config.js b/src/plot_api/plot_config.js index c78a955280f..26b82e590a7 100644 --- a/src/plot_api/plot_config.js +++ b/src/plot_api/plot_config.js @@ -172,6 +172,18 @@ var configAttributes = { 'to their autorange values.' ].join(' ') }, + doubleClickDelay: { + valType: 'number', + dflt: 300, + min: 0, + description: [ + 'Sets the delay for registering a double-click in ms.', + 'This is the time interval (in ms) between first mousedown and', + '2nd mouseup to constitute a double-click.', + 'This setting propagates to all on-subplot double clicks', + '(except for geo and mapbox) and on-legend double clicks.' + ].join(' ') + }, showAxisDragHandles: { valType: 'boolean', diff --git a/src/plots/cartesian/dragbox.js b/src/plots/cartesian/dragbox.js index 31c795b011d..d362eefb1f5 100644 --- a/src/plots/cartesian/dragbox.js +++ b/src/plots/cartesian/dragbox.js @@ -51,9 +51,9 @@ var SHOWZOOMOUTTIP = true; // ew - same for horizontal axis function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) { // mouseDown stores ms of first mousedown event in the last - // DBLCLICKDELAY ms on the drag bars + // `gd._context.doubleClickDelay` ms on the drag bars // numClicks stores how many mousedowns have been seen - // within DBLCLICKDELAY so we can check for click or doubleclick events + // within `gd._context.doubleClickDelay` so we can check for click or doubleclick events // dragged stores whether a drag has occurred, so we don't have to // redraw unnecessarily, ie if no move bigger than MINDRAG or MINZOOM px var zoomlayer = gd._fullLayout._zoomlayer; diff --git a/src/plots/layout_attributes.js b/src/plots/layout_attributes.js index cbcd32af91d..e88c9df39df 100644 --- a/src/plots/layout_attributes.js +++ b/src/plots/layout_attributes.js @@ -432,7 +432,6 @@ module.exports = { ].join(' '), editType: 'none' }), - _deprecated: { title: { valType: 'string', diff --git a/test/jasmine/assets/double_click.js b/test/jasmine/assets/double_click.js index 222ecd7e5a4..5227726e684 100644 --- a/test/jasmine/assets/double_click.js +++ b/test/jasmine/assets/double_click.js @@ -1,6 +1,6 @@ var click = require('./click'); var getNodeCoords = require('./get_node_coords'); -var DBLCLICKDELAY = require('../../../src/constants/interactions').DBLCLICKDELAY; +var DBLCLICKDELAY = require('@src/plot_api/plot_config').dfltConfig.doubleClickDelay; /* * Double click on a point. diff --git a/test/jasmine/tests/annotations_test.js b/test/jasmine/tests/annotations_test.js index f0fd23cc143..a7221120915 100644 --- a/test/jasmine/tests/annotations_test.js +++ b/test/jasmine/tests/annotations_test.js @@ -7,7 +7,7 @@ var Lib = require('@src/lib'); var Loggers = require('@src/lib/loggers'); var Axes = require('@src/plots/cartesian/axes'); var HOVERMINTIME = require('@src/components/fx').constants.HOVERMINTIME; -var DBLCLICKDELAY = require('@src/constants/interactions').DBLCLICKDELAY; +var DBLCLICKDELAY = require('@src/plot_api/plot_config').dfltConfig.doubleClickDelay; var d3 = require('d3'); var createGraphDiv = require('../assets/create_graph_div'); diff --git a/test/jasmine/tests/bar_test.js b/test/jasmine/tests/bar_test.js index 9a108e5644a..12afaa1494d 100644 --- a/test/jasmine/tests/bar_test.js +++ b/test/jasmine/tests/bar_test.js @@ -8,7 +8,7 @@ var Drawing = require('@src/components/drawing'); var Axes = require('@src/plots/cartesian/axes'); var click = require('../assets/click'); -var DBLCLICKDELAY = require('../../../src/constants/interactions').DBLCLICKDELAY; +var DBLCLICKDELAY = require('@src/plot_api/plot_config').dfltConfig.doubleClickDelay; var createGraphDiv = require('../assets/create_graph_div'); var destroyGraphDiv = require('../assets/destroy_graph_div'); var failTest = require('../assets/fail_test'); diff --git a/test/jasmine/tests/click_test.js b/test/jasmine/tests/click_test.js index d5f0024b5b1..6c87ab00055 100644 --- a/test/jasmine/tests/click_test.js +++ b/test/jasmine/tests/click_test.js @@ -1,7 +1,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 DBLCLICKDELAY = require('@src/plot_api/plot_config').dfltConfig.doubleClickDelay; var d3 = require('d3'); var createGraphDiv = require('../assets/create_graph_div'); diff --git a/test/jasmine/tests/dragelement_test.js b/test/jasmine/tests/dragelement_test.js index 4e4811e8b9e..058dad10a0e 100644 --- a/test/jasmine/tests/dragelement_test.js +++ b/test/jasmine/tests/dragelement_test.js @@ -6,7 +6,6 @@ var destroyGraphDiv = require('../assets/destroy_graph_div'); var mouseEvent = require('../assets/mouse_event'); var touchEvent = require('../assets/touch_event'); - describe('dragElement', function() { 'use strict'; @@ -19,6 +18,9 @@ describe('dragElement', function() { this.gd._fullLayout = { _hoverlayer: d3.select(this.hoverlayer) }; + this.gd._context = { + doubleClickDelay: 300 + }; this.element.innerHTML = 'drag element'; this.gd.appendChild(this.element); @@ -138,6 +140,49 @@ describe('dragElement', function() { expect(args[1].type).toBe('mousedown'); }); + it('should pass numClicks and event to clickFn on mouseup after no/small mousemove w/ custom doubleClickDelay', function(done) { + var args = []; + + this.gd._context.doubleClickDelay = 1000; + + var options = { + element: this.element, + gd: this.gd, + clickFn: function() { + args = arguments; + }, + moveFn: function() { + expect('should not call moveFn').toBe(true); + }, + doneFn: function() { + expect('should not call doneFn').toBe(true); + } + }; + dragElement.init(options); + + mouseEvent('mousedown', this.x, this.y); + mouseEvent('mouseup', this.x, this.y); + + expect(args.length).toBe(2); + expect(args[0]).toEqual(1); + // click gets the mousedown event, as that's guaranteed to have + // the correct target + expect(args[1].type).toBe('mousedown'); + + var _this = this; + setTimeout(function() { + mouseEvent('mousedown', _this.x, _this.y); + mouseEvent('mousemove', _this.x + 3, _this.y + 3); + mouseEvent('mouseup', _this.x, _this.y); + + expect(args.length).toBe(2); + expect(args[0]).toEqual(2); + expect(args[1].type).toBe('mousedown'); + + done(); + }, 500); + }); + it('should add a cover slip div to the DOM', function() { var options = { element: this.element, gd: this.gd }; dragElement.init(options); diff --git a/test/jasmine/tests/geo_test.js b/test/jasmine/tests/geo_test.js index 50bb3d76cf2..897a84981fe 100644 --- a/test/jasmine/tests/geo_test.js +++ b/test/jasmine/tests/geo_test.js @@ -17,7 +17,7 @@ var mouseEvent = require('../assets/mouse_event'); var click = require('../assets/click'); var drag = require('../assets/drag'); -var DBLCLICKDELAY = require('@src/constants/interactions').DBLCLICKDELAY; +var DBLCLICKDELAY = require('@src/plot_api/plot_config').dfltConfig.doubleClickDelay; var HOVERMINTIME = require('@src/components/fx').constants.HOVERMINTIME; // use local topojson files diff --git a/test/jasmine/tests/legend_scroll_test.js b/test/jasmine/tests/legend_scroll_test.js index 9eb9530a25f..bf3dc324713 100644 --- a/test/jasmine/tests/legend_scroll_test.js +++ b/test/jasmine/tests/legend_scroll_test.js @@ -1,7 +1,7 @@ var Plotly = require('@lib/index'); var Lib = require('@src/lib'); var constants = require('@src/components/legend/constants'); -var DBLCLICKDELAY = require('@src/constants/interactions').DBLCLICKDELAY; +var DBLCLICKDELAY = require('@src/plot_api/plot_config').dfltConfig.doubleClickDelay; var d3 = require('d3'); var createGraph = require('../assets/create_graph_div'); diff --git a/test/jasmine/tests/legend_test.js b/test/jasmine/tests/legend_test.js index ffad2c86cfa..8e833191150 100644 --- a/test/jasmine/tests/legend_test.js +++ b/test/jasmine/tests/legend_test.js @@ -1,7 +1,7 @@ var Plotly = require('@lib/index'); var Plots = require('@src/plots/plots'); var Lib = require('@src/lib'); -var DBLCLICKDELAY = require('@src/constants/interactions').DBLCLICKDELAY; +var DBLCLICKDELAY = require('@src/plot_api/plot_config').dfltConfig.doubleClickDelay; var Legend = require('@src/components/legend'); var getLegendData = require('@src/components/legend/get_legend_data'); @@ -1759,3 +1759,71 @@ describe('legend DOM', function() { .then(done); }); }); + +describe('legend with custom doubleClickDelay', function() { + var gd; + + beforeEach(function() { + gd = createGraphDiv(); + }); + + afterEach(destroyGraphDiv); + + function click(index) { + return function() { + var item = d3.selectAll('rect.legendtoggle')[0][index]; + item.dispatchEvent(new MouseEvent('mousedown')); + item.dispatchEvent(new MouseEvent('mouseup')); + }; + } + + it('should differentiate clicks and double-clicks according *doubleClickDelay* config', function(done) { + var tLong = 1.5 * DBLCLICKDELAY; + var tShort = 0.75 * DBLCLICKDELAY; + + var clickCnt = 0; + var dblClickCnt = 0; + + function _assert(msg, _clickCnt, _dblClickCnt) { + return function() { + expect(clickCnt).toBe(_clickCnt, msg + '| clickCnt'); + expect(dblClickCnt).toBe(_dblClickCnt, msg + '| dblClickCnt'); + clickCnt = 0; + dblClickCnt = 0; + }; + } + + Plotly.plot(gd, [ + {y: [1, 2, 1]}, + {y: [2, 1, 2]} + ], {}, { + doubleClickDelay: tLong + }) + .then(function() { + gd.on('plotly_legendclick', function() { clickCnt++; }); + gd.on('plotly_legenddoubleclick', function() { dblClickCnt++; }); + }) + .then(click(0)).then(delay(tLong / 2)) + .then(_assert('[long] after click + (t/2) delay', 1, 0)) + .then(delay(tLong + 10)) + .then(click(0)).then(delay(DBLCLICKDELAY + 1)).then(click(0)) + .then(_assert('[long] after click + (DBLCLICKDELAY+1) delay + click', 2, 1)) + .then(delay(tLong + 10)) + .then(click(0)).then(delay(1.1 * tLong)).then(click(0)) + .then(_assert('[long] after click + (1.1*t) delay + click', 2, 0)) + .then(delay(tLong + 10)) + .then(function() { + return Plotly.plot(gd, [], {}, {doubleClickDelay: tShort}); + }) + .then(click(0)).then(delay(tShort / 2)) + .then(_assert('[short] after click + (t/2) delay', 1, 0)) + .then(delay(tShort + 10)) + .then(click(0)).then(delay(DBLCLICKDELAY + 1)).then(click(0)) + .then(_assert('[short] after click + (DBLCLICKDELAY+1) delay + click', 2, 0)) + .then(delay(tShort + 10)) + .then(click(0)).then(delay(1.1 * tShort)).then(click(0)) + .then(_assert('[short] after click + (1.1*t) delay + click', 2, 0)) + .catch(failTest) + .then(done); + }, 3 * jasmine.DEFAULT_TIMEOUT_INTERVAL); +}); diff --git a/test/jasmine/tests/select_test.js b/test/jasmine/tests/select_test.js index 3437485e91a..34eb1d035fb 100644 --- a/test/jasmine/tests/select_test.js +++ b/test/jasmine/tests/select_test.js @@ -4,7 +4,7 @@ var Plotly = require('@lib/index'); var Lib = require('@src/lib'); var click = require('../assets/click'); var doubleClick = require('../assets/double_click'); -var DBLCLICKDELAY = require('../../../src/constants/interactions').DBLCLICKDELAY; +var DBLCLICKDELAY = require('@src/plot_api/plot_config').dfltConfig.doubleClickDelay; var createGraphDiv = require('../assets/create_graph_div'); var destroyGraphDiv = require('../assets/destroy_graph_div');