@@ -1110,49 +1110,32 @@ describe('hover info', function() {
11101110
11111111 } ) ;
11121112
1113- describe ( 'centered' , function ( ) {
1114- var trace1 = {
1115- x : [ 'giraffes' ] ,
1116- y : [ 5 ] ,
1117- name : 'LA Zoo' ,
1118- type : 'bar' ,
1119- text : [ 'Way too long hover info!' ]
1120- } ;
1121- var trace2 = {
1122- x : [ 'giraffes' ] ,
1123- y : [ 5 ] ,
1124- name : 'SF Zoo' ,
1125- type : 'bar' ,
1126- text : [ 'San Francisco' ]
1127- } ;
1128- var data = [ trace1 , trace2 ] ;
1129- var layout = { width : 600 , height : 300 , barmode : 'stack' } ;
1130-
1131- var gd ;
1132-
1133- beforeEach ( function ( done ) {
1134- gd = createGraphDiv ( ) ;
1135- Plotly . plot ( gd , data , layout ) . then ( done ) ;
1113+ function hoverInfoNodes ( traceName ) {
1114+ var g = d3 . selectAll ( 'g.hoverlayer g.hovertext' ) . filter ( function ( ) {
1115+ return ! d3 . select ( this ) . select ( '[data-unformatted="' + traceName + '"]' ) . empty ( ) ;
11361116 } ) ;
11371117
1138- function centeredHoverInfoNodes ( ) {
1139- var g = d3 . selectAll ( 'g.hoverlayer g.hovertext' ) . filter ( function ( ) {
1140- return ! d3 . select ( this ) . select ( '[data-unformatted="LA Zoo"]' ) . empty ( ) ;
1141- } ) ;
1118+ return {
1119+ primaryText : g . select ( 'text:not([data-unformatted="' + traceName + '"])' ) . node ( ) ,
1120+ primaryBox : g . select ( 'path' ) . node ( ) ,
1121+ secondaryText : g . select ( '[data-unformatted="' + traceName + '"]' ) . node ( ) ,
1122+ secondaryBox : g . select ( 'rect' ) . node ( )
1123+ } ;
1124+ }
11421125
1143- return {
1144- primaryText : g . select ( 'text:not([data-unformatted="LA Zoo"])' ) . node ( ) ,
1145- primaryBox : g . select ( 'path' ) . node ( ) ,
1146- secondaryText : g . select ( '[data-unformatted="LA Zoo"]' ) . node ( ) ,
1147- secondaryBox : g . select ( 'rect' ) . node ( )
1148- } ;
1149- }
1126+ function ensureCentered ( hoverInfoNodes ) {
1127+ expect ( hoverInfoNodes . primaryText . getAttribute ( 'text-anchor' ) ) . toBe ( 'middle' ) ;
1128+ expect ( hoverInfoNodes . secondaryText . getAttribute ( 'text-anchor' ) ) . toBe ( 'middle' ) ;
1129+ return hoverInfoNodes ;
1130+ }
11501131
1151- function ensureCentered ( hoverInfoNodes ) {
1152- expect ( hoverInfoNodes . primaryText . getAttribute ( 'text-anchor' ) ) . toBe ( 'middle' ) ;
1153- expect ( hoverInfoNodes . secondaryText . getAttribute ( 'text-anchor' ) ) . toBe ( 'middle' ) ;
1154- return hoverInfoNodes ;
1155- }
1132+ function assertLabelsInsideBoxes ( nodes , msgPrefix ) {
1133+ var msgPrefixFmt = msgPrefix ? '[' + msgPrefix + '] ' : '' ;
1134+
1135+ assertElemInside ( nodes . primaryText , nodes . primaryBox ,
1136+ msgPrefixFmt + 'Primary text inside box' ) ;
1137+ assertElemInside ( nodes . secondaryText , nodes . secondaryBox ,
1138+ msgPrefixFmt + 'Secondary text inside box' ) ;
11561139
11571140 function assertElemInside ( elem , container , msg ) {
11581141 var elemBB = elem . getBoundingClientRect ( ) ;
@@ -1162,14 +1145,23 @@ describe('hover info', function() {
11621145 contBB . top < elemBB . top &&
11631146 contBB . bottom > elemBB . bottom ) . toBe ( true , msg ) ;
11641147 }
1148+ }
1149+
1150+ function assertSecondaryRightToPrimaryBox ( nodes , msgPrefix ) {
1151+ var msgPrefixFmt = msgPrefix ? '[' + msgPrefix + '] ' : '' ;
1152+
1153+ assertElemRightTo ( nodes . secondaryBox , nodes . primaryBox ,
1154+ msgPrefixFmt + 'Secondary box right to primary box' ) ;
1155+ assertElemTopsAligned ( nodes . secondaryBox , nodes . primaryBox ,
1156+ msgPrefixFmt + 'Top edges of primary and secondary boxes aligned' ) ;
11651157
11661158 function assertElemRightTo ( elem , refElem , msg ) {
11671159 var elemBB = elem . getBoundingClientRect ( ) ;
11681160 var refElemBB = refElem . getBoundingClientRect ( ) ;
11691161 expect ( elemBB . left >= refElemBB . right ) . toBe ( true , msg ) ;
11701162 }
11711163
1172- function assertTopsAligned ( elem1 , elem2 , msg ) {
1164+ function assertElemTopsAligned ( elem1 , elem2 , msg ) {
11731165 var elem1BB = elem1 . getBoundingClientRect ( ) ;
11741166 var elem2BB = elem2 . getBoundingClientRect ( ) ;
11751167
@@ -1178,21 +1170,105 @@ describe('hover info', function() {
11781170 var tolerance = 1.1 ;
11791171 expect ( elem1BB . top - elem2BB . top ) . toBeWithin ( 0 , tolerance , msg ) ;
11801172 }
1173+ }
1174+
1175+ describe ( 'centered' , function ( ) {
1176+ var trace1 = {
1177+ x : [ 'giraffes' ] ,
1178+ y : [ 5 ] ,
1179+ name : 'LA Zoo' ,
1180+ type : 'bar' ,
1181+ text : [ 'Way too long hover info!' ]
1182+ } ;
1183+ var trace2 = {
1184+ x : [ 'giraffes' ] ,
1185+ y : [ 5 ] ,
1186+ name : 'SF Zoo' ,
1187+ type : 'bar' ,
1188+ text : [ 'San Francisco' ]
1189+ } ;
1190+ var data = [ trace1 , trace2 ] ;
1191+ var layout = { width : 600 , height : 300 , barmode : 'stack' } ;
1192+
1193+ var gd ;
1194+
1195+ beforeEach ( function ( done ) {
1196+ gd = createGraphDiv ( ) ;
1197+ Plotly . plot ( gd , data , layout ) . then ( done ) ;
1198+ } ) ;
11811199
11821200 it ( 'renders labels inside boxes' , function ( ) {
11831201 _hover ( gd , 300 , 150 ) ;
11841202
1185- var nodes = ensureCentered ( centeredHoverInfoNodes ( ) ) ;
1186- assertElemInside ( nodes . primaryText , nodes . primaryBox , 'Primary text inside box' ) ;
1187- assertElemInside ( nodes . secondaryText , nodes . secondaryBox , 'Secondary text inside box' ) ;
1203+ var nodes = ensureCentered ( hoverInfoNodes ( 'LA Zoo' ) ) ;
1204+ assertLabelsInsideBoxes ( nodes ) ;
11881205 } ) ;
11891206
11901207 it ( 'renders secondary info box right to primary info box' , function ( ) {
11911208 _hover ( gd , 300 , 150 ) ;
11921209
1193- var nodes = ensureCentered ( centeredHoverInfoNodes ( ) ) ;
1194- assertElemRightTo ( nodes . secondaryBox , nodes . primaryBox , 'Secondary box right to primary box' ) ;
1195- assertTopsAligned ( nodes . secondaryBox , nodes . primaryBox , 'Top edges of primary and secondary boxes aligned' ) ;
1210+ var nodes = ensureCentered ( hoverInfoNodes ( 'LA Zoo' ) ) ;
1211+ assertSecondaryRightToPrimaryBox ( nodes ) ;
1212+ } ) ;
1213+ } ) ;
1214+
1215+ describe ( 'centered' , function ( ) {
1216+ var trace1 = {
1217+ x : [ 'giraffes' ] ,
1218+ y : [ 5 ] ,
1219+ name : 'LA Zoo' ,
1220+ type : 'bar' ,
1221+ text : [ 'Way too long hover info!' ]
1222+ } ;
1223+ var trace2 = {
1224+ x : [ 'giraffes' ] ,
1225+ y : [ 5 ] ,
1226+ name : 'SF Zoo' ,
1227+ type : 'bar' ,
1228+ text : [ 'Way too looooong hover info!' ]
1229+ } ;
1230+ var trace3 = {
1231+ x : [ 'giraffes' ] ,
1232+ y : [ 5 ] ,
1233+ name : 'NY Zoo' ,
1234+ type : 'bar' ,
1235+ text : [ 'New York' ]
1236+ } ;
1237+ var data = [ trace1 , trace2 , trace3 ] ;
1238+ var layout = { width : 600 , height : 300 } ;
1239+
1240+ var gd ;
1241+
1242+ beforeEach ( function ( done ) {
1243+ gd = createGraphDiv ( ) ;
1244+ Plotly . plot ( gd , data , layout ) . then ( done ) ;
1245+ } ) ;
1246+
1247+ function calcLineOverlap ( minA , maxA , minB , maxB ) {
1248+ expect ( minA ) . toBeLessThan ( maxA ) ;
1249+ expect ( minB ) . toBeLessThan ( maxB ) ;
1250+
1251+ var overlap = Math . min ( maxA , maxB ) - Math . max ( minA , minB ) ;
1252+ return Math . max ( 0 , overlap ) ;
1253+ }
1254+
1255+ it ( 'stacks nicely upon each other' , function ( ) {
1256+ _hover ( gd , 300 , 150 ) ;
1257+
1258+ var nodesLA = ensureCentered ( hoverInfoNodes ( 'LA Zoo' ) ) ;
1259+ var nodesSF = ensureCentered ( hoverInfoNodes ( 'SF Zoo' ) ) ;
1260+
1261+ // Ensure layout correct
1262+ assertLabelsInsideBoxes ( nodesLA , 'LA Zoo' ) ;
1263+ assertLabelsInsideBoxes ( nodesSF , 'SF Zoo' ) ;
1264+ assertSecondaryRightToPrimaryBox ( nodesLA , 'LA Zoo' ) ;
1265+ assertSecondaryRightToPrimaryBox ( nodesSF , 'SF Zoo' ) ;
1266+
1267+ // Ensure stacking, finally
1268+ var boxLABB = nodesLA . primaryBox . getBoundingClientRect ( ) ;
1269+ var boxSFBB = nodesSF . primaryBox . getBoundingClientRect ( ) ;
1270+ expect ( calcLineOverlap ( boxLABB . top , boxLABB . bottom , boxSFBB . top , boxSFBB . bottom ) )
1271+ . toBeWithin ( 0 , 1 ) ; // Be robust against floating point arithmetic and subtle future layout changes
11961272 } ) ;
11971273 } ) ;
11981274} ) ;
0 commit comments