Refuse to untick the last allowed submission format - #210
Merged
Merged
Conversation
With validation on and neither format allowed, api-data.ts cannot reach either validator: `valid` starts false, both branches are skipped, and every submission takes the 400. The experiment silently stops collecting, and the error the researcher eventually finds in the log says the DATA is invalid -- pointing at the participant's submission, when the submission never mattered. Two clicks in a panel that gives no sign the second one means something categorically different from the first. There is no legitimate reading of "validate submissions, accept nothing", so the control now declines rather than saving it, the way TagListInput declines a duplicate. Declining is just not calling setValidationSettings: the group is controlled, so the tick mark never moves and the save effect never fires. Two things that were not free: The refusal needs a voice. A control that declines silently is indistinguishable from one that is broken, so the checkboxes gain the same always-one-line message slot TagListInput uses -- steady-state guidance swapped for the reason, never a block that mounts underneath and pushes the rest of the panel down on every click. The transient-notice machinery is now `useTransientNotice`, shared by both, so the two refusals in this panel say their piece for the same duration. And the refusal needs to reach the accessibility tree. Ark has already flipped the real <input> by the time onValueChange runs; refusing leaves React state untouched, so React re-renders with the same `checked` prop and never resets the node. What the researcher SEES stays correct -- the tick is drawn from the controlled group value -- but the hidden input is what a screen reader reads, and it now says unchecked: the opposite of what happened, and worse than no feedback at all. `resyncFormatInputs` puts the property back. This is a UI floor, not a guarantee. The server is unchanged, so a document that already holds the state still behaves as it did. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SHZNijLwUhn26yCbYbn1UN
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 to #209. Option 1 from the discussion there, UI only — no server change.
Why
With validation on and neither format allowed,
functions/src/api-data.ts:86-104cannot reach either validator:Every submission takes the 400,
requiredFieldsis never consulted, and the experiment silently stops collecting. The error the researcher eventually finds in the log says"The data are not valid according to the validation parameters set for this experiment."— pointing at the participant's submission, when the submission never mattered.It's two clicks in a panel that gives no sign the second one means something categorically different from the first.
What changed
The checkbox group declines to go empty, the way
TagListInputdeclines a duplicate. Declining is just not callingsetValidationSettings— the group is controlled, so the tick mark never moves and the save effect never fires.Two things that weren't free:
The refusal needs a voice. A control that declines silently is indistinguishable from one that is broken, so the checkboxes gain the same always-one-line message slot
TagListInputuses — steady-state guidance swapped for the reason, never a block that mounts underneath and pushes the rest of the panel down on every click. The transient-notice machinery is extracted touseTransientNoticeand shared by both, so the two refusals in this panel say their piece for the same duration.TagListInput's 25 tests pass unchanged across that extraction.The refusal needs to reach the accessibility tree. Ark has already flipped the real
<input>by the timeonValueChangeruns; refusing leaves React state untouched, so React re-renders with the samecheckedprop and never resets the node. What the researcher sees stays correct — the tick is drawn from the controlled group value — but the hidden input is what a screen reader reads, and it now says unchecked: the opposite of what happened.resyncFormatInputsputs the property back. This was caught bytoBeChecked(), which reads the property rather than the attribute.Scope
This is a UI floor, not a guarantee. The server is unchanged, so a document that already holds the state — or one written any other way — behaves exactly as it did. That was a deliberate call, not an oversight.
Tests
Three cases added. Two fail with the guard disabled.
🤖 Generated with Claude Code
https://claude.ai/code/session_01SHZNijLwUhn26yCbYbn1UN