@@ -1397,6 +1397,383 @@ describe('axis zoom/pan and main plot zoom', function() {
1397
1397
. then ( done ) ;
1398
1398
} ) ;
1399
1399
} ) ;
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
+ } ) ;
1400
1777
} ) ;
1401
1778
1402
1779
describe ( 'Event data:' , function ( ) {
0 commit comments