-
Notifications
You must be signed in to change notification settings - Fork 0
π¨ Palette: μ 보(Score) λ·°μ μ κ·Όμ±(Accessibility) λ° ν΄ν νμ κ°μ #977
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,6 @@ | ||
| ## 2024-05-19 - Replace HTML disabled with aria-disabled="true" for Accessible Tooltips | ||
| **Learning:** Native HTML `disabled` attributes completely hide elements from screen readers and block all pointer/hover events, preventing tooltips from functioning for disabled elements. | ||
| **Action:** Replace `disabled` with `aria-disabled="true"`, enforce block click handlers via `e.preventDefault()`, and add a title tooltip directly to the element to maintain full tooltip accessibility and keyboard focus support for visually impaired and mouse users. | ||
| ## 2025-02-12 - Playwright Frontend Verification for Score Components | ||
| **Learning:** In headless Playwright scripts interacting with the BandScope desktop frontend on `localhost:5173`, interacting with dynamic or conditionally rendered components (like the "Open Project" button, demo track rows, or nested tabs like "Score") requires careful handling of element counts and using `force=True` on `.first` locator clicks due to overlapping layers or strict mode violations in complex UIs. The "Score" tab specifically can be consistently found using `page.locator("nav a").filter(has_text="Score")`. | ||
| **Action:** When writing Playwright verification scripts for this UI, always handle potential `count() == 0` edge cases for conditional elements (e.g., demo lists vs home states), use `.first.click(force=True)` to bypass overlapping pointer-events interception, and ensure enough `page.wait_for_timeout` delays for CSS transitions/data loading before capturing screenshots of disabled or state-dependent buttons. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -134,8 +134,14 @@ export function ScoreView({ song, projectId, onSongUpdate }: ScoreViewProps) { | |
| <p className="mt-1 max-w-2xl text-sm text-slate-400">{t("scoreViewSubtitle")}</p> | ||
| </div> | ||
| <Button | ||
| onClick={projectId ? () => void handleAttach(projectId) : undefined} | ||
| disabled={!projectId || isAttaching} | ||
| onClick={(e) => { | ||
| if (!projectId || isAttaching) { | ||
| e.preventDefault(); | ||
| } else { | ||
| void handleAttach(projectId); | ||
| } | ||
| }} | ||
| aria-disabled={!projectId || isAttaching} | ||
|
Comment on lines
+137
to
+144
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. π‘ Disabled attach button still shows no tooltip The attach button was changed from Prompt for agentsWas this helpful? React with π or π to provide feedback. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. π Info: Enabled state renders aria-disabled="false" instead of omitting it
Was this helpful? React with π or π to provide feedback. |
||
| variant="secondary" | ||
| className="min-h-11 border border-cyan-300/20 bg-cyan-300/10 font-semibold text-cyan-50 hover:bg-cyan-300/20" | ||
| > | ||
|
|
@@ -183,20 +189,32 @@ export function ScoreView({ song, projectId, onSongUpdate }: ScoreViewProps) { | |
| > | ||
| <button | ||
| type="button" | ||
| onClick={projectId ? () => void openAttachment(projectId, attachment) : undefined} | ||
| disabled={!projectId} | ||
| onClick={(e) => { | ||
| if (!projectId) { | ||
| e.preventDefault(); | ||
| } else { | ||
| void openAttachment(projectId, attachment); | ||
| } | ||
| }} | ||
| aria-disabled={!projectId} | ||
|
Comment on lines
+192
to
+199
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. π‘ Disabled open-score button shows no tooltip The open-score list button uses Prompt for agentsWas this helpful? React with π or π to provide feedback. |
||
| aria-current={selected?.id === attachment.id ? "true" : undefined} | ||
| aria-label={`${t("scoreOpen")}: ${attachment.fileName}`} | ||
| className="flex min-h-10 min-w-0 flex-1 items-center gap-2 text-left text-sm font-semibold text-slate-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-cyan-300 disabled:cursor-not-allowed disabled:opacity-60" | ||
| className="flex min-h-10 min-w-0 flex-1 items-center gap-2 text-left text-sm font-semibold text-slate-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-cyan-300 aria-disabled:cursor-not-allowed aria-disabled:opacity-60" | ||
| > | ||
| <FileMusic className="size-4 shrink-0 text-cyan-300" aria-hidden="true" /> | ||
| <span className="truncate">{attachment.fileName}</span> | ||
| </button> | ||
| <Button | ||
| variant="outline" | ||
| size="icon" | ||
| onClick={projectId ? () => void handleRemove(projectId, attachment) : undefined} | ||
| disabled={!projectId} | ||
| onClick={(e) => { | ||
| if (!projectId) { | ||
| e.preventDefault(); | ||
| } else { | ||
| void handleRemove(projectId, attachment); | ||
| } | ||
| }} | ||
| aria-disabled={!projectId} | ||
|
Comment on lines
+210
to
+217
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. π‘ Disabled remove-score button shows no tooltip The remove button uses Prompt for agentsWas this helpful? React with π or π to provide feedback. |
||
| aria-label={`${t("scoreRemove")}: ${attachment.fileName}`} | ||
| className="size-10 border-rose-300/25 text-rose-200 hover:bg-rose-400/10" | ||
| > | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -292,8 +292,14 @@ export function ScoreViewer({ data, fileName, onStatusChange }: ScoreViewerProps | |
| size="icon-lg" | ||
| className="size-14" | ||
| aria-label={t("scoreViewerPrevPage")} | ||
| disabled={pageNumber <= 1} | ||
| onClick={goToPreviousPage} | ||
| aria-disabled={pageNumber <= 1} | ||
| onClick={(e) => { | ||
| if (pageNumber <= 1) { | ||
| e.preventDefault(); | ||
| } else { | ||
| goToPreviousPage(); | ||
| } | ||
| }} | ||
|
Comment on lines
+295
to
+302
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. π‘ Disabled previous-page button shows no tooltip The previous-page button uses Prompt for agentsWas this helpful? React with π or π to provide feedback. |
||
| > | ||
| <ChevronLeft className="size-6" aria-hidden="true" /> | ||
| </Button> | ||
|
|
@@ -305,8 +311,14 @@ export function ScoreViewer({ data, fileName, onStatusChange }: ScoreViewerProps | |
| size="icon-lg" | ||
| className="size-14" | ||
| aria-label={t("scoreViewerNextPage")} | ||
| disabled={pageNumber >= pageCount} | ||
| onClick={goToNextPage} | ||
| aria-disabled={pageNumber >= pageCount} | ||
| onClick={(e) => { | ||
| if (pageNumber >= pageCount) { | ||
| e.preventDefault(); | ||
| } else { | ||
| goToNextPage(); | ||
| } | ||
| }} | ||
|
Comment on lines
+314
to
+321
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. π‘ Disabled next-page button shows no tooltip The next-page button uses Prompt for agentsWas this helpful? React with π or π to provide feedback. |
||
| > | ||
| <ChevronRight className="size-6" aria-hidden="true" /> | ||
| </Button> | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π Info: Click guards match the aria-disabled conditions
Each new onClick guard uses the same condition as its
aria-disabledattribute (e.g.ScoreViewer.tsx:297,ScoreViewer.tsx:316), so clicking or keyboard-activating a visually-disabled button only callspreventDefault()and never runs the action. No action leaks through.Was this helpful? React with π or π to provide feedback.