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
45 changes: 45 additions & 0 deletions .changeset/18459-navigation-mode-list-seven.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
'@objectstack/spec': patch
---

fix(spec): the four `z.unknown()` navigation doors in `ComponentPropsMap` list all seven `NavigationModeSchema` modes (#18459)

Clause-②: no

`object-grid`, `object-map`, `object-gantt` and `object-tree` declare `navigation`
as `z.unknown()`, so the `.describe()` on each is the WHOLE published account of
what a mode may be — nothing else in the protocol narrows those four doors, and
the generated reference renders their type as `any` beside that sentence. Three
of them listed six of the seven `NavigationModeSchema` values (no `new_window`)
and `object-grid`, the precedent the other three copied, listed five (no
`popover` either). An author reading the shipped reference was told a value the
platform honours does not exist.

**Re-measured at the current `.objectui-sha` pin `87af769e9a3e`, not at the pin
the finding was taken at.** All four blocks hand `schema.navigation` straight
into the shared `useNavigationOverlay` hook; that hook types its own mode union
AS this package's `NavigationModeSchema` (its own docblock: *"the seven modes
this hook switches on are exactly the seven the exported union publishes"*,
held by a parity test on the objectui side); its click router carries a
`new_window` branch that delegates to `onNavigate` and otherwise falls through
to a `window.open`, and `object-gantt` additionally implements that action
itself. `popover` is an overlay mode in the same router and every one of the
four passes it an anchor. So all seven modes reach all four doors.

**Why `object-grid` is in the same change.** It is the row the other three were
copied from and it understates by two rather than one; correcting three while
leaving the source of the pattern intact would leave the family in the state
that produced the defect. All four now name the schema as well as the values,
so the next member added to `NavigationModeSchema` has a named edge into these
rows instead of four independently drifting lists.

**Why `Clause-②: no`.** The doors stay `z.unknown()` — a `navigation` value is
accepted before and after this change, whatever its `mode` reads. Nothing is
added to, removed from or narrowed on any authorable surface: the diff is four
description strings and the reference page regenerated from them, and
`check:authorable-surface`, `check:api-surface` and `check:export-origins` all
pass with no artifact to regenerate. What moves is what an author is TOLD, which
is why this ships as a `patch` rather than as no changeset at all: the sentence
is published bytes — `src/ui/component.zod.ts` ships verbatim under this
package's `files[]` entry `src/**/*.zod.ts`, and the compiled string ships in
`dist/ui/index.js` and `dist/ui/index.mjs`.
8 changes: 4 additions & 4 deletions content/docs/references/ui/component.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ Sort field and direction pair
| **filter** | `{ field: string; operator: Enum<'equals' \| 'not_equals' \| 'contains' \| 'not_contains' \| 'icontains' \| …>; value?: string \| number \| boolean \| null \| (string \| number)[] }[]` | optional | Base query filter — the ViewFilterRule array form `[{ field, operator, value }, ...]`, the one filter orthography every `filter` door in this map shares; lowered to the wire `$filter`. The MongoDB-style record form is refused — see migration `element-data-source-and-object-block-filter-rule-array` |
| **sort** | `{ field: string; order: Enum<'asc' \| 'desc'> }[]` | optional | Task order for the fetched bars — the SortItem array form `[{ field, order }, ...]`, the one sort orthography every declared `sort` door on this platform shares; lowered to the wire `$orderby`. The legacy string clause (`name desc`) is refused — see migration `object-block-sort-item-array` |
| **gantt** | `{ startDateField: string; endDateField: string; titleField: string; progressField?: string; … }` | optional | Gantt-timeline configuration, the author face — the same block `ListViewSchema.gantt` declares, and the one the renderer validates this node against. Taken WHOLE when present: the flat top-level spelling beside it is ignored |
| **navigation** | `any` | optional | Task-click navigation config (`{ mode: page \| drawer \| modal \| split \| popover \| none }`); renderer default `drawer` |
| **navigation** | `any` | optional | Task-click navigation config (`{ mode: page \| drawer \| modal \| split \| popover \| new_window \| none }`) — all seven `NavigationModeSchema` values, since the shared `useNavigationOverlay` hook types its own mode union as that schema; renderer default `drawer` |
| **label** | `string \| Record<string, string>` | optional | Gantt label — the second link of the exported PNG/PDF file-name chain, after `gantt.exportFileName` and before the bound object's own label |
| **skipWeekends** | `boolean` | optional | Measure duration and auto-schedule math in WORKING days, skipping Saturdays and Sundays |
| **holidays** | `string[]` | optional | Additional non-working dates for the working calendar, ISO `yyyy-mm-dd` strings; folded into a Set for the duration math |
Expand Down Expand Up @@ -542,7 +542,7 @@ Sort field and direction pair
| **bulkActions** | `any[]` | optional | Bulk action names shown on selection |
| **batchActions** | `any[]` | optional | Alternate spelling the renderer reads FIRST (`batchActions ?? bulkActions`) |
| **bulkActionDefs** | `any[]` | optional | Inline bulk-action definitions (full defs, not names) |
| **navigation** | `any` | optional | Row-click navigation config (`{ mode: page \| drawer \| modal \| split \| none }`) |
| **navigation** | `any` | optional | Row-click navigation config (`{ mode: page \| drawer \| modal \| split \| popover \| new_window \| none }`) — all seven `NavigationModeSchema` values, since the shared `useNavigationOverlay` hook types its own mode union as that schema |
| **editable** | `boolean` | optional | Enable inline cell editing |
| **singleClickEdit** | `boolean` | optional | Enter cell edit on single click (default true when editable) |
| **resizable** | `boolean` | optional | Allow column resize (read before `resizableColumns`) |
Expand Down Expand Up @@ -666,7 +666,7 @@ View filter rule
| **sort** | `{ field: string; order: Enum<'asc' \| 'desc'> }[]` | optional | Marker order for the fetched records — the SortItem array form `[{ field, order }, ...]`, the one sort orthography every declared `sort` door on this platform shares; lowered to the wire `$orderby`. The legacy string clause (`name desc`) is refused — see migration `object-block-sort-item-array` |
| **map** | `{ latitudeField?: string; longitudeField?: string; locationField?: string; titleField?: string; … }` | optional | Map field config, the author face — the same block `ListViewSchema.map` declares, and the one the renderer validates this node against. Taken WHOLE when present: the flat top-level spelling beside it is ignored |
| **mapStyle** | `string` | optional | MapLibre style URL or spec, overriding the public demo tiles. Read before `map.style`; NOT the base node `style`, which is an inline CSS record |
| **navigation** | `any` | optional | Marker-click navigation config (`{ mode: page \| drawer \| modal \| split \| popover \| none }`) |
| **navigation** | `any` | optional | Marker-click navigation config (`{ mode: page \| drawer \| modal \| split \| popover \| new_window \| none }`) — all seven `NavigationModeSchema` values, since the shared `useNavigationOverlay` hook types its own mode union as that schema |
| **enableClustering** | `boolean` | optional | Group nearby markers into clusters. Absent, the renderer clusters only above 100 markers |

### Nested Shape: `ObjectMapProps.data[provider='object']`
Expand Down Expand Up @@ -874,7 +874,7 @@ Sort field and direction pair
| **staticData** | `any[]` | optional | Inline records — read SECOND by `resolveRecordSourceConfig`, wrapped into a `{ provider: 'value' }` config |
| **filter** | `{ field: string; operator: Enum<'equals' \| 'not_equals' \| 'contains' \| 'not_contains' \| 'icontains' \| …>; value?: string \| number \| boolean \| null \| (string \| number)[] }[]` | optional | Base query filter — the ViewFilterRule array form `[{ field, operator, value }, ...]`, the one filter orthography every `filter` door in this map shares; lowered to the wire `$filter`. The MongoDB-style record form is refused — see migration `element-data-source-and-object-block-filter-rule-array` |
| **tree** | `{ parentField?: string; labelField?: string; fields?: string[]; defaultExpandedDepth?: integer }` | optional | Tree/hierarchy configuration, the author face — the same block `ListViewSchema.tree` declares: `{ parentField?, labelField?, fields?, defaultExpandedDepth? }`. `parentField` auto-detects from the object schema when omitted |
| **navigation** | `any` | optional | Row-click navigation config (`{ mode: page \| drawer \| modal \| split \| popover \| none }`) |
| **navigation** | `any` | optional | Row-click navigation config (`{ mode: page \| drawer \| modal \| split \| popover \| new_window \| none }`) — all seven `NavigationModeSchema` values, since the shared `useNavigationOverlay` hook types its own mode union as that schema |

### Nested Shape: `ObjectTreeProps.data[provider='object']`

Expand Down
8 changes: 4 additions & 4 deletions packages/spec/src/ui/component.zod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2847,7 +2847,7 @@ export const ObjectGridPropsSchema = lazySchema(() => strictObject({
bulkActions: z.array(z.unknown()).optional().describe('Bulk action names shown on selection'),
batchActions: z.array(z.unknown()).optional().describe('Alternate spelling the renderer reads FIRST (`batchActions ?? bulkActions`)'),
bulkActionDefs: z.array(z.unknown()).optional().describe('Inline bulk-action definitions (full defs, not names)'),
navigation: z.unknown().optional().describe('Row-click navigation config ({ mode: page | drawer | modal | split | none })'),
navigation: z.unknown().optional().describe('Row-click navigation config ({ mode: page | drawer | modal | split | popover | new_window | none }) — all seven `NavigationModeSchema` values, since the shared `useNavigationOverlay` hook types its own mode union as that schema'),
editable: z.boolean().optional().describe('Enable inline cell editing'),
singleClickEdit: z.boolean().optional().describe('Enter cell edit on single click (default true when editable)'),
resizable: z.boolean().optional().describe('Allow column resize (read before `resizableColumns`)'),
Expand Down Expand Up @@ -3650,7 +3650,7 @@ export const ObjectMapPropsSchema = lazySchema(() => strictObject({
mapStyle: z.string().optional()
.describe('MapLibre style URL or spec, overriding the public demo tiles. Read before `map.style`; NOT the base node `style`, which is an inline CSS record'),
navigation: z.unknown().optional()
.describe('Marker-click navigation config ({ mode: page | drawer | modal | split | popover | none })'),
.describe('Marker-click navigation config ({ mode: page | drawer | modal | split | popover | new_window | none }) — all seven `NavigationModeSchema` values, since the shared `useNavigationOverlay` hook types its own mode union as that schema'),
enableClustering: z.boolean().optional()
.describe('Group nearby markers into clusters. Absent, the renderer clusters only above 100 markers'),
}));
Expand Down Expand Up @@ -3777,7 +3777,7 @@ export const ObjectGanttPropsSchema = lazySchema(() => strictObject({
gantt: GanttConfigSchema.optional()
.describe('Gantt-timeline configuration, the author face — the same block `ListViewSchema.gantt` declares, and the one the renderer validates this node against. Taken WHOLE when present: the flat top-level spelling beside it is ignored'),
navigation: z.unknown().optional()
.describe('Task-click navigation config ({ mode: page | drawer | modal | split | popover | none }); renderer default `drawer`'),
.describe('Task-click navigation config ({ mode: page | drawer | modal | split | popover | new_window | none }) — all seven `NavigationModeSchema` values, since the shared `useNavigationOverlay` hook types its own mode union as that schema; renderer default `drawer`'),
label: I18nLabelSchema.optional()
.describe('Gantt label — the second link of the exported PNG/PDF file-name chain, after `gantt.exportFileName` and before the bound object\'s own label'),
skipWeekends: z.boolean().optional()
Expand Down Expand Up @@ -3936,7 +3936,7 @@ export const ObjectTreePropsSchema = lazySchema(() => strictObject({
tree: TreeConfigSchema.optional()
.describe('Tree/hierarchy configuration, the author face — the same block `ListViewSchema.tree` declares: { parentField?, labelField?, fields?, defaultExpandedDepth? }. `parentField` auto-detects from the object schema when omitted'),
navigation: z.unknown().optional()
.describe('Row-click navigation config ({ mode: page | drawer | modal | split | popover | none })'),
.describe('Row-click navigation config ({ mode: page | drawer | modal | split | popover | new_window | none }) — all seven `NavigationModeSchema` values, since the shared `useNavigationOverlay` hook types its own mode union as that schema'),
}));
/** Author state (ADR-0122: the bare name is the author state). */
export type ObjectTreeProps = z.input<typeof ObjectTreePropsSchema>;
Expand Down
Loading