fix(bindx-form): stop a blur from wiping every error on the field - #108
Closed
matej21 wants to merge 2 commits into
Closed
fix(bindx-form): stop a blur from wiping every error on the field#108matej21 wants to merge 2 commits into
matej21 wants to merge 2 commits into
Conversation
Blurring an input clears all errors on the field, so a server validation error and a client error raised elsewhere both vanish while the value is still invalid. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Euwkf2wtutqvtE4YRutU5R
The handler tags the HTML5 validation message it adds with a code and clears exactly that error, instead of resetting the whole field. Field-error clearing now takes a FieldErrorFilter (source and/or code) in place of the source-only argument the store already accepted but nothing passed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Euwkf2wtutqvtE4YRutU5R
matej21
force-pushed
the
fix/blur-clears-all-field-errors
branch
from
September 9, 2026 14:41
2505631 to
2408c98
Compare
Member
Author
|
Consolidated into #109, merged there. |
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.
Symptom
A user saves, the server returns a validation error on a field, the user tabs through that field — and the message silently disappears while the value is still invalid. The same happens to any client-side error some other code put on the field (a custom validation, a parse error): one blur wipes it.
Cause
useFormInputValidationHandlercallsfield.clearErrors()unconditionally in two places:onBlur, right before re-adding the HTML5 validation messageuseEffectbelow it, whenever the validity message changesFieldHandle.clearErrors()dispatchedclearFieldErrors(...)with no narrowing, so the whole field was reset. A blur was a global error reset for that field.The machinery for a narrower clear already existed and was simply never used:
ErrorStore.clearFieldErrors(fieldKey, source?)filtered by source, and theCLEAR_FIELD_ERRORSaction already carried thesourcefield — no caller ever passed it.setValuealready distinguishes "mine to clear" from "not mine" viaclearNonStickyFieldErrors.Mechanism
The handler tags the error it creates with a code (
html5-validity) and clears exactly that error. Field-error clearing takes a filter instead of a bare source:An omitted key matches anything, so
clearErrors()with no argument still clears everything. The filter is threaded throughFieldRef.clearErrors→clearFieldErrorsaction →ActionDispatcher→SnapshotStore→ErrorStore, replacing thesource?: 'client' | 'server'parameter that was there before.HTML5_VALIDATION_ERROR_CODEis exported from@contember/bindx-formso consumers can recognise the hook's own error.Rejected:
clearErrors('client')— clearing by source only. It fixes the server-error case but not the second requirement: a JSON parse error, or any other client-side validation raised by other code, is alsosource: 'client'and would still be wiped on blur. Ownership is not the same question as source.Also rejected: identity-based removal (
removeError(error)keeping a reference to the instance it added). It needs the hook to hold a live reference across renders and rekeys, and adds a second removal API for one caller. Thecodefield already exists onBindxError, is free-form, and was unused for this.Scope kept deliberately narrow: only the field error path takes a filter.
clearEntityErrors/clearRelationErrorskeep theirsource?parameter — nothing needs code-level narrowing there, and widening them would be an unused API.Other
clearErrors()callersNothing else in the repo relied on the clear-everything behaviour, and no call site changes meaning:
EntityHandle.$clearErrors(),HasOneHandle.$clearErrors(),HasManyListHandle.clearErrors()— untouched, still clear all errors on their target.ErrorStore.clearAllServerErrors— internal call updated from'server'to{ source: 'server' }, same behaviour.PlaceholderHandleand the collector proxies implementclearErrors()with no parameters — still valid against the widened signature.'client'now passes{ source: 'client' }.One behavioural nuance worth knowing: a filtered clear that matches nothing no longer bumps the error-state version. Subscriber notification is unchanged (it happens in
SnapshotStoreeither way).Tests
packages/bindx-form/tests/formInputValidation.test.tsxdrives the realFormFieldScope/FormInput/FormErrorpath:The first three failed before the change. happy-dom computes
validitybut leavesvalidationMessageempty, so the tests set the message withsetCustomValidity.tests/unit/store/snapshotStore.test.tsgains a case for clearing by code.bun run typecheckclean,bun run test2031 pass / 0 fail (browser tests not run),bun run lint0 errors.Interaction with #107
#107 (
feat/json-field, still open at the time of writing) hit exactly this bug: its JSON parse error was wiped on blur, so it works around it by adding anonBlurhook toFormInputHandlerand re-reporting the error after the blur. This PR branches offmainand does not depend on it. Once both land, that workaround can be revisited — with this fix the parse error survives the blur on its own, so the re-report may no longer be needed (theonBlurhandler hook may still be wanted for other reasons; that call is best made on #107).🤖 Generated with Claude Code
https://claude.ai/code/session_01Euwkf2wtutqvtE4YRutU5R