diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js index e00e3acc045..155d3a6fd0c 100644 --- a/src/plot_api/plot_api.js +++ b/src/plot_api/plot_api.js @@ -152,7 +152,6 @@ exports.plot = function(gd, data, layout, config) { Plots.supplyDefaults(gd); var fullLayout = gd._fullLayout; - var hasCartesian = fullLayout._has('cartesian'); // Legacy polar plots @@ -375,11 +374,21 @@ exports.plot = function(gd, data, layout, config) { if(!plotDone || !plotDone.then) plotDone = Promise.resolve(); return plotDone.then(function() { - gd.emit('plotly_afterplot'); + emitAfterPlot(gd); return gd; }); }; +function emitAfterPlot(gd) { + var fullLayout = gd._fullLayout; + + if(fullLayout._redrawFromAutoMarginCount) { + fullLayout._redrawFromAutoMarginCount--; + } else { + gd.emit('plotly_afterplot'); + } +} + exports.setPlotConfig = function setPlotConfig(obj) { return Lib.extendFlat(defaultConfig, obj); }; @@ -1327,6 +1336,8 @@ exports.restyle = function restyle(gd, astr, val, _traces) { if(flags.style) seq.push(subroutines.doTraceStyle); if(flags.colorbars) seq.push(subroutines.doColorBars); + + seq.push(emitAfterPlot); } seq.push(Plots.rehover); @@ -1704,6 +1715,8 @@ exports.relayout = function relayout(gd, astr, val) { if(flags.ticks) seq.push(subroutines.doTicksRelayout); if(flags.modebar) seq.push(subroutines.doModeBar); if(flags.camera) seq.push(subroutines.doCamera); + + seq.push(emitAfterPlot); } seq.push(Plots.rehover); @@ -2191,6 +2204,8 @@ exports.update = function update(gd, traceUpdate, layoutUpdate, _traces) { if(relayoutFlags.ticks) seq.push(subroutines.doTicksRelayout); if(relayoutFlags.modebar) seq.push(subroutines.doModeBar); if(relayoutFlags.camera) seq.push(subroutines.doCamera); + + seq.push(emitAfterPlot); } seq.push(Plots.rehover); @@ -2348,6 +2363,7 @@ exports.react = function(gd, data, layout, config) { if(relayoutFlags.ticks) seq.push(subroutines.doTicksRelayout); if(relayoutFlags.modebar) seq.push(subroutines.doModeBar); if(relayoutFlags.camera) seq.push(subroutines.doCamera); + seq.push(emitAfterPlot); } seq.push(Plots.rehover); diff --git a/src/plot_api/subroutines.js b/src/plot_api/subroutines.js index 252d78250e7..d45aa6683d9 100644 --- a/src/plot_api/subroutines.js +++ b/src/plot_api/subroutines.js @@ -28,7 +28,7 @@ var cleanAxisConstraints = axisConstraints.clean; var doAutoRange = require('../plots/cartesian/autorange').doAutoRange; exports.layoutStyles = function(gd) { - return Lib.syncOrAsync([Plots.doAutoMargin, exports.lsInner], gd); + return Lib.syncOrAsync([Plots.doAutoMargin, lsInner], gd); }; function overlappingDomain(xDomain, yDomain, domains) { @@ -46,7 +46,7 @@ function overlappingDomain(xDomain, yDomain, domains) { return false; } -exports.lsInner = function(gd) { +function lsInner(gd) { var fullLayout = gd._fullLayout; var gs = fullLayout._size; var pad = gs.p; @@ -327,7 +327,7 @@ exports.lsInner = function(gd) { ModeBar.manage(gd); return gd._promises.length && Promise.all(gd._promises); -}; +} function findMainSubplot(ax, fullLayout) { var subplotList = fullLayout._subplots; diff --git a/src/plots/plots.js b/src/plots/plots.js index 176358c0f10..a6aa8c7ecbd 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -1788,8 +1788,15 @@ plots.doAutoMargin = function(gd) { gs.h = Math.round(fullLayout.height) - gs.t - gs.b; // if things changed and we're not already redrawing, trigger a redraw - if(!fullLayout._replotting && oldmargins !== '{}' && - oldmargins !== JSON.stringify(fullLayout._size)) { + if(!fullLayout._replotting && + oldmargins !== '{}' && + oldmargins !== JSON.stringify(fullLayout._size) + ) { + if('_redrawFromAutoMarginCount' in fullLayout) { + fullLayout._redrawFromAutoMarginCount++; + } else { + fullLayout._redrawFromAutoMarginCount = 1; + } return Registry.call('plot', gd); } }; diff --git a/test/jasmine/tests/plot_api_test.js b/test/jasmine/tests/plot_api_test.js index 01cca99320c..fbc6c433f3b 100644 --- a/test/jasmine/tests/plot_api_test.js +++ b/test/jasmine/tests/plot_api_test.js @@ -539,6 +539,7 @@ describe('Test plot api', function() { supplyAllDefaults(gd); Plots.doCalcdata(gd); + gd.emit = function() {}; return gd; } @@ -699,6 +700,7 @@ describe('Test plot api', function() { gd.calcdata = gd._fullData.map(function(trace) { return [{x: 1, y: 1, trace: trace}]; }); + gd.emit = function() {}; } it('calls Scatter.arraysToCalcdata and Plots.style on scatter styling', function() { @@ -2566,11 +2568,14 @@ describe('Test plot api', function() { ]; var gd; - var plotCalls; + var afterPlotCnt; beforeEach(function() { gd = createGraphDiv(); + spyOn(plotApi, 'plot').and.callThrough(); + spyOn(Registry, 'call').and.callThrough(); + mockedMethods.forEach(function(m) { spyOn(subroutines, m).and.callThrough(); subroutines[m].calls.reset(); @@ -2584,13 +2589,14 @@ describe('Test plot api', function() { afterEach(destroyGraphDiv); function countPlots() { - plotCalls = 0; - - gd.on('plotly_afterplot', function() { plotCalls++; }); + plotApi.plot.calls.reset(); subroutines.layoutStyles.calls.reset(); annotations.draw.calls.reset(); annotations.drawOne.calls.reset(); images.draw.calls.reset(); + + afterPlotCnt = 0; + gd.on('plotly_afterplot', function() { afterPlotCnt++; }); } function countCalls(counts) { @@ -2602,8 +2608,14 @@ describe('Test plot api', function() { subroutines[m].calls.reset(); }); - expect(plotCalls).toBe(counts.plot || 0, 'calls to Plotly.plot'); - plotCalls = 0; + // calls to Plotly.plot via plot_api.js or Registry.call('plot') + var plotCalls = plotApi.plot.calls.count() + + Registry.call.calls.all() + .filter(function(d) { return d.args[0] === 'plot'; }) + .length; + expect(plotCalls).toBe(counts.plot || 0, 'Plotly.plot calls'); + plotApi.plot.calls.reset(); + Registry.call.calls.reset(); // only consider annotation and image draw calls if we *don't* do a full plot. if(!counts.plot) { @@ -2614,6 +2626,9 @@ describe('Test plot api', function() { annotations.draw.calls.reset(); annotations.drawOne.calls.reset(); images.draw.calls.reset(); + + expect(afterPlotCnt).toBe(1, 'plotly_afterplot should be called only once per edit'); + afterPlotCnt = 0; } it('can add / remove traces', function(done) { @@ -2654,6 +2669,7 @@ describe('Test plot api', function() { .then(function() { // didn't pick it up, as we modified in place!!! expect(d3.selectAll('.point').size()).toBe(3); + countCalls({plot: 0}); data[0].y = [1, 2, 3, 4, 5]; return Plotly.react(gd, data, layout); @@ -2661,7 +2677,6 @@ describe('Test plot api', function() { .then(function() { // new object, we picked it up! expect(d3.selectAll('.point').size()).toBe(5); - countCalls({plot: 1}); }) .catch(failTest) @@ -2683,6 +2698,7 @@ describe('Test plot api', function() { .then(function() { // didn't pick it up, as we didn't modify datarevision expect(d3.selectAll('.point').size()).toBe(3); + countCalls({plot: 0}); data[0].y.push(5); layout.datarevision = 'bananas'; @@ -2869,7 +2885,7 @@ describe('Test plot api', function() { sizex: 1, sizey: 1 }]; - Plotly.react(gd, data, layout); + return Plotly.react(gd, data, layout); }) .then(function() { countCalls({imageDraw: 1}); @@ -2883,7 +2899,7 @@ describe('Test plot api', function() { layout.images[0].y = 0.8; layout.images[0].sizey = 0.4; - Plotly.react(gd, data, layout); + return Plotly.react(gd, data, layout); }) .then(function() { countCalls({imageDraw: 1});