Skip to content

Commit 4bd8b85

Browse files
authored
Merge pull request #7470 from my-tien/fix-edit-shapes
Fix NaN positions for shapes when dragging them.
2 parents f7c4692 + 7cce307 commit 4bd8b85

File tree

3 files changed

+300
-4
lines changed

3 files changed

+300
-4
lines changed

draftlogs/7470_fix.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
- Fix editable shapes (regression introduced in 2.34.0) [[#7470](https://github.com/plotly/plotly.js/pull/7470)],
2+
with thanks to @my-tien for the contribution!

src/components/shapes/draw_newshape/newshapes.js

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
'use strict';
22

3+
var axis_ids = require('../../../plots/cartesian/axis_ids');
4+
35
var dragHelpers = require('../../dragelement/helpers');
46
var drawMode = dragHelpers.drawMode;
57
var openMode = dragHelpers.openMode;
@@ -84,10 +86,23 @@ function newShapes(outlines, dragOptions) {
8486
case 'line':
8587
case 'rect':
8688
case 'circle':
87-
modifyItem('x0', afterEdit.x0 - (beforeEdit.x0shift || 0));
88-
modifyItem('x1', afterEdit.x1 - (beforeEdit.x1shift || 0));
89-
modifyItem('y0', afterEdit.y0 - (beforeEdit.y0shift || 0));
90-
modifyItem('y1', afterEdit.y1 - (beforeEdit.y1shift || 0));
89+
90+
var xaxis = axis_ids.getFromId(gd, beforeEdit.xref);
91+
if (beforeEdit.xref.charAt(0) === 'x' && xaxis.type.includes('category')) {
92+
modifyItem('x0', afterEdit.x0 - (beforeEdit.x0shift || 0));
93+
modifyItem('x1', afterEdit.x1 - (beforeEdit.x1shift || 0));
94+
} else {
95+
modifyItem('x0', afterEdit.x0);
96+
modifyItem('x1', afterEdit.x1);
97+
}
98+
var yaxis = axis_ids.getFromId(gd, beforeEdit.yref);
99+
if (beforeEdit.yref.charAt(0) === 'y' && yaxis.type.includes('category')) {
100+
modifyItem('y0', afterEdit.y0 - (beforeEdit.y0shift || 0));
101+
modifyItem('y1', afterEdit.y1 - (beforeEdit.y1shift || 0));
102+
} else {
103+
modifyItem('y0', afterEdit.y0);
104+
modifyItem('y1', afterEdit.y1);
105+
}
91106
break;
92107

93108
case 'path':

test/jasmine/tests/draw_newshape_test.js

Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1658,3 +1658,282 @@ describe('Activate and edit editable shapes', function() {
16581658
.then(done, done.fail);
16591659
});
16601660
});
1661+
1662+
describe('Activate and edit editable shapes - date axes', function() {
1663+
var fig = {
1664+
data: [
1665+
{
1666+
x: [
1667+
0,
1668+
50
1669+
],
1670+
y: [
1671+
0,
1672+
50
1673+
]
1674+
}
1675+
],
1676+
layout: {
1677+
width: 800,
1678+
height: 600,
1679+
margin: {
1680+
t: 100,
1681+
b: 50,
1682+
l: 100,
1683+
r: 50
1684+
},
1685+
xaxis: {
1686+
type: 'date',
1687+
range: ["1975-07-01", "2380-07-01"]
1688+
},
1689+
yaxis: {
1690+
range: [301.78041543026706, -18.694362017804156]
1691+
},
1692+
shapes: [
1693+
{
1694+
editable: true,
1695+
layer: 'below',
1696+
type: 'rect',
1697+
line: {
1698+
width: 5
1699+
},
1700+
fillcolor: 'red',
1701+
opacity: 0.5,
1702+
xref: 'xaxis',
1703+
yref: 'yaxis',
1704+
x0: '2025-01-01',
1705+
y0: 25,
1706+
x1: '2075-01-01',
1707+
y1: 75
1708+
},
1709+
{
1710+
editable: true,
1711+
layer: 'top',
1712+
type: 'circle',
1713+
line: {
1714+
width: 5
1715+
},
1716+
fillcolor: 'green',
1717+
opacity: 0.5,
1718+
xref: 'xaxis',
1719+
yref: 'yaxis',
1720+
x0: '2125-01-01',
1721+
y0: 25,
1722+
x1: '2175-01-01',
1723+
y1: 75
1724+
}
1725+
]
1726+
},
1727+
config: {
1728+
editable: false,
1729+
modeBarButtonsToAdd: [
1730+
'drawline',
1731+
'drawopenpath',
1732+
'drawclosedpath',
1733+
'drawcircle',
1734+
'drawrect',
1735+
'eraseshape'
1736+
]
1737+
}
1738+
};
1739+
1740+
var gd;
1741+
1742+
beforeEach(function() {
1743+
gd = createGraphDiv();
1744+
});
1745+
1746+
afterEach(destroyGraphDiv);
1747+
1748+
['mouse'].forEach(function(device) {
1749+
it('reactangle using ' + device, function(done) {
1750+
var i = 0; // shape index
1751+
1752+
Plotly.newPlot(gd, {
1753+
data: fig.data,
1754+
layout: fig.layout,
1755+
config: fig.config
1756+
})
1757+
1758+
// shape between 175, 160 and 255, 230
1759+
.then(function() { click(200, 160); }) // activate shape
1760+
.then(function() {
1761+
var id = gd._fullLayout._activeShapeIndex;
1762+
expect(id).toEqual(i, 'activate shape by clicking border');
1763+
1764+
var shapes = gd._fullLayout.shapes;
1765+
var obj = shapes[id]._input;
1766+
expect(obj.type).toEqual('rect');
1767+
print(obj);
1768+
assertPos({
1769+
x0: obj.x0,
1770+
y0: obj.y0,
1771+
x1: obj.x1,
1772+
y1: obj.y1
1773+
}, {
1774+
x0: '2025-01-01',
1775+
y0: 25,
1776+
x1: '2075-01-01',
1777+
y1: 75
1778+
});
1779+
})
1780+
.then(function() { drag([[255, 230], [300, 200]]); }) // move vertex
1781+
.then(function() {
1782+
var id = gd._fullLayout._activeShapeIndex;
1783+
expect(id).toEqual(i, 'keep shape active after drag corner');
1784+
1785+
var shapes = gd._fullLayout.shapes;
1786+
var obj = shapes[id]._input;
1787+
expect(obj.type).toEqual('rect');
1788+
print(obj);
1789+
assertPos({
1790+
x0: obj.x0,
1791+
y0: obj.y0,
1792+
x1: obj.x1,
1793+
y1: obj.y1
1794+
}, {
1795+
x0: '2024-12-30 20:44:36.1846',
1796+
y0: 24.997032640949552,
1797+
x1: '2103-01-15 16:20:58.3385',
1798+
y1: 53.63323442136499
1799+
});
1800+
})
1801+
.then(function() { drag([[300, 200], [255, 230]]); }) // move vertex back
1802+
.then(function() {
1803+
var id = gd._fullLayout._activeShapeIndex;
1804+
expect(id).toEqual(i, 'keep shape active after drag corner');
1805+
1806+
var shapes = gd._fullLayout.shapes;
1807+
var obj = shapes[id]._input;
1808+
expect(obj.type).toEqual('rect');
1809+
print(obj);
1810+
assertPos({
1811+
x0: obj.x0,
1812+
y0: obj.y0,
1813+
x1: obj.x1,
1814+
y1: obj.y1
1815+
}, {
1816+
x0: '2024-12-30 20:44:36.1846',
1817+
y0: 25,
1818+
x1: '2074-12-31 18:56:02.9538',
1819+
y1: 75
1820+
});
1821+
})
1822+
.then(function() { drag([[215, 195], [300, 200]]); }) // move shape
1823+
.then(function() {
1824+
var id = gd._fullLayout._activeShapeIndex;
1825+
expect(id).toEqual(i, 'keep shape active after drag corner');
1826+
1827+
var shapes = gd._fullLayout.shapes;
1828+
var obj = shapes[id]._input;
1829+
expect(obj.type).toEqual('rect');
1830+
print(obj);
1831+
assertPos({
1832+
x0: obj.x0,
1833+
y0: obj.y0,
1834+
x1: obj.x1,
1835+
y1: obj.y1
1836+
}, {
1837+
x0: '2077-12-16 18:31:40.8',
1838+
y0: 24.997032640949552,
1839+
x1: '2127-12-18 16:43:07.5692',
1840+
y1: 74.99821958456974
1841+
});
1842+
})
1843+
.then(function() { drag([[300, 200], [215, 195]]); }) // move shape back
1844+
.then(function() {
1845+
var id = gd._fullLayout._activeShapeIndex;
1846+
expect(id).toEqual(i, 'keep shape active after drag corner');
1847+
1848+
var shapes = gd._fullLayout.shapes;
1849+
var obj = shapes[id]._input;
1850+
expect(obj.type).toEqual('rect');
1851+
print(obj);
1852+
assertPos({
1853+
x0: obj.x0,
1854+
y0: obj.y0,
1855+
x1: obj.x1,
1856+
y1: obj.y1
1857+
}, {
1858+
x0: '2024-12-30 20:44:36.1846',
1859+
y0: 25,
1860+
x1: '2074-12-31 18:56:02.9538',
1861+
y1: 75
1862+
});
1863+
})
1864+
.then(function() { click(100, 100); })
1865+
.then(function() {
1866+
var id = gd._fullLayout._activeShapeIndex;
1867+
expect(id).toEqual(undefined, 'deactivate shape by clicking outside');
1868+
})
1869+
.then(function() { click(255, 230); })
1870+
.then(function() {
1871+
var id = gd._fullLayout._activeShapeIndex;
1872+
expect(id).toEqual(i, 'activate shape by clicking on corner');
1873+
})
1874+
.then(function() { click(215, 195); })
1875+
.then(function() {
1876+
var id = gd._fullLayout._activeShapeIndex;
1877+
expect(id).toEqual(undefined, 'deactivate shape by clicking inside');
1878+
})
1879+
1880+
.then(done, done.fail);
1881+
});
1882+
1883+
it('circle using ' + device, function(done) {
1884+
var i = 1; // shape index
1885+
1886+
Plotly.newPlot(gd, {
1887+
data: fig.data,
1888+
layout: fig.layout,
1889+
config: fig.config
1890+
})
1891+
1892+
// next shape
1893+
.then(function() { click(355, 225); }) // activate shape
1894+
.then(function() {
1895+
var id = gd._fullLayout._activeShapeIndex;
1896+
expect(id).toEqual(i, 'activate shape by clicking border');
1897+
1898+
var shapes = gd._fullLayout.shapes;
1899+
var obj = shapes[id]._input;
1900+
expect(obj.type).toEqual('circle');
1901+
print(obj);
1902+
assertPos({
1903+
x0: obj.x0,
1904+
y0: obj.y0,
1905+
x1: obj.x1,
1906+
y1: obj.y1
1907+
}, {
1908+
x0: '2125-01-01',
1909+
x1: '2175-01-01',
1910+
y0: 25,
1911+
y1: 75
1912+
});
1913+
})
1914+
.then(function() { drag([[338, 196], [300, 175]]); }) // move vertex
1915+
.then(function() {
1916+
var id = gd._fullLayout._activeShapeIndex;
1917+
expect(id).toEqual(i, 'keep shape active after drag corner');
1918+
1919+
var shapes = gd._fullLayout.shapes;
1920+
var obj = shapes[id]._input;
1921+
expect(obj.type).toEqual('circle');
1922+
print(obj);
1923+
assertPos({
1924+
x0: obj.x0,
1925+
y0: obj.y0,
1926+
x1: obj.x1,
1927+
y1: obj.y1
1928+
}, {
1929+
x0: '2186-11-02 07:04:22.7446',
1930+
y0: 74.99821958456971,
1931+
x1: '2113-03-01 18:44:58.3385',
1932+
y1: 10.04154302670623
1933+
});
1934+
})
1935+
1936+
.then(done, done.fail);
1937+
});
1938+
});
1939+
});

0 commit comments

Comments
 (0)