@@ -2037,3 +2037,136 @@ describe('Test multi-axis shapes', function () {
20372037 . then ( done , done . fail ) ;
20382038 } ) ;
20392039} ) ;
2040+
2041+ describe ( 'Shape labels with pixel sizemode' , ( ) => {
2042+ let gd ;
2043+ beforeEach ( ( ) => {
2044+ gd = createGraphDiv ( ) ;
2045+ } ) ;
2046+ afterEach ( destroyGraphDiv ) ;
2047+
2048+ const getLabelNodes = ( ) => d3SelectAll ( '.shape-label-text' ) ;
2049+ const data = [
2050+ {
2051+ type : 'scatter' ,
2052+ x : [ 1 , 2 ] ,
2053+ y : [ 1 , 2 ]
2054+ }
2055+ ] ;
2056+
2057+ it ( 'positions labels inside shapes with xsizemode/ysizemode pixel' , ( done ) => {
2058+ Plotly . newPlot ( gd , data , {
2059+ width : 600 ,
2060+ height : 400 ,
2061+ shapes : [
2062+ {
2063+ type : 'circle' ,
2064+ label : { text : 'Circle Label' } ,
2065+ xref : 'x' ,
2066+ yref : 'y' ,
2067+ xsizemode : 'pixel' ,
2068+ ysizemode : 'pixel' ,
2069+ xanchor : 1.5 ,
2070+ yanchor : 1.5 ,
2071+ x0 : - 30 ,
2072+ x1 : 30 ,
2073+ y0 : - 30 ,
2074+ y1 : 30
2075+ } ,
2076+ {
2077+ type : 'rect' ,
2078+ label : { text : 'Rect Label' } ,
2079+ xref : 'x' ,
2080+ yref : 'y' ,
2081+ xsizemode : 'pixel' ,
2082+ ysizemode : 'pixel' ,
2083+ xanchor : 1.5 ,
2084+ yanchor : 1.5 ,
2085+ x0 : - 40 ,
2086+ x1 : 40 ,
2087+ y0 : - 25 ,
2088+ y1 : 25
2089+ }
2090+ ]
2091+ } )
2092+ . then ( ( ) => {
2093+ const labels = getLabelNodes ( ) ;
2094+ expect ( labels . size ( ) ) . toBe ( 2 ) ;
2095+
2096+ const shapeNodes = d3SelectAll ( '.shapelayer .shape-group path' ) ;
2097+ expect ( shapeNodes . size ( ) ) . toBe ( 2 ) ;
2098+
2099+ // Each label should be positioned within or near its shape
2100+ labels . each ( function ( _ , i ) {
2101+ const labelBB = this . getBoundingClientRect ( ) ;
2102+ const shapeBB = shapeNodes [ 0 ] [ i ] . getBoundingClientRect ( ) ;
2103+ // Label center should be inside the shape bounding box
2104+ const labelCenterX = labelBB . left + labelBB . width / 2 ;
2105+ const labelCenterY = labelBB . top + labelBB . height / 2 ;
2106+
2107+ expect ( labelCenterX ) . toBeGreaterThan ( shapeBB . left ) ;
2108+ expect ( labelCenterX ) . toBeLessThan ( shapeBB . right ) ;
2109+ expect ( labelCenterY ) . toBeGreaterThan ( shapeBB . top ) ;
2110+ expect ( labelCenterY ) . toBeLessThan ( shapeBB . bottom ) ;
2111+ } ) ;
2112+ } )
2113+ . then ( done , done . fail ) ;
2114+ } ) ;
2115+
2116+ it ( 'positions labels inside shapes with paper ref and pixel sizemode' , ( done ) => {
2117+ Plotly . newPlot ( gd , data , {
2118+ width : 600 ,
2119+ height : 400 ,
2120+ shapes : [
2121+ {
2122+ type : 'circle' ,
2123+ label : { text : 'Circle Label' } ,
2124+ xref : 'x' ,
2125+ xsizemode : 'pixel' ,
2126+ xanchor : 1.5 ,
2127+ x0 : - 25 ,
2128+ x1 : 25 ,
2129+ yref : 'paper' ,
2130+ ysizemode : 'pixel' ,
2131+ yanchor : 0.5 ,
2132+ y0 : - 25 ,
2133+ y1 : 25
2134+ } ,
2135+ {
2136+ type : 'rect' ,
2137+ label : { text : 'Rect Label' } ,
2138+ xref : 'x' ,
2139+ xsizemode : 'pixel' ,
2140+ xanchor : 1.5 ,
2141+ x0 : - 30 ,
2142+ x1 : 30 ,
2143+ yref : 'paper' ,
2144+ ysizemode : 'pixel' ,
2145+ yanchor : 0.5 ,
2146+ y0 : - 20 ,
2147+ y1 : 20
2148+ }
2149+ ]
2150+ } )
2151+ . then ( ( ) => {
2152+ const labels = getLabelNodes ( ) ;
2153+ expect ( labels . size ( ) ) . toBe ( 2 ) ;
2154+
2155+ const shapeNodes = d3SelectAll ( '.shapelayer .shape-group path' ) ;
2156+
2157+ labels . each ( function ( _ , i ) {
2158+ const labelBB = this . getBoundingClientRect ( ) ;
2159+ const shapeBB = shapeNodes [ 0 ] [ i ] . getBoundingClientRect ( ) ;
2160+ // Label center should be inside the shape bounding box
2161+ const labelCenterX = labelBB . left + labelBB . width / 2 ;
2162+ const labelCenterY = labelBB . top + labelBB . height / 2 ;
2163+
2164+ expect ( labelCenterX ) . toBeGreaterThan ( shapeBB . left ) ;
2165+ expect ( labelCenterX ) . toBeLessThan ( shapeBB . right ) ;
2166+ expect ( labelCenterY ) . toBeGreaterThan ( shapeBB . top ) ;
2167+ expect ( labelCenterY ) . toBeLessThan ( shapeBB . bottom ) ;
2168+ } ) ;
2169+ } )
2170+ . then ( done , done . fail ) ;
2171+ } ) ;
2172+ } ) ;
0 commit comments