@@ -1257,6 +1257,9 @@ describe('Combobox', () => {
12571257 . nativeElement as HTMLElement ;
12581258 } ;
12591259
1260+ const wait = ( milliseconds : number ) =>
1261+ new Promise ( resolve => setTimeout ( resolve , milliseconds ) ) ;
1262+
12601263 afterEach ( async ( ) => await runAccessibilityChecks ( fixture . nativeElement ) ) ;
12611264
12621265 it ( 'should auto-generate an ID on the widget when none is provided' , async ( ) => {
@@ -1276,6 +1279,45 @@ describe('Combobox', () => {
12761279 expect ( widgetElement . id ) . toMatch ( / ^ n g - l i s t b o x - / ) ;
12771280 expect ( comboboxElement . getAttribute ( 'aria-controls' ) ) . toBe ( widgetElement . id ) ;
12781281 } ) ;
1282+
1283+ it ( 'should close the popup once focus leaves it entirely, even when the trigger never regains focus' , async ( ) => {
1284+ await expand ( ComboboxDialogExample ) ;
1285+ const filterInput = widgetElement . querySelector ( 'input' ) as HTMLInputElement ;
1286+
1287+ comboboxElement . dispatchEvent (
1288+ new FocusEvent ( 'focusout' , { bubbles : true , relatedTarget : filterInput } ) ,
1289+ ) ;
1290+ filterInput . dispatchEvent ( new FocusEvent ( 'focusin' , { bubbles : true } ) ) ; // Focus enters popup
1291+ await wait ( 100 ) ;
1292+ expect ( comboboxElement . getAttribute ( 'aria-expanded' ) ) . toBe ( 'true' ) ;
1293+
1294+ // Focus leaves the popup, never passing back through the trigger
1295+ filterInput . dispatchEvent (
1296+ new FocusEvent ( 'focusout' , { bubbles : true , relatedTarget : document . body } ) ,
1297+ ) ;
1298+ await wait ( 100 ) ;
1299+
1300+ expect ( comboboxElement . getAttribute ( 'aria-expanded' ) ) . toBe ( 'false' ) ;
1301+ } ) ;
1302+
1303+ it ( 'should keep the popup open when focus moves from the widget back to the combobox' , async ( ) => {
1304+ await expand ( ComboboxDialogExample ) ;
1305+ const filterInput = widgetElement . querySelector ( 'input' ) as HTMLInputElement ;
1306+
1307+ comboboxElement . dispatchEvent (
1308+ new FocusEvent ( 'focusout' , { bubbles : true , relatedTarget : filterInput } ) ,
1309+ ) ;
1310+ filterInput . dispatchEvent ( new FocusEvent ( 'focusin' , { bubbles : true } ) ) ;
1311+ await wait ( 100 ) ;
1312+
1313+ filterInput . dispatchEvent (
1314+ new FocusEvent ( 'focusout' , { bubbles : true , relatedTarget : comboboxElement } ) ,
1315+ ) ;
1316+ comboboxElement . dispatchEvent ( new FocusEvent ( 'focusin' , { bubbles : true } ) ) ;
1317+ await wait ( 100 ) ;
1318+
1319+ expect ( comboboxElement . getAttribute ( 'aria-expanded' ) ) . toBe ( 'true' ) ;
1320+ } ) ;
12791321 } ) ;
12801322} ) ;
12811323
0 commit comments