@@ -24,6 +24,17 @@ import {
2424 waitUntil ,
2525} from '@open-wc/testing' ;
2626
27+ const keyboardEvent = ( code : string , shiftKey = false ) : KeyboardEvent =>
28+ new KeyboardEvent ( 'keydown' , {
29+ bubbles : true ,
30+ composed : true ,
31+ cancelable : true ,
32+ code,
33+ shiftKey,
34+ } ) ;
35+ const tabEvent = keyboardEvent ( 'Tab' ) ;
36+ const shiftTabEvent = keyboardEvent ( 'Tab' , true ) ;
37+
2738describe ( 'Overlays' , ( ) => {
2839 let testDiv ! : HTMLDivElement ;
2940 let openOverlays : ( ( ) => void ) [ ] = [ ] ;
@@ -124,11 +135,13 @@ describe('Overlays', () => {
124135
125136 expect ( button ) . to . exist ;
126137
127- Overlay . open ( button , 'click' , outerPopover , {
128- delayed : false ,
129- placement,
130- offset : 10 ,
131- } ) ;
138+ openOverlays . push (
139+ await Overlay . open ( button , 'click' , outerPopover , {
140+ delayed : false ,
141+ placement,
142+ offset : 10 ,
143+ } )
144+ ) ;
132145
133146 // Wait for the DOM node to be stolen and reparented into the overlay
134147 await waitForPredicate (
@@ -198,10 +211,12 @@ describe('Overlays', () => {
198211
199212 expect ( button ) . to . exist ;
200213
201- await Overlay . open ( button , 'click' , outerPopover , {
202- delayed : true ,
203- offset : 10 ,
204- } ) ;
214+ openOverlays . push (
215+ await Overlay . open ( button , 'click' , outerPopover , {
216+ delayed : true ,
217+ offset : 10 ,
218+ } )
219+ ) ;
205220
206221 // Wait for the DOM node to be stolen and reparented into the overlay
207222 await waitUntil (
@@ -357,11 +372,13 @@ describe('Overlays', () => {
357372
358373 const dialog = el . querySelector ( 'sp-dialog' ) as Dialog ;
359374
360- Overlay . open ( el , 'click' , dialog , {
361- delayed : false ,
362- placement : 'bottom' ,
363- offset : 10 ,
364- } ) ;
375+ openOverlays . push (
376+ await Overlay . open ( el , 'click' , dialog , {
377+ delayed : false ,
378+ placement : 'bottom' ,
379+ offset : 10 ,
380+ } )
381+ ) ;
365382
366383 await waitUntil (
367384 ( ) =>
@@ -379,4 +396,92 @@ describe('Overlays', () => {
379396 'content is returned'
380397 ) ;
381398 } ) ;
399+
400+ it ( 'closes an inline overlay when tabbing past the content' , async ( ) => {
401+ const el = await fixture < HTMLDivElement > ( html `
402+ < div >
403+ < button class ="trigger "> Trigger</ button >
404+ < div class ="content ">
405+ < input />
406+ </ div >
407+ </ div >
408+ ` ) ;
409+
410+ const trigger = el . querySelector ( '.trigger' ) as HTMLElement ;
411+ const content = el . querySelector ( '.content' ) as HTMLElement ;
412+
413+ openOverlays . push ( await Overlay . open ( trigger , 'inline' , content , { } ) ) ;
414+
415+ await waitUntil (
416+ ( ) => ! ! el . querySelector ( 'span[tabindex="-1"]' ) ,
417+ 'returnFocusElement available'
418+ ) ;
419+
420+ const overlays = document . querySelectorAll ( 'active-overlay' ) ;
421+ const overlay = overlays [ 0 ] ;
422+
423+ expect ( overlay ) . to . not . be . undefined ;
424+
425+ trigger . dispatchEvent ( tabEvent ) ;
426+
427+ await waitUntil (
428+ ( ) => ! ! el . querySelector ( 'span[tabindex="-1"]' ) ,
429+ 'returnFocusElement persists on forward tab'
430+ ) ;
431+
432+ content . dispatchEvent ( shiftTabEvent ) ;
433+
434+ expect ( document . activeElement === overlay . returnFocusElement ) . to . be
435+ . true ;
436+
437+ content . dispatchEvent ( tabEvent ) ;
438+
439+ await waitUntil (
440+ ( ) => el . querySelector ( 'span[tabindex="-1"]' ) === null ,
441+ 'returnFocusElement no longer available'
442+ ) ;
443+ } ) ;
444+
445+ it ( 'closes an inline overlay when tabbing before the trigger' , async ( ) => {
446+ const el = await fixture < HTMLDivElement > ( html `
447+ < div >
448+ < button class ="trigger "> Trigger</ button >
449+ < div class ="content ">
450+ < label >
451+ Content in an inline overlay.
452+ < input />
453+ </ label >
454+ </ div >
455+ </ div >
456+ ` ) ;
457+
458+ const trigger = el . querySelector ( '.trigger' ) as HTMLElement ;
459+ const content = el . querySelector ( '.content' ) as HTMLElement ;
460+
461+ openOverlays . push ( await Overlay . open ( trigger , 'inline' , content , { } ) ) ;
462+
463+ await waitUntil (
464+ ( ) => ! ! el . querySelector ( 'span[tabindex="-1"]' ) ,
465+ 'returnFocusElement available'
466+ ) ;
467+
468+ const overlays = document . querySelectorAll ( 'active-overlay' ) ;
469+ const overlay = overlays [ 0 ] ;
470+
471+ await elementUpdated ( overlay ) ;
472+
473+ trigger . dispatchEvent ( tabEvent ) ;
474+
475+ await waitUntil (
476+ ( ) => ! ! el . querySelector ( 'span[tabindex="-1"]' ) ,
477+ 'returnFocusElement persists on forward tab'
478+ ) ;
479+
480+ trigger . dispatchEvent ( shiftTabEvent ) ;
481+
482+ await waitUntil (
483+ ( ) => el . querySelector ( 'span[tabindex="-1"]' ) === null ,
484+ 'returnFocusElement no longer available'
485+ ) ;
486+ } ) ;
382487} ) ;
0 commit comments