File tree Expand file tree Collapse file tree 4 files changed +128
-0
lines changed Expand file tree Collapse file tree 4 files changed +128
-0
lines changed Original file line number Diff line number Diff line change @@ -383,6 +383,51 @@ describe('Rendering composition', () => {
383
383
items . forEach ( item => assertMenuItem ( item , { tag : 'button' } ) )
384
384
} )
385
385
)
386
+
387
+ it (
388
+ 'should mark all the elements between Menu.Items and Menu.Item with role none' ,
389
+ suppressConsoleLogs ( async ( ) => {
390
+ render (
391
+ < Menu >
392
+ < Menu . Button > Trigger</ Menu . Button >
393
+ < div className = "outer" >
394
+ < Menu . Items >
395
+ < div className = "py-1 inner" >
396
+ < Menu . Item as = "button" > Item A</ Menu . Item >
397
+ < Menu . Item as = "button" > Item B</ Menu . Item >
398
+ </ div >
399
+ < div className = "py-1 inner" >
400
+ < Menu . Item as = "button" > Item C</ Menu . Item >
401
+ < Menu . Item >
402
+ < div >
403
+ < div className = "outer" > Item D</ div >
404
+ </ div >
405
+ </ Menu . Item >
406
+ </ div >
407
+ < div className = "py-1 inner" >
408
+ < form className = "inner" >
409
+ < Menu . Item as = "button" > Item E</ Menu . Item >
410
+ </ form >
411
+ </ div >
412
+ </ Menu . Items >
413
+ </ div >
414
+ </ Menu >
415
+ )
416
+
417
+ // Open menu
418
+ await click ( getMenuButton ( ) )
419
+
420
+ expect . hasAssertions ( )
421
+
422
+ document . querySelectorAll ( '.outer' ) . forEach ( element => {
423
+ expect ( element ) . not . toHaveAttribute ( 'role' , 'none' )
424
+ } )
425
+
426
+ document . querySelectorAll ( '.inner' ) . forEach ( element => {
427
+ expect ( element ) . toHaveAttribute ( 'role' , 'none' )
428
+ } )
429
+ } )
430
+ )
386
431
} )
387
432
388
433
describe ( 'Keyboard interactions' , ( ) => {
Original file line number Diff line number Diff line change @@ -307,6 +307,24 @@ let Items = forwardRefWithAs(function Items<TTag extends ElementType = typeof DE
307
307
let id = `headlessui-menu-items-${ useId ( ) } `
308
308
let searchDisposables = useDisposables ( )
309
309
310
+ useIsoMorphicEffect ( ( ) => {
311
+ let container = state . itemsRef . current
312
+ if ( ! container ) return
313
+ if ( state . menuState !== MenuStates . Open ) return
314
+
315
+ let walker = document . createTreeWalker ( container , NodeFilter . SHOW_ELEMENT , {
316
+ acceptNode ( node : HTMLElement ) {
317
+ if ( node . getAttribute ( 'role' ) === 'menuitem' ) return NodeFilter . FILTER_REJECT
318
+ if ( node . hasAttribute ( 'role' ) ) return NodeFilter . FILTER_SKIP
319
+ return NodeFilter . FILTER_ACCEPT
320
+ } ,
321
+ } )
322
+
323
+ while ( walker . nextNode ( ) ) {
324
+ ; ( walker . currentNode as HTMLElement ) . setAttribute ( 'role' , 'none' )
325
+ }
326
+ } )
327
+
310
328
let handleKeyDown = useCallback (
311
329
( event : ReactKeyboardEvent < HTMLDivElement > ) => {
312
330
searchDisposables . dispose ( )
Original file line number Diff line number Diff line change @@ -602,6 +602,53 @@ describe('Rendering composition', () => {
602
602
)
603
603
} )
604
604
)
605
+
606
+ it (
607
+ 'should mark all the elements between Menu.Items and Menu.Item with role none' ,
608
+ suppressConsoleLogs ( async ( ) => {
609
+ renderTemplate ( {
610
+ template : `
611
+ <Menu>
612
+ <MenuButton>Trigger</MenuButton>
613
+ <div className="outer">
614
+ <MenuItems>
615
+ <div className="py-1 inner">
616
+ <MenuItem as="button">Item A</MenuItem>
617
+ <MenuItem as="button">Item B</MenuItem>
618
+ </div>
619
+ <div className="py-1 inner">
620
+ <MenuItem as="button">Item C</MenuItem>
621
+ <MenuItem>
622
+ <div>
623
+ <div className="outer">Item D</div>
624
+ </div>
625
+ </MenuItem>
626
+ </div>
627
+ <div className="py-1 inner">
628
+ <form className="inner">
629
+ <MenuItem as="button">Item E</MenuItem>
630
+ </form>
631
+ </div>
632
+ </MenuItems>
633
+ </div>
634
+ </Menu>
635
+ ` ,
636
+ } )
637
+
638
+ // Open menu
639
+ await click ( getMenuButton ( ) )
640
+
641
+ expect . hasAssertions ( )
642
+
643
+ document . querySelectorAll ( '.outer' ) . forEach ( element => {
644
+ expect ( element ) . not . toHaveAttribute ( 'role' , 'none' )
645
+ } )
646
+
647
+ document . querySelectorAll ( '.inner' ) . forEach ( element => {
648
+ expect ( element ) . toHaveAttribute ( 'role' , 'none' )
649
+ } )
650
+ } )
651
+ )
605
652
} )
606
653
607
654
describe ( 'Keyboard interactions' , ( ) => {
Original file line number Diff line number Diff line change @@ -278,6 +278,24 @@ export let MenuItems = defineComponent({
278
278
let id = `headlessui-menu-items-${ useId ( ) } `
279
279
let searchDebounce = ref < ReturnType < typeof setTimeout > | null > ( null )
280
280
281
+ watchEffect ( ( ) => {
282
+ let container = api . itemsRef . value
283
+ if ( ! container ) return
284
+ if ( api . menuState . value !== MenuStates . Open ) return
285
+
286
+ let walker = document . createTreeWalker ( container , NodeFilter . SHOW_ELEMENT , {
287
+ acceptNode ( node : HTMLElement ) {
288
+ if ( node . getAttribute ( 'role' ) === 'menuitem' ) return NodeFilter . FILTER_REJECT
289
+ if ( node . hasAttribute ( 'role' ) ) return NodeFilter . FILTER_SKIP
290
+ return NodeFilter . FILTER_ACCEPT
291
+ } ,
292
+ } )
293
+
294
+ while ( walker . nextNode ( ) ) {
295
+ ; ( walker . currentNode as HTMLElement ) . setAttribute ( 'role' , 'none' )
296
+ }
297
+ } )
298
+
281
299
function handleKeyDown ( event : KeyboardEvent ) {
282
300
if ( searchDebounce . value ) clearTimeout ( searchDebounce . value )
283
301
You can’t perform that action at this time.
0 commit comments