From 3d90a12592490d45cf5f054121b4917f06136bde Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 24 Sep 2026 23:52:38 +0000 Subject: [PATCH] fix(components): ui:calendar designer stops offering mode 'default' (objectui#10377) The registration inputs enum offered 'default' beside single/multiple/range. CalendarSchema.mode and the UiCalendarSchema mirror both refuse it, and react-day-picker 10 has no such mode: measured, it drops the authored selection and clicks select nothing. The enum now lists the contract's modes, pinned enum-subset-of-mirror by a safeParse test. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01877XiBYSaRCk2CU7cMSg3S --- .../10377-calendar-designer-mode-enum.md | 5 ++ .../calendar.designerModeEnum-10377.test.ts | 57 +++++++++++++++++++ .../src/renderers/form/calendar.tsx | 5 +- 3 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 .changeset/10377-calendar-designer-mode-enum.md create mode 100644 packages/components/src/renderers/form/__tests__/calendar.designerModeEnum-10377.test.ts diff --git a/.changeset/10377-calendar-designer-mode-enum.md b/.changeset/10377-calendar-designer-mode-enum.md new file mode 100644 index 0000000000..4527accc9b --- /dev/null +++ b/.changeset/10377-calendar-designer-mode-enum.md @@ -0,0 +1,5 @@ +--- +'@object-ui/components': patch +--- + +`ui:calendar`: the designer no longer offers `mode: 'default'` (objectui#10377). `CalendarSchema.mode` and the `UiCalendarSchema` mirror admit only `single`, `multiple` and `range`, so a document authored with `default` was refused on save; react-day-picker 10 has no such mode either, and under it the calendar dropped the authored selection and ignored clicks. The registration `inputs` enum now lists exactly the contract's modes. diff --git a/packages/components/src/renderers/form/__tests__/calendar.designerModeEnum-10377.test.ts b/packages/components/src/renderers/form/__tests__/calendar.designerModeEnum-10377.test.ts new file mode 100644 index 0000000000..a2fd0a0f62 --- /dev/null +++ b/packages/components/src/renderers/form/__tests__/calendar.designerModeEnum-10377.test.ts @@ -0,0 +1,57 @@ +/** + * ObjectUI + * Copyright (c) 2024-present ObjectStack Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +/** + * objectui#10377 — every `mode` the `ui:calendar` designer offers is a mode + * the contract admits. + * + * The registration `inputs` offered `'default'` beside `single` / `multiple` / + * `range`. `CalendarSchema.mode` (TS) and the `UiCalendarSchema` mirror both + * refuse it, so an author who picked it in the designer stored a document the + * save-gate rejects. `DayPicker` (react-day-picker 10) has no `default` mode + * either: under it the picker drops the authored selection and a click selects + * nothing. The enum now lists the contract's modes only. + * + * The pin is enum ⊆ contract, judged by the mirror itself: nothing here + * restates the list of modes, so a value added to the designer enum that the + * mirror refuses turns this red, whatever the value is. + */ +import { describe, it, expect } from 'vitest'; +import { ComponentRegistry } from '@object-ui/core'; +import { UiCalendarSchema } from '@object-ui/types/zod'; +// Registers the renderers at module scope, NOT inside a hook (objectui#3010). +import '../../../renderers'; + +/** The values the `ui:calendar` designer offers for `mode`. */ +function designerModes(): unknown[] { + const input = (ComponentRegistry.getConfig('ui:calendar')?.inputs ?? []).find( + (i) => i.name === 'mode', + ); + return (input?.enum ?? []).map((e) => + typeof e === 'object' && e !== null ? (e as { value: unknown }).value : e, + ); +} + +describe('ui:calendar designer `mode` enum ⊆ UiCalendarSchema.mode (objectui#10377)', () => { + it('the registration offers a `mode` enum at all (non-vacuity)', () => { + expect(designerModes().length).toBeGreaterThan(0); + }); + + it('every offered mode passes UiCalendarSchema.safeParse', () => { + const refused = designerModes().filter( + (mode) => !UiCalendarSchema.safeParse({ type: 'ui:calendar', mode }).success, + ); + expect(refused).toEqual([]); + }); + + it('control: the mirror still refuses `default`, so the check above can fail', () => { + const result = UiCalendarSchema.safeParse({ type: 'ui:calendar', mode: 'default' }); + expect(result.success).toBe(false); + expect(result.error?.issues.map((i) => i.path.join('.'))).toContain('mode'); + }); +}); diff --git a/packages/components/src/renderers/form/calendar.tsx b/packages/components/src/renderers/form/calendar.tsx index 54d1d54767..b9a6e73df1 100644 --- a/packages/components/src/renderers/form/calendar.tsx +++ b/packages/components/src/renderers/form/calendar.tsx @@ -68,7 +68,10 @@ ComponentRegistry.register('calendar', skipFallback: true, label: 'Calendar', inputs: [ - { name: 'mode', type: 'enum', enum: ['default', 'single', 'multiple', 'range'] }, + // Exactly the modes `CalendarSchema.mode` admits (objectui#10377). The + // designer used to offer `'default'` too, which both authoring faces + // refuse; `DayPicker` has no such mode and drops the selection under it. + { name: 'mode', type: 'enum', enum: ['single', 'multiple', 'range'] }, { name: 'className', type: 'string' } ], defaultProps: {