Skip to content

Commit dcaebc2

Browse files
authored
Merge pull request #2831 from plotly/subplot-overlaying-toggle-relayouts
Fix overlaying subplot configuration relayouts
2 parents e1ecd18 + 5f2f7ce commit dcaebc2

File tree

7 files changed

+150
-57
lines changed

7 files changed

+150
-57
lines changed

src/plot_api/subroutines.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,8 @@ function lsInner(gd) {
118118
// to put them
119119
var lowerBackgroundIDs = [];
120120
var lowerDomains = [];
121-
subplotSelection.each(function(subplot) {
121+
subplotSelection.each(function(d) {
122+
var subplot = d[0];
122123
var plotinfo = fullLayout._plots[subplot];
123124

124125
if(plotinfo.mainplot) {
@@ -161,7 +162,8 @@ function lsInner(gd) {
161162
fullLayout._plots[subplot].bg = d3.select(this);
162163
});
163164

164-
subplotSelection.each(function(subplot) {
165+
subplotSelection.each(function(d) {
166+
var subplot = d[0];
165167
var plotinfo = fullLayout._plots[subplot];
166168
var xa = plotinfo.xaxis;
167169
var ya = plotinfo.yaxis;

src/plots/cartesian/axes.js

+8-5
Original file line numberDiff line numberDiff line change
@@ -1500,7 +1500,8 @@ axes.makeClipPaths = function(gd) {
15001500
});
15011501
};
15021502

1503-
/** Main multi-axis drawing routine!
1503+
/**
1504+
* Main multi-axis drawing routine!
15041505
*
15051506
* @param {DOM element} gd : graph div
15061507
* @param {string or array of strings} arg : polymorphic argument
@@ -1525,8 +1526,9 @@ axes.doTicks = function(gd, arg, skipTitle) {
15251526
var fullLayout = gd._fullLayout;
15261527

15271528
if(arg === 'redraw') {
1528-
fullLayout._paper.selectAll('g.subplot').each(function(subplot) {
1529-
var plotinfo = fullLayout._plots[subplot];
1529+
fullLayout._paper.selectAll('g.subplot').each(function(d) {
1530+
var id = d[0];
1531+
var plotinfo = fullLayout._plots[id];
15301532
var xa = plotinfo.xaxis;
15311533
var ya = plotinfo.yaxis;
15321534

@@ -1556,14 +1558,15 @@ axes.doTicks = function(gd, arg, skipTitle) {
15561558
}));
15571559
};
15581560

1559-
/** Per axis drawing routine!
1561+
/**
1562+
* Per-axis drawing routine!
15601563
*
15611564
* This routine draws axis ticks and much more (... grids, labels, title etc.)
15621565
* Supports multiple argument signatures.
15631566
* N.B. this thing is async in general (because of MathJax rendering)
15641567
*
15651568
* @param {DOM element} gd : graph div
1566-
* @param {string or array of strings} arg : polymorphic argument
1569+
* @param {string or object} arg : polymorphic argument
15671570
* @param {boolean} skipTitle : optional flag to skip axis title draw/update
15681571
* @return {promise}
15691572
*

src/plots/cartesian/graph_interact.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ exports.initInteractions = function initInteractions(gd) {
2727
return;
2828
}
2929

30-
if(!fullLayout._has('cartesian') && !fullLayout._has('gl2d') && !fullLayout._has('splom')) return;
30+
if(!fullLayout._has('cartesian') && !fullLayout._has('splom')) return;
3131

3232
var subplots = Object.keys(fullLayout._plots || {}).sort(function(a, b) {
3333
// sort overlays last, then by x axis number, then y axis number

src/plots/cartesian/index.js

+48-34
Original file line numberDiff line numberDiff line change
@@ -358,37 +358,27 @@ exports.drawFramework = function(gd) {
358358
var subplotData = makeSubplotData(gd);
359359

360360
var subplotLayers = fullLayout._cartesianlayer.selectAll('.subplot')
361-
.data(subplotData, Lib.identity);
361+
.data(subplotData, String);
362362

363363
subplotLayers.enter().append('g')
364-
.attr('class', function(name) { return 'subplot ' + name; });
364+
.attr('class', function(d) { return 'subplot ' + d[0]; });
365365

366366
subplotLayers.order();
367367

368368
subplotLayers.exit()
369369
.call(purgeSubplotLayers, fullLayout);
370370

371-
subplotLayers.each(function(name) {
372-
var plotinfo = fullLayout._plots[name];
371+
subplotLayers.each(function(d) {
372+
var id = d[0];
373+
var plotinfo = fullLayout._plots[id];
373374

374-
// keep ref to plot group
375375
plotinfo.plotgroup = d3.select(this);
376-
377-
// initialize list of overlay subplots
378-
plotinfo.overlays = [];
379-
380376
makeSubplotLayer(gd, plotinfo);
381377

382-
// fill in list of overlay subplots
383-
if(plotinfo.mainplot) {
384-
var mainplot = fullLayout._plots[plotinfo.mainplot];
385-
mainplot.overlays.push(plotinfo);
386-
}
387-
388378
// make separate drag layers for each subplot,
389379
// but append them to paper rather than the plot groups,
390380
// so they end up on top of the rest
391-
plotinfo.draglayer = ensureSingle(fullLayout._draggers, 'g', name);
381+
plotinfo.draglayer = ensureSingle(fullLayout._draggers, 'g', id);
392382
});
393383
};
394384

@@ -400,27 +390,49 @@ exports.rangePlot = function(gd, plotinfo, cdSubplot) {
400390

401391
function makeSubplotData(gd) {
402392
var fullLayout = gd._fullLayout;
403-
var subplotData = [];
404-
var overlays = [];
405-
406-
for(var k in fullLayout._plots) {
407-
var plotinfo = fullLayout._plots[k];
408-
var xa2 = plotinfo.xaxis._mainAxis;
409-
var ya2 = plotinfo.yaxis._mainAxis;
393+
var ids = fullLayout._subplots.cartesian;
394+
var len = ids.length;
395+
var subplotData = new Array(len);
396+
var i, j, id, plotinfo, xa, ya;
397+
398+
for(i = 0; i < len; i++) {
399+
id = ids[i];
400+
plotinfo = fullLayout._plots[id];
401+
xa = plotinfo.xaxis;
402+
ya = plotinfo.yaxis;
403+
404+
var xa2 = xa._mainAxis;
405+
var ya2 = ya._mainAxis;
410406
var mainplot = xa2._id + ya2._id;
407+
var mainplotinfo = fullLayout._plots[mainplot];
408+
plotinfo.overlays = [];
411409

412-
if(mainplot !== k && fullLayout._plots[mainplot]) {
410+
if(mainplot !== id && mainplotinfo) {
411+
// link 'main plot' ref in overlaying plotinfo
413412
plotinfo.mainplot = mainplot;
414-
plotinfo.mainplotinfo = fullLayout._plots[mainplot];
415-
overlays.push(k);
413+
plotinfo.mainplotinfo = mainplotinfo;
414+
// fill in list of overlaying subplots in 'main plot'
415+
mainplotinfo.overlays.push(plotinfo);
416416
} else {
417-
subplotData.push(k);
418417
plotinfo.mainplot = undefined;
418+
plotinfo.mainPlotinfo = undefined;
419419
}
420420
}
421421

422-
// main subplots before overlays
423-
subplotData = subplotData.concat(overlays);
422+
// use info about axis layer and overlaying pattern
423+
// to clean what need to be cleaned up in exit selection
424+
for(i = 0; i < len; i++) {
425+
id = ids[i];
426+
plotinfo = fullLayout._plots[id];
427+
xa = plotinfo.xaxis;
428+
ya = plotinfo.yaxis;
429+
430+
var d = [id, xa.layer, ya.layer, xa.overlaying || '', ya.overlaying || ''];
431+
for(j = 0; j < plotinfo.overlays.length; j++) {
432+
d.push(plotinfo.overlays[j].id);
433+
}
434+
subplotData[i] = d;
435+
}
424436

425437
return subplotData;
426438
}
@@ -517,7 +529,9 @@ function makeSubplotLayer(gd, plotinfo) {
517529
if(!hasOnlyLargeSploms) {
518530
ensureSingleAndAddDatum(plotinfo.gridlayer, 'g', plotinfo.xaxis._id);
519531
ensureSingleAndAddDatum(plotinfo.gridlayer, 'g', plotinfo.yaxis._id);
520-
plotinfo.gridlayer.selectAll('g').sort(axisIds.idSort);
532+
plotinfo.gridlayer.selectAll('g')
533+
.map(function(d) { return d[0]; })
534+
.sort(axisIds.idSort);
521535
}
522536

523537
plotinfo.xlines
@@ -534,13 +548,13 @@ function purgeSubplotLayers(layers, fullLayout) {
534548

535549
var overlayIdsToRemove = {};
536550

537-
layers.each(function(subplotId) {
551+
layers.each(function(d) {
552+
var id = d[0];
538553
var plotgroup = d3.select(this);
539554

540555
plotgroup.remove();
541-
removeSubplotExtras(subplotId, fullLayout);
542-
543-
overlayIdsToRemove[subplotId] = true;
556+
removeSubplotExtras(id, fullLayout);
557+
overlayIdsToRemove[id] = true;
544558

545559
// do not remove individual axis <clipPath>s here
546560
// as other subplots may need them

src/plots/plots.js

+6-14
Original file line numberDiff line numberDiff line change
@@ -757,6 +757,8 @@ plots.cleanPlot = function(newFullData, newFullLayout, oldFullData, oldFullLayou
757757
};
758758

759759
plots.linkSubplots = function(newFullData, newFullLayout, oldFullData, oldFullLayout) {
760+
var i, j;
761+
760762
var oldSubplots = oldFullLayout._plots || {};
761763
var newSubplots = newFullLayout._plots = {};
762764
var newSubplotList = newFullLayout._subplots;
@@ -768,32 +770,22 @@ plots.linkSubplots = function(newFullData, newFullLayout, oldFullData, oldFullLa
768770

769771
var ids = newSubplotList.cartesian.concat(newSubplotList.gl2d || []);
770772

771-
var i, j, id, ax;
772-
773773
for(i = 0; i < ids.length; i++) {
774-
id = ids[i];
774+
var id = ids[i];
775775
var oldSubplot = oldSubplots[id];
776776
var xaxis = axisIDs.getFromId(mockGd, id, 'x');
777777
var yaxis = axisIDs.getFromId(mockGd, id, 'y');
778778
var plotinfo;
779779

780+
// link or create subplot object
780781
if(oldSubplot) {
781782
plotinfo = newSubplots[id] = oldSubplot;
782-
783-
if(plotinfo.xaxis.layer !== xaxis.layer) {
784-
plotinfo.xlines.attr('d', null);
785-
plotinfo.xaxislayer.selectAll('*').remove();
786-
}
787-
788-
if(plotinfo.yaxis.layer !== yaxis.layer) {
789-
plotinfo.ylines.attr('d', null);
790-
plotinfo.yaxislayer.selectAll('*').remove();
791-
}
792783
} else {
793784
plotinfo = newSubplots[id] = {};
794785
plotinfo.id = id;
795786
}
796787

788+
// update x and y axis layout object refs
797789
plotinfo.xaxis = xaxis;
798790
plotinfo.yaxis = yaxis;
799791

@@ -821,7 +813,7 @@ plots.linkSubplots = function(newFullData, newFullLayout, oldFullData, oldFullLa
821813
// anchored axes to the axes they're anchored to
822814
var axList = axisIDs.list(mockGd, null, true);
823815
for(i = 0; i < axList.length; i++) {
824-
ax = axList[i];
816+
var ax = axList[i];
825817
var mainAx = null;
826818

827819
if(ax.overlaying) {

test/jasmine/tests/cartesian_test.js

+82
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,88 @@ describe('subplot creation / deletion:', function() {
659659
.then(done);
660660
});
661661

662+
it('should clear obsolete content out of axis layers when changing overlaying configuation', function(done) {
663+
function data() {
664+
return [
665+
{x: [1, 2], y: [1, 2]},
666+
{x: [1, 2], y: [1, 2], xaxis: 'x2', yaxis: 'y2'}
667+
];
668+
}
669+
670+
function fig0() {
671+
return {
672+
data: data(),
673+
layout: {
674+
xaxis2: {side: 'top', overlaying: 'x'},
675+
yaxis2: {side: 'right', overlaying: 'y'}
676+
}
677+
};
678+
}
679+
680+
function fig1() {
681+
return {
682+
data: data(),
683+
layout: {
684+
xaxis2: {side: 'top', domain: [0.37, 1]},
685+
yaxis2: {side: 'right', overlaying: 'y'}
686+
}
687+
};
688+
}
689+
690+
function getParentClassName(query, level) {
691+
level = level || 1;
692+
var cl = gd.querySelector('g.cartesianlayer');
693+
var node = cl.querySelector(query);
694+
while(level--) node = node.parentNode;
695+
return node.getAttribute('class');
696+
}
697+
698+
function _assert(msg, exp) {
699+
expect(getParentClassName('.xtick'))
700+
.toBe(exp.xtickParent, 'xitck parent - ' + msg);
701+
expect(getParentClassName('.x2tick'))
702+
.toBe(exp.x2tickParent, 'x2tick parent - ' + msg);
703+
expect(getParentClassName('.trace' + gd._fullData[0].uid, 2))
704+
.toBe(exp.trace0Parent, 'data[0] parent - ' + msg);
705+
expect(getParentClassName('.trace' + gd._fullData[1].uid, 2))
706+
.toBe(exp.trace1Parent, 'data[1] parent - ' + msg);
707+
}
708+
709+
Plotly.react(gd, fig0())
710+
.then(function() {
711+
_assert('x2/y2 both overlays', {
712+
xtickParent: 'xaxislayer-above',
713+
x2tickParent: 'x2y2-x',
714+
trace0Parent: 'plot',
715+
trace1Parent: 'x2y2'
716+
});
717+
})
718+
.then(function() {
719+
return Plotly.react(gd, fig1());
720+
})
721+
.then(function() {
722+
_assert('x2 free / y2 overlaid', {
723+
xtickParent: 'xaxislayer-above',
724+
x2tickParent: 'xaxislayer-above',
725+
trace0Parent: 'plot',
726+
trace1Parent: 'plot'
727+
});
728+
})
729+
.then(function() {
730+
return Plotly.react(gd, fig0());
731+
})
732+
.then(function() {
733+
_assert('back to x2/y2 both overlays', {
734+
xtickParent: 'xaxislayer-above',
735+
x2tickParent: 'x2y2-x',
736+
trace0Parent: 'plot',
737+
trace1Parent: 'x2y2'
738+
});
739+
})
740+
.catch(failTest)
741+
.then(done);
742+
});
743+
662744
it('clear axis ticks, labels and title when relayout an axis to `*visible:false*', function(done) {
663745
function _assert(xaxis, yaxis) {
664746
var g = d3.select('.subplot.xy');

test/jasmine/tests/splom_test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,7 @@ describe('@gl Test splom interactions:', function() {
457457
subplots.each(function(d, i) {
458458
var actual = this.children.length;
459459
var expected = typeof exp.innerSubplotNodeCnt === 'function' ?
460-
exp.innerSubplotNodeCnt(d, i) :
460+
exp.innerSubplotNodeCnt(d[0], i) :
461461
exp.innerSubplotNodeCnt;
462462
if(actual !== expected) {
463463
failedSubplots.push([d, actual, 'vs', expected].join(' '));

0 commit comments

Comments
 (0)