test: add regression for global aria-posinset across sections - #10554
test: add regression for global aria-posinset across sections#10554gonzoblasco wants to merge 10 commits into
Conversation
Locks in that aria-posinset is global (1..4) across sections in a virtualized menu and matches aria-setsize, instead of restarting per group. Passes against current main; guards the behavior from the discussion in adobe#9556.
|
Thanks for the PR! You'll need to sign the CLA and then close/re-open the PR to rerun the check |
The previous helper flattened sections into plain items, so the DOM never contained the sectioned menu structure the test claims to cover. Render each section as a labelled group (role=presentation wrapper + role=group with aria-labelledby heading), mirroring the real sectioned menu, and assert the groups are present before checking the global posinset values.
|
Fixed in 1af6cf2 - the helper now renders the real sectioned structure instead of flattening. Each section renders as a role=presentation wrapper with a labelled group inside (role=group + aria-labelledby pointing at the section heading), mirroring how the sectioned menu is built. The test now asserts the two groups are present and each is labelled by its heading before checking the global posinset values, so the DOM shape is locked in as part of the regression. Verified: 4/4 in useMenu.test.tsx, full lint (format, types, oxlint, constraints) clean. |
Render each section through the real useMenuSection hook instead of hand-rolling the group structure, so any future logic added to the hook (per-section count restart, references) is exercised by this test.
| // sectioned menu structure, so this test locks the real DOM shape. | ||
| let groups = getAllByRole2('group'); | ||
| expect(groups).toHaveLength(2); | ||
| // Each group is labelled by its section heading, which is present in the DOM. |
There was a problem hiding this comment.
| // Each group is labelled by its section heading, which is present in the DOM. |
| // The sections must actually render as groups, mirroring the real | ||
| // sectioned menu structure, so this test locks the real DOM shape. |
There was a problem hiding this comment.
| // The sections must actually render as groups, mirroring the real | |
| // sectioned menu structure, so this test locks the real DOM shape. |
| // aria-posinset should be global (1..4) and match aria-setsize, not restart | ||
| // per section (which would report 1..2 for both groups). |
There was a problem hiding this comment.
| // aria-posinset should be global (1..4) and match aria-setsize, not restart | |
| // per section (which would report 1..2 for both groups). |
This comment is the name of the test
| let {itemProps, headingProps, groupProps} = useMenuSection({ | ||
| heading: node.rendered, | ||
| 'aria-label': node['aria-label'] | ||
| }); |
There was a problem hiding this comment.
I think you meant this?
| let {itemProps, headingProps, groupProps} = useMenuSection({ | |
| heading: node.rendered, | |
| 'aria-label': node['aria-label'] | |
| }); | |
| let {itemProps, headingProps, groupProps} = useMenuSection({ | |
| heading: node.props.title, | |
| 'aria-label': node['aria-label'] | |
| }); |
though not sure what the aria-label there is for? it could just be omitted i think?
There was a problem hiding this comment.
Applied in 6b65027 - the heading now uses node.props.title (matching how Section renders it) and the redundant aria-label is dropped, since with a heading present useMenuSection labels the group via aria-labelledby.
Verified: 4/4 in useMenu.test.tsx, full lint clean.
There was a problem hiding this comment.
The aria-label is the fallback accessible name for sections without a heading. useMenuSection labels the group via aria-labelledby when a heading is present, and falls back to aria-label when the section has no heading (the heading is optional in the ARIA menu pattern). Without it, an unheaded section group would have no accessible name at all.
I kept it and added a test that locks that branch: a Section with aria-label and no title renders a group with aria-label and no aria-labelledby. So the accessible-name contract of sectioned menus is covered on both paths - labelled-by-heading and labelled-by-aria-label.
Verified: 5/5 in useMenu.test.tsx, full lint clean.
Use node.props.title for the section heading (matching how Section renders it) and drop the redundant aria-label - with a heading present, useMenuSection labels the group via aria-labelledby.
useMenuSection labels a section group via aria-labelledby when a heading is present, and falls back to aria-label when the section has no heading. The new test locks that fallback branch so the accessible name contract of sectioned menus stays covered.
snowystinger
left a comment
There was a problem hiding this comment.
you might find this an easier test to write at the RAC level instead of the hook level
| return ( | ||
| <div {...itemProps}> | ||
| {node.props.title && <span {...headingProps}>{node.props.title}</span>} | ||
| <div {...groupProps}> |
There was a problem hiding this comment.
i know that in our RAC implementation, headings + menu items are siblings. as in, items are not grouped in a wrapper separate from the heading. and as @snowystinger said, probably easier to do this as the RAC level
snowystinger senalo en 3919829010 que el span del heading debe seguir renderizando node.rendered, no node.props.title - matching how the real MenuSection component in @adobe/react-spectrum does it. The hook already uses node.rendered for the heading prop, this restores the same source for the visible span. Also explored migrating the test to react-aria-components level with a real Virtualizer (per snowystinger's suggestion in 5096476622) - found that the Virtualizer path exposes different posinset behavior (resets per section, presumably a separate issue from the original adobe#10487 hook contract this PR locks in). Keeping the test at the hook level where the original contract was verified.
|
Both points addressed in a0bde03:
Since that is a different contract and a different fix, I kept the test at the hook level where the original #10487 contract was verified, and left the Virtualizer-level finding as a separate observation rather than mixing it into this PR. Happy to open a follow-up issue for the Virtualizer behavior if it is in scope. Verified: 5/5 in useMenu.test.tsx, full lint clean. |

What
Test-only PR: adds a regression test that locks in global
aria-posinsetacross sections in a virtualized menu.Why
From the discussion in #9556:
aria-posinsetmust be consistent witharia-setsize(the global item count) for sectioned collections. The current code already produces the correct global position (1..4 across two sections, matchingaria-setsize=4), but nothing guards it. This test pins the behavior so a future refactor of the index computation cannot silently reintroduce per-section restart.Notes
main(verified locally, 4/4 in useMenu.test.tsx).