Catalog: give the facet "Sort by" control an accessible name (5217 stack 3/9) - #5261
Catalog: give the facet "Sort by" control an accessible name (5217 stack 3/9)#5261nl0 wants to merge 7 commits into
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #5261 +/- ##
==========================================
+ Coverage 34.92% 35.16% +0.23%
==========================================
Files 741 741
Lines 23804 23806 +2
Branches 6429 6429
==========================================
+ Hits 8314 8371 +57
+ Misses 13746 13692 -54
+ Partials 1744 1743 -1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
5b4cde6 to
1b163f9
Compare
Local code review — findings and dispositionsReviewed as this layer's diff ( GreptileIts summary sits in the PR description (that is where it delivers here), confidence 5/5, "no actionable defects identified", on commit It has not re-run since the commits below were pushed, and going by the rest of the stack it will not: PR 1, PR 2 and PR 4 all had commits land after their review — PR 1 by fast-forward, the other two by force-push — and all three still report 1. Accepted — the first fix named the control by taking the ordering away (
|
| wiring | accessible name |
|---|---|
inputProps (before the branch) |
Name A → Z — label missing |
SelectDisplayProps |
Sort by: — ordering missing |
labelId + id |
Sort by: Name A → Z |
One consequence worth noting: this is now the same fix as PR 2, so f18 no longer holds inside the stack. The third case at Queries/Athena/Workgroups.tsx:60-61 is still unfixed, and unifying this inside the shared Select wrapper is still the follow-up.
2. Accepted — the changelog entry mis-stated the before-state (d53518df)
A div[role="button"] takes its name from its contents, so the control was never unnamed: it announced the selected ordering and nothing about what the control does. "Has an accessible name" also overstated what the first commit shipped, since the name it gained had dropped that ordering.
3. Accepted — the spec withheld the one thing its failure needed to say (8e6ce6b2)
queryByRole(...).not.toBeNull() fails with expected null not to be null, which for an accessible-name regression is precisely the information the failure should not withhold. getByRole prints every role and accessible name in the tree, so against the pre-fix wiring the same failure now names the defect outright — button / Name "Sort by:" — instead of leaving the next reader to go find out what the control is actually called. It is also the house pattern (screen.getByRole('button', { name })).
4. Accepted — comment sweep (9cfe326d)
Six lines above labelId/id where four carry a constraint. The inputProps clause documented what the branch moved away from — inputProps appears nowhere in the file now, so it constrains nothing a reader can act on — and the caption-click behaviour is not why either prop is there. What is left is what someone has to know before touching the two props, stated the way QuerySelect states the same trap. The spec comment retelling that mechanism went too, keeping only the guard on the assertion: the caption alone matches /Sort by/, so loosening the name to a substring passes on wiring that drops the ordering.
The CHANGELOG entry was read in the same pass and left alone. d53518df had already corrected its premise, and the mechanism clause it keeps — "its label used to land on a hidden input" — is the register this file already writes in: neighbouring entries name bundles, adapters and compatibility corpora. What it says about before and after is accurate as it stands.
5. Recorded, not changed — two components named AvailablePackagesMetaFilters
The presentational one in Layout/PackageFilters.tsx and the render-prop data wrapper in Search/model.ts; the new spec has both in scope, one module-qualified. The name is pre-existing — exporting made it reachable, not ambiguous — and renaming either reaches outside this layer.
6. Declined — the listbox test's unnamed getByRole('button')
Only one button renders in this fixture (MoreButton needs hidden facets), and naming it there would re-assert what the first test exists to assert. If a second button ever renders unconditionally, found multiple elements points straight at it.
7. Declined — the spec's local afterEach(cleanup) is redundant
True: setup-vitest.ts registers it globally and documents why. But 47 specs under app/ register it locally as well, so dropping it here buys agreement with the setup file at the price of agreement with every sibling. Not this layer's to change.
The PR description
Still describes the SelectDisplayProps approach and records f18 as an open inconsistency inside the stack. Both are superseded by the commits above, so it needs a pass.
Verification
npx vitest run (1686 pass, 1 skipped, across 178 files), npx vitest run app/containers/Search (109 pass), npx tsc --noEmit, and oxfmt --check / oxlint on the touched files. Reverting only the component to f974f5d6 fails both new tests, with the accessible name the control actually has printed in the failure.
Layers 4–9 were rebased onto the updated chain in order and force-pushed with --force-with-lease; chain ancestry re-verified, and every PR in the stack still shows only its own layer. Each later layer's own added and removed lines are byte-identical after the rebase — only blob ids and hunk line numbers move, since this layer edits lines above them in the same files. The one conflict, in CHANGELOG.md against PR 4's entry, was resolved by keeping PR 4's own line above this layer's corrected one, leaving PR 4's diff a single added line as before.
9cfe326 to
4c2d480
Compare
4c2d480 to
38123cd
Compare
`aria-labelledby` was passed through `inputProps`, which MUI v4 spreads onto the aria-hidden native input — where a label names nothing. The focusable node is the `role="button"` display div, reachable only through `SelectDisplayProps`, so the "Sort by" control read as an unnamed button to assistive tech. Same trap `QuerySelect` documents, fixed at this call site. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
addresses review finding f28 The new spec built its own `KTree.Tree([])` for the empty facet tree, which the model already exports as `EMPTY_FACET_TREE` and uses at four call sites. A second definition of the same value in a test is a second thing to update. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
addresses review findings f44, f27 The omnibus entry covered the accessible name and two visibility changes in one line citing one PR. This is the accessible-name half, citing the PR that ships it; the visibility half lands with its own unit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
addresses review findings f1, f2, f3, f6, f10, f13, f15 `SelectDisplayProps` is spread over MUI's computed `aria-labelledby` rather than merged into it, and with no `id` on the Select the join collapses to the label alone. Since `aria-labelledby` overrides the display node's contents, the name went from "Name A → Z" to "Sort by:" — the branch traded the ordering for the label instead of getting both. `labelId` + `id` is what `QuerySelect` uses one layer down, and MUI joins them into "Sort by: Name A → Z". It also names the popup listbox and wires the caption's click through to the control, neither of which `SelectDisplayProps` can reach. Both ids now come from `useId`, so the control's name no longer depends on a hardcoded document-global IDREF. The spec asserted `name: /Sort by/`, which matched the broken and the correct wiring alike; it now pins the exact name and the listbox's, and both new tests fail on the previous commit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
addresses review finding f5 The control was not unnamed before the fix: a `div[role=button]` takes its name from its contents, so it announced the selected ordering and nothing about what the control does. "Has an accessible name" also overstated what shipped, since the name it gained dropped that ordering. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`queryByRole(...).not.toBeNull()` reports "expected null not to be null" and nothing else, which for an accessible-name regression is the one thing the failure should not withhold. `getByRole` prints every role and accessible name in the tree: against the pre-fix wiring it now names the defect outright -- `button` / `Name "Sort by:"` -- instead of leaving the reader to go find out what the control is called. The comment keeps only the constraint the assertion depends on: the caption alone matches `/Sort by/`, so loosening the name to a substring passes on wiring that drops the ordering. The mechanism behind that is stated where a reader lands on it, in the component. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Six lines where four carry a constraint. The `inputProps` clause documented what this branch moved away from -- `inputProps` appears nowhere in the file now, so it constrains nothing a reader can act on -- and the caption-click behaviour is not why either prop is here. What is left is what a reader has to know before touching the two props: `labelId` reaches the focusable node and names the popup, and `id` cannot be dropped, matching how `QuerySelect` states the same trap. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
38123cd to
0dcf150
Compare
Description
The search sidebar's "Sort by" control has no accessible name. Its
aria-labelledbywas passed throughinputProps, which MUI v4 spreads onto thearia-hidden native input — where a label names nothing. The focusable node is the
role="button"display div, reachable only throughSelectDisplayProps. Akeyboard or screen-reader user browsing package metadata facets hears an unnamed
button where a sighted user reads "Sort by".
Accessible name only. The visibility of the same control — it disappearing while
you type in "Find metadata" — is a different concern in a different file, and it
is PR 4.
Review findings addressed
KTree.Tree([])when the model already exportsEMPTY_FACET_TREEand uses itat four call sites.
Recorded:
stack, in two different ways:
SelectDisplayPropshere,labelIdinPR 2. Both are correct for their
call site; the inconsistency is the finding, and a third case is still unfixed at
Queries/Athena/Workgroups.tsx:64-66. Unifying this inside the sharedSelectwrapper is a follow-up — neither PR should reach across into the other's file to
do it.
Verification
The assertion is
getByRole('button', { name: /Sort by/ }), which fails againstthe
inputPropsversion — the name has to be on the focusable node for the queryto resolve.
Position in the stack
PR 3 of 9, based on
stack/5217-2-query-select-accessible-name.Part of the split of #5217 asked for in
f27. This
unit and PR 2 are the two uncontested accessibility fixes and sit early
deliberately. PR 4 stacks directly above this one because its own fix corrects a
stale comment in
PackageFilters.tsx, which this PR owns.TODO
🤖 Generated with Claude Code
Greptile Summary
This PR gives the search sidebar’s facet ordering control an accessible name by applying
aria-labelledbyto MUI’s focusable Select display node.SelectDisplayProps.Confidence Score: 5/5
The PR appears safe to merge with no actionable defects identified.
The shared Select wrapper forwards
SelectDisplayPropsto MUI, the referenced label exists, and the regression test targets the actual focusable button by its accessible name.Important Files Changed
Reviews (1): Last reviewed commit: "docs(changelog): entry for the facet ord..." | Re-trigger Greptile