GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing github.com — fix(site/src): expose the date range picker trigger as a combobox by jeremyruppel · Pull Request #31940 · coder/coder Conversation
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Ticket | Surface | Visible change |
|---|---|---|
| DEQ-184 | Date range picker trigger and quick-range list (AI Gateway sessions; also AI spend and Users "Last seen") | None. Role, name, and keyboard only, so no screenshots. |
Accessibility self-review (axe-core 4.11.1, run against the Closed, OpenShowsOnlyQuickPicks, CustomRangeExpanded, and ListSessionsFilter/Default stories): no violations. The closed state has no incompletes. While open, the only incomplete, aria-valid-attr-value, is axe saying it can't confirm the aria-controls target exists. I checked by hand that the target exists.
Review findings not acted on:
play assertions in DateTimeRangePicker.stories.tsx (ClosedWithCustomRange, IntraDayTriggerLabel, SelectQuickPick). This PR only changed their queries. Moving them is out of scope.color-contrast incomplete in CustomRangeExpanded: this is on main too, in the calendar, which this PR doesn't touch.DateTimeRangePicker trigger. 26 non-test lines, all under site/.aria-label. The old ${label}: ${value} form tied the name to the value. Now the name stays fixed and the value is the text.aria-expanded and aria-controls are set explicitly because oxlint requires them on role="combobox". Like Radix, aria-controls is set only while the popover is open, so it never points at a missing id.main to resolve the UsersPage.test.tsx conflict, defaulted label in the destructuring, made aria-controls conditional, named the trigger key handler, dropped the aria-expanded test checks, added an ArrowUp test, and trimmed a stale story comment.label, and the trigger comment was shortened.🤖 Generated by Coder Agents on behalf of @jeremyruppel.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
The combobox approach is sound. I checked it in Storybook: the trigger is announced as combobox "Time range" [collapsed]: Last 24 hours, ArrowDown, ArrowUp, Enter, and Space open it, focus lands on the checked radio, and the dialog and radiogroup names no longer collide. Keeping the radiogroup instead of a listbox is the right call, since the popover also holds the calendar.
This PR can't merge yet: it conflicts with main in UsersPage.test.tsx (blocking), and there is 1 should-fix plus 4 nits across 6 inline comments. Bounced as round 1 on DEQ-184. No follow-up tickets.
Dropped after cross-check: removing the explicit aria-expanded/aria-controls as duplicates of Radix. oxlint jsx-a11y/role-has-required-aria-props fails without them, as the PR body says.
🤖 Generated by Coder Agents on behalf of @chrifro.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
|
||
| await user.click(await screen.findByRole("button", { name: "Last seen" })); | ||
| await user.click( | ||
| await screen.findByRole("combobox", { name: "Last seen" }), |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Blocking This file conflicts with main, so GitHub can't merge the PR (merge gate)
#31943 (8fca118991) changed the last assertion in this test to await screen.findByRole("button", { name: /Over 30 days/ }). Your branch rewrites the same lines. Please rebase on main, keep your combobox query and the async wait, and re-run CI.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Merged origin/main into the branch (no rebase, since this is a single PR). Kept the combobox query and the async waitFor on its text content. Fixed in 53a014d.
🤖 Generated by Coder Agents on behalf of @jeremyruppel.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| activePreset?.label ?? | ||
| formatCustomLabel(value.start, value.end); | ||
|
|
||
| const accessibleName = label ?? "Time range"; |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Should-fix accessibleName is just label with a default (Simplicity SD3)
The same destructuring already defaults props inline (size = "sm"), and label has no other use now. Could this be label = "Time range" in the destructuring, with aria-label={label} on the trigger and on PopoverContent? The new JSDoc already documents the default.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Done: label = "Time range" in the destructuring, and aria-label={label} on the trigger and on PopoverContent. Fixed in 53a014d.
🤖 Generated by Coder Agents on behalf of @jeremyruppel.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| role="combobox" | ||
| aria-label={accessibleName} | ||
| aria-expanded={open} | ||
| aria-controls={contentId} |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Nit aria-controls points at an id that isn't in the DOM while the popover is closed (Reuse RO1, Styling SA6, Simplicity SD5)
I checked in Storybook: after Escape, aria-controls is still set and document.getElementById(...) returns null. That's the aria-valid-attr-value incomplete you mention. Radix sets it only while open. aria-controls={open ? contentId : undefined} keeps oxlint passing (I checked) and matches what Radix does.
While you're here: could the inline onKeyDown become a named handleTriggerKeyDown next to handleQuickPickKeyDown? (React RP6)
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Done: aria-controls={open ? contentId : undefined}, and the handler is now handleTriggerKeyDown, next to handleQuickPickKeyDown. axe no longer marks the closed story as incomplete. Fixed in 53a014d.
🤖 Generated by Coder Agents on behalf of @jeremyruppel.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| await user.tab(); | ||
| await user.keyboard("{Enter}"); | ||
| const trigger = screen.getByRole("combobox", { name: "Time range" }); | ||
| expect(trigger).toHaveAttribute("aria-expanded", "false"); |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Nit These attribute assertions technically fall under FE1's attribute rule (Tests TS5)
I'm not blocking on this: the ARIA state is the fix itself, and Pixel can't capture it. Two optional tweaks: drop the aria-expanded checks (Radix sets that anyway) and keep the aria-controls to dialog id link. ArrowUp, which this PR also adds, has no test.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Dropped the aria-expanded checks, kept the aria-controls-to-dialog link, and added an "opens with ArrowUp" test. Fixed in 53a014d.
🤖 Generated by Coder Agents on behalf of @jeremyruppel.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| @@ -61,9 +61,7 @@ export const Default: Story = { | |||
|
|
|||
| // The picker trigger renders the committed preset label and opens | |||
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Nit This comment says the trigger "renders the committed preset label", but the query below now finds it by its fixed name (Comments CD4)
Trim it to what the play still does, or delete it.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Trimmed it to "The picker opens with the committed preset selected." Fixed in 53a014d.
🤖 Generated by Coder Agents on behalf of @jeremyruppel.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
…ole-state # Conflicts: # site/src/pages/UsersPage/UsersPage.test.tsx
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Approving at 53a014d. The date range picker trigger is now a select-only combobox: combobox "Time range" with the range as its value, ArrowDown/ArrowUp/Enter/Space open it with focus on the checked radio in "Quick ranges", and aria-controls points at the named dialog only while open (verified in Storybook on ListSessionsFilter and DateTimeRangePicker). Round 1 findings are addressed; I confirmed oxlint requires the explicit aria-expanded/aria-controls. Nits, not blocking: the existing play assertions in DateTimeRangePicker.stories.tsx and ListSessionsFilter.stories.tsx could move to Vitest later, and the comment at ListSessionsFilter.stories.tsx:62 restates the code. Follow-ups filed: none.
🤖 Generated by Coder Agents on behalf of @bartekgatzcoder.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
53a014d6
Deployed
There was an error while loading. Please reload this page.
Successfully merging this pull request may close these issues.
There was an error while loading. Please reload this page.