diff --git a/src/components/modebar/buttons.js b/src/components/modebar/buttons.js index 9635b30cc37..23b6ae8a72c 100644 --- a/src/components/modebar/buttons.js +++ b/src/components/modebar/buttons.js @@ -11,7 +11,6 @@ var Plotly = require('../../plotly'); var Lib = require('../../lib'); -var setCursor = require('../../lib/setcursor'); var downloadImage = require('../../snapshot/download'); var Icons = require('../../../build/ploticon'); @@ -171,13 +170,6 @@ modeBarButtons.hoverCompareCartesian = { click: handleCartesian }; -var DRAGCURSORS = { - pan: 'move', - zoom: 'crosshair', - select: 'crosshair', - lasso: 'crosshair' -}; - function handleCartesian(gd, ev) { var button = ev.currentTarget, astr = button.getAttribute('data-attr'), @@ -227,18 +219,7 @@ function handleCartesian(gd, ev) { aobj[astr] = val; } - Plotly.relayout(gd, aobj).then(function() { - if(astr === 'dragmode') { - if(fullLayout._has('cartesian')) { - setCursor( - fullLayout._paper.select('.nsewdrag'), - DRAGCURSORS[val] - ); - } - Plotly.Fx.supplyLayoutDefaults(gd.layout, fullLayout, gd._fullData); - Plotly.Fx.init(gd); - } - }); + Plotly.relayout(gd, aobj); } modeBarButtons.zoom3d = { diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js index 8b3cd559c4b..2dfbd3bf1dd 100644 --- a/src/plot_api/plot_api.js +++ b/src/plot_api/plot_api.js @@ -2429,6 +2429,9 @@ Plotly.relayout = function relayout(gd, astr, val) { var subplotIds; manageModeBar(gd); + Plotly.Fx.supplyLayoutDefaults(gd.layout, fullLayout, gd._fullData); + Plotly.Fx.init(gd); + subplotIds = Plots.getSubplotIds(fullLayout, 'gl3d'); for(i = 0; i < subplotIds.length; i++) { scene = fullLayout[subplotIds[i]]._scene; diff --git a/src/plots/cartesian/dragbox.js b/src/plots/cartesian/dragbox.js index 508fa6a2d51..ff6f7bc52bf 100644 --- a/src/plots/cartesian/dragbox.js +++ b/src/plots/cartesian/dragbox.js @@ -52,7 +52,8 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) { pw = xa[0]._length, ph = ya[0]._length, MINDRAG = constants.MINDRAG, - MINZOOM = constants.MINZOOM; + MINZOOM = constants.MINZOOM, + isMainDrag = (ns + ew === 'nsew'); for(var i = 1; i < subplots.length; i++) { var subplotXa = subplots[i].x(), @@ -75,21 +76,24 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) { dragClass = ns + ew + 'drag'; var dragger3 = plotinfo.draglayer.selectAll('.' + dragClass).data([0]); + dragger3.enter().append('rect') .classed('drag', true) .classed(dragClass, true) .style({fill: 'transparent', 'stroke-width': 0}) .attr('data-subplot', plotinfo.id); + dragger3.call(Drawing.setRect, x, y, w, h) .call(setCursor, cursor); + var dragger = dragger3.node(); // still need to make the element if the axes are disabled // but nuke its events (except for maindrag which needs them for hover) // and stop there - if(!yActive && !xActive) { + if(!yActive && !xActive && !isSelectOrLasso(fullLayout.dragmode)) { dragger.onmousedown = null; - dragger.style.pointerEvents = (ns + ew === 'nsew') ? 'all' : 'none'; + dragger.style.pointerEvents = isMainDrag ? 'all' : 'none'; return dragger; } @@ -107,7 +111,8 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) { doubleclick: doubleClick, prepFn: function(e, startX, startY) { var dragModeNow = gd._fullLayout.dragmode; - if(ns + ew === 'nsew') { + + 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) { @@ -131,7 +136,7 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) { dragOptions.doneFn = dragDone; clearSelect(); } - else if(dragModeNow === 'select' || dragModeNow === 'lasso') { + else if(isSelectOrLasso(dragModeNow)) { prepSelect(e, startX, startY, dragOptions, dragModeNow); } } @@ -668,3 +673,9 @@ function removeZoombox(gd) { .selectAll('.zoombox,.js-zoombox-backdrop,.js-zoombox-menu,.zoombox-corners') .remove(); } + +function isSelectOrLasso(dragmode) { + var modes = ['lasso', 'select']; + + return modes.indexOf(dragmode) !== -1; +} diff --git a/test/jasmine/tests/fx_test.js b/test/jasmine/tests/fx_test.js index 59ac3a146fc..4a89b547472 100644 --- a/test/jasmine/tests/fx_test.js +++ b/test/jasmine/tests/fx_test.js @@ -1,69 +1,135 @@ -var Fx = require('@src/plots/cartesian/graph_interact'); +var Plotly = require('@lib/index'); var Plots = require('@src/plots/plots'); +var Fx = require('@src/plots/cartesian/graph_interact'); + +var d3 = require('d3'); +var createGraphDiv = require('../assets/create_graph_div'); +var destroyGraphDiv = require('../assets/destroy_graph_div'); + + +describe('Fx defaults', function() { + 'use strict'; + + var layoutIn, layoutOut, fullData; + + beforeEach(function() { + layoutIn = {}; + layoutOut = { + _has: Plots._hasPlotType + }; + fullData = [{}]; + }); + + it('should default (blank version)', function() { + Fx.supplyLayoutDefaults(layoutIn, layoutOut, fullData); + expect(layoutOut.hovermode).toBe('closest', 'hovermode to closest'); + expect(layoutOut.dragmode).toBe('zoom', 'dragmode to zoom'); + }); + + it('should default (cartesian version)', function() { + layoutOut._basePlotModules = [{ name: 'cartesian' }]; + + Fx.supplyLayoutDefaults(layoutIn, layoutOut, fullData); + expect(layoutOut.hovermode).toBe('x', 'hovermode to x'); + expect(layoutOut.dragmode).toBe('zoom', 'dragmode to zoom'); + expect(layoutOut._isHoriz).toBe(false, 'isHoriz to false'); + }); + + it('should default (cartesian horizontal version)', function() { + layoutOut._basePlotModules = [{ name: 'cartesian' }]; + fullData[0] = { orientation: 'h' }; + + Fx.supplyLayoutDefaults(layoutIn, layoutOut, fullData); + expect(layoutOut.hovermode).toBe('y', 'hovermode to y'); + expect(layoutOut.dragmode).toBe('zoom', 'dragmode to zoom'); + expect(layoutOut._isHoriz).toBe(true, 'isHoriz to true'); + }); + + it('should default (gl3d version)', function() { + layoutOut._basePlotModules = [{ name: 'gl3d' }]; + + Fx.supplyLayoutDefaults(layoutIn, layoutOut, fullData); + expect(layoutOut.hovermode).toBe('closest', 'hovermode to closest'); + expect(layoutOut.dragmode).toBe('zoom', 'dragmode to zoom'); + }); + + it('should default (geo version)', function() { + layoutOut._basePlotModules = [{ name: 'geo' }]; -describe('Test FX', function() { + Fx.supplyLayoutDefaults(layoutIn, layoutOut, fullData); + expect(layoutOut.hovermode).toBe('closest', 'hovermode to closest'); + expect(layoutOut.dragmode).toBe('zoom', 'dragmode to zoom'); + }); + + it('should default (multi plot type version)', function() { + layoutOut._basePlotModules = [{ name: 'cartesian' }, { name: 'gl3d' }]; + + Fx.supplyLayoutDefaults(layoutIn, layoutOut, fullData); + expect(layoutOut.hovermode).toBe('x', 'hovermode to x'); + expect(layoutOut.dragmode).toBe('zoom', 'dragmode to zoom'); + }); +}); + +describe('relayout', function() { 'use strict'; - describe('defaults', function() { - - var layoutIn, layoutOut, fullData; - - beforeEach(function() { - layoutIn = {}; - layoutOut = { - _has: Plots._hasPlotType - }; - fullData = [{}]; - }); - - it('should default (blank version)', function() { - Fx.supplyLayoutDefaults(layoutIn, layoutOut, fullData); - expect(layoutOut.hovermode).toBe('closest', 'hovermode to closest'); - expect(layoutOut.dragmode).toBe('zoom', 'dragmode to zoom'); - }); - - it('should default (cartesian version)', function() { - layoutOut._basePlotModules = [{ name: 'cartesian' }]; - - Fx.supplyLayoutDefaults(layoutIn, layoutOut, fullData); - expect(layoutOut.hovermode).toBe('x', 'hovermode to x'); - expect(layoutOut.dragmode).toBe('zoom', 'dragmode to zoom'); - expect(layoutOut._isHoriz).toBe(false, 'isHoriz to false'); - }); - - it('should default (cartesian horizontal version)', function() { - layoutOut._basePlotModules = [{ name: 'cartesian' }]; - fullData[0] = { orientation: 'h' }; - - Fx.supplyLayoutDefaults(layoutIn, layoutOut, fullData); - expect(layoutOut.hovermode).toBe('y', 'hovermode to y'); - expect(layoutOut.dragmode).toBe('zoom', 'dragmode to zoom'); - expect(layoutOut._isHoriz).toBe(true, 'isHoriz to true'); - }); - - it('should default (gl3d version)', function() { - layoutOut._basePlotModules = [{ name: 'gl3d' }]; - - Fx.supplyLayoutDefaults(layoutIn, layoutOut, fullData); - expect(layoutOut.hovermode).toBe('closest', 'hovermode to closest'); - expect(layoutOut.dragmode).toBe('zoom', 'dragmode to zoom'); - }); - - it('should default (geo version)', function() { - layoutOut._basePlotModules = [{ name: 'geo' }]; - - Fx.supplyLayoutDefaults(layoutIn, layoutOut, fullData); - expect(layoutOut.hovermode).toBe('closest', 'hovermode to closest'); - expect(layoutOut.dragmode).toBe('zoom', 'dragmode to zoom'); - }); - - it('should default (multi plot type version)', function() { - layoutOut._basePlotModules = [{ name: 'cartesian' }, { name: 'gl3d' }]; - - Fx.supplyLayoutDefaults(layoutIn, layoutOut, fullData); - expect(layoutOut.hovermode).toBe('x', 'hovermode to x'); - expect(layoutOut.dragmode).toBe('zoom', 'dragmode to zoom'); - }); + var gd; + + beforeEach(function() { + gd = createGraphDiv(); + }); + + afterEach(destroyGraphDiv); + + it('should update main drag with correct', function(done) { + + function assertMainDrag(cursor, isActive) { + expect(d3.selectAll('rect.nsewdrag').size()).toEqual(1, 'number of nodes'); + var mainDrag = d3.select('rect.nsewdrag'), + node = mainDrag.node(); + + expect(mainDrag.classed('cursor-' + cursor)).toBe(true, 'cursor ' + cursor); + expect(mainDrag.style('pointer-events')).toEqual('all', 'pointer event'); + expect(!!node.onmousedown).toBe(isActive, 'mousedown handler'); + } + + Plotly.plot(gd, [{ + y: [2, 1, 2] + }]).then(function() { + assertMainDrag('crosshair', true); + + return Plotly.relayout(gd, 'dragmode', 'pan'); + }).then(function() { + assertMainDrag('move', true); + + return Plotly.relayout(gd, 'dragmode', 'drag'); + }).then(function() { + assertMainDrag('crosshair', true); + + return Plotly.relayout(gd, 'xaxis.fixedrange', true); + }).then(function() { + assertMainDrag('ns-resize', true); + + return Plotly.relayout(gd, 'yaxis.fixedrange', true); + }).then(function() { + assertMainDrag('pointer', false); + + return Plotly.relayout(gd, 'dragmode', 'drag'); + }).then(function() { + assertMainDrag('pointer', false); + + return Plotly.relayout(gd, 'dragmode', 'lasso'); + }).then(function() { + assertMainDrag('pointer', true); + + return Plotly.relayout(gd, 'dragmode', 'select'); + }).then(function() { + assertMainDrag('pointer', true); + + return Plotly.relayout(gd, 'xaxis.fixedrange', false); + }).then(function() { + assertMainDrag('ew-resize', true); + }).then(done); }); });