Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/10377-calendar-designer-mode-enum.md
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
@@ -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');
});
});
5 changes: 4 additions & 1 deletion packages/components/src/renderers/form/calendar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down
Loading