diff --git a/src/lib/prepare_regl.js b/src/lib/prepare_regl.js index 3c6ca297083..228d134208a 100644 --- a/src/lib/prepare_regl.js +++ b/src/lib/prepare_regl.js @@ -23,8 +23,12 @@ var createRegl = require('regl'); * @param {array} extensions : list of extension to pass to createRegl */ module.exports = function prepareRegl(gd, extensions) { - gd._fullLayout._glcanvas.each(function(d) { + var fullLayout = gd._fullLayout; + + fullLayout._glcanvas.each(function(d) { if(d.regl) return; + // only parcoords needs pick layer + if(d.pick && !fullLayout._has('parcoords')) return; d.regl = createRegl({ canvas: this, diff --git a/src/plots/cartesian/dragbox.js b/src/plots/cartesian/dragbox.js index ebd542fdbdb..8ee86a6da09 100644 --- a/src/plots/cartesian/dragbox.js +++ b/src/plots/cartesian/dragbox.js @@ -144,40 +144,44 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) { var dragOptions = { element: dragger, gd: gd, - plotinfo: plotinfo, - prepFn: function(e, startX, startY) { - var dragModeNow = gd._fullLayout.dragmode; - - recomputeAxisLists(); - - if(!allFixedRanges) { - if(isMainDrag) { - // main dragger handles all drag modes, and changes - // to pan (or to zoom if it already is pan) on shift - if(e.shiftKey) { - if(dragModeNow === 'pan') dragModeNow = 'zoom'; - else if(!isSelectOrLasso(dragModeNow)) dragModeNow = 'pan'; - } - else if(e.ctrlKey) { - dragModeNow = 'pan'; - } + plotinfo: plotinfo + }; + + dragOptions.prepFn = function(e, startX, startY) { + var dragModeNow = gd._fullLayout.dragmode; + + recomputeAxisLists(); + + if(!allFixedRanges) { + if(isMainDrag) { + // main dragger handles all drag modes, and changes + // to pan (or to zoom if it already is pan) on shift + if(e.shiftKey) { + if(dragModeNow === 'pan') dragModeNow = 'zoom'; + else if(!isSelectOrLasso(dragModeNow)) dragModeNow = 'pan'; + } + else if(e.ctrlKey) { + dragModeNow = 'pan'; } - // all other draggers just pan - else dragModeNow = 'pan'; } + // all other draggers just pan + else dragModeNow = 'pan'; + } - if(dragModeNow === 'lasso') dragOptions.minDrag = 1; - else dragOptions.minDrag = undefined; + if(dragModeNow === 'lasso') dragOptions.minDrag = 1; + else dragOptions.minDrag = undefined; - if(isSelectOrLasso(dragModeNow)) { - dragOptions.xaxes = xaxes; - dragOptions.yaxes = yaxes; - prepSelect(e, startX, startY, dragOptions, dragModeNow); - } - else if(allFixedRanges) { + if(isSelectOrLasso(dragModeNow)) { + dragOptions.xaxes = xaxes; + dragOptions.yaxes = yaxes; + // this attaches moveFn, clickFn, doneFn on dragOptions + prepSelect(e, startX, startY, dragOptions, dragModeNow); + } else { + dragOptions.clickFn = clickFn; + + if(allFixedRanges) { clearSelect(zoomlayer); - } - else if(dragModeNow === 'zoom') { + } else if(dragModeNow === 'zoom') { dragOptions.moveFn = zoomMove; dragOptions.doneFn = zoomDone; @@ -187,58 +191,58 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) { dragOptions.minDrag = 1; zoomPrep(e, startX, startY); - } - else if(dragModeNow === 'pan') { + } else if(dragModeNow === 'pan') { dragOptions.moveFn = plotDrag; dragOptions.doneFn = dragTail; clearSelect(zoomlayer); } - }, - clickFn: function(numClicks, evt) { - removeZoombox(gd); + } + }; - if(numClicks === 2 && !singleEnd) doubleClick(); + function clickFn(numClicks, evt) { + removeZoombox(gd); - if(isMainDrag) { - Fx.click(gd, evt, plotinfo.id); + if(numClicks === 2 && !singleEnd) doubleClick(); + + if(isMainDrag) { + Fx.click(gd, evt, plotinfo.id); + } + else if(numClicks === 1 && singleEnd) { + var ax = ns ? ya0 : xa0, + end = (ns === 's' || ew === 'w') ? 0 : 1, + attrStr = ax._name + '.range[' + end + ']', + initialText = getEndText(ax, end), + hAlign = 'left', + vAlign = 'middle'; + + if(ax.fixedrange) return; + + if(ns) { + vAlign = (ns === 'n') ? 'top' : 'bottom'; + if(ax.side === 'right') hAlign = 'right'; } - else if(numClicks === 1 && singleEnd) { - var ax = ns ? ya0 : xa0, - end = (ns === 's' || ew === 'w') ? 0 : 1, - attrStr = ax._name + '.range[' + end + ']', - initialText = getEndText(ax, end), - hAlign = 'left', - vAlign = 'middle'; - - if(ax.fixedrange) return; - - if(ns) { - vAlign = (ns === 'n') ? 'top' : 'bottom'; - if(ax.side === 'right') hAlign = 'right'; - } - else if(ew === 'e') hAlign = 'right'; - - if(gd._context.showAxisRangeEntryBoxes) { - d3.select(dragger) - .call(svgTextUtils.makeEditable, { - gd: gd, - immediate: true, - background: gd._fullLayout.paper_bgcolor, - text: String(initialText), - fill: ax.tickfont ? ax.tickfont.color : '#444', - horizontalAlign: hAlign, - verticalAlign: vAlign - }) - .on('edit', function(text) { - var v = ax.d2r(text); - if(v !== undefined) { - Registry.call('relayout', gd, attrStr, v); - } - }); - } + else if(ew === 'e') hAlign = 'right'; + + if(gd._context.showAxisRangeEntryBoxes) { + d3.select(dragger) + .call(svgTextUtils.makeEditable, { + gd: gd, + immediate: true, + background: gd._fullLayout.paper_bgcolor, + text: String(initialText), + fill: ax.tickfont ? ax.tickfont.color : '#444', + horizontalAlign: hAlign, + verticalAlign: vAlign + }) + .on('edit', function(text) { + var v = ax.d2r(text); + if(v !== undefined) { + Registry.call('relayout', gd, attrStr, v); + } + }); } } - }; + } dragElement.init(dragOptions); @@ -349,6 +353,38 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) { gd._dragged = zoomDragged; updateZoombox(zb, corners, box, path0, dimmed, lum); + // what event data do we emit here? In gl3d, camera location is emitted. + // what is needed for relayouting a cartesian plot? + // for plotly_relayout, the payload is always 'updates' + // updates[ax._name + '.range[0]'] = ax.range[0]; + // updates[ax._name + '.range[1]'] = ax.range[1]; + // For plotly_relayout, the event is emitted at the end of zoomDone. The payload is not + // computed until zoomAxRanges is called. + // Actual drawing is spread out in several functions + // updateSubplots + // ticksAndAnnotations + // relayout() in plot_api.js + // zoom + // zoomMove + // no drawing + // zoomDone + // zoomAxRanges + // no drawing but modifying updates + // dragtail + // updateSubplots + // drawing subplots + // relayout() in plot_api.js + // zoomwheel + // zoomwheel + // updateSubplots + // ticksandannotations + // this modifies the updates + // dragtail on delay + // updateSubplots + // relayout() in plot_api.js + // + computeZoomUpdates(); + gd.emit('plotly_relayouting', updates); dimmed = true; } @@ -358,7 +394,13 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) { if(Math.min(box.h, box.w) < MINDRAG * 2) { return removeZoombox(gd); } + computeZoomUpdates(); + removeZoombox(gd); + dragTail(); + showDoubleClickNotifier(gd); + } + function computeZoomUpdates() { // TODO: edit linked axes in zoomAxRanges and in dragTail if(zoomMode === 'xy' || zoomMode === 'x') { zoomAxRanges(xaxes, box.l / pw, box.r / pw, updates, links.xaxes); @@ -366,10 +408,6 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) { if(zoomMode === 'xy' || zoomMode === 'y') { zoomAxRanges(yaxes, (ph - box.b) / ph, (ph - box.t) / ph, updates, links.yaxes); } - - removeZoombox(gd); - dragTail(); - showDoubleClickNotifier(gd); } // scroll zoom, on all draggers except corners @@ -378,7 +416,6 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) { var redrawTimer = null; var REDRAWDELAY = constants.REDRAWDELAY; var mainplot = plotinfo.mainplot ? gd._fullLayout._plots[plotinfo.mainplot] : plotinfo; - function zoomWheel(e) { // deactivate mousewheel scrolling on embedded graphs // devs can override this with layout._enablescrollzoom, @@ -489,6 +526,8 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) { if(yActive) dragAxList(yaxes, dy); updateSubplots([xActive ? -dx : 0, yActive ? -dy : 0, pw, ph]); ticksAndAnnotations(yActive, xActive); + // updates computed in ticksAndAnnotations + gd.emit('plotly_relayouting', updates); return; } @@ -561,6 +600,8 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) { updateSubplots([x0, y0, pw - dx, ph - dy]); ticksAndAnnotations(yActive, xActive); + // updates computed in ticksAndAnnotations + gd.emit('plotly_relayouting', updates); } // Draw ticks and annotations (and other components) when ranges change. diff --git a/src/plots/geo/zoom.js b/src/plots/geo/zoom.js index ac5d234e9b1..b4212d03df5 100644 --- a/src/plots/geo/zoom.js +++ b/src/plots/geo/zoom.js @@ -80,6 +80,7 @@ function zoomScoped(geo, projection) { .scale(d3.event.scale) .translate(d3.event.translate); geo.render(); + geo.graphDiv.emit('plotly_relayouting', {'projection.scale': projection.scale() / geo.fitScale}); } function syncCb(set) { @@ -153,8 +154,8 @@ function zoomNonClipped(geo, projection) { } geo.render(); + geo.graphDiv.emit('plotly_relayouting', {'projection.scale': projection.scale() / geo.fitScale}); } - function handleZoomend() { d3.select(this).style(zoomendStyle); sync(geo, projection, syncCb); diff --git a/src/plots/gl3d/scene.js b/src/plots/gl3d/scene.js index af02713a09c..a5a5f4954cc 100644 --- a/src/plots/gl3d/scene.js +++ b/src/plots/gl3d/scene.js @@ -215,7 +215,15 @@ function initializeGLPlot(scene, fullLayout, canvas, gl) { scene.saveCamera(scene.graphDiv.layout); scene.graphDiv.emit('plotly_relayout', update); }; + var relayoutingCallback = function(scene) { + if(scene.fullSceneLayout.dragmode === false) return; + var update = {}; + update[scene.id + '.camera'] = getLayoutCamera(scene.camera); + // scene.saveCamera(scene.graphDiv.layout); + scene.graphDiv.emit('plotly_relayouting', update); + }; + scene.glplot.canvas.addEventListener('mousemove', relayoutingCallback.bind(null, scene)); scene.glplot.canvas.addEventListener('mouseup', relayoutCallback.bind(null, scene)); scene.glplot.canvas.addEventListener('wheel', relayoutCallback.bind(null, scene), passiveSupported ? {passive: false} : false); diff --git a/src/plots/mapbox/mapbox.js b/src/plots/mapbox/mapbox.js index 18db3935a4d..0bbb20a735f 100644 --- a/src/plots/mapbox/mapbox.js +++ b/src/plots/mapbox/mapbox.js @@ -169,6 +169,12 @@ proto.createMap = function(calcData, fullLayout, resolve, reject) { self.yaxis.p2c = function() { return evt.lngLat.lat; }; Fx.hover(gd, evt, self.id); + + var update = {}; + var view = self.getView(); + update[self.id] = Lib.extendFlat({}, view); + gd.emit('plotly_relayouting', update); + }); map.on('click', function(evt) { diff --git a/src/plots/polar/polar.js b/src/plots/polar/polar.js index 5b8264194a3..89f1f89b940 100644 --- a/src/plots/polar/polar.js +++ b/src/plots/polar/polar.js @@ -680,24 +680,30 @@ proto.updateMainDrag = function(fullLayout, polarLayout) { zb.attr('d', path1); corners.attr('d', cpath); dragBox.transitionZoombox(zb, corners, dimmed, lum); + var updateObj = {}; + computeZoomUpdates(updateObj); + gd.emit('plotly_relayouting', updateObj); dimmed = true; } - function zoomDone() { - dragBox.removeZoombox(gd); - - if(r0 === null || r1 === null) return; - - dragBox.showDoubleClickNotifier(gd); - + function computeZoomUpdates(update) { var radialAxis = _this.radialAxis; var radialRange = radialAxis.range; var drange = radialRange[1] - radialRange[0]; - var updateObj = {}; - updateObj[_this.id + '.radialaxis.range'] = [ + update[_this.id + '.radialaxis.range'] = [ radialRange[0] + r0 * drange / radius, radialRange[0] + r1 * drange / radius ]; + } + + function zoomDone() { + dragBox.removeZoombox(gd); + + if(r0 === null || r1 === null) return; + var updateObj = {}; + computeZoomUpdates(updateObj); + + dragBox.showDoubleClickNotifier(gd); Registry.call('relayout', gd, updateObj); } diff --git a/src/plots/ternary/ternary.js b/src/plots/ternary/ternary.js index 436397bc5aa..627abbbc0c1 100644 --- a/src/plots/ternary/ternary.js +++ b/src/plots/ternary/ternary.js @@ -587,17 +587,22 @@ proto.initInteractions = function() { .duration(200); dimmed = true; } + var updates = {}; + computeZoomUpdates(updates); + gd.emit('plotly_relayouting', updates); } + function computeZoomUpdates(attrs) { + attrs[_this.id + '.aaxis.min'] = mins.a; + attrs[_this.id + '.baxis.min'] = mins.b; + attrs[_this.id + '.caxis.min'] = mins.c; + } function zoomDone() { removeZoombox(gd); if(mins === mins0) return; - var attrs = {}; - attrs[_this.id + '.aaxis.min'] = mins.a; - attrs[_this.id + '.baxis.min'] = mins.b; - attrs[_this.id + '.caxis.min'] = mins.c; + computeZoomUpdates(attrs); Registry.call('relayout', gd, attrs); @@ -670,14 +675,19 @@ proto.initInteractions = function() { .select('.scatterlayer').selectAll('.trace') .call(Drawing.hideOutsideRangePoints, _this); } - } - - function dragDone() { var attrs = {}; + computeDragUpdates(attrs); + gd.emit('plotly_relayout', attrs); + } + function computeDragUpdates(attrs) { attrs[_this.id + '.aaxis.min'] = mins.a; attrs[_this.id + '.baxis.min'] = mins.b; attrs[_this.id + '.caxis.min'] = mins.c; + } + function dragDone() { + var attrs = {}; + computeDragUpdates(attrs); Registry.call('relayout', gd, attrs); } diff --git a/test/jasmine/tests/gl2d_plot_interact_test.js b/test/jasmine/tests/gl2d_plot_interact_test.js index dc9ece04979..15f5c92169d 100644 --- a/test/jasmine/tests/gl2d_plot_interact_test.js +++ b/test/jasmine/tests/gl2d_plot_interact_test.js @@ -8,7 +8,7 @@ var ScatterGl = require('@src/traces/scattergl'); var createGraphDiv = require('../assets/create_graph_div'); var destroyGraphDiv = require('../assets/destroy_graph_div'); -var fail = require('../assets/fail_test'); +var failTest = require('../assets/fail_test'); var mouseEvent = require('../assets/mouse_event'); var touchEvent = require('../assets/touch_event'); var drag = require('../assets/drag'); @@ -202,7 +202,7 @@ describe('@gl Test gl plot side effects', function() { .then(function() { expect(gd.querySelector('.gl-canvas-context').width).toBe(300); }) - .catch(fail) + .catch(failTest) .then(done); }); }); @@ -350,7 +350,7 @@ describe('@gl Test gl2d plots', function() { 'yaxis.range[1]': jasmine.any(Number) })); }) - .catch(fail) + .catch(failTest) .then(done); }); @@ -381,7 +381,7 @@ describe('@gl Test gl2d plots', function() { .then(function() { expect(readPixel(gd.querySelector('.gl-canvas-context'), 108, 100)[0]).not.toBe(0); }) - .catch(fail) + .catch(failTest) .then(done); }); @@ -423,7 +423,7 @@ describe('@gl Test gl2d plots', function() { expect(scene.error2d.draw).toHaveBeenCalledTimes(2, 'twice for x AND y'); expect(scene.scatter2d.draw).toHaveBeenCalledTimes(3, 'both traces have markers'); }) - .catch(fail) + .catch(failTest) .then(done); }); @@ -451,7 +451,7 @@ describe('@gl Test gl2d plots', function() { expect(readPixel(gd.querySelector('.gl-canvas-context'), 158, 100)[3]).not.toBe(0); expect(readPixel(gd.querySelector('.gl-canvas-focus'), 168, 100)[3]).not.toBe(0); }) - .catch(fail) + .catch(failTest) .then(done); }); @@ -490,7 +490,7 @@ describe('@gl Test gl2d plots', function() { expect(readPixel(gd.querySelector('.gl-canvas-context'), 158, 100)[3]).not.toBe(0); expect(readPixel(gd.querySelector('.gl-canvas-focus'), 168, 100)[3]).not.toBe(0); }) - .catch(fail) + .catch(failTest) .then(done); }); @@ -514,7 +514,7 @@ describe('@gl Test gl2d plots', function() { expect(countCanvases()).toBe(0); expect(d3.selectAll('.scatterlayer > .trace').size()).toBe(1); }) - .catch(fail) + .catch(failTest) .then(done); }); @@ -567,7 +567,7 @@ describe('@gl Test gl2d plots', function() { expect(gd.layout.xaxis.range).toBeCloseToArray([6, 8], 3); expect(gd.layout.yaxis.range).toBeCloseToArray([5, 7], 3); }) - .catch(fail) + .catch(failTest) .then(done); }); @@ -634,7 +634,7 @@ describe('@gl Test gl2d plots', function() { expect(gd.layout.xaxis.range).toBeCloseToArray([-8.2, 24.2], 1); expect(gd.layout.yaxis.range).toBeCloseToArray([-0.12, 16.1], 1); }) - .catch(fail) + .catch(failTest) .then(done); }); @@ -690,7 +690,7 @@ describe('@gl Test gl2d plots', function() { .then(function() { assertAnnotation([327, 331]); }) - .catch(fail) + .catch(failTest) .then(done); }); @@ -744,7 +744,7 @@ describe('@gl Test gl2d plots', function() { expect(relayoutCallback).toHaveBeenCalledTimes(1); }) - .catch(fail) + .catch(failTest) .then(done); }); @@ -768,7 +768,7 @@ describe('@gl Test gl2d plots', function() { .then(function() { expect(ScatterGl.calc).toHaveBeenCalledTimes(2); }) - .catch(fail) + .catch(failTest) .then(done); }); @@ -852,7 +852,7 @@ describe('@gl Test gl2d plots', function() { expect(scene.selectBatch).toBe(null, msg); expect(scene.unselectBatch).toBe(null, msg); }) - .catch(fail) + .catch(failTest) .then(done); }); @@ -869,7 +869,7 @@ describe('@gl Test gl2d plots', function() { .then(function() { expect(readPixel(gd.querySelector('.gl-canvas-context'), 100, 80)[0]).toBe(0); }) - .catch(fail) + .catch(failTest) .then(done); }); @@ -914,7 +914,7 @@ describe('@gl Test gl2d plots', function() { // and 105545275 after. expect(total).toBeGreaterThan(4e6); }) - .catch(fail) + .catch(failTest) .then(done); }); @@ -945,7 +945,21 @@ describe('@gl Test gl2d plots', function() { expect(opts.positions) .toBeCloseToArray([1, 1, 2, 2, 3, 1]); }) - .catch(fail) + .catch(failTest) + .then(done); + }); + + it('should create two WebGL contexts per graph', function(done) { + var fig = Lib.extendDeep({}, require('@mocks/gl2d_stacked_subplots.json')); + + Plotly.plot(gd, fig).then(function() { + var cnt = 0; + d3.select(gd).selectAll('canvas').each(function(d) { + if(d.regl) cnt++; + }); + expect(cnt).toBe(2); + }) + .catch(failTest) .then(done); }); }); @@ -987,7 +1001,7 @@ describe('Test scattergl autorange:', function() { expect(gd._fullLayout.xaxis.range).toBeCloseToArray(glRangeX, 'x range'); expect(gd._fullLayout.yaxis.range).toBeCloseToArray(glRangeY, 'y range'); }) - .catch(fail) + .catch(failTest) .then(done); }); }); @@ -1026,7 +1040,7 @@ describe('Test scattergl autorange:', function() { expect(gd._fullLayout.xaxis.range).toBeCloseToArray([-0.079, 1.079], 2, 'x range'); expect(gd._fullLayout.yaxis.range).toBeCloseToArray([-0.105, 1.105], 2, 'y range'); }) - .catch(fail) + .catch(failTest) .then(done); }); @@ -1044,7 +1058,7 @@ describe('Test scattergl autorange:', function() { expect(gd._fullLayout.xaxis.range).toBeCloseToArray([-0.119, 1.119], 2, 'x range'); expect(gd._fullLayout.yaxis.range).toBeCloseToArray([-0.199, 1.199], 2, 'y range'); }) - .catch(fail) + .catch(failTest) .then(done); }); }); diff --git a/test/jasmine/tests/parcoords_test.js b/test/jasmine/tests/parcoords_test.js index 06a65999815..a4ea3e41e86 100644 --- a/test/jasmine/tests/parcoords_test.js +++ b/test/jasmine/tests/parcoords_test.js @@ -770,8 +770,8 @@ describe('@gl parcoords Lifecycle methods', function() { }); describe('@gl parcoords basic use', function() { - var mockCopy, - gd; + var mockCopy; + var gd; beforeEach(function(done) { mockCopy = Lib.extendDeep({}, mock); @@ -789,6 +789,14 @@ describe('@gl parcoords basic use', function() { afterAll(purgeGraphDiv); + it('should create three WebGL contexts per graph', function() { + var cnt = 0; + d3.select(gd).selectAll('canvas').each(function(d) { + if(d.regl) cnt++; + }); + expect(cnt).toBe(3); + }); + it('`Plotly.plot` should have proper fields on `gd.data` on initial rendering', function() { expect(gd.data.length).toEqual(1); diff --git a/test/jasmine/tests/select_test.js b/test/jasmine/tests/select_test.js index 1a0e6323e97..cd9f60d03cf 100644 --- a/test/jasmine/tests/select_test.js +++ b/test/jasmine/tests/select_test.js @@ -612,9 +612,115 @@ describe('@flaky Test select box and lasso in general:', function() { }) .catch(failTest) .then(done); - }); + it('should clear selected points on double click only on pan/lasso modes', function(done) { + var gd = createGraphDiv(); + var fig = Lib.extendDeep({}, require('@mocks/0.json')); + fig.data = [fig.data[0]]; + fig.layout.xaxis.autorange = false; + fig.layout.xaxis.range = [2, 8]; + fig.layout.yaxis.autorange = false; + fig.layout.yaxis.range = [0, 3]; + + function _assert(msg, exp) { + expect(gd.layout.xaxis.range) + .toBeCloseToArray(exp.xrng, 2, 'xaxis range - ' + msg); + expect(gd.layout.yaxis.range) + .toBeCloseToArray(exp.yrng, 2, 'yaxis range - ' + msg); + + if(exp.selpts === null) { + expect('selectedpoints' in gd.data[0]) + .toBe(false, 'cleared selectedpoints - ' + msg); + } else { + expect(gd.data[0].selectedpoints) + .toBeCloseToArray(exp.selpts, 2, 'selectedpoints - ' + msg); + } + } + + Plotly.plot(gd, fig).then(function() { + _assert('base', { + xrng: [2, 8], + yrng: [0, 3], + selpts: null + }); + return Plotly.relayout(gd, 'xaxis.range', [0, 10]); + }) + .then(function() { + _assert('after xrng relayout', { + xrng: [0, 10], + yrng: [0, 3], + selpts: null + }); + return doubleClick(200, 200); + }) + .then(function() { + _assert('after double-click under dragmode zoom', { + xrng: [2, 8], + yrng: [0, 3], + selpts: null + }); + return Plotly.relayout(gd, 'dragmode', 'select'); + }) + .then(function() { + _assert('after relayout to select', { + xrng: [2, 8], + yrng: [0, 3], + selpts: null + }); + return drag([[100, 100], [400, 400]]); + }) + .then(function() { + _assert('after selection', { + xrng: [2, 8], + yrng: [0, 3], + selpts: [40, 41, 42, 43, 44, 45, 46, 47, 48] + }); + return doubleClick(200, 200); + }) + .then(function() { + _assert('after double-click under dragmode select', { + xrng: [2, 8], + yrng: [0, 3], + selpts: null + }); + return drag([[100, 100], [400, 400]]); + }) + .then(function() { + _assert('after selection 2', { + xrng: [2, 8], + yrng: [0, 3], + selpts: [40, 41, 42, 43, 44, 45, 46, 47, 48] + }); + return Plotly.relayout(gd, 'dragmode', 'pan'); + }) + .then(function() { + _assert('after relayout to pan', { + xrng: [2, 8], + yrng: [0, 3], + selpts: [40, 41, 42, 43, 44, 45, 46, 47, 48] + }); + return Plotly.relayout(gd, 'yaxis.range', [0, 20]); + }) + .then(function() { + _assert('after yrng relayout', { + xrng: [2, 8], + yrng: [0, 20], + selpts: [40, 41, 42, 43, 44, 45, 46, 47, 48] + }); + return doubleClick(200, 200); + }) + .then(function() { + _assert('after double-click under dragmode pan', { + xrng: [2, 8], + yrng: [0, 3], + // N.B. does not clear selection! + selpts: [40, 41, 42, 43, 44, 45, 46, 47, 48] + }); + }) + .catch(failTest) + .then(done); + }); }); describe('@flaky Test select box and lasso per trace:', function() {