From 1e083a33d985884314787283b91ebfe31ae376cd Mon Sep 17 00:00:00 2001 From: ayaangazali Date: Thu, 20 Aug 2026 10:55:36 -0700 Subject: [PATCH] fix(html): make remaining mouse-only controls keyboard accessible Four controls were unreachable with the keyboard, in the same way tabs, chips, expandable titles and the image diff switcher were before #41434, #42149, #42310 and #42311. The settings gear and the Metadata toggle declared role="button" without a tabIndex, so assistive tech announced a button that could not be operated. The tag chips and the trace viewer "Show all" carried no role at all, so they were not announced as controls in the first place. All four now render native buttons with a focus ring. The error and warning badge in the action list is deliberately left alone: it sits inside a treeitem, so giving it an accessible name adds that name to every action row, which is a regression in the tree rather than an improvement. --- packages/html-reporter/src/common.css | 12 ++++++++++++ packages/html-reporter/src/headerView.spec.ts | 10 ++++++++++ packages/html-reporter/src/headerView.tsx | 11 ++++++----- packages/html-reporter/src/labels.css | 9 +++++++++ packages/html-reporter/src/labels.tsx | 8 +++++--- packages/html-reporter/src/metadataView.css | 9 +++++++++ packages/html-reporter/src/testFilesView.tsx | 4 ++-- packages/trace-viewer/src/ui/actionList.css | 14 ++++++++++++++ packages/trace-viewer/src/ui/actionList.tsx | 2 +- 9 files changed, 68 insertions(+), 11 deletions(-) diff --git a/packages/html-reporter/src/common.css b/packages/html-reporter/src/common.css index c67df07e6aa16..b7c7735641df1 100644 --- a/packages/html-reporter/src/common.css +++ b/packages/html-reporter/src/common.css @@ -211,6 +211,18 @@ article, aside, details, figcaption, figure, footer, header, main, menu, nav, se user-select: none; } +button.subnav-item { + background: none; + font-family: inherit; + font-size: inherit; + cursor: pointer; +} + +.subnav-item:focus-visible { + outline: 1px solid var(--color-accent-fg); + outline-offset: -1px; +} + .subnav-item:hover { background-color: var(--color-canvas-subtle); } diff --git a/packages/html-reporter/src/headerView.spec.ts b/packages/html-reporter/src/headerView.spec.ts index b63213b1e8c3b..0aa9c753fad55 100644 --- a/packages/html-reporter/src/headerView.spec.ts +++ b/packages/html-reporter/src/headerView.spec.ts @@ -57,3 +57,13 @@ test('should toggle filters', async ({ page, mount }) => { await component.getByRole('textbox').fill('annot:annotation type=annotation description'); await expect(filterText).toHaveValue('annot:annotation type=annotation description'); }); + +test('settings button should open with the keyboard', async ({ mount, page }) => { + const component = await mount('headerView/Default'); + const settings = component.getByRole('button', { name: 'Settings' }); + await settings.focus(); + await expect(settings).toBeFocused(); + await expect(settings).toHaveAttribute('aria-expanded', 'false'); + await page.keyboard.press('Enter'); + await expect(settings).toHaveAttribute('aria-expanded', 'true'); +}); diff --git a/packages/html-reporter/src/headerView.tsx b/packages/html-reporter/src/headerView.tsx index 86e8db01492bf..f36978d5df0fc 100644 --- a/packages/html-reporter/src/headerView.tsx +++ b/packages/html-reporter/src/headerView.tsx @@ -128,24 +128,25 @@ const NavLink: React.FC<{ }; const SettingsButton: React.FC = () => { - const settingsRef = React.useRef(null); + const settingsRef = React.useRef(null); const [settingsOpen, setSettingsOpen] = React.useState(false); const [theme, setTheme] = useThemeSetting(); return <> -
{ setSettingsOpen(!settingsOpen); e.preventDefault(); }} onMouseDown={preventDefault}> {icons.settings()} -
+ void, colorIndex?: number, }> = ({ label, href, onClick, colorIndex, trimAtSymbolPrefix }) => { - const baseLabel = onClick(e, label) : undefined}> - {trimAtSymbolPrefix && label.startsWith('@') ? label.slice(1) : label} - ; + const className = clsx('label', 'label-color-' + (colorIndex !== undefined ? colorIndex : hashStringToInt(label))); + const text = trimAtSymbolPrefix && label.startsWith('@') ? label.slice(1) : label; + const baseLabel = onClick + ? + : {text}; return href ? {baseLabel} diff --git a/packages/html-reporter/src/metadataView.css b/packages/html-reporter/src/metadataView.css index b91a5e830c7e3..9a6ce397f2fcb 100644 --- a/packages/html-reporter/src/metadataView.css +++ b/packages/html-reporter/src/metadataView.css @@ -18,6 +18,15 @@ cursor: pointer; user-select: none; color: var(--color-fg-default); + padding: 0; + background: none; + border: none; + font: inherit; +} + +.metadata-toggle:focus-visible { + outline: 1px solid var(--color-accent-fg); + outline-offset: 1px; } .metadata-toggle-second-line { diff --git a/packages/html-reporter/src/testFilesView.tsx b/packages/html-reporter/src/testFilesView.tsx index 84dbc988093bd..0c09a40da0a65 100644 --- a/packages/html-reporter/src/testFilesView.tsx +++ b/packages/html-reporter/src/testFilesView.tsx @@ -81,9 +81,9 @@ export const TestFilesHeader: React.FC<{ const isMetadataInTopLine = !showProject && !filteredStats; const metadataToggleButton = !isMetadataEmpty(report.metadata) && ( -
+
+ ); const leftSuperHeader =
diff --git a/packages/trace-viewer/src/ui/actionList.css b/packages/trace-viewer/src/ui/actionList.css index 435b6ada3f161..e9b3e01dc9fbd 100644 --- a/packages/trace-viewer/src/ui/actionList.css +++ b/packages/trace-viewer/src/ui/actionList.css @@ -85,6 +85,20 @@ border-bottom: 1px solid transparent; } +button.action-list-show-all { + padding: 0; + background: none; + border: none; + color: inherit; + font: inherit; + text-align: left; +} + +.action-list-show-all:focus-visible { + outline: 1px solid var(--vscode-focusBorder); + outline-offset: -1px; +} + .action-icons:hover { border-bottom: 1px solid var(--vscode-sideBarTitle-foreground); } diff --git a/packages/trace-viewer/src/ui/actionList.tsx b/packages/trace-viewer/src/ui/actionList.tsx index a9c871a9b397d..f595c3b22805c 100644 --- a/packages/trace-viewer/src/ui/actionList.tsx +++ b/packages/trace-viewer/src/ui/actionList.tsx @@ -112,7 +112,7 @@ export const ActionList: React.FC = ({ }, [setSelectedTime]); return
- {selectedTime &&
Show all
} + {selectedTime && }