diff --git a/src/components/rangeslider/draw.js b/src/components/rangeslider/draw.js index fac1e9334ef..0e68681356b 100644 --- a/src/components/rangeslider/draw.js +++ b/src/components/rangeslider/draw.js @@ -376,6 +376,7 @@ function drawRangePlot(rangeSlider, gd, axisOpts, opts) { }; mockFigure.layout[oppAxisName] = { + type: oppAxisOpts.type, domain: [0, 1], range: oppAxisOpts.range.slice(), calendar: oppAxisOpts.calendar diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js index 2e3f0c373ca..629518b1347 100644 --- a/src/plot_api/plot_api.js +++ b/src/plot_api/plot_api.js @@ -288,11 +288,19 @@ Plotly.plot = function(gd, data, layout, config) { uid = trace.uid; if(!isVisible || !Registry.traceIs(trace, '2dMap')) { - fullLayout._paper.selectAll( + var query = ( '.hm' + uid + ',.contour' + uid + ',#clip' + uid - ).remove(); + ); + + fullLayout._paper + .selectAll(query) + .remove(); + + fullLayout._infolayer.selectAll('g.rangeslider-container') + .selectAll(query) + .remove(); } if(!isVisible || !trace._module.colorbar) { diff --git a/src/plots/cartesian/index.js b/src/plots/cartesian/index.js index 41a626bd2b0..0c1472fff11 100644 --- a/src/plots/cartesian/index.js +++ b/src/plots/cartesian/index.js @@ -160,6 +160,11 @@ exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) .remove(); } } + + oldFullLayout._infolayer.selectAll('g.rangeslider-container') + .select('g.scatterlayer') + .selectAll('g.trace') + .remove(); } var hadCartesian = (oldFullLayout._has && oldFullLayout._has('cartesian')); diff --git a/src/plots/plots.js b/src/plots/plots.js index 87fb69e66b2..6f4dfcf913e 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -571,23 +571,25 @@ plots.cleanPlot = function(newFullData, newFullLayout, oldFullData, oldFullLayou if(oldUid === newTrace.uid) continue oldLoop; } - // clean old heatmap, contour, and scatter traces - // - // Note: This is also how scatter traces (cartesian and scatterternary) get - // removed since otherwise the scatter module is not called (and so the join - // doesn't register the removal) if scatter traces disappear entirely. + var query = ( + '.hm' + oldUid + + ',.contour' + oldUid + + ',#clip' + oldUid + + ',.trace' + oldUid + ); + + // clean old heatmap, contour traces and clip paths + // that rely on uid identifiers if(hasPaper) { - oldFullLayout._paper.selectAll( - '.hm' + oldUid + - ',.contour' + oldUid + - ',#clip' + oldUid + - ',.trace' + oldUid - ).remove(); + oldFullLayout._paper.selectAll(query).remove(); } - // clean old colorbars + // clean old colorbars and range slider plot if(hasInfoLayer) { oldFullLayout._infolayer.selectAll('.cb' + oldUid).remove(); + + oldFullLayout._infolayer.selectAll('g.rangeslider-container') + .selectAll(query).remove(); } } }; diff --git a/src/traces/contour/plot.js b/src/traces/contour/plot.js index e33beda01f1..894b53efc7a 100644 --- a/src/traces/contour/plot.js +++ b/src/traces/contour/plot.js @@ -56,7 +56,11 @@ function plotOne(gd, plotinfo, cd) { heatmapPlot(gd, plotinfo, [cd]); } // in case this used to be a heatmap (or have heatmap fill) - else fullLayout._paper.selectAll('.hm' + uid).remove(); + else { + fullLayout._paper.selectAll('.hm' + uid).remove(); + fullLayout._infolayer.selectAll('g.rangeslider-container') + .selectAll('.hm' + uid).remove(); + } makeCrossings(pathinfo); findAllPaths(pathinfo); diff --git a/src/traces/heatmap/plot.js b/src/traces/heatmap/plot.js index 02fbf077ee7..48bc073f14d 100644 --- a/src/traces/heatmap/plot.js +++ b/src/traces/heatmap/plot.js @@ -36,6 +36,8 @@ function plotOne(gd, plotinfo, cd) { // in case this used to be a contour map fullLayout._paper.selectAll('.contour' + uid).remove(); + fullLayout._infolayer.selectAll('g.rangeslider-container') + .selectAll('.contour' + uid).remove(); if(trace.visible !== true) { fullLayout._paper.selectAll('.' + id).remove(); diff --git a/test/image/baselines/range_slider_multiple.png b/test/image/baselines/range_slider_multiple.png index 49e835105b4..fd21495ea7a 100644 Binary files a/test/image/baselines/range_slider_multiple.png and b/test/image/baselines/range_slider_multiple.png differ diff --git a/test/image/mocks/range_slider_multiple.json b/test/image/mocks/range_slider_multiple.json index ad88f263890..4885806a707 100644 --- a/test/image/mocks/range_slider_multiple.json +++ b/test/image/mocks/range_slider_multiple.json @@ -2,7 +2,7 @@ "data": [ { "x": [ 1, 2, 3 ], - "y": [ 4, 5, 6 ], + "y": [ 4, 5e5, 6e8 ], "type": "bar" }, { @@ -31,7 +31,7 @@ }, "yaxis": { "domain": [ 0.3, 0.8 ], - "type": "linear" + "type": "log" }, "yaxis2": { "anchor": "x2", diff --git a/test/jasmine/tests/range_slider_test.js b/test/jasmine/tests/range_slider_test.js index 170f464e2e5..f023429df6b 100644 --- a/test/jasmine/tests/range_slider_test.js +++ b/test/jasmine/tests/range_slider_test.js @@ -294,43 +294,140 @@ describe('the range slider', function() { it('should not add the slider to the DOM by default', function(done) { Plotly.plot(gd, [{ x: [1, 2, 3], y: [2, 3, 4] }], {}) - .then(function() { - var rangeSlider = getRangeSlider(); - expect(rangeSlider).not.toBeDefined(); - }) - .then(done); + .then(function() { + var rangeSlider = getRangeSlider(); + expect(rangeSlider).not.toBeDefined(); + }) + .then(done); }); it('should add the slider if rangeslider is set to anything', function(done) { Plotly.plot(gd, [{ x: [1, 2, 3], y: [2, 3, 4] }], {}) - .then(function() { Plotly.relayout(gd, 'xaxis.rangeslider', 'exists'); }) - .then(function() { - var rangeSlider = getRangeSlider(); - expect(rangeSlider).toBeDefined(); - }) - .then(done); + .then(function() { + return Plotly.relayout(gd, 'xaxis.rangeslider', 'exists'); + }) + .then(function() { + var rangeSlider = getRangeSlider(); + expect(rangeSlider).toBeDefined(); + }) + .then(done); }); it('should add the slider if visible changed to `true`', function(done) { Plotly.plot(gd, [{ x: [1, 2, 3], y: [2, 3, 4] }], {}) - .then(function() { Plotly.relayout(gd, 'xaxis.rangeslider.visible', true); }) - .then(function() { - var rangeSlider = getRangeSlider(); - expect(rangeSlider).toBeDefined(); - expect(countRangeSliderClipPaths()).toEqual(1); - }) - .then(done); + .then(function() { + return Plotly.relayout(gd, 'xaxis.rangeslider.visible', true); + }) + .then(function() { + var rangeSlider = getRangeSlider(); + expect(rangeSlider).toBeDefined(); + expect(countRangeSliderClipPaths()).toEqual(1); + }) + .then(done); }); it('should remove the slider if changed to `false` or `undefined`', function(done) { - Plotly.plot(gd, [{ x: [1, 2, 3], y: [2, 3, 4] }], { xaxis: { rangeslider: { visible: true }}}) - .then(function() { Plotly.relayout(gd, 'xaxis.rangeslider.visible', false); }) - .then(function() { - var rangeSlider = getRangeSlider(); - expect(rangeSlider).not.toBeDefined(); - expect(countRangeSliderClipPaths()).toEqual(0); - }) - .then(done); + Plotly.plot(gd, [{ + x: [1, 2, 3], + y: [2, 3, 4] + }], { + xaxis: { + rangeslider: { visible: true } + } + }) + .then(function() { + return Plotly.relayout(gd, 'xaxis.rangeslider.visible', false); + }) + .then(function() { + var rangeSlider = getRangeSlider(); + expect(rangeSlider).not.toBeDefined(); + expect(countRangeSliderClipPaths()).toEqual(0); + }) + .then(done); + }); + + it('should clear traces in range plot when needed', function(done) { + + function count(query) { + return d3.select(getRangeSlider()).selectAll(query).size(); + } + + Plotly.plot(gd, [{ + type: 'scatter', + x: [1, 2, 3], + y: [2, 1, 2] + }, { + type: 'bar', + x: [1, 2, 3], + y: [2, 5, 2] + }], { + xaxis: { + rangeslider: { visible: true } + } + }) + .then(function() { + expect(count('g.scatterlayer > g.trace')).toEqual(1); + expect(count('g.barlayer > g.trace')).toEqual(1); + + return Plotly.restyle(gd, 'visible', false); + }) + .then(function() { + expect(count('g.scatterlayer > g.trace')).toEqual(0); + expect(count('g.barlayer > g.trace')).toEqual(0); + + return Plotly.restyle(gd, 'visible', true); + }) + .then(function() { + expect(count('g.scatterlayer > g.trace')).toEqual(1); + expect(count('g.barlayer > g.trace')).toEqual(1); + + return Plotly.deleteTraces(gd, [0, 1]); + }) + .then(function() { + expect(count('g.scatterlayer > g.trace')).toEqual(0); + expect(count('g.barlayer > g.trace')).toEqual(0); + + return Plotly.addTraces(gd, [{ + type: 'heatmap', + z: [[1, 2, 3], [2, 1, 3]] + }]); + }) + .then(function() { + expect(count('g.imagelayer > g.hm')).toEqual(1); + + return Plotly.restyle(gd, 'visible', false); + }) + .then(function() { + expect(count('g.imagelayer > g.hm')).toEqual(0); + + return Plotly.restyle(gd, { + visible: true, + type: 'contour' + }); + }) + .then(function() { + expect(count('g.maplayer > g.contour')).toEqual(1); + + return Plotly.restyle(gd, 'type', 'heatmap'); + }) + .then(function() { + expect(count('g.imagelayer > g.hm')).toEqual(1); + expect(count('g.maplayer > g.contour')).toEqual(0); + + return Plotly.restyle(gd, 'type', 'contour'); + }) + .then(function() { + expect(count('g.imagelayer > g.hm')).toEqual(0); + expect(count('g.maplayer > g.contour')).toEqual(1); + + return Plotly.deleteTraces(gd, [0]); + }) + .then(function() { + expect(count('g.imagelayer > g.hm')).toEqual(0); + expect(count('g.maplayer > g.contour')).toEqual(0); + }) + .then(done); + }); });