From 5116b7b40b25bc8c76a7d93c53105b61c13266ca Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 12 Sep 2026 14:38:18 +0000 Subject: [PATCH 1/2] fix(spec): declare the calendar config key the object-calendar prescription already names The object-calendar door refuses a flat `allDayField` and prescribes `calendar: { startDateField, endDateField, titleField, colorField, allDayField }`, and that block's `calendar` prop `.describe()` publishes the same five-key shape to the generated reference docs. `CalendarConfigSchema` was a strictObject of four keys and refused the prescribed shape by name, so an author who followed the diagnostic verbatim on a stored view was refused a second time, by a different schema, with a different message. Measured which half was wrong rather than picking: the key is honoured, not inert. At the objectui pin this repo builds against, ListView's `collectViewFields` reads `calendar.allDayField` into the fetch projection and its calendar branch forwards the authored block onto the object-calendar node, where `getCalendarConfig` resolves it; objectui made it load-bearing in the render itself. It is a field binding like its four neighbours, which is what separates it from `defaultView` -- a UI preference that keeps its own declared home as an object-calendar component prop and stays refused here. Pinned in both directions: the prescribed shape is accepted at the config schema and through the stored-view door, and the flat spelling, `defaultView` and an unknown key are all still refused. The lead pin reads the key list out of the prescription the runtime prints and asks the config schema to accept each one, so a future diagnostic naming a non-member goes red on the class. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH --- ...r-config-allday-prescription-17054.test.ts | 166 ++++++++++++++++++ packages/spec/src/ui/view.zod.ts | 28 +++ 2 files changed, 194 insertions(+) create mode 100644 packages/spec/src/ui/calendar-config-allday-prescription-17054.test.ts diff --git a/packages/spec/src/ui/calendar-config-allday-prescription-17054.test.ts b/packages/spec/src/ui/calendar-config-allday-prescription-17054.test.ts new file mode 100644 index 0000000000..57db78d4e0 --- /dev/null +++ b/packages/spec/src/ui/calendar-config-allday-prescription-17054.test.ts @@ -0,0 +1,166 @@ +import { describe, it, expect } from 'vitest'; + +import { CalendarConfigSchema, ListViewSchema } from './view.zod'; +import { ComponentPropsMap } from './component.zod'; + +/** + * [#17054] Two faces of THIS package disagreed about whether `allDayField` is a + * member of the calendar config, and the disagreement was a class-(c) authoring + * trap: the `object-calendar` door refused a flat `allDayField` and prescribed + * `calendar: { startDateField, endDateField, titleField, colorField, + * allDayField }`, a shape `CalendarConfigSchema` refused BY NAME. An author who + * followed the prescription on a stored view was refused a second time, by a + * different schema, with a different message, and nothing in either message + * said the key was not a member at all — so the natural next move is to assume + * a typo and try more spellings. + * + * The round measured that the key is honoured, not inert, so the schema was the + * wrong half: at the objectui pin `53ded82b` this repo builds against, + * `ListView`'s `collectViewFields` reads `calendar.allDayField` into the fetch + * projection and its calendar branch forwards the authored block onto the + * `object-calendar` node, where `getCalendarConfig` resolves it; objectui#8026 + * makes it load-bearing in the render. `CalendarConfigSchema` now declares it. + * + * ⭐ Both directions are pinned. The first block proves the prescribed shape is + * accepted; the second proves what the widening did NOT cost — the flat + * spelling is still refused, `defaultView` (the neighbouring objectui-LOCAL + * knob, a UI preference rather than a field binding) is still refused, and an + * unknown key is still refused in the same shape as before. + */ + +/** The four keys that were declared before this card, in the prescription's own order. */ +const FOUR = { + startDateField: 'start_date', + endDateField: 'end_date', + titleField: 'subject', + colorField: 'status', +} as const; + +/** A list view that parses clean apart from whatever the case under test adds. */ +const baseView = (calendar: Record) => ({ + type: 'calendar' as const, + name: 'my_cal', + columns: ['subject'], + calendar, +}); + +const objectCalendar = ComponentPropsMap['object-calendar']; + +/** The `object-calendar` refusal that carries the flat-key prescription. */ +const flatRefusalMessage = (): string => { + const r = objectCalendar.safeParse({ objectName: 'events', allDayField: 'is_all_day' }); + expect(r.success).toBe(false); + const issue = r.success === false + ? r.error.issues.find((i) => i.code === 'unrecognized_keys') + : undefined; + expect(issue).toBeDefined(); + return String(issue?.message ?? ''); +}; + +describe('[#17054] the `object-calendar` prescription names only keys `CalendarConfigSchema` accepts', () => { + /** + * ⭐ The pin for the DEFECT CLASS, not for one key. It reads the key list out + * of the prescription the runtime actually prints and asks the config schema + * to accept each one, so any future edit that makes the diagnostic name a + * non-member goes red here — including a key nobody has thought of yet. + */ + it('every key the prescription names inside `calendar: { … }` is accepted by CalendarConfigSchema', () => { + const message = flatRefusalMessage(); + const named = /calendar:\s*\{([^}]*)\}/.exec(message); + expect(named, `the prescription no longer spells a \`calendar: { … }\` shape: ${message}`).not.toBeNull(); + + const keys = String(named?.[1] ?? '') + .split(',') + .map((k) => k.trim()) + .filter((k) => k.length > 0); + // Control: the extraction found a real list, not an empty one that would + // make the assertion below vacuously true. + expect(keys.length).toBeGreaterThanOrEqual(5); + expect(keys).toContain('allDayField'); + + for (const key of keys) { + const r = CalendarConfigSchema.safeParse({ startDateField: 'start_date', [key]: 'some_field' }); + expect( + r.success, + `the prescription names \`${key}\`, which CalendarConfigSchema refuses`, + ).toBe(true); + } + }); + + it('ACCEPTS the prescribed shape verbatim — the four declared keys plus `allDayField`', () => { + const r = CalendarConfigSchema.safeParse({ ...FOUR, allDayField: 'is_all_day' }); + expect(r.success).toBe(true); + }); + + it('ACCEPTS the prescribed shape through the stored-view door, where the second refusal used to land', () => { + const r = ListViewSchema.safeParse(baseView({ ...FOUR, allDayField: 'is_all_day' })); + expect(r.success).toBe(true); + }); + + it('control: the same view without `allDayField` parses too — the door was never the problem', () => { + const r = ListViewSchema.safeParse(baseView({ ...FOUR })); + expect(r.success).toBe(true); + }); +}); + +describe('[#17054] what the widening did NOT open', () => { + /** + * The flat spelling stays refused. `allDayField` became a member of the + * `calendar` config object, ⛔ not a second authorable spelling on the block + * (one key per concept, Prime Directive #12) — and the refusal keeps carrying + * the prescription that now points somewhere real. + */ + it('REFUSES a flat `allDayField` on `object-calendar`, still, and still prescribes the nested shape', () => { + const r = objectCalendar.safeParse({ objectName: 'events', allDayField: 'is_all_day' }); + expect(r.success).toBe(false); + const issue = r.success === false + ? r.error.issues.find((i) => i.code === 'unrecognized_keys') + : undefined; + expect((issue as { keys?: string[] } | undefined)?.keys).toEqual(['allDayField']); + expect(issue?.message).toContain('Write this as a key of the `calendar` config object instead'); + }); + + /** + * `defaultView` is the neighbour that proves the opening is one key wide. It + * is honoured by the same renderer and declared in objectui's own + * sanctioned-local list, and it stays refused here because it is a UI + * preference, not a field binding — and it already has a declared spec home + * as an `object-calendar` component prop. + */ + it('REFUSES `defaultView` on the calendar config, still — the opening is exactly one key wide', () => { + const r = CalendarConfigSchema.safeParse({ ...FOUR, defaultView: 'month' }); + expect(r.success).toBe(false); + const issue = r.success === false + ? r.error.issues.find((i) => i.code === 'unrecognized_keys') + : undefined; + expect((issue as { keys?: string[] } | undefined)?.keys).toEqual(['defaultView']); + }); + + it('REFUSES an unknown key on the calendar config, still, in the same shape as before', () => { + const r = CalendarConfigSchema.safeParse({ ...FOUR, bogusKeyXy: 'x' }); + expect(r.success).toBe(false); + const issue = r.success === false + ? r.error.issues.find((i) => i.code === 'unrecognized_keys') + : undefined; + expect((issue as { keys?: string[] } | undefined)?.keys).toEqual(['bogusKeyXy']); + expect(issue?.message).toContain('Unrecognized key(s) on this calendar configuration'); + }); + + it('REFUSES an unknown key through the stored-view door, reported at `calendar`', () => { + const r = ListViewSchema.safeParse(baseView({ ...FOUR, bogusKeyXy: 'x' })); + expect(r.success).toBe(false); + const issue = r.success === false + ? r.error.issues.find((i) => i.code === 'unrecognized_keys') + : undefined; + expect(issue?.path.join('.')).toBe('calendar'); + expect((issue as { keys?: string[] } | undefined)?.keys).toEqual(['bogusKeyXy']); + }); + + it('still REQUIRES `startDateField` — `allDayField` alone is not a calendar binding', () => { + const r = CalendarConfigSchema.safeParse({ allDayField: 'is_all_day' }); + expect(r.success).toBe(false); + expect( + r.success === false && r.error.issues.some((i) => i.path.join('.') === 'startDateField'), + ).toBe(true); + }); +}); diff --git a/packages/spec/src/ui/view.zod.ts b/packages/spec/src/ui/view.zod.ts index 721c3930ef..3ecc02a254 100644 --- a/packages/spec/src/ui/view.zod.ts +++ b/packages/spec/src/ui/view.zod.ts @@ -1365,6 +1365,34 @@ export const CalendarConfigSchema = lazySchema(() => strictObject({ endDateField: z.string().optional().describe('Field providing the event end date/time (defaults to a single-day event)'), titleField: z.string().optional().describe('Field displayed as the event title. Omit to fall back to the record display name (ADR-0079 resolver chain)'), colorField: z.string().optional().describe('Field to derive each event color from (it names a field, not a color): the option color declared on that field for the record value, else the value itself when it already is a color literal (hex, rgb() or hsl()), else the calendar theme-aware palette color hashed from the value'), + /** + * [#17054] The fifth binding, and the one this schema was missing while two + * other faces of this same package already published it as a member: the + * `object-calendar` flat-key prescription names it verbatim + * (`OBJECT_CALENDAR_FLAT_FIELD_GUIDANCE` in `ui/component.zod.ts`) and that + * block's `calendar` prop `.describe()` spells the config as + * `{ startDateField, endDateField?, titleField?, colorField?, allDayField? }` + * — text that ships to `content/docs/references/ui/component.mdx`. An author + * who followed the prescription on a STORED VIEW was refused here by name. + * + * Declared rather than trimmed because the renderer honours it: at the + * objectui pin `53ded82b` this repo builds against, `ListView`'s + * `collectViewFields` reads `calendar.allDayField` into the fetch projection + * (`plugin-list/src/ListView.tsx`) and its calendar branch forwards the + * authored block onto the `object-calendar` node, where `getCalendarConfig` + * resolves it; objectui#8026 then makes it load-bearing in the render itself. + * It is a FIELD BINDING like its four neighbours, which is what separates it + * from `defaultView` — the renderer's initial view mode, a UI preference with + * its own declared home as an `object-calendar` component prop and no + * business on a field-binding config. + * + * ⛔ No default field name. An undeclared `allDayField` leaves the renderer's + * existing inference untouched (an event with no end date draws as all-day); + * a DECLARED one is absolute — a record whose flag is absent or false is not + * all-day, because letting the inference overrule a declared key is this + * card's own defect inverted. + */ + allDayField: z.string().optional().describe('Field carrying the all-day flag for each event (names a boolean field, not a value): a record whose flag is true is drawn as an all-day band rather than at a clock time, and one whose flag is absent or false is not all-day. Omit to leave the renderer inference in place — an event with no end date draws as all-day'), })); /** From 20a8797fa0d2a728221f3396744552870fbf1587 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 12 Sep 2026 14:48:18 +0000 Subject: [PATCH 2/2] chore(spec): regenerate authorable surface and reference docs, add the changeset Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH --- .../17054-calendar-config-all-day-field.md | 26 +++++++++++++++++++ content/docs/references/api/protocol.mdx | 4 +-- content/docs/references/data/object.mdx | 2 +- content/docs/references/ui/view.mdx | 15 ++++++----- packages/spec/authorable-surface/ui.json | 1 + 5 files changed, 39 insertions(+), 9 deletions(-) create mode 100644 .changeset/17054-calendar-config-all-day-field.md diff --git a/.changeset/17054-calendar-config-all-day-field.md b/.changeset/17054-calendar-config-all-day-field.md new file mode 100644 index 0000000000..7c968a670a --- /dev/null +++ b/.changeset/17054-calendar-config-all-day-field.md @@ -0,0 +1,26 @@ +--- +"@objectstack/spec": minor +--- + +`CalendarConfigSchema` now declares **`allDayField`** — the fifth field binding on a calendar config, and the one key the rest of this package already published as a member while the schema refused it by name. + +**The trap this closes.** The `object-calendar` door refuses a flat `allDayField` and prescribes, verbatim: *"Write this as a key of the `calendar` config object instead — `calendar: { startDateField, endDateField, titleField, colorField, allDayField }`."* That block's `calendar` prop `.describe()` publishes the same five-key shape, and it ships to `content/docs/references/ui/component.mdx`. An author who followed the prescription on a stored view was refused a **second** time, by a different schema with a different message — `Unrecognized key(s) on this calendar configuration: allDayField` — and neither message said the key was not a member at all, so the natural next move was to assume a typo and try more spellings. + +**Why the schema was the wrong half, measured rather than assumed.** The key is honoured, not inert. At the objectui pin this repo builds against, `ListView`'s `collectViewFields` reads `calendar.allDayField` into the fetch projection and its calendar branch forwards the authored block onto the `object-calendar` node, where `getCalendarConfig` resolves it; objectui then made it load-bearing in the render itself. Trimming the prescription instead would have left a shipped capability with no protocol carrier — and the mirror that carries it today keeps `.passthrough()` explicitly so the key is not stripped, which means a later hardening there would silently drop it. + +**What is authorable, and what still is not.** + +```ts +// accepted +calendar: { startDateField: 'start_date', endDateField: 'end_date', + titleField: 'subject', colorField: 'status', allDayField: 'is_all_day' } + +// still refused — one key per concept, not a second authorable spelling +{ type: 'object-calendar', allDayField: 'is_all_day' } +``` + +`allDayField` **names a boolean field, not a value**: a record whose flag is true draws as an all-day band rather than at a clock time, and one whose flag is absent or false is not all-day. Omit it and the renderer's existing inference is untouched — an event with no end date draws as all-day — so every calendar that never authored the key renders exactly as before. + +**The opening is one key wide.** `defaultView` stays refused on this config: it is the renderer's initial view mode, a UI preference rather than a field binding, and it already has its own declared home as an `object-calendar` component prop. Unknown keys are refused in the same shape as before, and `startDateField` is still required. + +Purely additive: nothing that parsed before is refused now, and no key is renamed or removed. diff --git a/content/docs/references/api/protocol.mdx b/content/docs/references/api/protocol.mdx index efe38cc223..8d5b2cbea3 100644 --- a/content/docs/references/api/protocol.mdx +++ b/content/docs/references/api/protocol.mdx @@ -1641,7 +1641,7 @@ The published metadata item body, opaque by ruling (1C). Shape is the item's own | **navigation** | `{ mode?: Enum<'page' \| 'drawer' \| 'modal' \| 'split' \| 'popover' \| 'new_window' \| 'none'>; view?: string; preventNavigation?: boolean; openNewTab?: boolean; … }` | optional | Configuration for item click navigation (page, drawer, modal, etc.) | | **pagination** | `{ pageSize?: integer; pageSizeOptions?: integer[] }` | optional | Pagination configuration | | **kanban** | `{ groupByField: string; summarizeField?: string; columns: string[] }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | -| **calendar** | `{ startDateField: string; endDateField?: string; titleField?: string; colorField?: string }` | optional | Calendar configuration — applies when the view renders as a calendar layout | +| **calendar** | `{ startDateField: string; endDateField?: string; titleField?: string; colorField?: string; … }` | optional | Calendar configuration — applies when the view renders as a calendar layout | | **gantt** | `{ startDateField: string; endDateField: string; titleField: string; progressField?: string; … }` | optional | Gantt-timeline configuration — applies when the view renders as a gantt layout | | **gallery** | `{ coverField?: string; coverFit?: Enum<'cover' \| 'contain'>; cardSize?: Enum<'small' \| 'medium' \| 'large'>; titleField?: string; … }` | optional | Gallery/card view configuration | | **timeline** | `{ startDateField: string; endDateField?: string; titleField: string; groupByField?: string; … }` | optional | Timeline view configuration | @@ -1726,7 +1726,7 @@ The published metadata item body, opaque by ruling (1C). Shape is the item's own | **navigation** | `{ mode?: Enum<'page' \| 'drawer' \| 'modal' \| 'split' \| 'popover' \| 'new_window' \| 'none'>; view?: string; preventNavigation?: boolean; openNewTab?: boolean; … }` | optional | Configuration for item click navigation (page, drawer, modal, etc.) | | **pagination** | `{ pageSize?: integer; pageSizeOptions?: integer[] }` | optional | Pagination configuration | | **kanban** | `{ groupByField: string; summarizeField?: string; columns: string[] }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | -| **calendar** | `{ startDateField: string; endDateField?: string; titleField?: string; colorField?: string }` | optional | Calendar configuration — applies when the view renders as a calendar layout | +| **calendar** | `{ startDateField: string; endDateField?: string; titleField?: string; colorField?: string; … }` | optional | Calendar configuration — applies when the view renders as a calendar layout | | **gantt** | `{ startDateField: string; endDateField: string; titleField: string; progressField?: string; … }` | optional | Gantt-timeline configuration — applies when the view renders as a gantt layout | | **gallery** | `{ coverField?: string; coverFit?: Enum<'cover' \| 'contain'>; cardSize?: Enum<'small' \| 'medium' \| 'large'>; titleField?: string; … }` | optional | Gallery/card view configuration | | **timeline** | `{ startDateField: string; endDateField?: string; titleField: string; groupByField?: string; … }` | optional | Timeline view configuration | diff --git a/content/docs/references/data/object.mdx b/content/docs/references/data/object.mdx index f76e1a4a7f..5100b42d5c 100644 --- a/content/docs/references/data/object.mdx +++ b/content/docs/references/data/object.mdx @@ -375,7 +375,7 @@ const result = ApiMethod.parse(data); | **navigation** | `{ mode?: Enum<'page' \| 'drawer' \| 'modal' \| 'split' \| 'popover' \| 'new_window' \| 'none'>; view?: string; preventNavigation?: boolean; openNewTab?: boolean; … }` | optional | Configuration for item click navigation (page, drawer, modal, etc.) | | **pagination** | `{ pageSize?: integer; pageSizeOptions?: integer[] }` | optional | Pagination configuration | | **kanban** | `{ groupByField: string; summarizeField?: string; columns: string[] }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | -| **calendar** | `{ startDateField: string; endDateField?: string; titleField?: string; colorField?: string }` | optional | Calendar configuration — applies when the view renders as a calendar layout | +| **calendar** | `{ startDateField: string; endDateField?: string; titleField?: string; colorField?: string; … }` | optional | Calendar configuration — applies when the view renders as a calendar layout | | **gantt** | `{ startDateField: string; endDateField: string; titleField: string; progressField?: string; … }` | optional | Gantt-timeline configuration — applies when the view renders as a gantt layout | | **gallery** | `{ coverField?: string; coverFit?: Enum<'cover' \| 'contain'>; cardSize?: Enum<'small' \| 'medium' \| 'large'>; titleField?: string; … }` | optional | Gallery/card view configuration | | **timeline** | `{ startDateField: string; endDateField?: string; titleField: string; groupByField?: string; … }` | optional | Timeline view configuration | diff --git a/content/docs/references/ui/view.mdx b/content/docs/references/ui/view.mdx index 9394710c28..40450f447a 100644 --- a/content/docs/references/ui/view.mdx +++ b/content/docs/references/ui/view.mdx @@ -107,6 +107,7 @@ Appearance and visualization configuration | **endDateField** | `string` | optional | Field providing the event end date/time (defaults to a single-day event) | | **titleField** | `string` | optional | Field displayed as the event title. Omit to fall back to the record display name (ADR-0079 resolver chain) | | **colorField** | `string` | optional | Field to derive each event color from (it names a field, not a color): the option color declared on that field for the record value, else the value itself when it already is a color literal (hex, rgb() or hsl()), else the calendar theme-aware palette color hashed from the value | +| **allDayField** | `string` | optional | Field carrying the all-day flag for each event (names a boolean field, not a value): a record whose flag is true is drawn as an all-day band rather than at a clock time, and one whose flag is absent or false is not all-day. Omit to leave the renderer inference in place — an event with no end date draws as all-day | --- @@ -799,7 +800,7 @@ Map view configuration | **navigation** | `{ mode?: Enum<'page' \| 'drawer' \| 'modal' \| 'split' \| 'popover' \| 'new_window' \| 'none'>; view?: string; preventNavigation?: boolean; openNewTab?: boolean; … }` | optional | Configuration for item click navigation (page, drawer, modal, etc.) | | **pagination** | `{ pageSize?: integer; pageSizeOptions?: integer[] }` | optional | Pagination configuration | | **kanban** | `{ groupByField: string; summarizeField?: string; columns: string[] }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | -| **calendar** | `{ startDateField: string; endDateField?: string; titleField?: string; colorField?: string }` | optional | Calendar configuration — applies when the view renders as a calendar layout | +| **calendar** | `{ startDateField: string; endDateField?: string; titleField?: string; colorField?: string; … }` | optional | Calendar configuration — applies when the view renders as a calendar layout | | **gantt** | `{ startDateField: string; endDateField: string; titleField: string; progressField?: string; … }` | optional | Gantt-timeline configuration — applies when the view renders as a gantt layout | | **gallery** | `{ coverField?: string; coverFit?: Enum<'cover' \| 'contain'>; cardSize?: Enum<'small' \| 'medium' \| 'large'>; titleField?: string; … }` | optional | Gallery/card view configuration | | **timeline** | `{ startDateField: string; endDateField?: string; titleField: string; groupByField?: string; … }` | optional | Timeline view configuration | @@ -943,6 +944,7 @@ View filter rule | **endDateField** | `string` | optional | Field providing the event end date/time (defaults to a single-day event) | | **titleField** | `string` | optional | Field displayed as the event title. Omit to fall back to the record display name (ADR-0079 resolver chain) | | **colorField** | `string` | optional | Field to derive each event color from (it names a field, not a color): the option color declared on that field for the record value, else the value itself when it already is a color literal (hex, rgb() or hsl()), else the calendar theme-aware palette color hashed from the value | +| **allDayField** | `string` | optional | Field carrying the all-day flag for each event (names a boolean field, not a value): a record whose flag is true is drawn as an all-day band rather than at a clock time, and one whose flag is absent or false is not all-day. Omit to leave the renderer inference in place — an event with no end date draws as all-day | ### Nested Shape: `ListView.gantt` @@ -1204,7 +1206,7 @@ Tab configuration for multi-tab view interface | **navigation** | `{ mode?: Enum<'page' \| 'drawer' \| 'modal' \| 'split' \| 'popover' \| 'new_window' \| 'none'>; view?: string; preventNavigation?: boolean; openNewTab?: boolean; … }` | optional | Configuration for item click navigation (page, drawer, modal, etc.) | | **pagination** | `{ pageSize?: integer; pageSizeOptions?: integer[] }` | optional | Pagination configuration | | **kanban** | `{ groupByField: string; summarizeField?: string; columns: string[] }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | -| **calendar** | `{ startDateField: string; endDateField?: string; titleField?: string; colorField?: string }` | optional | Calendar configuration — applies when the view renders as a calendar layout | +| **calendar** | `{ startDateField: string; endDateField?: string; titleField?: string; colorField?: string; … }` | optional | Calendar configuration — applies when the view renders as a calendar layout | | **gantt** | `{ startDateField: string; endDateField: string; titleField: string; progressField?: string; … }` | optional | Gantt-timeline configuration — applies when the view renders as a gantt layout | | **gallery** | `{ coverField?: string; coverFit?: Enum<'cover' \| 'contain'>; cardSize?: Enum<'small' \| 'medium' \| 'large'>; titleField?: string; … }` | optional | Gallery/card view configuration | | **timeline** | `{ startDateField: string; endDateField?: string; titleField: string; groupByField?: string; … }` | optional | Timeline view configuration | @@ -1339,6 +1341,7 @@ View filter rule | **endDateField** | `string` | optional | Field providing the event end date/time (defaults to a single-day event) | | **titleField** | `string` | optional | Field displayed as the event title. Omit to fall back to the record display name (ADR-0079 resolver chain) | | **colorField** | `string` | optional | Field to derive each event color from (it names a field, not a color): the option color declared on that field for the record value, else the value itself when it already is a color literal (hex, rgb() or hsl()), else the calendar theme-aware palette color hashed from the value | +| **allDayField** | `string` | optional | Field carrying the all-day flag for each event (names a boolean field, not a value): a record whose flag is true is drawn as an all-day band rather than at a clock time, and one whose flag is absent or false is not all-day. Omit to leave the renderer inference in place — an event with no end date draws as all-day | ### Nested Shape: `ObjectListView.gantt` @@ -1800,7 +1803,7 @@ Tab configuration for multi-tab view interface | **navigation** | `{ mode?: Enum<'page' \| 'drawer' \| 'modal' \| 'split' \| 'popover' \| 'new_window' \| 'none'>; view?: string; preventNavigation?: boolean; openNewTab?: boolean; … }` | optional | Configuration for item click navigation (page, drawer, modal, etc.) | | **pagination** | `{ pageSize?: integer; pageSizeOptions?: integer[] }` | optional | Pagination configuration | | **kanban** | `{ groupByField: string; summarizeField?: string; columns: string[] }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | -| **calendar** | `{ startDateField: string; endDateField?: string; titleField?: string; colorField?: string }` | optional | Calendar configuration — applies when the view renders as a calendar layout | +| **calendar** | `{ startDateField: string; endDateField?: string; titleField?: string; colorField?: string; … }` | optional | Calendar configuration — applies when the view renders as a calendar layout | | **gantt** | `{ startDateField: string; endDateField: string; titleField: string; progressField?: string; … }` | optional | Gantt-timeline configuration — applies when the view renders as a gantt layout | | **gallery** | `{ coverField?: string; coverFit?: Enum<'cover' \| 'contain'>; cardSize?: Enum<'small' \| 'medium' \| 'large'>; titleField?: string; … }` | optional | Gallery/card view configuration | | **timeline** | `{ startDateField: string; endDateField?: string; titleField: string; groupByField?: string; … }` | optional | Timeline view configuration | @@ -1885,7 +1888,7 @@ Tab configuration for multi-tab view interface | **navigation** | `{ mode?: Enum<'page' \| 'drawer' \| 'modal' \| 'split' \| 'popover' \| 'new_window' \| 'none'>; view?: string; preventNavigation?: boolean; openNewTab?: boolean; … }` | optional | Configuration for item click navigation (page, drawer, modal, etc.) | | **pagination** | `{ pageSize?: integer; pageSizeOptions?: integer[] }` | optional | Pagination configuration | | **kanban** | `{ groupByField: string; summarizeField?: string; columns: string[] }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | -| **calendar** | `{ startDateField: string; endDateField?: string; titleField?: string; colorField?: string }` | optional | Calendar configuration — applies when the view renders as a calendar layout | +| **calendar** | `{ startDateField: string; endDateField?: string; titleField?: string; colorField?: string; … }` | optional | Calendar configuration — applies when the view renders as a calendar layout | | **gantt** | `{ startDateField: string; endDateField: string; titleField: string; progressField?: string; … }` | optional | Gantt-timeline configuration — applies when the view renders as a gantt layout | | **gallery** | `{ coverField?: string; coverFit?: Enum<'cover' \| 'contain'>; cardSize?: Enum<'small' \| 'medium' \| 'large'>; titleField?: string; … }` | optional | Gallery/card view configuration | | **timeline** | `{ startDateField: string; endDateField?: string; titleField: string; groupByField?: string; … }` | optional | Timeline view configuration | @@ -2126,7 +2129,7 @@ This schema accepts one of the following structures: | **navigation** | `{ mode?: Enum<'page' \| 'drawer' \| 'modal' \| 'split' \| 'popover' \| 'new_window' \| 'none'>; view?: string; preventNavigation?: boolean; openNewTab?: boolean; … }` | optional | Configuration for item click navigation (page, drawer, modal, etc.) | | **pagination** | `{ pageSize?: integer; pageSizeOptions?: integer[] }` | optional | Pagination configuration | | **kanban** | `{ groupByField: string; summarizeField?: string; columns: string[] }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | -| **calendar** | `{ startDateField: string; endDateField?: string; titleField?: string; colorField?: string }` | optional | Calendar configuration — applies when the view renders as a calendar layout | +| **calendar** | `{ startDateField: string; endDateField?: string; titleField?: string; colorField?: string; … }` | optional | Calendar configuration — applies when the view renders as a calendar layout | | **gantt** | `{ startDateField: string; endDateField: string; titleField: string; progressField?: string; … }` | optional | Gantt-timeline configuration — applies when the view renders as a gantt layout | | **gallery** | `{ coverField?: string; coverFit?: Enum<'cover' \| 'contain'>; cardSize?: Enum<'small' \| 'medium' \| 'large'>; titleField?: string; … }` | optional | Gallery/card view configuration | | **timeline** | `{ startDateField: string; endDateField?: string; titleField: string; groupByField?: string; … }` | optional | Timeline view configuration | @@ -2302,7 +2305,7 @@ This schema accepts one of the following structures: | **navigation** | `{ mode?: Enum<'page' \| 'drawer' \| 'modal' \| 'split' \| 'popover' \| 'new_window' \| 'none'>; view?: string; preventNavigation?: boolean; openNewTab?: boolean; … }` | optional | Configuration for item click navigation (page, drawer, modal, etc.) | | **pagination** | `{ pageSize?: integer; pageSizeOptions?: integer[] }` | optional | Pagination configuration | | **kanban** | `{ groupByField: string; summarizeField?: string; columns: string[] }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | -| **calendar** | `{ startDateField: string; endDateField?: string; titleField?: string; colorField?: string }` | optional | Calendar configuration — applies when the view renders as a calendar layout | +| **calendar** | `{ startDateField: string; endDateField?: string; titleField?: string; colorField?: string; … }` | optional | Calendar configuration — applies when the view renders as a calendar layout | | **gantt** | `{ startDateField: string; endDateField: string; titleField: string; progressField?: string; … }` | optional | Gantt-timeline configuration — applies when the view renders as a gantt layout | | **gallery** | `{ coverField?: string; coverFit?: Enum<'cover' \| 'contain'>; cardSize?: Enum<'small' \| 'medium' \| 'large'>; titleField?: string; … }` | optional | Gallery/card view configuration | | **timeline** | `{ startDateField: string; endDateField?: string; titleField: string; groupByField?: string; … }` | optional | Timeline view configuration | diff --git a/packages/spec/authorable-surface/ui.json b/packages/spec/authorable-surface/ui.json index 625ae5cfcc..7c63716cd7 100644 --- a/packages/spec/authorable-surface/ui.json +++ b/packages/spec/authorable-surface/ui.json @@ -167,6 +167,7 @@ "ui/BulkActionParam:placeholder", "ui/BulkActionParam:required", "ui/BulkActionParam:type", + "ui/CalendarConfig:allDayField", "ui/CalendarConfig:colorField", "ui/CalendarConfig:endDateField", "ui/CalendarConfig:startDateField",