fix(plugin-calendar): authored ISO currentDate is parsed to a Date instead of crashing the calendar (#4452) - #4484
Merged
Conversation
…at the renderer boundary (#4452) `plugin-calendar:calendar-view` declares `currentDate` as `type: 'string'` ("ISO date string for initial calendar date") while `CalendarViewProps.currentDate` is a `Date`. Nothing converted between them: the authored string rode the trailing `{...props}` spread into `useState`'s initial `selectedDate`, and the header's `selectedDate.toLocaleDateString(…)` threw `selectedDate.toLocaleDateString is not a function`. The one spelling the input documents was the one spelling that could not work. The renderer now owes the conversion at its own boundary: `currentDate` is destructured out so the spread cannot carry the raw value (the #4433 consumed-key pattern), parsed once per authored value, and handed to `CalendarView` as the `Date` its prop type declares. Off-spec input gets the same answer as an absent key — the component default — and an `Invalid Date` is never manufactured and passed on. A `Date` instance passes through untouched. The parse is memoised on the authored value, because `CalendarView` re-seeds its `selectedDate` from this prop in an effect keyed on the prop's identity. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Qqyix2QcnpUC9XeYVDzx3
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
Collaborator
Author
|
ACCEPT — step-7 复核 by PM session
Flipping ready + arming auto-merge. Generated by Claude Code Generated by Claude Code |
yinlianghui
marked this pull request as ready for review
August 12, 2026 19:31
This was referenced Aug 12, 2026
Merged
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.
Closes #4452
The defect
plugin-calendar:calendar-viewdeclares the input{ name: 'currentDate', type: 'string', label: 'Current Date', description: 'ISO date string for initial calendar date' }, whileCalendarViewProps.currentDateis aDate. Nothing converted between the two. The authored string rode the renderer's trailing{...props}spread intoCalendarView, becameuseState's initialselectedDate, and the header calledselectedDate.toLocaleDateString(…)on a string:The one spelling the input documents was the one spelling that could not work, and there was no correct authored value at all —
type: 'string'cannot express aDate.This is the mirror image of #4433, in the same renderer: there an undocumented authored key overwrote a computed prop; here the component documents the key, its type and its format, and honouring that documentation is what took the calendar down.
The fix
Per the ruling on #4452 — authored metadata is the contract, so the renderer owes the conversion — all of it inside
packages/plugin-calendar/src/calendar-view-renderer.tsx:currentDateis destructured out of the incoming props, so the spread can no longer carry the raw value (the plugin-calendar: authoringeventson acalendar-viewnode crashes the component — the SDUI action object overwrites the computed CalendarEvent array #4433 consumed-key pattern). Both authoring channels land there: the node's own key and aprops: { currentDate }container.Datethe prop type declares;Date— gets the same answer as an absent key:undefined, soCalendarView's own default parameter applies. AnInvalid Dateis never manufactured and passed on; it does not throw, it renders the literal text "Invalid Date" into the header and the date picker, which is a silent wrong answer where the default is a usable calendar;Dateinstance passes through untouched. That is not authored metadata (type: 'string'cannot express it) but a React host handing the widget its real declared prop type, and its behaviour is unchanged by this card, invalid instances included.CalendarViewre-seeds itsselectedDatestate from this prop in an effect keyed on the prop's identity, so a freshDateper render would re-seed on every render — throwing away the user's own Previous/Next navigation and driving the effect against its ownsetState. A dedicated case pins this.No change to
CalendarView, to the registry declaration, or to any other package.Red first
The tests were written and run before the source change. 5 red, 3 green, exactly the predicted split — the three green ones are the must-not-change pins:
with the card's own crash verbatim in the DOM the assertion dumped:
After the fix:
Tests 8 passed (8); the whole packageTests 31 passed (31).One honest note on the repro date
The card's verbatim repro value,
2026-08-12T08:00:00.000Z, is in the month this card was filed in, so it can pin the crash and nothing more: a fix that parsed the string and then quietly dropped it to "today" would still satisfy it. The honouring assertions therefore use a date 14 months out. 14 is more than 12, so the authored month is always in a different calendar year than today — which lets the header assertion read year digits rather than a locale-dependent month name — and the event-visibility assertion is the locale-free half of the same fact, since a month grid only renders the events of the month it is showing. Both dates are in the suite, doing their two different jobs.Verification
pnpm exec vitest run packages/plugin-calendar/— 6 files, 31 tests, green.pnpm exec vitest run packages/app-shell/src/__tests__/widget-dom-leak-sweep.test.tsx— 39 tests green, unchanged, no ledger edit. The sweep plantsbind/events/ariaLabel/ariaDescribedBy/zzcanary*/reference_to/props: { colorVariant, zzcanaryprop }, and nocurrentDatecanary, so the calendar targets' measured leak set does not move: still zero leaked attributes and still noplugin-calendarrow inLEAK_LEDGER. The ledger file was not touched.tsc --noEmitandtsc -p tsconfig.test.jsonforpackages/plugin-calendar— both green.pnpm --filter @object-ui/plugin-calendar lint— 0 errors.check-changeset-presence,check-changeset-no-major,check-control-bytes,check-type-check-coverage— green.dist/*.d.tsare byte-identical (diff -rclean). Runtime-only, sopatch— the escalation rule of fix(i18n): retire the reader-lesscommon.searchkey from the ten locale packs (#4392) #4403/fix(i18n): retire the orphaned report.editor.* namespace, 105 keys across ten packs (#4145) #4177 does not trigger.Relation to #4425 / #4453
Consuming this declared input is a forward-compatible piece of the #4425 phase-2 whitelist migration tracked for this package as #4453. The key stops being an unread value that rides the spread and becomes a declared input the renderer actually reads — which is the shape phase 2 will require of every declared input, so this conversion survives that migration rather than being redone by it. This PR changes no widget contract and no whitelist; that decision stays with #4425.
Generated by Claude Code