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', () => {
383383 items . forEach ( item => assertMenuItem ( item , { tag : 'button' } ) )
384384 } )
385385 )
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+ )
386431} )
387432
388433describe ( '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
307307 let id = `headlessui-menu-items-${ useId ( ) } `
308308 let searchDisposables = useDisposables ( )
309309
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+
310328 let handleKeyDown = useCallback (
311329 ( event : ReactKeyboardEvent < HTMLDivElement > ) => {
312330 searchDisposables . dispose ( )
Original file line number Diff line number Diff line change @@ -602,6 +602,53 @@ describe('Rendering composition', () => {
602602 )
603603 } )
604604 )
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+ )
605652} )
606653
607654describe ( 'Keyboard interactions' , ( ) => {
Original file line number Diff line number Diff line change @@ -278,6 +278,24 @@ export let MenuItems = defineComponent({
278278 let id = `headlessui-menu-items-${ useId ( ) } `
279279 let searchDebounce = ref < ReturnType < typeof setTimeout > | null > ( null )
280280
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+
281299 function handleKeyDown ( event : KeyboardEvent ) {
282300 if ( searchDebounce . value ) clearTimeout ( searchDebounce . value )
283301
You can’t perform that action at this time.
0 commit comments