Skip to content

Commit 7d3b93e

Browse files
authored
Merge pull request #6046 from plotly/uirevision-autorange-fix
fix uirevision autorange bug
2 parents 5144ab6 + 84a2574 commit 7d3b93e

File tree

3 files changed

+95
-28
lines changed

3 files changed

+95
-28
lines changed

draftlogs/6046_fix.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Fix interaction between `uirevision` and `autorange`. Because we push `autorange` and `range` back into `layout`, there can be times it looks like we're applying GUI-driven changes on top of explicit autorange and other times it's an implicit autorange, even though the user's intent was always implicit. This fix treats them as equivalent. [[#6046](https://github.com/plotly/plotly.js/pull/6046)]

src/plot_api/plot_api.js

+37-8
Original file line numberDiff line numberDiff line change
@@ -2396,7 +2396,8 @@ function findUIPattern(key, patternSpecs) {
23962396
var spec = patternSpecs[i];
23972397
var match = key.match(spec.pattern);
23982398
if(match) {
2399-
return {head: match[1], attr: spec.attr};
2399+
var head = match[1] || '';
2400+
return {head: head, tail: key.substr(head.length + 1), attr: spec.attr};
24002401
}
24012402
}
24022403
}
@@ -2448,26 +2449,54 @@ function valsMatch(v1, v2) {
24482449

24492450
function applyUIRevisions(data, layout, oldFullData, oldFullLayout) {
24502451
var layoutPreGUI = oldFullLayout._preGUI;
2451-
var key, revAttr, oldRev, newRev, match, preGUIVal, newNP, newVal;
2452+
var key, revAttr, oldRev, newRev, match, preGUIVal, newNP, newVal, head, tail;
24522453
var bothInheritAutorange = [];
2454+
var newAutorangeIn = {};
24532455
var newRangeAccepted = {};
24542456
for(key in layoutPreGUI) {
24552457
match = findUIPattern(key, layoutUIControlPatterns);
24562458
if(match) {
2457-
revAttr = match.attr || (match.head + '.uirevision');
2459+
head = match.head;
2460+
tail = match.tail;
2461+
revAttr = match.attr || (head + '.uirevision');
24582462
oldRev = nestedProperty(oldFullLayout, revAttr).get();
24592463
newRev = oldRev && getNewRev(revAttr, layout);
2464+
24602465
if(newRev && (newRev === oldRev)) {
24612466
preGUIVal = layoutPreGUI[key];
24622467
if(preGUIVal === null) preGUIVal = undefined;
24632468
newNP = nestedProperty(layout, key);
24642469
newVal = newNP.get();
2470+
24652471
if(valsMatch(newVal, preGUIVal)) {
2466-
if(newVal === undefined && key.substr(key.length - 9) === 'autorange') {
2467-
bothInheritAutorange.push(key.substr(0, key.length - 10));
2472+
if(newVal === undefined && tail === 'autorange') {
2473+
bothInheritAutorange.push(head);
24682474
}
24692475
newNP.set(undefinedToNull(nestedProperty(oldFullLayout, key).get()));
24702476
continue;
2477+
} else if(tail === 'autorange' || tail.substr(0, 6) === 'range[') {
2478+
// Special case for (auto)range since we push it back into the layout
2479+
// so all null should be treated equivalently to autorange: true with any range
2480+
var pre0 = layoutPreGUI[head + '.range[0]'];
2481+
var pre1 = layoutPreGUI[head + '.range[1]'];
2482+
var preAuto = layoutPreGUI[head + '.autorange'];
2483+
if(preAuto || (preAuto === null && pre0 === null && pre1 === null)) {
2484+
// Only read the input layout once and stash the result,
2485+
// so we get it before we start modifying it
2486+
if(!(head in newAutorangeIn)) {
2487+
var newContainer = nestedProperty(layout, head).get();
2488+
newAutorangeIn[head] = newContainer && (
2489+
newContainer.autorange ||
2490+
(newContainer.autorange !== false && (
2491+
!newContainer.range || newContainer.range.length !== 2)
2492+
)
2493+
);
2494+
}
2495+
if(newAutorangeIn[head]) {
2496+
newNP.set(undefinedToNull(nestedProperty(oldFullLayout, key).get()));
2497+
continue;
2498+
}
2499+
}
24712500
}
24722501
}
24732502
} else {
@@ -2478,12 +2507,12 @@ function applyUIRevisions(data, layout, oldFullData, oldFullLayout) {
24782507
// so remove it from _preGUI for next time.
24792508
delete layoutPreGUI[key];
24802509

2481-
if(key.substr(key.length - 8, 6) === 'range[') {
2482-
newRangeAccepted[key.substr(0, key.length - 9)] = 1;
2510+
if(match && match.tail.substr(0, 6) === 'range[') {
2511+
newRangeAccepted[match.head] = 1;
24832512
}
24842513
}
24852514

2486-
// Special logic for `autorange`, since it interacts with `range`:
2515+
// More special logic for `autorange`, since it interacts with `range`:
24872516
// If the new figure's matching `range` was kept, and `autorange`
24882517
// wasn't supplied explicitly in either the original or the new figure,
24892518
// we shouldn't alter that - but we may just have done that, so fix it.

test/jasmine/tests/plot_api_react_test.js

+57-20
Original file line numberDiff line numberDiff line change
@@ -1366,6 +1366,59 @@ describe('Plotly.react and uirevision attributes', function() {
13661366
.then(done, done.fail);
13671367
});
13681368

1369+
function setCartesianRanges(xRange, yRange) {
1370+
return function() {
1371+
return Registry.call('_guiRelayout', gd, {
1372+
'xaxis.range': xRange,
1373+
'yaxis.range': yRange
1374+
});
1375+
};
1376+
}
1377+
1378+
function checkCartesianRanges(xRange, yRange, msg) {
1379+
return checkState([], {
1380+
'xaxis.range': [xRange],
1381+
'yaxis.range': [yRange]
1382+
}, msg);
1383+
}
1384+
1385+
it('treats explicit and implicit cartesian autorange the same', function(done) {
1386+
function fig(explicit, uirevision) {
1387+
return {
1388+
data: [{z: [[1, 2], [3, 4]], type: 'heatmap', x: [0, 1, 2], y: [3, 4, 5]}],
1389+
layout: {
1390+
xaxis: explicit ? {autorange: true, range: [0, 2]} : {},
1391+
yaxis: explicit ? {autorange: true, range: [3, 5]} : {},
1392+
uirevision: uirevision
1393+
}
1394+
};
1395+
}
1396+
1397+
// First go from implicit to explicit and back after zooming in
1398+
Plotly.newPlot(gd, fig(false, 'a'))
1399+
.then(checkCartesianRanges([0, 2], [3, 5], 'initial implicit'))
1400+
.then(setCartesianRanges([2, 4], [5, 7]))
1401+
.then(checkCartesianRanges([2, 4], [5, 7], 'zoomed from implicit'))
1402+
.then(_react(fig(true, 'a')))
1403+
.then(checkCartesianRanges([2, 4], [5, 7], 'react to explicit'))
1404+
.then(_react(fig(true, 'a')))
1405+
.then(checkCartesianRanges([2, 4], [5, 7], 'react to STAY explicit'))
1406+
.then(_react(fig(false, 'a')))
1407+
.then(checkCartesianRanges([2, 4], [5, 7], 'back to implicit'))
1408+
// then go from explicit to implicit and back after zooming in
1409+
.then(_react(fig(true, 'b')))
1410+
.then(checkCartesianRanges([0, 2], [3, 5], 'new uirevision explicit'))
1411+
.then(setCartesianRanges([4, 6], [7, 9]))
1412+
.then(checkCartesianRanges([4, 6], [7, 9], 'zoomed from explicit'))
1413+
.then(_react(fig(false, 'b')))
1414+
.then(checkCartesianRanges([4, 6], [7, 9], 'react to implicit'))
1415+
.then(_react(fig(false, 'b')))
1416+
.then(checkCartesianRanges([4, 6], [7, 9], 'react to STAY implicit'))
1417+
.then(_react(fig(true, 'b')))
1418+
.then(checkCartesianRanges([4, 6], [7, 9], 'back to explicit'))
1419+
.then(done, done.fail);
1420+
});
1421+
13691422
it('respects reverting an explicit cartesian axis range to auto', function(done) {
13701423
function fig(xRange, yRange) {
13711424
return {
@@ -1378,28 +1431,12 @@ describe('Plotly.react and uirevision attributes', function() {
13781431
};
13791432
}
13801433

1381-
function setRanges(xRange, yRange) {
1382-
return function() {
1383-
return Registry.call('_guiRelayout', gd, {
1384-
'xaxis.range': xRange,
1385-
'yaxis.range': yRange
1386-
});
1387-
};
1388-
}
1389-
1390-
function checkRanges(xRange, yRange) {
1391-
return checkState([], {
1392-
'xaxis.range': [xRange],
1393-
'yaxis.range': [yRange]
1394-
});
1395-
}
1396-
13971434
Plotly.newPlot(gd, fig([1, 3], [4, 6]))
1398-
.then(checkRanges([1, 3], [4, 6]))
1399-
.then(setRanges([2, 4], [5, 7]))
1400-
.then(checkRanges([2, 4], [5, 7]))
1435+
.then(checkCartesianRanges([1, 3], [4, 6], 'initial explicit ranges'))
1436+
.then(setCartesianRanges([2, 4], [5, 7]))
1437+
.then(checkCartesianRanges([2, 4], [5, 7], 'zoomed to different explicit'))
14011438
.then(_react(fig(undefined, undefined)))
1402-
.then(checkRanges([0, 2], [3, 5]))
1439+
.then(checkCartesianRanges([0, 2], [3, 5], 'react to autorange'))
14031440
.then(done, done.fail);
14041441
});
14051442

0 commit comments

Comments
 (0)