Skip to content

✅ test: shared render helper, axe backfill and role-based queries - #677

Merged
futjesus merged 1 commit into
mainfrom
test/infra-and-axe
Jul 27, 2026
Merged

✅ test: shared render helper, axe backfill and role-based queries#677
futjesus merged 1 commit into
mainfrom
test/infra-and-axe

Conversation

@futjesus

Copy link
Copy Markdown
Member

Summary

Final PR of the audit series (stacked on #676). Test infrastructure, axe coverage for the remaining test files, and — per the new project rule — every query rewritten to the Testing Library philosophy, fixing the component semantics that blocked role-based queries instead of skipping tests.

Changes

Infra

  • tests/utils/renderWithProviders (QueryClient + optional theme wrapper) with an @tests alias; the two VirtualizedTable test files that duplicated QueryClientProvider setup now use it. Lives outside lib/ so it never enters the bundle or coverage.

axe backfill (7 files) + the real violations it surfaced, fixed

  • MultiSelectDropdown / TagSelect: the role="combobox" div had no accessible name (a label for can't name a div) — now wired via aria-labelledby; hidden form-sync inputs marked aria-hidden.
  • VirtualizedTable Actions: role="presentation" on focusable buttons voided their semantics — removed.
  • Sidebar NavigationOption: dropped the invalid role="option" (li outside a listbox, axe aria-required-parent). With role="button" it now renders a real <button> inside the li — keyboard operable and screen-reader announced, no skipped test.
  • Input: the password eye toggle was a clickable <svg> — now a real button with a Show/Hide password accessible name.
  • PhoneNumberInput: the country search input had only a placeholder — now has an accessible name.

Testing Library philosophy as a project rule

All queries in the touched tests use getByRole + accessible name; the rule (role-first priority, no testid/querySelector, missing semantics = fix the component) is now documented in CLAUDE.md's Testing section.

Follow-up (documented, not in this PR)

The legacy data-label/querySelector queries in the Filter dropdown and Breadcrumb tests need the BadgeMultiSelect options to gain real option/checkbox semantics before they can be role-queried — that's a component redesign of the same shape as NavigationOption's, worth its own PR.

Testing

Full suite green: 47 files, 558 tests, 0 skipped, lint, types, prettier, coverage thresholds.

@futjesus
futjesus force-pushed the chore/update-dependencies branch 2 times, most recently from 32035db to 0ce5b08 Compare July 27, 2026 15:53
Base automatically changed from chore/update-dependencies to main July 27, 2026 15:58
- Add tests/utils/renderWithProviders with '@tests' alias; refactor the two
  VirtualizedTable test files that duplicated QueryClientProvider setup
- Backfill jest-axe checks in Button, MultiSelectDropdown, RadioCardGroup,
  Sidebar, Tabs, TagSelect and VirtualizedTable Actions tests
- Fix the violations axe surfaced instead of skipping tests:
  - MultiSelectDropdown/TagSelect combobox now gets aria-labelledby from its
    label; hidden sync inputs marked aria-hidden
  - VirtualizedTable Actions buttons lose the role=presentation that voided
    their semantics
  - Sidebar NavigationOption drops the invalid role=option (li outside a
    listbox); with role=button it now renders a real button inside the li,
    giving keyboard and screen-reader support
  - Input password toggle is a real button with a Show/Hide password label
  - PhoneNumberInput country search input gains an accessible name
- Rewrite queries to follow the Testing Library philosophy (getByRole with
  accessible name) and document the rule in CLAUDE.md
@futjesus
futjesus force-pushed the test/infra-and-axe branch from 382c6e2 to 824a02b Compare July 27, 2026 16:07
@futjesus
futjesus merged commit bb09a5f into main Jul 27, 2026
2 checks passed
@futjesus
futjesus deleted the test/infra-and-axe branch July 27, 2026 16:09
futjesus added a commit that referenced this pull request Jul 27, 2026
The NavigationOption hover/active colors lived in Sidebar.css keyed on
`li[role='option']`. #677 dropped that invalid role from the `<li>`, so the
rules stopped matching and every consumer theming through the
`--konstruct-sidebar-hover-bg` / `--konstruct-sidebar-active-bg` custom
properties silently fell back to the library default.

Rather than re-point the selector, move the styling into the cva variants:
a descriptor selector like `.konstruct-sidebar li:hover` (0,2,2) outranks a
single utility class (0,1,0), so a consumer could never win through
`className` even when the selector did match.

- Drop the dead `li[role='option']` rules from Sidebar.css; the variants
  already cover the layout, padding and anchor styling they mirrored
- Add the one rule that was not mirrored (`shrink-0`) to the variants
- Prefix the active classes with `data-[active=true]:` so tailwind-merge
  detects the conflict against a consumer override and drops the default
- Remove the now-unused `text-black` and duplicated `font-normal`
- Cover both directions with tests: an override wins, no override keeps the
  default

BREAKING CHANGE: `--konstruct-sidebar-hover-bg`, `--konstruct-sidebar-hover-color`,
`--konstruct-sidebar-active-bg` and `--konstruct-sidebar-active-color` no longer
do anything. Pass the colors to `NavigationOption` via `className` instead:
`hover:bg-metal-800 data-[active=true]:bg-metal-800`.
futjesus added a commit that referenced this pull request Jul 27, 2026
* 🐛 fix: let consumers retheme Sidebar options through className

The NavigationOption hover/active colors lived in Sidebar.css keyed on
`li[role='option']`. #677 dropped that invalid role from the `<li>`, so the
rules stopped matching and every consumer theming through the
`--konstruct-sidebar-hover-bg` / `--konstruct-sidebar-active-bg` custom
properties silently fell back to the library default.

Rather than re-point the selector, move the styling into the cva variants:
a descriptor selector like `.konstruct-sidebar li:hover` (0,2,2) outranks a
single utility class (0,1,0), so a consumer could never win through
`className` even when the selector did match.

- Drop the dead `li[role='option']` rules from Sidebar.css; the variants
  already cover the layout, padding and anchor styling they mirrored
- Add the one rule that was not mirrored (`shrink-0`) to the variants
- Prefix the active classes with `data-[active=true]:` so tailwind-merge
  detects the conflict against a consumer override and drops the default
- Remove the now-unused `text-black` and duplicated `font-normal`
- Cover both directions with tests: an override wins, no override keeps the
  default

BREAKING CHANGE: `--konstruct-sidebar-hover-bg`, `--konstruct-sidebar-hover-color`,
`--konstruct-sidebar-active-bg` and `--konstruct-sidebar-active-color` no longer
do anything. Pass the colors to `NavigationOption` via `className` instead:
`hover:bg-metal-800 data-[active=true]:bg-metal-800`.

* 📝 docs: tighten the no-comments rule to cover change rationale and CSS
Sign up for free to 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.

1 participant