refactor(ui-date-time-input): convert v2 to functional, drop Moment.js#2531
Open
balzss wants to merge 2 commits intoINSTUI-4791-time-date-input-rework-multi-versionfrom
Open
Conversation
Rewrites DateTimeInput v2 from a class to a functional component, removes
all Moment.js usage from internal logic, and extracts pure timezone-aware
date math to a sibling utils.ts. Drops setTimeout(0) shims, dead refs, and
the formatDateInput / DateInput dual-formatting redundancy.
Side effects of state updaters now happen after commit (via a snapshotRef),
which makes onChange StrictMode-safe.
BREAKING CHANGE:
- messageFormat prop type changed from a Moment format string (e.g. 'LLLL')
to a formatter function: (date: Date, locale: string, timezone: string) => string.
Default output is unchanged ("Monday, May 1, 2017 1:30 PM" in en-US).
- Typed input is now strictly governed by DateInput v2's locale parser.
v1's lenient Moment-based fallback ("Sep 4, 1986", "2017-05-01" in en-US,
etc.) is no longer accepted.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Tightens DateInput v2's locale parser so it can serve as the sole authority on what's accepted (which DateTimeInput v2 now relies on after dropping its Moment-based fallback). - Validate that each segment is digits and there are exactly 3 segments (so things like "May/4/2017" no longer slip through). - Reject impossible calendar dates (Feb 30, etc.) via a Date round-trip. - Strip bidi marks (U+200E / U+200F / U+061C) from the formatted input before parsing so the split regex doesn't have to care about them. - Centralize format options in a single FORMAT_OPTIONS constant shared by parse, format, and placeholder-hint generation. Forces gregory + latn so the input contract stays predictable across locales. - Replace the mirrored `inputMessages` state with a `hasInternalError` flag; render the `messages` prop directly and append `invalidDateErrorMessage` only when the internal error is on. Removes a stale-state surface where external `messages` updates could get masked by a previous internal error. - Generate the placeholder hint from `formatToParts` directly instead of regex-replacing digits in a formatted example. More robust to locales whose digits happen to collide with the example values. - Memoize `placeholderHint` and `selectedDate`. - Drop a stale TODO above `displayName`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
Contributor
|
If I clear the date in the input field, the time is cleared too automatically on master. Now this does not happen. |
Contributor
|
Can you please check the cypress tests? Some of them are failing in DateInput and DateTimeInput. |
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.


Builds on top of #2518. Rewrites
DateTimeInputv2 internals so the component is functional, Moment-free, and easier to maintain — public API stays the same except for the documentedmessageFormatchange. Also includes a follow-up commit hardeningDateInputv2's parser, sinceDateTimeInputnow leans on it as the sole authority for typed-input acceptance.Commits
refactor(ui-date-time-input): convert v2 to functional, drop Moment.js— the rewrite below.refactor(ui-date-input): harden v2 parser, simplify input messages— see DateInput v2 changes section.Summary (DateTimeInput v2)
useState/useEffect/useRef; mirrorsDateInputv2's hook style.DateTime.parsecall sites removed.formatMessageusesIntl.DateTimeFormat;parseIsoInTzhandles consumer ISO input (with offset →new Date(); without offset → wall-clock components inprops.timezonevia the existing offset-rebuild trick); typed-text fallback gone —DateInputv2's locale parser is now the sole authority.v2/utils.tsholds the pure timezone-aware date math (partsInTz,wallClockInTzToUtc,sameDayInTz,combineDateAndTime,setWallTime,parseIsoInTz,defaultMessageFormat). No React, no Moment. Marked as a candidate to promote to@instructure/ui-i18nonce a second consumer needs it.setTimeout(0)shims aroundonChange/onBlur(predate React 18 batching), the deadelementRef/handleRef, the redundant localformatDateInputre-pass on top ofDateInput's own format, and the stale-state-pronesetStateupdater incomponentDidUpdate.onChangeis now StrictMode-safe. Side effects moved out of state updaters via asnapshotRefsynced each render. Updaters are pure;onChangefires once, after commit.messageFormattest rewritten for the new function-shaped prop. 33/33 pass.DateInput v2 changes
Now that
DateTimeInputv2 forwards typed-input acceptance entirely toDateInputv2, the parser there has to be airtight. Changes in the second commit:Dateround-trip), the parser no longer accepts input like"May/4/2017"or"2/30/2024".U+200E/U+200F/U+061Cbefore splitting, so RTL-locale-formatted input parses cleanly.FORMAT_OPTIONSconstant (gregory+latn,2-digitmonth/day) feeds parsing, formatting, and the placeholder hint — keeping the input contract predictable across locales.inputMessagesstate with ahasInternalErrorflag; renders themessagesprop directly and appendsinvalidDateErrorMessageonly while the internal error is active. Removes a stale-state surface where externalmessagesupdates could get masked.Intl.DateTimeFormat.formatToPartsdirectly instead of regex-replacing digits in a formatted example. More robust to locales whose digits happen to collide with the example date.placeholderHintandselectedDate. Drops a staleTODOabovedisplayName.No public API changes here — purely internal refactor + stricter validation.
Breaking changes
messageFormat(DateTimeInput)'LLLL')(date: Date, locale: string, timezone: string) => stringDefault output of
messageFormatis unchanged ("Monday, May 1, 2017 1:30 PM"in en-US,"lundi 1 mai 2017 13:30"in fr-FR).Typed input acceptance is now strictly governed by
DateInputv2's locale parser. v1's lenient Moment-based fallback is gone — e.g. inen-US,"9/4/1986"and"09/04/1986"are accepted, but"Sep 4, 1986","May 1 2017","2017-05-01", and"2/30/2024"are no longer parsed.docs/guides/upgrade-guide.mdhas both entries documented.Test plan
pnpm run test:vitest ui-date-time-input(33/33 should pass)pnpm run test:vitest ui-date-inputpnpm run cy:component --spec cypress/component/DateTimeInput.cy.tsxpnpm run dev— exercise each example on/v11_7/DateTimeInput:1/18/2018+1:30 PM+Thursday, January 18, 2018 1:30 PM18/01/2018+16:00+jeudi 18 janvier 2018 16:009/4/1986, blur → accepted; typeSep 4, 1986, blur →Invalid date!2017-05-01, blur →Invalid date!(was accepted in v1)2/30/2024, blur →Invalid date!4/3/2022→Disabled dateshown/v11_7/DateInputexamples — placeholder hint, parsing, error message wiringregression-test/datetimeinputand/form-errorslook unchangedOut of scope
@instructure/ui-i18n(deferred until a second consumer — likelyTimeSelectv2's eventual hooks rewrite — needs them).DateInputv2'srenderCalendarIcon/width/margin/isInlineprops (separate enhancement).messageFormat's default away from a custom formatter to e.g.Intl.DateTimeFormatOptions— keeping it as a function gives consumers full control.🤖 Generated with Claude Code