diff --git a/src/plots/cartesian/dragbox.js b/src/plots/cartesian/dragbox.js index 937a3ebc034..0960a543d60 100644 --- a/src/plots/cartesian/dragbox.js +++ b/src/plots/cartesian/dragbox.js @@ -85,7 +85,7 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) { // graph-wide optimization flags var hasScatterGl, hasSplom, hasSVG; // collected changes to be made to the plot by relayout at the end - var updates; + var updates = {}; function recomputeAxisLists() { xa0 = plotinfo.xaxis; @@ -409,18 +409,12 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) { gd._dragged = zoomDragged; updateZoombox(zb, corners, box, path0, dimmed, lum); + computeZoomUpdates(); + gd.emit('plotly_relayouting', updates); dimmed = true; } - function zoomDone() { - updates = {}; - - // more strict than dragged, which allows you to come back to where you started - // and still count as dragged - if(Math.min(box.h, box.w) < MINDRAG * 2) { - return removeZoombox(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); @@ -430,6 +424,18 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) { zoomAxRanges(yaxes, (ph - box.b) / ph, (ph - box.t) / ph, updates, links.yaxes); updateMatchedAxRange('y', updates); } + } + + function zoomDone() { + updates = {}; + + // more strict than dragged, which allows you to come back to where you started + // and still count as dragged + if(Math.min(box.h, box.w) < MINDRAG * 2) { + return removeZoombox(gd); + } + + computeZoomUpdates(); removeZoombox(gd); dragTail(); @@ -515,6 +521,8 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) { updateSubplots(scrollViewBox); ticksAndAnnotations(); + gd.emit('plotly_relayouting', updates); + // then replot after a delay to make sure // no more scrolling is coming redrawTimer = setTimeout(function() { @@ -552,6 +560,7 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) { } updateSubplots([xActive ? -dx : 0, yActive ? -dy : 0, pw, ph]); ticksAndAnnotations(); + gd.emit('plotly_relayouting', updates); return; } @@ -626,6 +635,7 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) { updateMatchedAxRange('y'); updateSubplots([xStart, yStart, pw - dx, ph - dy]); ticksAndAnnotations(); + gd.emit('plotly_relayouting', updates); } function updateMatchedAxRange(axLetter, out) { diff --git a/src/plots/geo/zoom.js b/src/plots/geo/zoom.js index ee632a22af9..66ef7d15fd0 100644 --- a/src/plots/geo/zoom.js +++ b/src/plots/geo/zoom.js @@ -86,6 +86,13 @@ function zoomScoped(geo, projection) { .scale(d3.event.scale) .translate(d3.event.translate); geo.render(); + + var center = projection.invert(geo.midPt); + geo.graphDiv.emit('plotly_relayouting', { + 'geo.projection.scale': projection.scale() / geo.fitScale, + 'geo.center.lon': center[0], + 'geo.center.lat': center[1] + }); } function syncCb(set) { @@ -164,6 +171,16 @@ function zoomNonClipped(geo, projection) { didZoom = true; geo.render(); + + var rotate = projection.rotate(); + var center = projection.invert(geo.midPt); + geo.graphDiv.emit('plotly_relayouting', { + 'geo.projection.scale': projection.scale() / geo.fitScale, + 'geo.center.lon': center[0], + 'geo.center.lat': center[1], + 'geo.projection.rotation.lon': -rotate[0] + + }); } function handleZoomend() { @@ -261,6 +278,13 @@ function zoomClipped(geo, projection) { }) .on('zoom.redraw', function() { geo.render(); + + var _rotate = projection.rotate(); + geo.graphDiv.emit('plotly_relayouting', { + 'geo.projection.scale': projection.scale() / geo.fitScale, + 'geo.projection.rotation.lon': -_rotate[0], + 'geo.projection.rotation.lat': -_rotate[1] + }); }); function zoomstarted(dispatch) { diff --git a/src/plots/gl3d/scene.js b/src/plots/gl3d/scene.js index 57a317e72c8..0b5018bead2 100644 --- a/src/plots/gl3d/scene.js +++ b/src/plots/gl3d/scene.js @@ -272,6 +272,15 @@ function initializeGLPlot(scene, pixelRatio, canvas, gl) { } }, passiveSupported ? {passive: false} : false); + scene.glplot.canvas.addEventListener('mousemove', function() { + if(scene.fullSceneLayout.dragmode === false) return; + if(scene.camera.mouseListener.buttons === 0) return; + + var update = {}; + update[scene.id + '.camera'] = getLayoutCamera(scene.camera); + scene.graphDiv.emit('plotly_relayouting', update); + }); + if(!scene.staticMode) { scene.glplot.canvas.addEventListener('webglcontextlost', function(event) { if(gd && gd.emit) { diff --git a/src/plots/mapbox/mapbox.js b/src/plots/mapbox/mapbox.js index 12494985486..ce2791af0b2 100644 --- a/src/plots/mapbox/mapbox.js +++ b/src/plots/mapbox/mapbox.js @@ -182,6 +182,14 @@ proto.createMap = function(calcData, fullLayout, resolve, reject) { map.on('dragstart', unhover); map.on('zoomstart', unhover); + function emitUpdate() { + var viewNow = self.getView(); + gd.emit('plotly_relayouting', self.getViewEdits(viewNow)); + } + + map.on('drag', emitUpdate); + map.on('zoom', emitUpdate); + map.on('dblclick', function() { var optsNow = gd._fullLayout[self.id]; Registry.call('_storeDirectGUIEdit', gd.layout, gd._fullLayout._preGUI, self.getViewEdits(optsNow)); diff --git a/src/plots/polar/polar.js b/src/plots/polar/polar.js index a6f33e2da6c..1c450123545 100644 --- a/src/plots/polar/polar.js +++ b/src/plots/polar/polar.js @@ -836,6 +836,10 @@ proto.updateMainDrag = function(fullLayout) { corners.attr('d', cpath); dragBox.transitionZoombox(zb, corners, dimmed, lum); dimmed = true; + + var updateObj = {}; + computeZoomUpdates(updateObj); + gd.emit('plotly_relayouting', updateObj); } function zoomMove(dx, dy) { @@ -889,16 +893,22 @@ proto.updateMainDrag = function(fullLayout) { dragBox.removeZoombox(gd); if(r0 === null || r1 === null) return; + var updateObj = {}; + computeZoomUpdates(updateObj); dragBox.showDoubleClickNotifier(gd); + Registry.call('_guiRelayout', gd, updateObj); + } + + function computeZoomUpdates(update) { var rl = radialAxis._rl; var m = (rl[1] - rl[0]) / (1 - innerRadius / radius) / radius; var newRng = [ rl[0] + (r0 - innerRadius) * m, rl[0] + (r1 - innerRadius) * m ]; - Registry.call('_guiRelayout', gd, _this.id + '.radialaxis.range', newRng); + update[_this.id + '.radialaxis.range'] = newRng; } function zoomClick(numClicks, evt) { @@ -1037,6 +1047,18 @@ proto.updateRadialDrag = function(fullLayout, polarLayout, rngIndex) { moveFn2 = comp < 0.5 ? rotateMove : rerangeMove; } } + + var update = {}; + computeRadialAxisUpdates(update); + gd.emit('plotly_relayouting', update); + } + + function computeRadialAxisUpdates(update) { + if(angle1 !== null) { + update[_this.id + '.radialaxis.angle'] = angle1; + } else if(rprime !== null) { + update[_this.id + '.radialaxis.range[' + rngIndex + ']'] = rprime; + } } function doneFn() { @@ -1236,18 +1258,25 @@ proto.updateAngularDrag = function(fullLayout) { clearGlCanvases(gd); redrawReglTraces(gd); } - } - function doneFn() { - scatterTextPoints.select('text').attr('transform', null); + var update = {}; + computeRotationUpdates(update); + gd.emit('plotly_relayouting', update); + } - var updateObj = {}; + function computeRotationUpdates(updateObj) { updateObj[_this.id + '.angularaxis.rotation'] = rot1; if(_this.vangles) { updateObj[_this.id + '.radialaxis.angle'] = rrot1; } + } + function doneFn() { + scatterTextPoints.select('text').attr('transform', null); + + var updateObj = {}; + computeRotationUpdates(updateObj); Registry.call('_guiRelayout', gd, updateObj); } diff --git a/src/plots/ternary/ternary.js b/src/plots/ternary/ternary.js index 59d0245ce93..71e9ffe92d4 100644 --- a/src/plots/ternary/ternary.js +++ b/src/plots/ternary/ternary.js @@ -644,6 +644,8 @@ proto.initInteractions = function() { .duration(200); dimmed = true; } + + gd.emit('plotly_relayouting', makeUpdate(mins)); } function zoomDone() { @@ -720,6 +722,8 @@ proto.initInteractions = function() { .select('.scatterlayer').selectAll('.trace') .call(Drawing.hideOutsideRangePoints, _this); } + + gd.emit('plotly_relayouting', makeUpdate(mins)); } function dragDone() { diff --git a/test/jasmine/tests/cartesian_interact_test.js b/test/jasmine/tests/cartesian_interact_test.js index 8a220b999cc..20d1394452c 100644 --- a/test/jasmine/tests/cartesian_interact_test.js +++ b/test/jasmine/tests/cartesian_interact_test.js @@ -188,6 +188,43 @@ describe('main plot pan', function() { .then(done); }); + it('should emit plotly_relayouting events during pan interactions', function(done) { + var mock = Lib.extendDeep({}, require('@mocks/10.json')); + mock.layout.dragmode = 'pan'; + + function _drag(x0, y0, x1, y1, n) { + mouseEvent('mousedown', x0, y0); + var dx = (x1 - x0) / n; + var dy = (y1 - y0) / n; + for(var i = 0; i <= n; i++) { + mouseEvent('mousemove', x0 + dx * i, y0 + dy * i); + } + mouseEvent('mouseup', x1, y1); + } + + var nsteps = 10; var events = []; var relayoutCallback; + Plotly.plot(gd, mock.data, mock.layout) + .then(function() { + relayoutCallback = jasmine.createSpy('relayoutCallback'); + gd.on('plotly_relayout', relayoutCallback); + gd.on('plotly_relayouting', function(e) { + events.push(e); + }); + _drag(100, 150, 220, 250, nsteps); + }) + .then(function() { + expect(events.length).toEqual(nsteps); + var first = events.splice(0, 1)[0]; + var last = events.splice(-1, 1)[0]; + expect(first['xaxis.range[1]'] - first['xaxis.range[0]']).toBeCloseTo(6, 0); + expect(last['xaxis.range[1]'] - last['xaxis.range[0]']).toBeCloseTo(6, 0); + + expect(first['xaxis.range[1]'] - last['xaxis.range[1]']).toBeCloseTo(1, 0); + }) + .catch(failTest) + .then(done); + }); + it('should show/hide `cliponaxis: false` pts according to range', function(done) { function _assert(markerDisplay, textDisplay, barTextDisplay) { var gd3 = d3.select(gd); @@ -289,10 +326,10 @@ describe('axis zoom/pan and main plot zoom', function() { return document.querySelector('.' + directions + 'drag[data-subplot="' + subplot + '"]'); } - function doDrag(subplot, directions, dx, dy) { + function doDrag(subplot, directions, dx, dy, nsteps) { return function() { var dragger = getDragger(subplot, directions); - return drag(dragger, dx, dy); + return drag(dragger, dx, dy, undefined, undefined, undefined, nsteps); }; } @@ -311,7 +348,11 @@ describe('axis zoom/pan and main plot zoom', function() { var dy = opts.dy || 0; var dragger = getDragger(subplot, directions); var coords = getNodeCoords(dragger, edge); - mouseEvent('scroll', coords.x + dx, coords.y + dy, {deltaY: deltaY, element: dragger}); + var nsteps = opts.nsteps || 1; + + for(var i = 1; i <= nsteps; i++) { + mouseEvent('scroll', coords.x + dx, coords.y + dy, {deltaY: deltaY / nsteps * i, element: dragger}); + } return delay(constants.REDRAWDELAY + 10)(); }; } @@ -629,6 +670,44 @@ describe('axis zoom/pan and main plot zoom', function() { .then(done); }); + it('should emit plotly_relayouting events when drawing zoom selection', function(done) { + var nsteps = 10; var events = []; var relayoutCallback; + Plotly.plot(gd, [{ y: [1, 2, 1] }]) + .then(function() { + relayoutCallback = jasmine.createSpy('relayoutCallback'); + gd.on('plotly_relayout', relayoutCallback); + gd.on('plotly_relayouting', function(e) { + events.push(e); + }); + }) + .then(doDrag('xy', 'nsew', 100, 100, nsteps)) + .then(function() { + expect(events.length).toEqual(nsteps); + expect(relayoutCallback).toHaveBeenCalledTimes(1); + }) + .catch(failTest) + .then(done); + }); + + it('should emit plotly_relayouting events when zooming via mouse wheel', function(done) { + var nsteps = 10; var events = []; var relayoutCallback; + Plotly.plot(gd, [{ y: [1, 2, 1] }], {}, {scrollZoom: true}) + .then(function() { + relayoutCallback = jasmine.createSpy('relayoutCallback'); + gd.on('plotly_relayout', relayoutCallback); + gd.on('plotly_relayouting', function(e) { + events.push(e); + }); + }) + .then(doScroll('xy', 'nsew', 100, {edge: 'se', nsteps: nsteps})) + .then(function() { + expect(events.length).toEqual(nsteps); + expect(relayoutCallback).toHaveBeenCalledTimes(1); + }) + .catch(failTest) + .then(done); + }); + it('handles xy, x-only and y-only zoombox updates', function(done) { function _assert(msg, xrng, yrng) { expect(gd.layout.xaxis.range).toBeCloseToArray(xrng, 2, 'xrng - ' + msg); @@ -1466,7 +1545,7 @@ describe('axis zoom/pan and main plot zoom', function() { return drag.start() .then(_assert('just after start of zoombox', { nodeCnt: 4, - xrng: [-0.1927, 3.1927], + xrng: [1.5, 1.6880], hasDragData: true, zoombox: 'M269.5,114.5h-3v41h3ZM300.5,114.5h3v41h-3Z', clipTranslate: [0, 0] @@ -1474,7 +1553,7 @@ describe('axis zoom/pan and main plot zoom', function() { .then(delay(step)) .then(_assert('during zoombox drag', { nodeCnt: 5, - xrng: [-0.257, 4.257], + xrng: [2, 2.2507], hasDragData: true, zoombox: 'M269.5,114.5h-3v41h3ZM300.5,114.5h3v41h-3Z', clipTranslate: [0, 0] diff --git a/test/jasmine/tests/geo_test.js b/test/jasmine/tests/geo_test.js index 4a3146eb3a4..b2a5ebbd0b0 100644 --- a/test/jasmine/tests/geo_test.js +++ b/test/jasmine/tests/geo_test.js @@ -2165,4 +2165,46 @@ describe('Test geo zoom/pan/drag interactions:', function() { .catch(failTest) .then(done); }); + + describe('plotly_relayouting', function() { + var mocks = { + 'non-clipped': require('@mocks/geo_winkel-tripel'), + 'clipped': require('@mocks/geo_orthographic'), + 'scoped': require('@mocks/geo_europe-bubbles') + }; + ['non-clipped', 'clipped', 'scoped'].forEach(function(zoomHandler) { + ['pan'].forEach(function(dragmode) { + it('should emit events on ' + dragmode + ' for ' + zoomHandler, function(done) { + var events = []; var path = [[300, 300], [350, 300], [350, 400]]; + var relayoutCnt = 0; var relayoutEvent; + var fig = Lib.extendDeep({}, mocks[zoomHandler]); + fig.layout.dragmode = dragmode; + fig.layout.width = 700; + fig.layout.height = 500; + + gd = createGraphDiv(); + Plotly.plot(gd, fig) + .then(function() { + gd.on('plotly_relayout', function(e) { + relayoutCnt++; + relayoutEvent = e; + }); + gd.on('plotly_relayouting', function(e) { + events.push(e); + }); + return drag(path); + }) + .then(function() { + expect(events.length).toEqual(path.length - 1); + expect(relayoutCnt).toEqual(1); + Object.keys(relayoutEvent).sort().forEach(function(key) { + expect(Object.keys(events[0])).toContain(key); + }); + }) + .catch(failTest) + .then(done); + }); + }); + }); + }); }); diff --git a/test/jasmine/tests/gl3d_plot_interact_test.js b/test/jasmine/tests/gl3d_plot_interact_test.js index 821406f09bf..19c846d796c 100644 --- a/test/jasmine/tests/gl3d_plot_interact_test.js +++ b/test/jasmine/tests/gl3d_plot_interact_test.js @@ -929,6 +929,60 @@ describe('Test gl3d drag and wheel interactions', function() { .catch(failTest) .then(done); }); + + it('@gl should fire plotly_relayouting events', function(done) { + var sceneTarget, relayoutEvent; + var relayoutCnt = 0; + var events = []; + + var mock = { + data: [ + { type: 'scatter3d', x: [1, 2, 3], y: [2, 3, 1], z: [3, 1, 2] } + ], + layout: { + scene: { camera: { projection: {type: 'orthographic'}, eye: { x: 0.1, y: 0.1, z: 1 }}}, + width: 400, height: 400 + } + }; + + var nsteps = 10; + function _drag(target, start, end, n) { + return new Promise(function(resolve) { + mouseEvent('mousedown', start[0], start[1], {element: target, buttons: 1}); + var dx = (end[0] - start[0]) / n; + var dy = (end[1] - start[1]) / n; + for(var i = 1; i <= n; i++) { + mouseEvent('mousemove', start[0] + dx * n, start[1] + dy * n, {element: target, buttons: 1}); + } + mouseEvent('mouseup', end[0], end[1], {element: target, buttons: 1}); + setTimeout(resolve, 0); + }); + } + + Plotly.plot(gd, mock) + .then(function() { + gd.on('plotly_relayout', function(e) { + relayoutCnt++; + relayoutEvent = e; + }); + gd.on('plotly_relayouting', function(e) { + events.push(e); + }); + + sceneTarget = gd.querySelector('.svg-container .gl-container #scene canvas'); + + return _drag(sceneTarget, [200, 200], [100, 100], nsteps); + }) + .then(function() { + expect(events.length).toEqual(nsteps); + expect(relayoutCnt).toEqual(1); + Object.keys(relayoutEvent).sort().forEach(function(key) { + expect(Object.keys(events[0])).toContain(key); + }); + }) + .catch(failTest) + .then(done); + }); }); describe('Test gl3d relayout calls', function() { diff --git a/test/jasmine/tests/mapbox_test.js b/test/jasmine/tests/mapbox_test.js index b97d0d0ff27..8087bd41d58 100644 --- a/test/jasmine/tests/mapbox_test.js +++ b/test/jasmine/tests/mapbox_test.js @@ -887,6 +887,7 @@ describe('@noCI, mapbox plots', function() { it('should respond drag / scroll / double-click interactions', function(done) { var relayoutCnt = 0; var doubleClickCnt = 0; + var relayoutingCnt = 0; var evtData; gd.on('plotly_relayout', function(d) { @@ -894,6 +895,10 @@ describe('@noCI, mapbox plots', function() { evtData = d; }); + gd.on('plotly_relayouting', function() { + relayoutingCnt++; + }); + gd.on('plotly_doubleclick', function() { doubleClickCnt++; }); @@ -930,6 +935,7 @@ describe('@noCI, mapbox plots', function() { _drag(pointPos, p1, function() { expect(relayoutCnt).toBe(1, 'relayout cnt'); + expect(relayoutingCnt).toBe(2, 'relayouting cnt'); expect(doubleClickCnt).toBe(0, 'double click cnt'); _assert([-19.651, 13.751], 1.234); @@ -937,6 +943,7 @@ describe('@noCI, mapbox plots', function() { }) .then(function() { expect(relayoutCnt).toBe(2, 'relayout cnt'); + expect(relayoutingCnt).toBe(2, 'relayouting cnt'); expect(doubleClickCnt).toBe(1, 'double click cnt'); _assert([-4.710, 19.475], 1.234); @@ -944,6 +951,7 @@ describe('@noCI, mapbox plots', function() { }) .then(function() { expect(relayoutCnt).toBe(3, 'relayout cnt'); + expect(relayoutingCnt).toBeCloseTo(10, -1, 'relayouting cnt'); expect(doubleClickCnt).toBe(1, 'double click cnt'); expect(getMapInfo(gd).zoom).toBeGreaterThan(1.234); }) diff --git a/test/jasmine/tests/polar_test.js b/test/jasmine/tests/polar_test.js index 1896ad949dd..131eb0414ce 100644 --- a/test/jasmine/tests/polar_test.js +++ b/test/jasmine/tests/polar_test.js @@ -1247,6 +1247,119 @@ describe('Test polar interactions:', function() { }); }); }); + + describe('plotly_relayouting', function() { + afterEach(destroyGraphDiv); + + it('should emit events on radial drag area', function(done) { + var events = []; var path = [[375, 200], [-100, 0]]; var nsteps = 10; + var relayoutEvents = []; + var fig = Lib.extendDeep({}, require('@mocks/polar_scatter.json')); + // to avoid dragging on hover labels + fig.layout.hovermode = false; + + // adjust margins so that middle of plot area is at 300x300 + // with its middle at [200,200] + fig.layout.width = 400; + fig.layout.height = 400; + fig.layout.margin = {l: 50, t: 50, b: 50, r: 50}; + + function _drag(p0, dp, nsteps) { + var node = d3.select('.polar > .draglayer > .radialdrag').node(); + return drag(node, dp[0], dp[1], null, p0[0], p0[1], nsteps); + } + + var gd = createGraphDiv(); + Plotly.plot(gd, fig) + .then(function() { + gd.on('plotly_relayout', function(e) { + relayoutEvents.push(e); + }); + gd.on('plotly_relayouting', function(e) { + events.push(e); + }); + return _drag(path[0], path[1], nsteps); + }) + .then(function() { + var len = events.length; + expect(len).toEqual(nsteps); + expect(events[len - 1]['polar.radialaxis.range[1]']).toBeCloseTo(16, -1); + expect(relayoutEvents.length).toEqual(1); + Object.keys(relayoutEvents[0]).sort().forEach(function(key) { + expect(Object.keys(events[len - 1])).toContain(key); + }); + }) + .catch(failTest) + .then(done); + }); + + it('should emit events on inner radial drag area', function(done) { + var events = []; var path = [[150, 250], [175, 250]]; + var relayoutEvents = []; + var fig = Lib.extendDeep({}, require('@mocks/polar_scatter.json')); + + function _drag(p0, dp, nsteps) { + var node = d3.select('.polar > .draglayer > .maindrag').node(); + return drag(node, dp[0], dp[1], null, p0[0], p0[1], nsteps); + } + + var gd = createGraphDiv(); + Plotly.plot(gd, fig) + .then(function() { + gd.on('plotly_relayout', function(e) { + relayoutEvents.push(e); + }); + gd.on('plotly_relayouting', function(e) { + events.push(e); + }); + return _drag(path[0], path[1]); + }) + .then(function() { + expect(events.length).toEqual(path.length - 1); + expect(events[0]['polar.radialaxis.range']).toBeCloseToArray([6, 11], 0.1); + expect(relayoutEvents.length).toEqual(1); + Object.keys(relayoutEvents[0]).sort().forEach(function(key) { + expect(Object.keys(events[0])).toContain(key); + }); + }) + .catch(failTest) + .then(done); + }); + + it('should emit events on angular drag area', function(done) { + var events = []; var relayoutEvents = []; var nsteps = 10; + var fig = Lib.extendDeep({}, require('@mocks/polar_scatter.json')); + + function _drag(p0, dp, nsteps) { + var node = d3.select('.polar > .draglayer > .angulardrag').node(); + return drag(node, dp[0], dp[1], null, p0[0], p0[1], nsteps); + } + + var dragPos0 = [360, 180]; + + var gd = createGraphDiv(); + Plotly.plot(gd, fig) + .then(function() { + gd.on('plotly_relayout', function(e) { + relayoutEvents.push(e); + }); + gd.on('plotly_relayouting', function(e) { + events.push(e); + }); + return _drag(dragPos0, [0, -110], nsteps); + }) + .then(function() { + expect(events.length).toEqual(nsteps); + expect(events.splice(-1, 1)[0]['polar.angularaxis.rotation']).toBeCloseTo(29, 0); + expect(relayoutEvents.length).toEqual(1); + Object.keys(relayoutEvents[0]).sort().forEach(function(key) { + expect(Object.keys(events[0])).toContain(key); + }); + }) + .catch(failTest) + .then(done); + }); + }); }); describe('Test polar *gridshape linear* interactions', function() { diff --git a/test/jasmine/tests/ternary_test.js b/test/jasmine/tests/ternary_test.js index f6475dbab1a..565e3fa583a 100644 --- a/test/jasmine/tests/ternary_test.js +++ b/test/jasmine/tests/ternary_test.js @@ -514,6 +514,33 @@ describe('ternary plots', function() { .then(done); }); + describe('plotly_relayouting', function() { + ['pan', 'zoom'].forEach(function(dragmode) { + it('should emit plotly_relayouting events on ' + dragmode, function(done) { + var events = []; var path = [[350, 250], [375, 250], [375, 225]]; var relayoutCallback; + var fig = Lib.extendDeep({}, require('@mocks/ternary_simple')); + fig.layout.dragmode = dragmode; + + var gd = createGraphDiv(); + Plotly.plot(gd, fig) + .then(function() { + relayoutCallback = jasmine.createSpy('relayoutCallback'); + gd.on('plotly_relayout', relayoutCallback); + gd.on('plotly_relayouting', function(e) { + events.push(e); + }); + return drag(path); + }) + .then(function() { + expect(events.length).toEqual(path.length - 1); + // expect(relayoutCallback).toHaveBeenCalledTimes(1); + }) + .catch(failTest) + .then(done); + }); + }); + }); + function countTernarySubplot() { return d3.selectAll('.ternary').size(); }