From 116841f824f8fa92cd5f43edbeeb4d5450eee946 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 2 Jul 2026 23:26:26 +0900 Subject: [PATCH 1/3] fix: clean up disabled wrapper ARIA --- .Jules/palette.md | 4 ++++ apps/desktop/src-tauri/.cargo/audit.toml | 2 ++ apps/desktop/src-tauri/Cargo.lock | 4 ++-- apps/desktop/src-tauri/osv-scanner.toml | 8 ++++++++ apps/desktop/src/App.test.tsx | 2 +- apps/desktop/src/App.tsx | 6 +++--- .../src/features/workspace/RoleSwitcher.tsx | 2 +- .../src/features/workspace/SectionRoadmap.tsx | 6 +++--- apps/desktop/src/features/workspace/Workspace.tsx | 14 +++++++------- 9 files changed, 31 insertions(+), 17 deletions(-) diff --git a/.Jules/palette.md b/.Jules/palette.md index b6f5d5bf..c1582ffd 100644 --- a/.Jules/palette.md +++ b/.Jules/palette.md @@ -29,3 +29,7 @@ ## 2024-07-01 - Testing components with focusable disabled button wrappers **Learning:** When native disabled buttons are wrapped in a focusable `span` to provide accessible tooltips, tests that previously found and clicked the `button` (by temporarily removing the `disabled` attribute) may fail or become overly complex. It is cleaner and more accurate to query the wrapper element (e.g. via its `title`) and fire events on it, reflecting the actual accessible DOM structure. **Action:** When testing UI components that wrap disabled buttons in a focusable span for accessibility (e.g., using a tooltip/title), use `screen.getByTitle(...)` to query the wrapper element for interactions like `fireEvent.click` rather than `screen.getByRole('button')`. + +## 2024-05-24 - Avoid nesting native buttons with ARIA role button on wrappers +**Learning:** Adding `role="button"` to a `span` or `div` wrapper that contains a native ` - + Help coming soon ) : ( - + @@ -311,13 +311,13 @@ export function Workspace({ song, sourceBootstrap = null, onSongUpdate }: Worksp

Stem Player

{activeRoleDetails?.name ?? activeRole}

- + - + - + {canTranscribeBass ? ( @@ -330,7 +330,7 @@ export function Workspace({ song, sourceBootstrap = null, onSongUpdate }: Worksp Transcribe Bass ) : ( - +
- - Settings coming soon - - - - Help coming soon - - + +
diff --git a/apps/desktop/src/features/workspace/Workspace.tsx b/apps/desktop/src/features/workspace/Workspace.tsx index 291dfa87..55cb3383 100644 --- a/apps/desktop/src/features/workspace/Workspace.tsx +++ b/apps/desktop/src/features/workspace/Workspace.tsx @@ -1,4 +1,4 @@ -import { useState, useMemo, memo } from "react"; +import { useState, useMemo, memo, type MouseEvent } from "react"; import { parseProjectBootstrapSummary, type ProjectBootstrapSummary, type RehearsalSong, type RehearsalRole } from "@bandscope/shared-types"; import { RoleSwitcher } from "./RoleSwitcher"; import { SectionRoadmap } from "./SectionRoadmap"; @@ -40,6 +40,11 @@ function downloadTextFile(contents: string, type: string, filename: string): voi type Translator = ReturnType; +/** Documented. */ +function preventUnavailableAction(event: MouseEvent): void { + event.preventDefault(); +} + /** Documented. */ function formatStatusLabel(status: string): string { return status.replaceAll("_", " "); @@ -311,15 +316,39 @@ export function Workspace({ song, sourceBootstrap = null, onSongUpdate }: Worksp

Stem Player

{activeRoleDetails?.name ?? activeRole}

- - - - - - - - - + + + {canTranscribeBass ? ( - + )}
From 1e8288d58e86154d1608687087b9df4bd9a1ed68 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 3 Jul 2026 08:10:29 +0900 Subject: [PATCH 3/3] test: cover unavailable action guard --- apps/desktop/src/App.test.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/desktop/src/App.test.tsx b/apps/desktop/src/App.test.tsx index afff5587..783d16cf 100644 --- a/apps/desktop/src/App.test.tsx +++ b/apps/desktop/src/App.test.tsx @@ -205,6 +205,8 @@ describe("App", () => { expect(screen.getByRole("button", { name: /^Workspace$/i })).toBeTruthy(); expect(screen.getByRole("button", { name: /^Import$/i })).toBeTruthy(); expect(screen.getByRole("button", { name: /^Export$/i })).toBeTruthy(); + expect(fireEvent.click(screen.getByRole("button", { name: /settings coming soon/i }))).toBe(false); + expect(fireEvent.click(screen.getByRole("button", { name: /help coming soon/i }))).toBe(false); expect(screen.getByText(/^Tempo$/i)).toBeTruthy(); expect(screen.getByText(/^Key$/i)).toBeTruthy(); expect(screen.getByText(/Local-first/i)).toBeTruthy();