Skip to content

fix(html-reporter): use a button element for the chip header - #42149

Merged
Simon Knott (Skn0tt) merged 2 commits into
microsoft:mainfrom
skvortsov-dev:fix/html-reporter-keyboard-chip
Aug 6, 2026
Merged

fix(html-reporter): use a button element for the chip header#42149
Simon Knott (Skn0tt) merged 2 commits into
microsoft:mainfrom
skvortsov-dev:fix/html-reporter-keyboard-chip

Conversation

@skvortsov-dev

@skvortsov-devDenis Skvortsov (skvortsov-dev) commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Rationale

Came across this while reading through the html-reporter components. The chip header — every collapsible section of a test (Errors, Test Steps, Screenshots, Traces, Attachments) and every file row in the list — is a <div role="button">. A div can't take focus, so those sections cannot be expanded or collapsed with a keyboard at all: $$('.chip-header').map(e => e.tabIndex) returns -1 for each and Tab walks past them, even though the header already carries aria-expanded/aria-controls and chip.spec.ts asserts it as a button in an aria snapshot.

This converts the header to a <button> and adds a :focus-visible outline, the same way tab elements were converted in #41440 and #41434. No key handlers — Enter and Space come from the platform.

Before — Tab walks past every section header, so nothing can be expanded from the keyboard:

chip-keyboard-check-before.mov

After — the headers take focus, Enter and Space toggle them:

chip-keyboard-check-after.mov

Test

chip.spec.ts gets a keyboard case: the header takes focus and toggles on Enter and Space. On the current <div> it fails with toBeFocused receiving inactive.

The chip header is a <div role="button">, so it cannot take focus and the
collapsible sections of a test - Errors, Test Steps, Screenshots, Traces,
Attachments - cannot be expanded or collapsed with a keyboard at all. Make it
a button and add a :focus-visible outline, the same way tab elements were
converted.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Chip now renders a focusable <button> even when setExpanded is undefined, creating a misleading non-actionable control for keyboard/AT users.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Pull request overview

Improves keyboard accessibility in the html-reporter by converting Chip section headers from a non-focusable div role="button" to a native <button> and adding a keyboard interaction test.

Changes:

  • Replace chip header container with a <button> so it can receive focus and handle Enter/Space natively.
  • Add :focus-visible outline styling for the chip header.
  • Add a spec that verifies keyboard expand/collapse behavior.
File summaries
FileDescription
packages/html-reporter/src/chip.tsxConverts the chip header wrapper to a native button element.
packages/html-reporter/src/chip.cssStyles the new button-based header and adds a :focus-visible outline.
packages/html-reporter/src/chip.spec.tsAdds a keyboard-focused test for expand/collapse.
Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 1
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Comment threadpackages/html-reporter/src/chip.tsx Outdated
Comment on lines +37 to +45
<button
aria-expanded={!!expanded}
aria-controls={id}
className={clsx('chip-header', setExpanded && ' expanded-' + expanded)}
onClick={() => setExpanded?.(!expanded)}
title={typeof header === 'string' ? header : undefined}>
{setExpanded ? (expanded ? <icons.downArrow /> : <icons.rightArrow />) : <icons.spacer />}
{header}
</div>
</button>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with Copilot! SlowestTests uses this component in a static way, where it's not actually expandable. Denis Skvortsov (@skvortsov-dev) can you maybe update this so we switch between button and div depending on whether the element is expandable?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — the header is a <button type="button"> only when the chip is expandable, a plain <div> otherwise.

The Speedboard aria snapshots needed - text: /Slowest Tests/ rather than an exact match: without a role the header text coalesces with the neighbouring text node, which carries the run timestamp. chip.spec.ts also gets a case asserting a non-expandable chip exposes no button.

Component tests 17 passed, reporter-html.spec.ts + reporter-blob.spec.ts 238 passed, npm run lint clean.

SlowestTests renders Chip without setExpanded, so that header is static.
Render a button only when the chip is expandable and a plain div otherwise,
and mark the interactive case with type='button'.
The aria snapshots in reporter-html.spec.ts are updated accordingly: the
Speedboard header is no longer exposed as a button.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Ready to approve

The change correctly improves keyboard accessibility with native buttons, updates styling, and includes targeted tests and snapshot updates to validate the new behavior.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Review details
  • Files reviewed: 5/5 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

Copy link
Copy Markdown
Contributor

Test results for "tests 1"

1 flaky⚠️ [firefox-page] › page/page-event-request.spec.ts:181 › should return response body when Cross-Origin-Opener-Policy is set `@firefox-ubuntu-22.04-node20`

51238 passed, 1190 skipped


Merge workflow run.

@github-actions

Copy link
Copy Markdown
Contributor

Test results for "MCP"

1 failed
❌ [chrome] › mcp/unicode-serialization.spec.ts:33 › unicode serialization › handles console messages with lone surrogates @mcp-macos-latest-chrome

8049 passed, 1284 skipped


Merge workflow run.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@skvortsov-dev@Skn0tt