[Bug Fix] DropdownMenu: typeahead on indicator items, and Tab moves on past the trigger
PI
20h ago+24+24+01234567890123456789-13-13-01234567890123456789
Related issue
None opened. Follow-up of the shadcn audit on #579 (the DataTable Columns menu).
Description
Two menu keys behaved differently from shadcn (Base UI):
- Typeahead did nothing on items with an indicator (checkbox and radio items).
labelOf()also read comment nodes, so with Rails'annotate_rendered_view_with_filenamesthe label becameBefore RubyUI::DropdownMenuItemIndicatorStatusand no prefix matched. It now reads only text nodes and elements, inDropdownMenuandContextMenu. - Tab with the menu open closed it and left focus on the trigger. Base UI moves on to the next control, and Shift+Tab stops on the trigger.
close()already returns focus to the trigger, so Tab's default now runs from there; Shift+Tab is still prevented. The docs text and the controller header say so.


Dark


shadcn audit · main
shadcn audit ruby-ui/ruby_ui
base 1fd93db0 head 1fd93db0 shadcn 8a353667
✅ Theme neutral tokens match shadcn
DataTable (ref: bases/base/ui/dropdown-menu.tsx + style-vega.css @ 8a353667)
✅ Behavior click/Enter/ArrowDown open; ArrowDown/ArrowUp wrap; Home/End; Esc and outside click close and return focus to the trigger; menuitemcheckbox + aria-checked
Driven with Playwright on both sides, same key sequence.
↳ bases/base/ui/dropdown-menu.tsx
⚠️ Behavior typeahead: focus on Reviewer, type s -> focus stays on Reviewer
shadcn: typeahead: focus moves to Status
labelOf() reads comment nodes too: with annotate_rendered_view_with_filenames the label is "Before RubyUI::DropdownMenuItemIndicatorStatus", so no prefix ever matches. Same code in ContextMenu.
↳ bases/base/ui/dropdown-menu.tsx
⚠️ Behavior Tab with the menu open: closes, focus stays on the trigger
shadcn: Tab: closes, focus moves to the next control (Select all); Shift+Tab: closes, focus on the trigger
The controller prevented Tab's default after close().
↳ bases/base/ui/dropdown-menu.tsx
✅ Animation exit@dropdown-menu-content on close
Sampled right after Esc on both sides.
↳ style-vega.css
shadcn audit · this PR
shadcn audit ruby-ui/ruby_ui
base 1fd93db0 head ee918f1e shadcn 8a353667
✅ Theme neutral tokens match shadcn
DataTable (ref: bases/base/ui/dropdown-menu.tsx + style-vega.css @ 8a353667)
✅ Behavior click/Enter/ArrowDown open; ArrowDown/ArrowUp wrap; Home/End; Esc and outside click close and return focus to the trigger; menuitemcheckbox + aria-checked
Driven with Playwright on both sides, same key sequence.
↳ bases/base/ui/dropdown-menu.tsx
✅ Behavior typeahead: focus on Reviewer, type s -> Status
labelOf() keeps only text nodes and elements. ContextMenu gets the same fix.
↳ bases/base/ui/dropdown-menu.tsx
✅ Behavior Tab: closes, focus on Select all; Shift+Tab: closes, focus on the trigger
close() focuses the trigger first, then Tab's default moves on from it; Shift+Tab is prevented so it stops on the trigger.
↳ bases/base/ui/dropdown-menu.tsx
✅ Animation exit@dropdown-menu-content on close
Sampled right after Esc on both sides.
↳ style-vega.css
Testing instructions
cd gem && bundle exec rake→ 775 runs, 0 failures, StandardRB clean.cd mcp && bundle exec rake→ 33 runs, 0 failures./docs/data_table, Columns menu: ArrowDown to Reviewer, types→ focus on Status.- Same menu: Tab → it closes and focus lands on the "Select all" checkbox; reopen, Shift+Tab → focus on Columns.
/docs/context_menu: typeahead still moves through plain items.
🤖 Generated with Claude Code
Summary by cubic
Fixes two DropdownMenu and ContextMenu keyboard behaviors to match Base UI: typeahead now works on items with an indicator, and Tab moves focus past the trigger instead of leaving it on the trigger.
Changes
- Typeahead:
labelOf()reads only text nodes and elements, so comment nodes from Rails'annotate_rendered_view_with_filenamesno longer break prefix matching on checkbox and radio items. - Tab: closing the menu returns focus to the trigger first, so Tab's default carries on to the next control; Shift+Tab is still prevented and stops on the trigger. The closing menu is kept inert while it animates out so focus doesn't land back inside it, and reopening clears that.
Updated the docs text and the controller header comment to describe the new Tab behavior.