Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit eb728fe

Browse files
committed
fix and 🔒 editable: true uirevisions
1 parent 8f0f075 commit eb728fe

File tree

4 files changed

+188
-22
lines changed

4 files changed

+188
-22
lines changed

src/plot_api/plot_api.js

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2377,7 +2377,7 @@ var layoutUIControlPatterns = [
23772377
{pattern: /^(geo\d*)\.(projection|center)/},
23782378
{pattern: /^(ternary\d*\.[abc]axis)\.(min|title)$/},
23792379
{pattern: /^(polar\d*\.radialaxis)\.(auto)?range/, autofill: true},
2380-
{pattern: /^(polar\d*\.radialaxis)\.angle/},
2380+
{pattern: /^(polar\d*\.radialaxis)\.(angle|title)/},
23812381
{pattern: /^(polar\d*\.angularaxis)\.rotation/},
23822382
{pattern: /^(mapbox\d*)\.(center|zoom|bearing|pitch)/},
23832383

@@ -2449,6 +2449,18 @@ function getTraceIndexFromUid(uid, data, tracei) {
24492449
return data[tracei].uid ? -1 : tracei;
24502450
}
24512451

2452+
function valsMatch(v1, v2) {
2453+
var v1IsObj = Lib.isPlainObject(v1);
2454+
var v1IsArray = Array.isArray(v1);
2455+
if(v1IsObj || v1IsArray) {
2456+
return (
2457+
(v1IsObj && Lib.isPlainObject(v2)) ||
2458+
(v1IsArray && Array.isArray(v2))
2459+
) && JSON.stringify(v1) === JSON.stringify(v2);
2460+
}
2461+
return v1 === v2;
2462+
}
2463+
24522464
function applyUIRevisions(data, layout, oldFullData, oldFullLayout) {
24532465
var layoutPreGUI = oldFullLayout._preGUI;
24542466
var key, revAttr, oldRev, newRev, match, preGUIVal, newNP, newVal;
@@ -2473,7 +2485,7 @@ function applyUIRevisions(data, layout, oldFullData, oldFullLayout) {
24732485
// storing *that* in preGUI... oh well, for now at least I limit
24742486
// this to attributes that get autofilled, which AFAICT among
24752487
// the GUI-editable attributes is just axis.range/autorange.
2476-
if(newVal === preGUIVal || (match.autofill && newVal === undefined)) {
2488+
if(valsMatch(newVal, preGUIVal) || (match.autofill && newVal === undefined)) {
24772489
newNP.set(nestedProperty(oldFullLayout, key).get());
24782490
continue;
24792491
}
@@ -2538,7 +2550,7 @@ function applyUIRevisions(data, layout, oldFullData, oldFullLayout) {
25382550
if(preGUIVal === null) preGUIVal = undefined;
25392551
newNP = nestedProperty(newTrace, key);
25402552
newVal = newNP.get();
2541-
if(newVal === preGUIVal || (match.autofill && newVal === undefined)) {
2553+
if(valsMatch(newVal, preGUIVal) || (match.autofill && newVal === undefined)) {
25422554
newNP.set(nestedProperty(fullInput, key).get());
25432555
continue;
25442556
}

src/traces/parcoords/plot.js

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88

99
'use strict';
1010

11-
var Registry = require('../../registry');
12-
1311
var parcoords = require('./parcoords');
1412
var prepareRegl = require('../../lib/prepare_regl');
1513

@@ -44,27 +42,33 @@ module.exports = function plot(gd, cdparcoords) {
4442

4543
var gdDimension = gdDimensionsOriginalOrder[i][originalDimensionIndex];
4644
var newConstraints = newRanges.map(function(r) { return r.slice(); });
45+
46+
// Store constraint range in preGUI
47+
// This one doesn't work if it's stored in pieces in _storeDirectGUIEdit
48+
// because it's an array of variable dimensionality. So store the whole
49+
// thing at once manually.
50+
var aStr = 'dimensions[' + originalDimensionIndex + '].constraintrange';
51+
var preGUI = fullLayout._tracePreGUI[gd._fullData[fullIndices[i]]._fullInput.uid];
52+
if(preGUI[aStr] === undefined) {
53+
var initialVal = gdDimension.constraintrange;
54+
preGUI[aStr] = initialVal || null;
55+
}
56+
57+
var fullDimension = gd._fullData[fullIndices[i]].dimensions[originalDimensionIndex];
58+
4759
if(!newConstraints.length) {
4860
delete gdDimension.constraintrange;
61+
delete fullDimension.constraintrange;
4962
newConstraints = null;
5063
}
5164
else {
5265
if(newConstraints.length === 1) newConstraints = newConstraints[0];
5366
gdDimension.constraintrange = newConstraints;
67+
fullDimension.constraintrange = newConstraints.slice();
5468
// wrap in another array for restyle event data
5569
newConstraints = [newConstraints];
5670
}
5771

58-
var aStr = 'dimensions[' + originalDimensionIndex + '].constraintrange';
59-
60-
var editData = {};
61-
editData[aStr] = newConstraints && newConstraints[0];
62-
Registry.call('_storeDirectGUIEdit',
63-
gd.data[inputIndices[i]],
64-
fullLayout._tracePreGUI[gd._fullData[fullIndices[i]]._fullInput.uid],
65-
editData
66-
);
67-
6872
var restyleData = {};
6973
restyleData[aStr] = newConstraints;
7074
gd.emit('plotly_restyle', [restyleData, [inputIndices[i]]]);

test/jasmine/assets/drag.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ var getNodeCoords = require('./get_node_coords');
77
* optionally specify an edge ('n', 'se', 'w' etc)
88
* to grab it by an edge or corner (otherwise the middle is used)
99
*/
10-
function drag(node, dx, dy, edge, x0, y0, nsteps) {
10+
function drag(node, dx, dy, edge, x0, y0, nsteps, noCover) {
1111
nsteps = nsteps || 1;
1212

1313
var coords = getNodeCoords(node, edge);
@@ -17,7 +17,8 @@ function drag(node, dx, dy, edge, x0, y0, nsteps) {
1717
mouseEvent('mousemove', fromX, fromY, {element: node});
1818
mouseEvent('mousedown', fromX, fromY, {element: node});
1919

20-
var promise = waitForDragCover().then(function(dragCoverNode) {
20+
var promise = (noCover ? Promise.resolve(node) : waitForDragCover())
21+
.then(function(dragCoverNode) {
2122
var toX;
2223
var toY;
2324

@@ -28,7 +29,7 @@ function drag(node, dx, dy, edge, x0, y0, nsteps) {
2829
}
2930

3031
mouseEvent('mouseup', toX, toY, {element: dragCoverNode});
31-
return waitForDragCoverRemoval();
32+
return noCover || waitForDragCoverRemoval();
3233
});
3334

3435
return promise;

test/jasmine/tests/plot_api_react_test.js

Lines changed: 153 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -900,6 +900,20 @@ describe('Plotly.react and uirevision attributes', function() {
900900

901901
afterEach(destroyGraphDiv);
902902

903+
function checkCloseIfArray(val1, val2, msg) {
904+
if(Array.isArray(val1) && Array.isArray(val2)) {
905+
if(Array.isArray(val1[0]) && Array.isArray(val2[0])) {
906+
expect(val1).toBeCloseTo2DArray(val2, 2, msg);
907+
}
908+
else {
909+
expect(val1).toBeCloseToArray(val2, 2, msg);
910+
}
911+
}
912+
else {
913+
expect(val1).toBe(val2, msg);
914+
}
915+
}
916+
903917
function checkState(dataKeys, layoutKeys, msg) {
904918
var np = Lib.nestedProperty;
905919
return function() {
@@ -913,17 +927,19 @@ describe('Plotly.react and uirevision attributes', function() {
913927
var val = traceKeys[key];
914928
var valIn = Array.isArray(val) ? val[0] : val;
915929
var valOut = Array.isArray(val) ? val[val.length - 1] : val;
916-
expect(np(trace, key).get()).toEqual(valIn, msg + ': data[' + i + '].' + key);
917-
expect(np(fullTrace, key).get()).toEqual(valOut, msg + ': _fullData[' + i + '].' + key);
930+
checkCloseIfArray(np(trace, key).get(), valIn, msg + ': data[' + i + '].' + key);
931+
checkCloseIfArray(np(fullTrace, key).get(), valOut, msg + ': _fullData[' + i + '].' + key);
932+
checkCloseIfArray(np(trace, key).get(), valIn, msg + ': data[' + i + '].' + key);
933+
checkCloseIfArray(np(fullTrace, key).get(), valOut, msg + ': _fullData[' + i + '].' + key);
918934
}
919935
});
920936

921937
for(var key in (layoutKeys || {})) {
922938
var val = layoutKeys[key];
923939
var valIn = Array.isArray(val) ? val[0] : val;
924940
var valOut = Array.isArray(val) ? val[val.length - 1] : val;
925-
expect(np(gd.layout, key).get()).toEqual(valIn, msg + ': layout.' + key);
926-
expect(np(gd._fullLayout, key).get()).toEqual(valOut, msg + ': _fullLayout.' + key);
941+
checkCloseIfArray(np(gd.layout, key).get(), valIn, msg + ': layout.' + key);
942+
checkCloseIfArray(np(gd._fullLayout, key).get(), valOut, msg + ': _fullLayout.' + key);
927943
}
928944
};
929945
}
@@ -1499,4 +1515,137 @@ describe('Plotly.react and uirevision attributes', function() {
14991515

15001516
_run(fig, editComponents, checkInitial, checkEdited).then(done);
15011517
});
1518+
1519+
it('preserves editable: true plot title and legend & colorbar positions using editrevision', function(done) {
1520+
function fig(mainRev, editRev) {
1521+
return {
1522+
data: [{y: [1, 2]}, {y: [2, 1]}, {z: [[1, 2], [3, 4]], type: 'heatmap'}],
1523+
layout: {
1524+
uirevision: mainRev,
1525+
editrevision: editRev
1526+
},
1527+
config: {editable: true}
1528+
};
1529+
}
1530+
1531+
function editEditable() {
1532+
return Registry.call('_guiUpdate', gd,
1533+
{'colorbar.x': 0.8, 'colorbar.y': 0.6},
1534+
{title: 'yep', 'legend.x': 1.1, 'legend.y': 0.9},
1535+
[2]
1536+
);
1537+
}
1538+
1539+
function checkAttrs(original) {
1540+
return checkState([{}, {}, {
1541+
'colorbar.x': original ? [undefined, 1.02] : 0.8,
1542+
'colorbar.y': original ? [undefined, 0.5] : 0.6
1543+
}], {
1544+
title: original ? [undefined, 'Click to enter Plot title'] : 'yep',
1545+
'legend.x': original ? [undefined, 1.02] : 1.1,
1546+
'legend.y': original ? [undefined, 1] : 0.9
1547+
});
1548+
}
1549+
1550+
_run(fig, editEditable, checkAttrs(true), checkAttrs).then(done);
1551+
});
1552+
1553+
it('preserves editable: true name, colorbar title and parcoords constraint range via trace.uirevision', function(done) {
1554+
function fig(mainRev, traceRev) {
1555+
return {
1556+
data: [{
1557+
type: 'parcoords',
1558+
dimensions: [
1559+
{label: 'a', values: [1, 2, 3, 5], constraintrange: [2.5, 3.5]},
1560+
{label: 'b', values: [7, 9, 5, 6]}
1561+
],
1562+
line: {showscale: true, color: [1, 2, 3, 4]},
1563+
uirevision: traceRev
1564+
}],
1565+
layout: {
1566+
uirevision: mainRev,
1567+
width: 400,
1568+
height: 400,
1569+
margin: {l: 100, r: 100, t: 100, b: 100}
1570+
},
1571+
config: {editable: true}
1572+
};
1573+
}
1574+
1575+
function attrs(original) {
1576+
return {
1577+
'dimensions[0].constraintrange': original ? [[2.5, 3.5]] : [[[1.5, 2.5], [2.938, 3.979]]],
1578+
'dimensions[1].constraintrange': original ? undefined : [[6.937, 7.979]],
1579+
'line.colorbar.title': original ? [undefined, 'Click to enter Colorscale title'] : 'color',
1580+
name: original ? [undefined, 'trace 0'] : 'name'
1581+
};
1582+
}
1583+
1584+
function axisDragNode(i) {
1585+
return document.querySelectorAll('.axis-brush .background')[i];
1586+
}
1587+
1588+
function editTrace() {
1589+
var _;
1590+
return Registry.call('_guiRestyle', gd,
1591+
{'line.colorbar.title': 'color', name: 'name'},
1592+
[0]
1593+
)
1594+
.then(function() {
1595+
return drag(axisDragNode(0), 0, 50, _, _, _, _, true);
1596+
})
1597+
.then(function() {
1598+
return drag(axisDragNode(0), 0, -50, _, _, _, _, true);
1599+
})
1600+
.then(function() {
1601+
return drag(axisDragNode(1), 0, -50, _, _, _, _, true);
1602+
});
1603+
}
1604+
1605+
_run(fig, editTrace, checkState([attrs(true)]), checkState([attrs()])).then(done);
1606+
});
1607+
1608+
it('preserves editable: true axis titles using the axis uirevisions', function(done) {
1609+
function fig(mainRev, axRev) {
1610+
return {
1611+
data: [
1612+
{y: [1, 2]},
1613+
{a: [1, 2], b: [2, 1], c: [1, 1], type: 'scatterternary'},
1614+
{r: [1, 2], theta: [1, 2], type: 'scatterpolar'}
1615+
],
1616+
layout: {
1617+
uirevision: mainRev,
1618+
xaxis: {uirevision: axRev},
1619+
yaxis: {uirevision: axRev},
1620+
ternary: {
1621+
aaxis: {uirevision: axRev},
1622+
baxis: {uirevision: axRev},
1623+
caxis: {uirevision: axRev}
1624+
},
1625+
polar: {radialaxis: {uirevision: axRev}}
1626+
},
1627+
config: {editable: true}
1628+
};
1629+
}
1630+
1631+
function attrs(original) {
1632+
return {
1633+
'xaxis.title': original ? [undefined, 'Click to enter X axis title'] : 'XXX',
1634+
'yaxis.title': original ? [undefined, 'Click to enter Y axis title'] : 'YYY',
1635+
'ternary.aaxis.title': original ? [undefined, 'Component A'] : 'AAA',
1636+
'ternary.baxis.title': original ? [undefined, 'Component B'] : 'BBB',
1637+
'ternary.caxis.title': original ? [undefined, 'Component C'] : 'CCC',
1638+
'polar.radialaxis.title': original ? [undefined, ''] : 'RRR'
1639+
};
1640+
}
1641+
1642+
function editComponents() {
1643+
return Registry.call('_guiRelayout', gd, attrs());
1644+
}
1645+
1646+
var checkInitial = checkState([], attrs(true));
1647+
var checkEdited = checkState([], attrs());
1648+
1649+
_run(fig, editComponents, checkInitial, checkEdited).then(done);
1650+
});
15021651
});

0 commit comments

Comments
 (0)