ec6f90f335d9cdf260297d35cedc79c3b7315a16
Two new building blocks for the platform-wide form-error UX rework.
Expense form adopts both as the validation that the pattern works
before the broader sweep across the ~29 useForm callers.
- `useFormScrollToError(handleSubmit, errors)` — wraps RHF's
handleSubmit. On validation failure it locates the first errored
field via `[name="..."]` (or id fallback), walks ancestors to find
the nearest scrolling container (key for forms inside Sheet /
Dialog bodies that own their own overflow-y), and
scrollTo({ behavior: 'smooth' }) + focus({ preventScroll }) on it.
Type-loose handleSubmit signature so 2-arg and 3-arg useForm()
callers (input vs transformed types) both work.
- `<FormErrorSummary errors={errors} labels={…}>` — top-of-form alert
banner listing each failed field as a clickable anchor. Renders
only when ≥2 errors (single-error case is handled by the hook
alone). role="alert" aria-live="polite" for SR users.
- expense-form-dialog adopts both: `onSubmitWithScroll(onSubmit)`
replaces the bare `handleSubmit(onSubmit)`, plus a labelled
`<FormErrorSummary>` at the top of the form. Closes the loop on
the silent-no-op zod-refine bug fixed in PR1 (the underlying
setValue() fix already routes errors through formState; this
surfaces them visibly).
tsc clean. 1419/1419 vitest pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Description
No description provided
Languages
TypeScript
98.7%
HTML
1%
CSS
0.1%
Shell
0.1%