Skip to content

Commit 33a9530

Browse files
committed
allow react-transition to diff when old/new fullData lengths mismatch
- should still go through calc (and hence a redraw after the transition) but Scatter.plot can handle this case fine.
1 parent 73284f4 commit 33a9530

File tree

3 files changed

+95
-7
lines changed

3 files changed

+95
-7
lines changed

src/plot_api/plot_api.js

+8-6
Original file line numberDiff line numberDiff line change
@@ -2819,7 +2819,7 @@ exports.react = function(gd, data, layout, config) {
28192819
};
28202820

28212821
function diffData(gd, oldFullData, newFullData, immutable, transition, newDataRevision) {
2822-
if(oldFullData.length !== newFullData.length) {
2822+
if(!transition && oldFullData.length !== newFullData.length) {
28232823
return {
28242824
fullReplot: true,
28252825
calc: true
@@ -2849,12 +2849,14 @@ function diffData(gd, oldFullData, newFullData, immutable, transition, newDataRe
28492849
var seenUIDs = {};
28502850

28512851
for(i = 0; i < oldFullData.length; i++) {
2852-
trace = newFullData[i]._fullInput;
2853-
if(Plots.hasMakesDataTransform(trace)) trace = newFullData[i];
2854-
if(seenUIDs[trace.uid]) continue;
2855-
seenUIDs[trace.uid] = 1;
2852+
if(newFullData[i]) {
2853+
trace = newFullData[i]._fullInput;
2854+
if(Plots.hasMakesDataTransform(trace)) trace = newFullData[i];
2855+
if(seenUIDs[trace.uid]) continue;
2856+
seenUIDs[trace.uid] = 1;
28562857

2857-
getDiffFlags(oldFullData[i]._fullInput, trace, [], diffOpts);
2858+
getDiffFlags(oldFullData[i]._fullInput, trace, [], diffOpts);
2859+
}
28582860
}
28592861

28602862
if(flags.calc || flags.plot) {

src/plots/plots.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,7 @@ plots.supplyDefaults = function(gd, opts) {
513513
plots.supplyDefaultsUpdateCalc = function(oldCalcdata, newFullData) {
514514
for(var i = 0; i < newFullData.length; i++) {
515515
var newTrace = newFullData[i];
516-
var cd0 = oldCalcdata[i][0];
516+
var cd0 = (oldCalcdata[i] || [])[0];
517517
if(cd0 && cd0.trace) {
518518
var oldTrace = cd0.trace;
519519
if(oldTrace._hasCalcTransform) {

test/jasmine/tests/transition_test.js

+86
Original file line numberDiff line numberDiff line change
@@ -880,4 +880,90 @@ describe('Plotly.react transitions:', function() {
880880
.catch(failTest)
881881
.then(done);
882882
});
883+
884+
it('should preserve trace object-constancy (# of traces mismatch case)', function(done) {
885+
var data1 = [{
886+
uid: 1,
887+
x: [5, 6, 7],
888+
y: [5, 6, 7],
889+
marker: {color: 'blue', size: 10}
890+
}, {
891+
uid: 2,
892+
x: [1, 2, 3],
893+
y: [1, 2, 3],
894+
marker: {color: 'red', size: 10}
895+
}];
896+
897+
var data2 = [{
898+
uid: 1,
899+
x: [1, 2, 3],
900+
y: [1, 2, 3],
901+
marker: {color: 'blue', size: 10}
902+
}];
903+
904+
var layout = {
905+
xaxis: {range: [-1, 8]},
906+
yaxis: {range: [-1, 8]},
907+
showlegend: false,
908+
transition: {duration: 10}
909+
};
910+
911+
var traceNodes;
912+
913+
function _assertTraceNodes(msg, traceNodesOrdered, ptsXY) {
914+
var traceNodesNew = gd.querySelectorAll('.scatterlayer > .trace');
915+
expect(traceNodesNew.length).toBe(traceNodesOrdered.length, 'same # of traces - ' + msg);
916+
917+
for(var i = 0; i < traceNodesNew.length; i++) {
918+
var node = traceNodesOrdered[i];
919+
920+
expect(traceNodesNew[i]).toBe(node, 'same trace node ' + i + ' - ' + msg);
921+
922+
var pt0 = node.querySelector('.points > path');
923+
var pt0XY = Drawing.getTranslate(pt0);
924+
expect(pt0XY.x).toBeCloseTo(ptsXY[i][0], 1, 'pt' + i + ' x - ' + msg);
925+
expect(pt0XY.y).toBeCloseTo(ptsXY[i][1], 1, 'pt' + i + ' y - ' + msg);
926+
}
927+
}
928+
929+
Plotly.react(gd, data1, layout)
930+
.then(function() {
931+
methods.push([gd._fullLayout._basePlotModules[0], 'plot']);
932+
methods.push([gd._fullLayout._basePlotModules[0], 'transitionAxes2']);
933+
addSpies();
934+
935+
traceNodes = gd.querySelectorAll('.scatterlayer > .trace');
936+
_assertTraceNodes('base', traceNodes, [[360, 90], [120, 210]]);
937+
})
938+
.then(function() { return Plotly.react(gd, data2, layout); })
939+
.then(function() {
940+
var msg = 'transition into data2';
941+
assertSpies(msg, [
942+
[Plots, 'transition2', 1],
943+
[Registry, 'call', 1],
944+
[gd._fullLayout._basePlotModules[0], 'plot', 2],
945+
[gd._fullLayout._basePlotModules[0], 'transitionAxes2', 0]
946+
]);
947+
948+
// N.B. traceNodes[1] is gone, but traceNodes[0] is the same
949+
_assertTraceNodes(msg, [traceNodes[0]], [[120, 210]]);
950+
})
951+
.then(function() { return Plotly.react(gd, data1, layout); })
952+
.then(function() {
953+
var msg = 'transition back to data1';
954+
assertSpies(msg, [
955+
[Plots, 'transition2', 1],
956+
[Registry, 'call', 1],
957+
[gd._fullLayout._basePlotModules[0], 'plot', 2],
958+
[gd._fullLayout._basePlotModules[0], 'transitionAxes2', 0]
959+
]);
960+
961+
// N.B. we have a "new" traceNodes[1] here,
962+
// the old one get removed from the DOM when transitioning into data2
963+
var traceNodesNew = gd.querySelectorAll('.scatterlayer > .trace');
964+
_assertTraceNodes(msg, [traceNodes[0], traceNodesNew[1]], [[360, 90], [120, 210]]);
965+
})
966+
.catch(failTest)
967+
.then(done);
968+
});
883969
});

0 commit comments

Comments
 (0)