Skip to content

fix: surface validation error for incomplete date segment values#10097

Open
AKnassa wants to merge 9 commits into
adobe:mainfrom
AKnassa:fix/incomplete-date-segment-validation
Open

fix: surface validation error for incomplete date segment values#10097
AKnassa wants to merge 9 commits into
adobe:mainfrom
AKnassa:fix/incomplete-date-segment-validation

Conversation

@AKnassa

@AKnassa AKnassa commented May 22, 2026

Copy link
Copy Markdown

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 value state remains null for incomplete dates — so getValidationResult never 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

adobe-design-system-changes

Solution

Introduce an isValuePartial flag that tracks whether the display buffer has some-but-not-all editable segments filled. This flag is:

  • Computed in useDateFieldState from displayValue.isComplete() / displayValue.isCleared()
  • Passed into getValidationResult to mark the field invalid and set valueMissing: true
  • Lifted from inner DateField components up to DatePicker / DateRangePicker via a private prop (privateSetIsValuePartialProp) so the parent's validation pipeline sees it
  • Reset synchronously when setValue() is called with a complete value, preventing stale errors after calendar selection

When 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 — adds isValuePartial to state; passes it to getValidationResult
  • useDatePickerState.ts / useDateRangePickerState.ts — lift partial state from inner fields; wrap setValue to reset flags on commit
  • utils.tsgetValidationResult / getRangeValidationResult accept isValuePartial; constraint errors take priority over the generic message
  • useDateField.ts — commits validation on blur when partial; pushes partial state to parent; empties hidden input when partial
  • useDatePicker.ts / useDateRangePicker.ts — forward privateSetIsValuePartialProp to inner fields
  • useFormValidationState.ts — exports privateSetIsValuePartialProp constant
  • intl/datepicker/*.json — adds incompleteValue key to all 36 locale files
  • scripts/missingTranslations.js — extended to scan intl/datepicker/ subdirectories

Test plan

  • DateField: clear a segment → blur → error appears; refill → error clears
  • DatePicker: clear a segment → blur → error appears; select from calendar → error clears on FIRST selection
  • DateRangePicker: clear a start/end segment → blur → error appears; select range → error clears on FIRST selection
  • TimeField: clear hour segment → blur → error appears; refill → error clears
  • Partial value with violated min/max shows descriptive message, not generic one
  • isRequired validation still works as before
  • validationBehavior="aria" path unaffected

AKnassa added 6 commits May 15, 2026 11:29
…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
@AKnassa AKnassa marked this pull request as ready for review May 22, 2026 04:56
@AKnassa AKnassa closed this May 22, 2026
@AKnassa AKnassa reopened this May 22, 2026
Comment thread packages/@adobe/react-spectrum/test/datepicker/DateField.test.js
@0xZeroTrace

Copy link
Copy Markdown

Hello @3bdo64 @AKnassa could you please share the expected timeline for this fix? We’ll use it to align our production deployment plans.

@AKnassa

AKnassa commented Jun 2, 2026

Copy link
Copy Markdown
Author

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)

@0xZeroTrace

Copy link
Copy Markdown

Thanks @AKnassa for the update. @3bdo64 Could you please look into the approval part.

…egment-validation

# Conflicts:
#	packages/react-aria/src/datepicker/useDatePicker.ts
@AKnassa AKnassa requested a review from 3bdo64 June 3, 2026 04:26

@LFDanLu LFDanLu left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

export const DatePickerAutofill = props => (
story if you use the storybook's controls to set the validationBehavior to aria

@snowystinger snowystinger left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.');

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@AKnassa

AKnassa commented Jun 11, 2026

Copy link
Copy Markdown
Author

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!

@0xZeroTrace

Copy link
Copy Markdown

@AKnassa Thanks for your best work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DatePicker required validation is inconsistent when clearing different segments (month vs day/year)

5 participants