@@ -1662,3 +1662,275 @@ describe('Test shapes', function() {
16621662 return coordinates ;
16631663 }
16641664} ) ;
1665+
1666+ describe ( 'Test multi-axis shapes' , function ( ) {
1667+ 'use strict' ;
1668+
1669+ var gd ;
1670+
1671+ beforeEach ( function ( ) { gd = createGraphDiv ( ) ; } ) ;
1672+ afterEach ( destroyGraphDiv ) ;
1673+
1674+ function getShape ( index ) {
1675+ var s = d3SelectAll ( '.shapelayer path[data-index="' + index + '"]' ) ;
1676+ expect ( s . size ( ) ) . toBe ( 1 ) ;
1677+ return s ;
1678+ }
1679+
1680+ function getBoundingBox ( index ) {
1681+ var node = getShape ( index ) . node ( ) ;
1682+ return node ? node . getBoundingClientRect ( ) : null ;
1683+ }
1684+
1685+ it ( 'renders all shape types with array xref and yref values' , function ( done ) {
1686+ Plotly . newPlot ( gd , [
1687+ { x : [ 1 , 2 ] , y : [ 1 , 2 ] } ,
1688+ { x : [ 1 , 2 ] , y : [ 1 , 2 ] , xaxis : 'x2' , yaxis : 'y2' }
1689+ ] , {
1690+ xaxis : { domain : [ 0 , 0.45 ] } ,
1691+ yaxis : { domain : [ 0 , 0.45 ] } ,
1692+ xaxis2 : { domain : [ 0.55 , 1 ] , anchor : 'y2' } ,
1693+ yaxis2 : { domain : [ 0.55 , 1 ] , anchor : 'x2' } ,
1694+ shapes : [
1695+ { type : 'line' , xref : [ 'x' , 'x2' ] , yref : [ 'y' , 'y2' ] , x0 : 1 , x1 : 2 , y0 : 1 , y1 : 2 } ,
1696+ { type : 'rect' , xref : [ 'x' , 'x2' ] , yref : [ 'y' , 'y2' ] , x0 : 1.5 , x1 : 1.5 , y0 : 1.5 , y1 : 1.5 } ,
1697+ { type : 'circle' , xref : [ 'x' , 'x2' ] , yref : [ 'y' , 'y2' ] , x0 : 1 , x1 : 2 , y0 : 1 , y1 : 2 } ,
1698+ { type : 'path' , xref : [ 'x' , 'x2' ] , yref : [ 'y' , 'y2' ] , path : 'M1,1L2,2' }
1699+ ]
1700+ } ) . then ( function ( ) {
1701+ expect ( getShape ( 0 ) ) . not . toBe ( null ) ;
1702+ expect ( getShape ( 1 ) ) . not . toBe ( null ) ;
1703+ expect ( getShape ( 2 ) ) . not . toBe ( null ) ;
1704+ expect ( getShape ( 3 ) ) . not . toBe ( null ) ;
1705+ } )
1706+ . then ( done , done . fail ) ;
1707+ } ) ;
1708+
1709+ it ( 'positions shapes correctly with side-by-side subplots' , function ( done ) {
1710+ Plotly . newPlot ( gd , [
1711+ { x : [ 1 , 2 ] , y : [ 1 , 2 ] } ,
1712+ { x : [ 1 , 2 ] , y : [ 1 , 2 ] , xaxis : 'x2' , yaxis : 'y2' }
1713+ ] , {
1714+ width : 800 , height : 400 ,
1715+ xaxis : { domain : [ 0 , 0.45 ] } ,
1716+ yaxis : { domain : [ 0 , 0.45 ] } ,
1717+ xaxis2 : { domain : [ 0.55 , 1 ] , anchor : 'y2' } ,
1718+ yaxis2 : { domain : [ 0.55 , 1 ] , anchor : 'x2' } ,
1719+ shapes : [
1720+ { type : 'line' , xref : [ 'x' , 'x2' ] , yref : [ 'y' , 'y2' ] , x0 : 1 , x1 : 2 , y0 : 1 , y1 : 2 } ,
1721+ { type : 'rect' , xref : [ 'x' , 'x2' ] , yref : [ 'y' , 'y2' ] , x0 : 1.5 , x1 : 1.5 , y0 : 1.5 , y1 : 1.5 } ,
1722+ { type : 'circle' , xref : [ 'x' , 'x2' ] , yref : [ 'y' , 'y2' ] , x0 : 1 , x1 : 2 , y0 : 1 , y1 : 2 } ,
1723+ { type : 'path' , xref : [ 'x' , 'x2' ] , yref : [ 'y' , 'y2' ] , path : 'M1,1L2,2' }
1724+ ]
1725+ } ) . then ( function ( ) {
1726+ var xa = gd . _fullLayout . xaxis ;
1727+ var xa2 = gd . _fullLayout . xaxis2 ;
1728+
1729+ var lineExpectedLeft = xa . l2p ( 1 ) + xa . _offset ;
1730+ var lineExpectedRight = xa2 . l2p ( 2 ) + xa2 . _offset ;
1731+ var lineBBox = getBoundingBox ( 0 ) ;
1732+
1733+ expect ( lineBBox . left ) . toBeCloseTo ( lineExpectedLeft ) ;
1734+ expect ( lineBBox . right ) . toBeCloseTo ( lineExpectedRight ) ;
1735+
1736+ var rectExpectedLeft = xa . l2p ( 1.5 ) + xa . _offset ;
1737+ var rectExpectedRight = xa2 . l2p ( 1.5 ) + xa2 . _offset ;
1738+ var rectBBox = getBoundingBox ( 1 ) ;
1739+
1740+ expect ( rectBBox . left ) . toBeCloseTo ( rectExpectedLeft ) ;
1741+ expect ( rectBBox . right ) . toBeCloseTo ( rectExpectedRight ) ;
1742+
1743+ var circleExpectedLeft = xa . l2p ( 1 ) + xa . _offset ;
1744+ var circleExpectedRight = xa2 . l2p ( 2 ) + xa2 . _offset ;
1745+ var circleBBox = getBoundingBox ( 2 ) ;
1746+
1747+ expect ( circleBBox . left ) . toBeCloseTo ( circleExpectedLeft ) ;
1748+ expect ( circleBBox . right ) . toBeCloseTo ( circleExpectedRight ) ;
1749+
1750+ var pathExpectedLeft = xa . l2p ( 1 ) + xa . _offset ;
1751+ var pathExpectedRight = xa2 . l2p ( 2 ) + xa2 . _offset ;
1752+ var pathBBox = getBoundingBox ( 3 ) ;
1753+
1754+ expect ( pathBBox . left ) . toBeCloseTo ( pathExpectedLeft ) ;
1755+ expect ( pathBBox . right ) . toBeCloseTo ( pathExpectedRight ) ;
1756+ } )
1757+ . then ( done , done . fail ) ;
1758+ } ) ;
1759+
1760+ it ( 'positions shapes correctly with overlaid axes' , function ( done ) {
1761+ Plotly . newPlot ( gd , [
1762+ { x : [ 1 , 2 ] , y : [ 1 , 2 ] } ,
1763+ { x : [ 1 , 2 ] , y : [ 50 , 100 ] , yaxis : 'y2' }
1764+ ] , {
1765+ width : 600 , height : 400 ,
1766+ yaxis : { range : [ 0 , 10 ] } ,
1767+ yaxis2 : { overlaying : 'y' , side : 'right' , range : [ 0 , 100 ] } ,
1768+ shapes : [
1769+ { type : 'line' , xref : 'x' , yref : [ 'y' , 'y2' ] , x0 : 1 , x1 : 2 , y0 : 1 , y1 : 20 } ,
1770+ { type : 'rect' , xref : 'x' , yref : [ 'y' , 'y2' ] , x0 : 1.5 , x1 : 1.5 , y0 : 1.5 , y1 : 50 } ,
1771+ { type : 'circle' , xref : 'x' , yref : [ 'y' , 'y2' ] , x0 : 1 , x1 : 2 , y0 : 1 , y1 : 65 } ,
1772+ { type : 'path' , xref : 'x' , yref : [ 'y' , 'y2' ] , path : 'M1,1L2,90' } ]
1773+ } ) . then ( function ( ) {
1774+ var ya = gd . _fullLayout . yaxis ;
1775+ var ya2 = gd . _fullLayout . yaxis2 ;
1776+
1777+ var lineExpectedBottom = ya . l2p ( 1 ) + ya . _offset ;
1778+ var lineExpectedTop = ya2 . l2p ( 20 ) + ya2 . _offset ;
1779+ var lineBBox = getBoundingBox ( 0 ) ;
1780+
1781+ expect ( lineBBox . bottom ) . toBeCloseTo ( lineExpectedBottom ) ;
1782+ expect ( lineBBox . top ) . toBeCloseTo ( lineExpectedTop ) ;
1783+
1784+ var rectExpectedBottom = ya . l2p ( 1.5 ) + ya . _offset ;
1785+ var rectExpectedTop = ya2 . l2p ( 50 ) + ya2 . _offset ;
1786+ var rectBBox = getBoundingBox ( 1 ) ;
1787+
1788+ expect ( rectBBox . bottom ) . toBeCloseTo ( rectExpectedBottom ) ;
1789+ expect ( rectBBox . top ) . toBeCloseTo ( rectExpectedTop ) ;
1790+
1791+ var circleExpectedBottom = ya . l2p ( 1 ) + ya . _offset ;
1792+ var circleExpectedTop = ya2 . l2p ( 65 ) + ya2 . _offset ;
1793+ var circleBBox = getBoundingBox ( 2 ) ;
1794+
1795+ expect ( circleBBox . bottom ) . toBeCloseTo ( circleExpectedBottom ) ;
1796+ expect ( circleBBox . top ) . toBeCloseTo ( circleExpectedTop ) ;
1797+
1798+ var pathExpectedBottom = ya . l2p ( 1 ) + ya . _offset ;
1799+ var pathExpectedTop = ya2 . l2p ( 90 ) + ya2 . _offset ;
1800+ var pathBBox = getBoundingBox ( 3 ) ;
1801+
1802+ expect ( pathBBox . bottom ) . toBeCloseTo ( pathExpectedBottom ) ;
1803+ expect ( pathBBox . top ) . toBeCloseTo ( pathExpectedTop ) ;
1804+ } )
1805+ . then ( done , done . fail ) ;
1806+ } ) ;
1807+
1808+ it ( 'adjusts shape position when one referenced axis is zoomed' , function ( done ) {
1809+ Plotly . newPlot ( gd , [
1810+ { x : [ 0 , 4 ] , y : [ 0 , 4 ] } ,
1811+ { x : [ 0 , 4 ] , y : [ 0 , 4 ] , xaxis : 'x2' , yaxis : 'y2' }
1812+ ] , {
1813+ width : 800 , height : 400 ,
1814+ xaxis : { domain : [ 0 , 0.45 ] , range : [ 0 , 4 ] } ,
1815+ yaxis : { range : [ 0 , 4 ] } ,
1816+ xaxis2 : { domain : [ 0.55 , 1 ] , anchor : 'y2' , range : [ 0 , 4 ] } ,
1817+ yaxis2 : { anchor : 'x2' , range : [ 0 , 4 ] } ,
1818+ shapes : [
1819+ { type : 'line' , xref : [ 'x' , 'x2' ] , yref : [ 'y' , 'y2' ] , x0 : 1 , x1 : 2 , y0 : 1 , y1 : 2 } ,
1820+ { type : 'rect' , xref : [ 'x' , 'x2' ] , yref : [ 'y' , 'y2' ] , x0 : 1.5 , x1 : 1.5 , y0 : 1.5 , y1 : 1.5 } ,
1821+ { type : 'circle' , xref : [ 'x' , 'x2' ] , yref : [ 'y' , 'y2' ] , x0 : 1 , x1 : 2 , y0 : 1 , y1 : 2 } ,
1822+ { type : 'path' , xref : [ 'x' , 'x2' ] , yref : [ 'y' , 'y2' ] , path : 'M1,1L2,2' }
1823+ ]
1824+ } ) . then ( function ( ) {
1825+ return Plotly . relayout ( gd , 'xaxis.range' , [ 0 , 2 ] ) ;
1826+ } ) . then ( function ( ) {
1827+ var xa = gd . _fullLayout . xaxis ;
1828+ var xa2 = gd . _fullLayout . xaxis2 ;
1829+
1830+ var lineExpectedLeft = xa . l2p ( 1 ) + xa . _offset ;
1831+ var lineExpectedRight = xa2 . l2p ( 2 ) + xa2 . _offset ;
1832+ var lineBBox = getBoundingBox ( 0 ) ;
1833+
1834+ expect ( lineBBox . left ) . toBeCloseTo ( lineExpectedLeft ) ;
1835+ expect ( lineBBox . right ) . toBeCloseTo ( lineExpectedRight ) ;
1836+
1837+ var rectExpectedLeft = xa . l2p ( 1.5 ) + xa . _offset ;
1838+ var rectExpectedRight = xa2 . l2p ( 1.5 ) + xa2 . _offset ;
1839+ var rectBBox = getBoundingBox ( 1 ) ;
1840+
1841+ expect ( rectBBox . left ) . toBeCloseTo ( rectExpectedLeft ) ;
1842+ expect ( rectBBox . right ) . toBeCloseTo ( rectExpectedRight ) ;
1843+
1844+ var circleExpectedLeft = xa . l2p ( 1 ) + xa . _offset ;
1845+ var circleExpectedRight = xa2 . l2p ( 2 ) + xa2 . _offset ;
1846+ var circleBBox = getBoundingBox ( 2 ) ;
1847+
1848+ expect ( circleBBox . left ) . toBeCloseTo ( circleExpectedLeft ) ;
1849+ expect ( circleBBox . right ) . toBeCloseTo ( circleExpectedRight ) ;
1850+
1851+ var pathExpectedLeft = xa . l2p ( 1 ) + xa . _offset ;
1852+ var pathExpectedRight = xa2 . l2p ( 2 ) + xa2 . _offset ;
1853+ var pathBBox = getBoundingBox ( 3 ) ;
1854+
1855+ expect ( pathBBox . left ) . toBeCloseTo ( pathExpectedLeft ) ;
1856+ expect ( pathBBox . right ) . toBeCloseTo ( pathExpectedRight ) ;
1857+ } )
1858+ . then ( done , done . fail ) ;
1859+ } ) ;
1860+
1861+ it ( 'updates shape when panning a referenced axis' , function ( done ) {
1862+ Plotly . newPlot ( gd , [
1863+ { x : [ 0 , 4 ] , y : [ 0 , 4 ] } ,
1864+ { x : [ 0 , 4 ] , y : [ 0 , 4 ] , xaxis : 'x2' , yaxis : 'y2' }
1865+ ] , {
1866+ width : 800 , height : 400 ,
1867+ xaxis : { domain : [ 0 , 0.45 ] , range : [ 0 , 4 ] } ,
1868+ yaxis : { range : [ 0 , 4 ] } ,
1869+ xaxis2 : { domain : [ 0.55 , 1 ] , anchor : 'y2' , range : [ 0 , 4 ] } ,
1870+ yaxis2 : { anchor : 'x2' , range : [ 0 , 4 ] } ,
1871+ shapes : [
1872+ { type : 'line' , xref : [ 'x' , 'x2' ] , yref : [ 'y' , 'y2' ] , x0 : 1 , x1 : 2 , y0 : 1 , y1 : 2 } ,
1873+ { type : 'rect' , xref : [ 'x' , 'x2' ] , yref : [ 'y' , 'y2' ] , x0 : 1.5 , x1 : 1.5 , y0 : 1.5 , y1 : 1.5 } ,
1874+ { type : 'circle' , xref : [ 'x' , 'x2' ] , yref : [ 'y' , 'y2' ] , x0 : 1 , x1 : 2 , y0 : 1 , y1 : 2 } ,
1875+ { type : 'path' , xref : [ 'x' , 'x2' ] , yref : [ 'y' , 'y2' ] , path : 'M1,1L2,2' }
1876+ ]
1877+ } ) . then ( function ( ) {
1878+ return Plotly . relayout ( gd , 'yaxis.range' , [ 1 , 5 ] ) ;
1879+ } ) . then ( function ( ) {
1880+ var ya = gd . _fullLayout . yaxis ;
1881+ var ya2 = gd . _fullLayout . yaxis2 ;
1882+
1883+ var lineExpectedBottom = ya . l2p ( 1 ) + ya . _offset ;
1884+ var lineExpectedTop = ya2 . l2p ( 2 ) + ya2 . _offset ;
1885+ var lineBBox = getBoundingBox ( 0 ) ;
1886+
1887+ expect ( lineBBox . bottom ) . toBeCloseTo ( lineExpectedBottom ) ;
1888+ expect ( lineBBox . top ) . toBeCloseTo ( lineExpectedTop ) ;
1889+
1890+ var rectExpectedBottom = ya . l2p ( 1.5 ) + ya . _offset ;
1891+ var rectExpectedTop = ya2 . l2p ( 1.5 ) + ya2 . _offset ;
1892+ var rectBBox = getBoundingBox ( 1 ) ;
1893+
1894+ expect ( rectBBox . bottom ) . toBeCloseTo ( rectExpectedBottom ) ;
1895+ expect ( rectBBox . top ) . toBeCloseTo ( rectExpectedTop ) ;
1896+
1897+ var circleExpectedBottom = ya . l2p ( 1 ) + ya . _offset ;
1898+ var circleExpectedTop = ya2 . l2p ( 2 ) + ya2 . _offset ;
1899+ var circleBBox = getBoundingBox ( 2 ) ;
1900+
1901+ expect ( circleBBox . bottom ) . toBeCloseTo ( circleExpectedBottom ) ;
1902+ expect ( circleBBox . top ) . toBeCloseTo ( circleExpectedTop ) ;
1903+
1904+ var pathExpectedBottom = ya . l2p ( 1 ) + ya . _offset ;
1905+ var pathExpectedTop = ya2 . l2p ( 2 ) + ya2 . _offset ;
1906+ var pathBBox = getBoundingBox ( 3 ) ;
1907+
1908+ expect ( pathBBox . bottom ) . toBeCloseTo ( pathExpectedBottom ) ;
1909+ expect ( pathBBox . top ) . toBeCloseTo ( pathExpectedTop ) ;
1910+ } )
1911+ . then ( done , done . fail ) ;
1912+ } ) ;
1913+
1914+ it ( 'handles autorange' , function ( done ) {
1915+ Plotly . newPlot ( gd , [
1916+ { x : [ 1 , 2 ] , y : [ 1 , 2 ] } ,
1917+ { x : [ 1 , 2 ] , y : [ 1 , 2 ] , xaxis : 'x2' , yaxis : 'y2' }
1918+ ] , {
1919+ xaxis : { domain : [ 0 , 0.45 ] } ,
1920+ yaxis : { domain : [ 0 , 0.45 ] } ,
1921+ xaxis2 : { domain : [ 0.55 , 1 ] , anchor : 'y2' } ,
1922+ yaxis2 : { domain : [ 0.55 , 1 ] , anchor : 'x2' } ,
1923+ shapes : [ {
1924+ type : 'rect' ,
1925+ xref : [ 'x' , 'x2' ] , yref : [ 'y' , 'y2' ] ,
1926+ x0 : 0 , x1 : 5 , y0 : 0 , y1 : 5
1927+ } ]
1928+ } ) . then ( function ( ) {
1929+ expect ( gd . _fullLayout . xaxis . range [ 0 ] ) . toBeLessThan ( 1 ) ;
1930+ expect ( gd . _fullLayout . yaxis . range [ 0 ] ) . toBeLessThan ( 1 ) ;
1931+ expect ( gd . _fullLayout . xaxis2 . range [ 1 ] ) . toBeGreaterThan ( 4 ) ;
1932+ expect ( gd . _fullLayout . yaxis2 . range [ 1 ] ) . toBeGreaterThan ( 4 ) ;
1933+ } )
1934+ . then ( done , done . fail ) ;
1935+ } ) ;
1936+ } ) ;
0 commit comments