Document how upstream and Radix test, and how both solve form participation - #2
Merged
Merged
Conversation
…ticipation
Two questions answered with evidence rather than assumption.
How the suites are built. This project splits work sensibly: cargo unit
tests cover the algorithmic modules (10 of 40 files in primitives/src --
slider, calendar, virtualizer, text_search, pointer, and so on) while 32
Playwright specs with 122 tests cover interaction in a real browser, and
CI runs check/test/fmt/docs/clippy with warnings denied. There is no
component-level DOM/ARIA unit test because Rust has no jsdom equivalent,
which is what hovinen's dioxus-test branch was exploring.
Where that suite stops is the revealing part: 167 keyboard.press and 84
toBeFocused assertions, but zero for aria-hidden and zero for
overflow/scrollY, and axe in only 3 specs of 32. It tests what a
component does and never what it must prevent -- the exact shape of the
gaps recorded in capability-gaps.md.
Radix uses vitest with testing-library and vitest-axe, with large suites
(select 1610 lines, radio-group 983, checkbox 829). But FormData appears
zero times in their checkbox, radio-group and select tests: they assert
the bubble input renders with type=radio and aria-hidden, never that
submitting a form produces the entry. The entry-list rules proposed here
therefore exceed what the reference implementation tests, and must stand
on the HTML spec rather than on Radix's precedent.
How the div-role=radio problem is solved. Radix renders a real hidden
input conditionally, gated on control.closest('form'), defaulting true
for SSR. This repo's Checkbox does the same thing unconditionally at
checkbox.rs:279-296, hidden via inline style and synced through
document::eval. The pattern is therefore already in this codebase and
was simply never extended to RadioGroup or Select, which makes fixing
them an application of the project's own existing solution rather than
new design.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K3xAwYCh5jJupi9U4CG5C7
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up research to PR #1, answering two questions with measurement rather than assumption. Documentation only.
How the existing suites are built — and where they stop
This project splits work sensibly: cargo unit tests cover the algorithmic modules (10 of 40 files in
primitives/src—slider,calendar,virtualizer,text_search,pointer, …), while 32 Playwright specs with 122 tests cover interaction in a real browser. CI runs check/test/fmt/docs/clippy with warnings denied. There is no component-level DOM/ARIA unit test because Rust has no jsdom equivalent — which is whathovinen'sdioxus-testbranch was exploring.Where the e2e suite stops is precise:
keyboard.presstoBeFocusedaria-hiddenoverflow/scrollYIt tests what a component does, never what it must prevent — and never where focus lands after a close, only that the thing closed. That is the exact shape of the gaps recorded in
capability-gaps.md, and why they survived 122 tests.Radix uses
vitest+@testing-library/react+vitest-axe, with large suites (select.test.tsx1,610 lines,radio-group983,checkbox829). ButFormDataappears zero times across their checkbox, radio-group and select tests: they assert the bubble input renders withtype="radio"andaria-hidden, never that submitting a form produces the entry.So the entry-list rules proposed in
conformance-harness.mdexceed what the reference implementation tests. Useful in both directions — the proposal isn't redundant, and it can't lean on Radix's precedent either. It stands on the HTML spec, which is enough.How
div role="radio"is made to submitIt can't be, directly — HTML restricts the entry list to submittable elements, and ARIA changes what an element is announced as, never what it submits as. Since Dioxus renders plain DOM rather than custom elements,
ElementInternals.setFormValue()is also unavailable. A real submittable element has to exist.Both references do that:
isFormControl = control ? !!form || !!control.closest('form') : true(defaulting true so SSR still bubbles) — then emits<input type="radio" aria-hidden tabIndex={-1} name value required disabled form>beside the visual button.Checkboxdoes the same thing unconditionally (checkbox.rs:279-296): a real<input type="checkbox">witharia_hidden,tabindex="-1", hidden by inline style, synced viadocument::eval.The pattern is therefore already in this codebase — applied to
CheckboxandSwitch, never extended toRadioGrouporSelect. Fixing them applies the project's own existing solution rather than introducing new design, which is also the strongest framing for an upstream PR. The only real choice is whether to match Radix's conditional rendering orCheckbox's unconditional one; either is defensible, a third invention is not.Generated by Claude Code