@@ -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
14021779describe ( 'Event data:' , function ( ) {
0 commit comments