@@ -1042,6 +1042,68 @@ describe('MatTooltip', () => {
10421042 expect ( tooltipDirective . _isTooltipVisible ( ) ) . toBe ( false ) ;
10431043 } ) ;
10441044
1045+ it ( 'should not treat a nested popover overlay as part of the trigger' , ( ) => {
1046+ // We don't bind mouse events on mobile devices.
1047+ if ( platform . IOS || platform . ANDROID ) {
1048+ return ;
1049+ }
1050+
1051+ // Regression test for #32747: a trigger can have a popover-based overlay nested inside
1052+ // it (e.g. an open mat-select panel). Test-dispatched events aren't trusted, so they
1053+ // can't exercise the `mouseenter` guard end-to-end; verify the check it relies on.
1054+ const nestedPopover = document . createElement ( 'div' ) ;
1055+ nestedPopover . setAttribute ( 'popover' , 'manual' ) ;
1056+ buttonElement . appendChild ( nestedPopover ) ;
1057+
1058+ const isPointerOverTrigger = ( tooltipDirective as any ) . _isPointerOverTrigger . bind (
1059+ tooltipDirective ,
1060+ ) ;
1061+ const elementFromPointSpy = spyOn ( document , 'elementFromPoint' ) ;
1062+
1063+ elementFromPointSpy . and . returnValue ( buttonElement ) ;
1064+ expect ( isPointerOverTrigger ( createMouseEvent ( 'mouseenter' ) ) )
1065+ . withContext ( 'hovering the trigger itself should count as over the trigger' )
1066+ . toBe ( true ) ;
1067+
1068+ elementFromPointSpy . and . returnValue ( nestedPopover ) ;
1069+ expect ( isPointerOverTrigger ( createMouseEvent ( 'mouseenter' ) ) )
1070+ . withContext (
1071+ 'hovering inside a nested popover overlay should not count as over the trigger' ,
1072+ )
1073+ . toBe ( false ) ;
1074+
1075+ nestedPopover . remove ( ) ;
1076+ } ) ;
1077+
1078+ it ( 'should treat a nested element sticking out of the trigger bounds as part of it' , ( ) => {
1079+ // We don't bind mouse events on mobile devices.
1080+ if ( platform . IOS || platform . ANDROID ) {
1081+ return ;
1082+ }
1083+
1084+ // Some trigger elements have children that intentionally render outside their own
1085+ // bounds, e.g. the `.mat-mdc-button-touch-target` span on buttons, which is positioned
1086+ // to extend past the visible button to provide a larger touch target. Those shouldn't be
1087+ // mistaken for a nested overlay (see the previous test).
1088+ const stickingOutChild = document . createElement ( 'span' ) ;
1089+ stickingOutChild . style . position = 'absolute' ;
1090+ stickingOutChild . style . inset = '-20px' ;
1091+ buttonElement . appendChild ( stickingOutChild ) ;
1092+
1093+ const isPointerOverTrigger = ( tooltipDirective as any ) . _isPointerOverTrigger . bind (
1094+ tooltipDirective ,
1095+ ) ;
1096+ spyOn ( document , 'elementFromPoint' ) . and . returnValue ( stickingOutChild ) ;
1097+
1098+ expect ( isPointerOverTrigger ( createMouseEvent ( 'mouseenter' ) ) )
1099+ . withContext (
1100+ 'a point over a child sticking out of the trigger should still count as over the trigger' ,
1101+ )
1102+ . toBe ( true ) ;
1103+
1104+ stickingOutChild . remove ( ) ;
1105+ } ) ;
1106+
10451107 it ( 'should not hide on mouseleave if the pointer goes from the trigger to the tooltip' , async ( ) => {
10461108 // We don't bind mouse events on mobile devices.
10471109 if ( platform . IOS || platform . ANDROID ) {
0 commit comments