diff --git a/src/plot_api/subroutines.js b/src/plot_api/subroutines.js index 2df2985554d..0a3c2e8a7c5 100644 --- a/src/plot_api/subroutines.js +++ b/src/plot_api/subroutines.js @@ -272,6 +272,7 @@ exports.lsInner = function(gd) { * ----- * x2 */ + var xPath = 'M0,0'; if(shouldShowLinesOrTicks(xa, subplot)) { leftYLineWidth = findCounterAxisLineWidth(xa, 'left', ya, axList); xLinesXLeft = xa._offset - (leftYLineWidth ? (pad + leftYLineWidth) : 0); @@ -288,17 +289,17 @@ exports.lsInner = function(gd) { xa._linepositions[subplot] = [xLinesYBottom, xLinesYTop]; } - var xPath = mainPath(xa, xLinePath, xLinePathFree); + xPath = mainPath(xa, xLinePath, xLinePathFree); if(extraSubplot && xa.showline && (xa.mirror === 'all' || xa.mirror === 'allticks')) { xPath += xLinePath(xLinesYBottom) + xLinePath(xLinesYTop); } plotinfo.xlines - .attr('d', xPath || 'M0,0') .style('stroke-width', xa._lw + 'px') .call(Color.stroke, xa.showline ? xa.linecolor : 'rgba(0,0,0,0)'); } + plotinfo.xlines.attr('d', xPath); /* * y lines that meet x axes get longer only by margin.pad, because @@ -311,6 +312,7 @@ exports.lsInner = function(gd) { * | * +----- */ + var yPath = 'M0,0'; if(shouldShowLinesOrTicks(ya, subplot)) { connectYBottom = findCounterAxisLineWidth(ya, 'bottom', xa, axList); yLinesYBottom = ya._offset + ya._length + (connectYBottom ? pad : 0); @@ -324,17 +326,17 @@ exports.lsInner = function(gd) { ya._linepositions[subplot] = [yLinesXLeft, yLinesXRight]; } - var yPath = mainPath(ya, yLinePath, yLinePathFree); + yPath = mainPath(ya, yLinePath, yLinePathFree); if(extraSubplot && ya.showline && (ya.mirror === 'all' || ya.mirror === 'allticks')) { yPath += yLinePath(yLinesXLeft) + yLinePath(yLinesXRight); } plotinfo.ylines - .attr('d', yPath || 'M0,0') .style('stroke-width', ya._lw + 'px') .call(Color.stroke, ya.showline ? ya.linecolor : 'rgba(0,0,0,0)'); } + plotinfo.ylines.attr('d', yPath); }); Plotly.Axes.makeClipPaths(gd); diff --git a/src/plots/cartesian/index.js b/src/plots/cartesian/index.js index 5b1fcf4dd6a..e4c0cfbdff3 100644 --- a/src/plots/cartesian/index.js +++ b/src/plots/cartesian/index.js @@ -306,6 +306,17 @@ exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) purgeSubplotLayers(oldFullLayout._cartesianlayer.selectAll('.subplot'), oldFullLayout); oldFullLayout._defs.selectAll('.axesclip').remove(); } + // otherwise look for subplots we need to remove + else if(oldSubplotList.cartesian) { + for(i = 0; i < oldSubplotList.cartesian.length; i++) { + var oldSubplotId = oldSubplotList.cartesian[i]; + if(newSubplotList.cartesian.indexOf(oldSubplotId) === -1) { + var selector = '.' + oldSubplotId + ',.' + oldSubplotId + '-x,.' + oldSubplotId + '-y'; + oldFullLayout._cartesianlayer.selectAll(selector).remove(); + removeSubplotExtras(oldSubplotId, oldFullLayout); + } + } + } }; exports.drawFramework = function(gd) { @@ -484,11 +495,9 @@ function purgeSubplotLayers(layers, fullLayout) { layers.each(function(subplotId) { var plotgroup = d3.select(this); - var clipId = 'clip' + fullLayout._uid + subplotId + 'plot'; plotgroup.remove(); - fullLayout._draggers.selectAll('g.' + subplotId).remove(); - fullLayout._defs.select('#' + clipId).remove(); + removeSubplotExtras(subplotId, fullLayout); overlayIdsToRemove[subplotId] = true; @@ -515,6 +524,11 @@ function purgeSubplotLayers(layers, fullLayout) { } } +function removeSubplotExtras(subplotId, fullLayout) { + fullLayout._draggers.selectAll('g.' + subplotId).remove(); + fullLayout._defs.select('#clip' + fullLayout._uid + subplotId + 'plot').remove(); +} + function joinLayer(parent, nodeType, className, dataVal) { var layer = parent.selectAll('.' + className) .data([dataVal || 0]); diff --git a/test/jasmine/tests/cartesian_test.js b/test/jasmine/tests/cartesian_test.js index 6ebf32f3ef5..6de808302f8 100644 --- a/test/jasmine/tests/cartesian_test.js +++ b/test/jasmine/tests/cartesian_test.js @@ -268,6 +268,37 @@ describe('relayout', function() { }); + describe('axis line visibility', function() { + var gd; + + beforeEach(function() { + gd = createGraphDiv(); + }); + + afterEach(destroyGraphDiv); + + it('can show and hide axis lines', function(done) { + Plotly.newPlot(gd, [{y: [1, 2]}], {width: 400, height: 400}) + .then(function() { + expect(gd.querySelector('.xlines-above').attributes.d.value).toBe('M0,0'); + expect(gd.querySelector('.ylines-above').attributes.d.value).toBe('M0,0'); + + return Plotly.relayout(gd, {'xaxis.showline': true, 'yaxis.showline': true}); + }) + .then(function() { + expect(gd.querySelector('.xlines-above').attributes.d.value).not.toBe('M0,0'); + expect(gd.querySelector('.ylines-above').attributes.d.value).not.toBe('M0,0'); + + return Plotly.relayout(gd, {'xaxis.showline': false, 'yaxis.showline': false}); + }) + .then(function() { + expect(gd.querySelector('.xlines-above').attributes.d.value).toBe('M0,0'); + expect(gd.querySelector('.ylines-above').attributes.d.value).toBe('M0,0'); + }) + .catch(failTest) + .then(done); + }); + }); }); describe('subplot creation / deletion:', function() { @@ -314,6 +345,29 @@ describe('subplot creation / deletion:', function() { .then(done); }); + it('should remove unused axes when deleting traces', function(done) { + Plotly.newPlot(gd, + [{y: [1, 2, 3]}, {y: [10, 30, 20], yaxis: 'y2'}], + {yaxis2: {side: 'right', overlaying: 'y', title: 'Hi!'}} + ) + .then(function() { + expect(gd.querySelectorAll('.xy2,.xy2-x,.xy2-y').length).not.toBe(0); + expect(gd.querySelectorAll('.y2title').length).toBe(1); + expect(gd._fullLayout._subplots.cartesian).toEqual(['xy', 'xy2']); + expect(gd._fullLayout._subplots.yaxis).toEqual(['y', 'y2']); + + return Plotly.deleteTraces(gd, [1]); + }) + .then(function() { + expect(gd.querySelectorAll('.xy2,.xy2-x,.xy2-y').length).toBe(0); + expect(gd.querySelectorAll('.y2title').length).toBe(0); + expect(gd._fullLayout._subplots.cartesian).toEqual(['xy']); + expect(gd._fullLayout._subplots.yaxis).toEqual(['y']); + }) + .catch(failTest) + .then(done); + }); + it('puts plot backgrounds behind everything except if they overlap', function(done) { function checkBGLayers(behindCount, x2y2Count) { expect(gd.querySelectorAll('.bglayer rect.bg').length).toBe(behindCount);