Skip to content

fix: group menu bar recent items and disable empty submenus - #160

Merged
Azganoth merged 3 commits into
mainfrom
bug/menu-bar-groups-and-empty-submenus
Aug 1, 2026
Merged

fix: group menu bar recent items and disable empty submenus#160
Azganoth merged 3 commits into
mainfrom
bug/menu-bar-groups-and-empty-submenus

Conversation

@Azganoth

@Azganoth Azganoth commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Summary

Three structural problems in the command menu bar, all in the same markup.

  • Recent items grouping. Radix renders MenuLabel as an unroled div, and nothing wrapped the items beneath it, so Recent files and Recent folders named nothing and the submenu read as one undifferentiated run of paths. Each block is now a MenubarGroup with aria-labelledby pointing at its label, so every path is announced inside its section. That boundary matters most when the same directory appears in both lists — a folder that was opened, plus a file inside it.
  • Empty submenus. A submenu trigger over an all-disabled command set stayed enabled. The submenu opened, Radix focused the empty role="menu" container, and arrow keys had nothing to move through. Triggers now derive disabled from the commands behind them.
  • Dead label. aria-label="Menu bar" sat on a roleless div, where it is never exposed. It is deleted rather than relocated: Radix already supplies role="menubar" on the root, and naming it "Menu bar" would repeat the role for the only menubar in the app.

RadioSubmenu is covered alongside CommandSubmenu because it has a real case, not for symmetry: all three view.sort.* commands are disabled when no folder context is open, so Sort articles by had the same empty-submenu shape as Table. LineEndingSubmenu and RecentItemsSubmenu are deliberately excluded — each holds a command that stays available on its own (Insert final newline on save, Clear recent items), so neither can reach the all-disabled state.

MenubarSubTrigger had no data-disabled styling, because nothing disabled it before. It now takes the same pointer-events-none and opacity-50 treatment MenubarItem already carried.

Related Issue

Closes #119

Verification

Automated (pnpm check:frontend, Windows 11, 922 frontend tests across 92 files, exit 0):

  • src/components/layout/CommandMenuBar.test.tsx gains five tests. Grouping asserts each path resolves within its own role="group". The submenu rule is covered from four directions: all-disabled (Table carries data-disabled and aria-disabled, and clicking it does not open), partially disabled (Table stays enabled and opens, with the unavailable items disabled inside), the radio case (Sort articles by disabled, Appearance not), and the always-available case (Line ending stays enabled while Copy as does not).
  • The new tests were confirmed to fail against the unmodified component: stashing only CommandMenuBar.tsx fails the grouping test and three of the four submenu tests. The fourth — a partially disabled trigger stays enabled — passes either way by design; it is the guard against over-disabling, not a regression test for this fix.
  • Coverage thresholds clear without change: statements 90.28%, branches 80.77%, functions 91.61%, lines 91.87%.

Manual, on Windows 11 through pnpm tauri dev, with Narrator. jsdom applies no Tailwind and has no accessibility tree, so the dimmed trigger and the spoken group boundary are only observable here:

  1. Format > Table and View > Sort articles by render dimmed and do not open when every command behind them is unavailable.
  2. Arrowing through File > Open recent announces each path within Recent files or Recent folders rather than as one continuous run.
  3. The issue's stability bar holds: roving tabindex, arrow traversal, typeahead, Escape handling, and focus return to the trigger on close are unchanged.

Not verified: platforms other than Windows, and a bundled build.

Notes

  • docs/reference.md extends the availability rule rather than adding a new one. The rule already said inactive commands are disabled rather than hidden; a trigger that opens onto nothing but disabled commands was the same situation one level up.
  • Out of scope and unchanged: command availability rules themselves; focus indicator styling on the triggers (Keyboard focus is invisible or misplaced across hand-built chrome #118, merged); the editor context popup, which exposes the same command IDs but has its own reachability problems (Editor context popup cannot be reached or operated by keyboard #120).
  • DropdownMenu.tsx has the same undecorated sub-trigger, left alone because nothing disables it there.

Radix renders MenuLabel as an unroled div, so the two headings named
nothing on their own and the submenu read as one undifferentiated run of
paths. Wrapping each block in MenubarGroup with aria-labelledby gives the
paths a section boundary, which matters most when the same directory
appears in both lists.
An enabled trigger over an all-disabled command set opened a menu Radix
focused with nothing in it, which arrow keys could not move through. The
availability rule already said inactive commands are disabled rather than
hidden; extending it to the trigger keeps the two consistent.

Both derived-state submenus are covered: `Sort articles by` is the radio
case, disabled with no folder context open. `Line ending` and `Open
recent` are deliberately not, because each holds a command that stays
available on its own.
`aria-label` on a generic element is not exposed, so the name was already
dropped. It is deleted rather than relocated: Radix supplies role="menubar"
on the root, and naming that "Menu bar" would only repeat the role for the
one menubar in the app.
@Azganoth Azganoth added the Bug Something isn't working label Aug 1, 2026
@Azganoth Azganoth self-assigned this Aug 1, 2026
@Azganoth
Azganoth merged commit 07f668c into main Aug 1, 2026
2 checks passed
@Azganoth
Azganoth deleted the bug/menu-bar-groups-and-empty-submenus branch August 1, 2026 21:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Menu bar recent items are ungrouped and fully disabled submenus open empty

1 participant