feat(fuselage): Migrate `Option`, `Options`, `Select` and `Menu` to `Item` components
Proposed changes (including videos or screenshots)
Renders Option, Options, Select and Menu with the Item family from #2281. Rows share one layout and one set of states, and the public rcx-option classes stay in place.
Item
- Menu-item rows (
role="menuitem*") are named by theirItemTitleand described by a labelledItemIcon, as anItemLinkis. - New
focusVisibleprop shows the focus ring for focus moved by a script, such as a menu's. The browser's:focus-visiblealso matches on hover there.
Menu
MenuItem, submenu triggers andMenuSectionrender asItem,ItemGroupHeaderandItemGroup.
Option, Options and Select
Optionrenders as anItemrow. TheOption*parts, and theirMenuItem*aliases, render as the matchingItempart:ItemMedia,ItemIcon,ItemContent,ItemTitle,ItemDescriptionandItemActions.OptionHeaderandOptionTitlerender asItemGroupHeader.Optionsdividers render asItemDivider, andSelectsections asItemGroupHeaderandItemGroup.- New
iconLabelprop onOptionnames what itsiconconveys. Without it, the icon stays decorative.
Fixes
Optionsscrolls to the keyboard cursor whenrenderItemrenders anItem.- The
CheckOptioncheckbox is hidden from assistive technology and made inert, so selection is announced once, througharia-selected. OptionSkeletonplaceholder rows are hidden from assistive technology.- The
Optionsempty state is a disabled option, so the listbox only contains options.
Issue(s)
Related: RocketChat/Rocket.Chat#42520, which applies the Item components in Rocket.Chat.
Further comments
Part of the Item migration, split into three independent PRs off main:
Why Options and Menu are in one PR: the public MenuItem* parts re-export the Option* parts.
- Migrating
Optionalone would putItemparts inside the oldMenuOptionrows. Those parts are only styled inside.rcx-item, and thercx-option__wrapperrule that menus still use would be gone, so every menu would break. - Migrating
Menualone would leave oldOptioncontent insideItemrows.
Merge order: please merge this PR first. Adding focusVisible and the ItemTitle ids changes generated React ids, so the other two PRs only need a rebase and yarn jest -u once this lands.
Commits are ordered so each one builds and passes on its own: Item and Menu first, then Option/Options/Select, then the alias tests. Each component change has jest-axe coverage and updated snapshots. fuselage.api.md and the visual-regression snapshots were regenerated for this branch.
š¤ Generated with Claude Code