Skip to content

Commit 8fea156

Browse files
committed
add _redrag tests
- 1 extendTraces during drag case - 1 plotly_relayout callback - 1 plotly_selecting callback
1 parent 8c04a94 commit 8fea156

File tree

2 files changed

+378
-0
lines changed

2 files changed

+378
-0
lines changed

src/plots/cartesian/dragbox.js

+1
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) {
228228
}
229229

230230
// TODO should we try to "re-select" under select/lasso modes?
231+
// probably best to wait for https://github.com/plotly/plotly.js/issues/1851
231232
}
232233
};
233234
};

test/jasmine/tests/cartesian_interact_test.js

+377
Original file line numberDiff line numberDiff line change
@@ -1397,6 +1397,383 @@ describe('axis zoom/pan and main plot zoom', function() {
13971397
.then(done);
13981398
});
13991399
});
1400+
1401+
describe('redrag behavior', function() {
1402+
function _assertZoombox(msg, exp) {
1403+
var gd3 = d3.select(gd);
1404+
var zb = gd3.select('g.zoomlayer').select('.zoombox-corners');
1405+
1406+
if(zb.size()) {
1407+
expect(zb.attr('d')).toBe(exp.zoombox, msg + '| zoombox path');
1408+
} else {
1409+
expect(false).toBe(exp.zoombox, msg + '| no zoombox');
1410+
}
1411+
}
1412+
1413+
function _assertClipRect(msg, exp) {
1414+
var gd3 = d3.select(gd);
1415+
var uid = gd._fullLayout._uid;
1416+
var clipRect = gd3.select('#clip' + uid + 'xyplot > rect');
1417+
var xy = Drawing.getTranslate(clipRect);
1418+
expect(xy.x).toBeCloseTo(exp.clipTranslate[0], 2, msg + '| clip rect translate.x');
1419+
expect(xy.y).toBeCloseTo(exp.clipTranslate[1], 2, msg + '| clip rect translate.y');
1420+
}
1421+
1422+
it('should handle extendTraces redraws during drag interactions', function(done) {
1423+
var step = 500;
1424+
var interval;
1425+
var xrngPrev;
1426+
1427+
function _assert(msg, exp) {
1428+
return function() {
1429+
var fullLayout = gd._fullLayout;
1430+
1431+
expect(fullLayout.xaxis.range).toBeCloseToArray(exp.xrng === 'previous' ?
1432+
xrngPrev :
1433+
exp.xrng, 2, msg + '|xaxis range');
1434+
expect(d3.select(gd).selectAll('.point').size()).toBe(exp.nodeCnt, msg + '|pt cnt');
1435+
expect(Boolean(gd._dragdata)).toBe(exp.hasDragData, msg + '|has gd._dragdata?');
1436+
_assertZoombox(msg, exp);
1437+
_assertClipRect(msg, exp);
1438+
1439+
xrngPrev = fullLayout.xaxis.range.slice();
1440+
};
1441+
}
1442+
1443+
Plotly.plot(gd, [{y: [1, 2, 1]}], {dragmode: 'zoom'})
1444+
.then(_assert('base', {
1445+
nodeCnt: 3,
1446+
xrng: [-0.128, 2.128],
1447+
hasDragData: false,
1448+
zoombox: false,
1449+
clipTranslate: [0, 0]
1450+
}))
1451+
.then(function() {
1452+
interval = setInterval(function() {
1453+
Plotly.extendTraces(gd, { y: [[Math.random()]] }, [0]);
1454+
}, step);
1455+
})
1456+
.then(delay(1.5 * step))
1457+
.then(_assert('after 1st extendTraces trace call', {
1458+
nodeCnt: 4,
1459+
xrng: [-0.1927, 3.1927],
1460+
hasDragData: false,
1461+
zoombox: false,
1462+
clipTranslate: [0, 0]
1463+
}))
1464+
.then(function() {
1465+
var drag = makeDragFns('xy', 'nsew', 30, 0);
1466+
return drag.start()
1467+
.then(_assert('just after start of zoombox', {
1468+
nodeCnt: 4,
1469+
xrng: [-0.1927, 3.1927],
1470+
hasDragData: true,
1471+
zoombox: 'M269.5,114.5h-3v41h3ZM300.5,114.5h3v41h-3Z',
1472+
clipTranslate: [0, 0]
1473+
}))
1474+
.then(delay(step))
1475+
.then(_assert('during zoombox drag', {
1476+
nodeCnt: 5,
1477+
xrng: [-0.257, 4.257],
1478+
hasDragData: true,
1479+
zoombox: 'M269.5,114.5h-3v41h3ZM300.5,114.5h3v41h-3Z',
1480+
clipTranslate: [0, 0]
1481+
}))
1482+
.then(drag.end);
1483+
})
1484+
.then(_assert('just after zoombox drag', {
1485+
nodeCnt: 5,
1486+
xrng: [2, 2.2507],
1487+
hasDragData: false,
1488+
zoombox: false,
1489+
clipTranslate: [0, 0]
1490+
}))
1491+
.then(delay(step))
1492+
.then(function() {
1493+
return Plotly.relayout(gd, {
1494+
dragmode: 'pan',
1495+
'xaxis.autorange': true,
1496+
'yaxis.autorange': true
1497+
});
1498+
})
1499+
.then(delay(step))
1500+
.then(_assert('after extendTraces two more steps / back to autorange:true', {
1501+
nodeCnt: 7,
1502+
xrng: [-0.385, 6.385],
1503+
hasDragData: false,
1504+
zoombox: false,
1505+
clipTranslate: [0, 0]
1506+
}))
1507+
.then(function() {
1508+
var drag = makeDragFns('xy', 'nsew', 60, 0);
1509+
return drag.start()
1510+
.then(_assert('just after pan start', {
1511+
nodeCnt: 7,
1512+
xrng: [-1.137, 5.633],
1513+
hasDragData: true,
1514+
zoombox: false,
1515+
clipTranslate: [-60, 0]
1516+
}))
1517+
.then(delay(step))
1518+
.then(_assert('during pan mousedown', {
1519+
nodeCnt: 8,
1520+
xrng: [-1.327, 6.572],
1521+
hasDragData: true,
1522+
zoombox: false,
1523+
clipTranslate: [-60, 0]
1524+
}))
1525+
.then(drag.end);
1526+
})
1527+
.then(_assert('just after pan end', {
1528+
nodeCnt: 8,
1529+
// N.B same xrng as just before on dragend
1530+
xrng: 'previous',
1531+
hasDragData: false,
1532+
zoombox: false,
1533+
clipTranslate: [0, 0]
1534+
}))
1535+
.then(delay(step))
1536+
.then(_assert('last extendTraces call', {
1537+
nodeCnt: 9,
1538+
// N.B. same range as previous assert
1539+
// as now that xaxis range is set
1540+
xrng: 'previous',
1541+
hasDragData: false,
1542+
zoombox: false,
1543+
clipTranslate: [0, 0]
1544+
}))
1545+
.catch(failTest)
1546+
.then(function() { clearInterval(interval); })
1547+
.then(done);
1548+
});
1549+
1550+
it('should handle plotly_relayout callback during drag interactions', function(done) {
1551+
var step = 500;
1552+
var relayoutTracker = [];
1553+
var restyleTracker = [];
1554+
var zCnt = 0;
1555+
var xrngPrev;
1556+
var yrngPrev;
1557+
1558+
function z() {
1559+
return [[1, 2, 3], [2, (zCnt++) * 5, 1], [3, 2, 1]];
1560+
}
1561+
1562+
function _assert(msg, exp) {
1563+
return function() {
1564+
var trace = gd._fullData[0];
1565+
var fullLayout = gd._fullLayout;
1566+
1567+
expect(fullLayout.xaxis.range).toBeCloseToArray(exp.xrng === 'previous' ?
1568+
xrngPrev :
1569+
exp.xrng, 2, msg + '|xaxis range');
1570+
expect(fullLayout.yaxis.range).toBeCloseToArray(exp.yrng === 'previous' ?
1571+
yrngPrev :
1572+
exp.yrng, 2, msg + '|yaxis range');
1573+
1574+
expect(trace.zmax).toBe(exp.zmax, msg + '|zmax');
1575+
expect(Boolean(gd._dragdata)).toBe(exp.hasDragData, msg + '|has gd._dragdata?');
1576+
expect(relayoutTracker.length).toBe(exp.relayoutCnt, msg + '|relayout cnt');
1577+
expect(restyleTracker.length).toBe(exp.restyleCnt, msg + '|restyle cnt');
1578+
_assertZoombox(msg, exp);
1579+
_assertClipRect(msg, exp);
1580+
1581+
xrngPrev = fullLayout.xaxis.range.slice();
1582+
yrngPrev = fullLayout.yaxis.range.slice();
1583+
};
1584+
}
1585+
1586+
Plotly.plot(gd, [{ type: 'heatmap', z: z() }], {dragmode: 'pan'})
1587+
.then(function() {
1588+
// inspired by https://github.com/plotly/plotly.js/issues/2687<Paste>
1589+
gd.on('plotly_relayout', function(d) {
1590+
relayoutTracker.unshift(d);
1591+
setTimeout(function() {
1592+
Plotly.restyle(gd, 'z', [z()]);
1593+
}, step);
1594+
});
1595+
gd.on('plotly_restyle', function(d) {
1596+
restyleTracker.unshift(d);
1597+
});
1598+
})
1599+
.then(_assert('base', {
1600+
zmax: 3,
1601+
xrng: [-0.5, 2.5],
1602+
yrng: [-0.5, 2.5],
1603+
relayoutCnt: 0,
1604+
restyleCnt: 0,
1605+
hasDragData: false,
1606+
zoombox: false,
1607+
clipTranslate: [0, 0]
1608+
}))
1609+
.then(doDrag('xy', 'nsew', 30, 30))
1610+
.then(_assert('after drag / before update #1', {
1611+
zmax: 3,
1612+
xrng: [-0.6707, 2.329],
1613+
yrng: [-0.1666, 2.833],
1614+
relayoutCnt: 1,
1615+
restyleCnt: 0,
1616+
hasDragData: false,
1617+
zoombox: false,
1618+
clipTranslate: [0, 0]
1619+
}))
1620+
.then(delay(step + 10))
1621+
.then(_assert('after update #1', {
1622+
zmax: 5,
1623+
xrng: [-0.6707, 2.329],
1624+
yrng: [-0.1666, 2.833],
1625+
relayoutCnt: 1,
1626+
restyleCnt: 1,
1627+
hasDragData: false,
1628+
zoombox: false,
1629+
clipTranslate: [0, 0]
1630+
}))
1631+
.then(doDrag('xy', 'nsew', 30, 30))
1632+
.then(delay(step / 2))
1633+
.then(function() {
1634+
var drag = makeDragFns('xy', 'nsew', 30, 30);
1635+
return drag.start()
1636+
.then(_assert('just after pan start', {
1637+
zmax: 5,
1638+
xrng: [-1.005, 1.994],
1639+
yrng: [0.5, 3.5],
1640+
relayoutCnt: 2,
1641+
restyleCnt: 1,
1642+
hasDragData: true,
1643+
zoombox: false,
1644+
clipTranslate: [-30, -30]
1645+
}))
1646+
.then(delay(step))
1647+
.then(_assert('after update #2 / during pan mousedown', {
1648+
zmax: 10,
1649+
xrng: 'previous',
1650+
yrng: 'previous',
1651+
relayoutCnt: 2,
1652+
restyleCnt: 2,
1653+
hasDragData: true,
1654+
zoombox: false,
1655+
clipTranslate: [-30, -30]
1656+
}))
1657+
.then(drag.end);
1658+
})
1659+
.then(_assert('after pan end', {
1660+
zmax: 10,
1661+
xrng: 'previous',
1662+
yrng: 'previous',
1663+
relayoutCnt: 3,
1664+
restyleCnt: 2,
1665+
hasDragData: false,
1666+
zoombox: false,
1667+
clipTranslate: [0, 0]
1668+
}))
1669+
.then(delay(step))
1670+
.then(_assert('after update #3', {
1671+
zmax: 15,
1672+
xrng: 'previous',
1673+
yrng: 'previous',
1674+
relayoutCnt: 3,
1675+
restyleCnt: 3,
1676+
hasDragData: false,
1677+
zoombox: false,
1678+
clipTranslate: [0, 0]
1679+
}))
1680+
.catch(failTest)
1681+
.then(done);
1682+
});
1683+
1684+
it('should handle react calls in plotly_selecting callback', function(done) {
1685+
var selectingTracker = [];
1686+
var selectedTracker = [];
1687+
1688+
function _assert(msg, exp) {
1689+
return function() {
1690+
var gd3 = d3.select(gd);
1691+
1692+
expect(gd3.selectAll('.point').size()).toBe(exp.nodeCnt, msg + '|pt cnt');
1693+
expect(Boolean(gd._dragdata)).toBe(exp.hasDragData, msg + '|has gd._dragdata?');
1694+
expect(selectingTracker.length).toBe(exp.selectingCnt, msg + '| selecting cnt');
1695+
expect(selectedTracker.length).toBe(exp.selectedCnt, msg + '| selected cnt');
1696+
1697+
var outline = d3.select('.zoomlayer > .select-outline');
1698+
if(outline.size()) {
1699+
expect(outline.attr('d')).toBe(exp.selectOutline, msg + '| selection outline path');
1700+
} else {
1701+
expect(false).toBe(exp.selectOutline, msg + '| no selection outline');
1702+
}
1703+
};
1704+
}
1705+
1706+
var trace0 = {mode: 'markers', x: [1, 2, 3], y: [1, 2, 1], marker: {opacity: 0.5}};
1707+
var trace1 = {mode: 'markers', x: [], y: [], marker: {size: 20}};
1708+
1709+
var layout = {
1710+
dragmode: 'select',
1711+
showlegend: false,
1712+
width: 400,
1713+
height: 400,
1714+
margin: {l: 0, r: 0, t: 0, b: 0}
1715+
};
1716+
1717+
Plotly.plot(gd, [trace0], layout)
1718+
.then(function() {
1719+
// inspired by https://github.com/plotly/plotly.js-crossfilter.js
1720+
gd.on('plotly_selecting', function(d) {
1721+
selectingTracker.unshift(d);
1722+
1723+
if(d && d.points) {
1724+
trace1.x = d.points.map(function(p) { return trace0.x[p.pointNumber]; });
1725+
trace1.y = d.points.map(function(p) { return trace0.y[p.pointNumber]; });
1726+
} else {
1727+
trace1.x = [];
1728+
trace1.y = [];
1729+
}
1730+
1731+
Plotly.react(gd, [trace0, trace1], layout);
1732+
});
1733+
1734+
gd.on('plotly_selected', function(d) {
1735+
selectedTracker.unshift(d);
1736+
Plotly.react(gd, [trace0], layout);
1737+
});
1738+
})
1739+
.then(_assert('base', {
1740+
nodeCnt: 3,
1741+
hasDragData: false,
1742+
selectingCnt: 0,
1743+
selectedCnt: 0,
1744+
selectOutline: false
1745+
}))
1746+
.then(function() {
1747+
var drag = makeDragFns('xy', 'nsew', 200, 200, 20, 20);
1748+
return drag.start()
1749+
.then(_assert('just after pan start', {
1750+
nodeCnt: 4,
1751+
hasDragData: true,
1752+
selectingCnt: 1,
1753+
selectedCnt: 0,
1754+
selectOutline: 'M20,20L20,220L220,220L220,20L20,20Z'
1755+
}))
1756+
.then(delay(100))
1757+
.then(_assert('while holding on mouse', {
1758+
nodeCnt: 4,
1759+
hasDragData: true,
1760+
selectingCnt: 1,
1761+
selectedCnt: 0,
1762+
selectOutline: 'M20,20L20,220L220,220L220,20L20,20Z'
1763+
}))
1764+
.then(drag.end);
1765+
})
1766+
.then(_assert('after drag', {
1767+
nodeCnt: 3,
1768+
hasDragData: false,
1769+
selectingCnt: 1,
1770+
selectedCnt: 1,
1771+
selectOutline: false
1772+
}))
1773+
.catch(failTest)
1774+
.then(done);
1775+
});
1776+
});
14001777
});
14011778

14021779
describe('Event data:', function() {

0 commit comments

Comments
 (0)