fix: surface validation error for incomplete date segment values#10097
fix: surface validation error for incomplete date segment values#10097AKnassa wants to merge 9 commits into
Conversation
…9624 These tests reproduce three bugs introduced by adobe#9510 (IncompleteDate): - adobe#9958: DatePicker required validation skips on month clear - adobe#9801: TimeField hidden input retains stale value after partial clear - adobe#9624: DateField partial value not flagged as invalid on blur Tests are intentionally failing — fix follows in a subsequent commit.
…dobe#9801, adobe#9624) When a user partially fills a date field (some segments typed, some still placeholders) the value is an IncompleteDate and was silently swallowed — no validation error was raised on form submit or blur. - Add `isValuePartial` flag to `DateFieldState`, derived from `IncompleteDate.isComplete / isCleared` on the display value. - Pass `isValuePartial` into `getValidationResult` / `getRangeValidationResult` so the built-in validation pipeline marks the field invalid and sets `valueMissing: true` in the ValidityState object. - Add "Please enter a value." i18n string (`incompleteValue`) used when the partial-state error is the active error. - Lift partial state up from the inner DateField(s) to DatePickerState and DateRangePickerState via a new private symbol prop (`privateSetIsValuePartialProp`) so those parents can include it in their own `builtinValidation` memo and revalidate reactively. - Wire the setter call in `useDateField` / `useDateRangePicker` so the parent receives updates whenever the user edits a segment. - Add regression tests covering: partial-fill validation, clear-after-partial resets error, full-fill clears error, and form-submit behaviour for DateField, DatePicker, and DateRangePicker.
…eneric incomplete message - useDatePickerState / useDateRangePickerState: wrap setValue so any committed date resets isValuePartial flags, preventing stale partial-state when consumer calls state.setValue() directly - utils.ts: reorders incompleteValue error push so constraint errors (rangeUnderflow, rangeOverflow, unavailableDate) are shown first; the generic "Please enter a value." only appears when there are no other errors
Previously only top-level intl/ dirs were checked, so the new incompleteValue key added under intl/datepicker/ was invisible to the translator tooling. Extended the glob to also scan intl/datepicker/ subdirectories.
Adds "incompleteValue": "Please enter a value." as an English placeholder to all 33 remaining locale JSON files under intl/datepicker/. Real translations will be provided separately via the standard translation workflow.
…ndar-selection fix Adds 3 new test cases in DatePicker and 1 in DateRangePicker covering: - Descriptive constraint error (not generic) when partial value violates min/max - "Takes two interactions" calendar-selection fix for DatePicker - Time-segment clearing on CalendarDateTime (issue adobe#9801) - "Takes two interactions" calendar-selection fix for DateRangePicker
|
Thanks @0xZeroTrace. The fix is complete, CI is green, and it has full test coverage; it's just awaiting maintainer review. Nothing's blocking it: there's no change request on the PR (the lone inline comment has no actionable feedback) |
…egment-validation # Conflicts: # packages/react-aria/src/datepicker/useDatePicker.ts
LFDanLu
left a comment
There was a problem hiding this comment.
The new behavior looks promising! One issue that I found when poking around locally was that a DateField with validationBehavior="aria" now immediately displays "Please enter a value." once you fill in any of it segments from a empty state OR delete a segment if it was already filled. You can reproduce this via the
snowystinger
left a comment
There was a problem hiding this comment.
Added some more tests for aria behaviour, please double check them. I also removed unnecessary comments, and added more checks for ui state during the tests. Please check those for correctness as well. Right now, two of the tests fail which I believe indicate bugs, one of which @LFDanLu pointed out
| .join(' '); | ||
|
|
||
| expect(input.validity.valid).toBe(false); | ||
| expect(description).toContain('Value must be 1/1/2030 or later.'); |
There was a problem hiding this comment.
it's partial, we don't know if it's a violation of min or max. native date inputs still say to complete the date
<form>
<label for="start">Start date:</label>
<input
type="date"
id="start"
name="trip-start"
value="2018-07-22"
min="2018-01-01"
max="2018-12-31" />
<button type="submit">Submit</button>
</form>
delete the year, then press submit
|
Thanks @LFDanLu @snowystinger ; went through the comments and the new tests @LFDanLu I can reproduce the aria issue. Root cause: with validationBehavior="aria" the builtin validation is displayed in realtime, and I was feeding the live partial-segment state straight into it; so every value looked "partial" mid-edit. I'm gating the partial error behind commitValidation() so it only appears after blur, while still clearing in realtime once the date is complete @snowystinger agreed on the min/max point; while the display is partial the committed value is stale, so constraints can't really be evaluated. I'm changing it to report only the generic incomplete message like native inputs do, and updating the two tests. Your DateRangePicker test also caught a second real bug: refilling a segment back to the same committed value never triggered a commit, so the error could never clear, fixing that too. If you'd rather partial behave differently for any of these, happy to adjust Really appreciate the detailed feedback from you both. Updated commit coming soon; please take a look when convenient! |
|
@AKnassa Thanks for your best work. |
Problem
When a user partially fills a date field (e.g., clears the month segment but leaves year and day set), no validation error was surfaced. The
valuestate remainsnullfor incomplete dates — sogetValidationResultnever saw it as invalid, and the field appeared valid even though the user had entered an incomplete value.Affects: DateField, DatePicker, DateRangePicker, TimeField
Fixes #9958, #9801, #9624
Solution
Introduce an
isValuePartialflag that tracks whether the display buffer has some-but-not-all editable segments filled. This flag is:useDateFieldStatefromdisplayValue.isComplete()/displayValue.isCleared()getValidationResultto mark the field invalid and setvalueMissing: trueDateFieldcomponents up toDatePicker/DateRangePickervia a private prop (privateSetIsValuePartialProp) so the parent's validation pipeline sees itsetValue()is called with a complete value, preventing stale errors after calendar selectionWhen a partial value also violates a constraint (min/max/unavailableDate), the descriptive constraint message is shown instead of the generic "Please enter a value." fallback.
Changes
useDateFieldState.ts— addsisValuePartialto state; passes it togetValidationResultuseDatePickerState.ts/useDateRangePickerState.ts— lift partial state from inner fields; wrapsetValueto reset flags on commitutils.ts—getValidationResult/getRangeValidationResultacceptisValuePartial; constraint errors take priority over the generic messageuseDateField.ts— commits validation on blur when partial; pushes partial state to parent; empties hidden input when partialuseDatePicker.ts/useDateRangePicker.ts— forwardprivateSetIsValuePartialPropto inner fieldsuseFormValidationState.ts— exportsprivateSetIsValuePartialPropconstantintl/datepicker/*.json— addsincompleteValuekey to all 36 locale filesscripts/missingTranslations.js— extended to scanintl/datepicker/subdirectoriesTest plan
isRequiredvalidation still works as beforevalidationBehavior="aria"path unaffected