Skip to content

Commit 94aeec6

Browse files
committed
fix(spec): react-tier ListView onNavigate declares the mode union it actually emits
The `onNavigate` overlay in `REACT_BLOCKS` declared the second argument as `(recordId, action: 'view' | 'edit') => void`. `'edit'` is emitted by no call site in the reference implementation and read by no branch; `'new_window'` — what a modifier-click and an authored `navigation: { mode: 'new_window' }` actually send — was not declared at all. The union becomes `'view' | 'new_window'`, and the description now names both modes and states that the slot is a closed navigation-MODE vocabulary. The generated `skills/objectstack-ui/references/react-blocks.md` is regenerated by `gen:react-blocks`; it carries no hand edits. Claude-Session: https://claude.ai/code/session_01JbZnqu8bt6YqfJsr9vaFb3 Co-authored-by: Claude <noreply@anthropic.com>
1 parent d4cb05c commit 94aeec6

3 files changed

Lines changed: 41 additions & 2 deletions

File tree

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
---
2+
'@objectstack/spec': minor
3+
---
4+
5+
React-tier `<ListView>`: the `onNavigate` declaration becomes
6+
`(recordId, action: 'view' | 'new_window') => void` — a declared value **no branch ever
7+
emitted** is removed, and the value **two reference call sites do emit** is added.
8+
9+
`REACT_BLOCKS`' ListView overlay declared the second argument as `'view' | 'edit'`. That
10+
sentence was false in both directions. `'edit'` is emitted by no call site in the
11+
reference implementation and read by no branch; `'new_window'` — what a Cmd/Ctrl- or
12+
middle-click, and an authored `navigation: { mode: 'new_window' }`, actually send — was
13+
not declared at all. An author reading this contract wrote a handler with one dead arm
14+
and one missing arm.
15+
16+
The second argument is a navigation-MODE token with a **closed vocabulary**, and the
17+
declaration now says so. That closedness is not new: the protocol's own retirement note
18+
for `view.list.navigation.view` (removed in 17.5.0, ADR-0049) records that anything
19+
outside the mode vocabulary "matched no branch". What this change corrects is the
20+
membership of the vocabulary, not its closedness.
21+
22+
## FROM → TO
23+
24+
| you wrote | write instead |
25+
| --- | --- |
26+
| `onNavigate={(id, action) => { if (action === 'edit') … }}` | delete that arm — nothing ever called it |
27+
| a handler with no `'new_window'` arm | handle `'new_window'`: open the record in a new browser tab. Omitting the arm leaves the modifier-click path doing nothing |
28+
| `onNavigate={(id) => …}` (one argument) | unchanged — the arity is untouched |
29+
30+
**The one-line fix:** replace the `'edit'` arm with a `'new_window'` arm.
31+
32+
Scope: this moves a **declaration**, not a type or a runtime check. `REACT_BLOCKS` types
33+
this prop as a documentation string (`ReactBlockDef[]`), so no `.d.ts` signature moves
34+
and nothing that compiles today stops compiling. The behaviour it describes is the
35+
reference implementation's, which already emits exactly these two values; the sibling's
36+
four declaration faces are corrected under objectui#9547 and its bump to
37+
`@objectstack/spec` >= 17.5.0.
38+
39+
Clause-②: yes

‎packages/spec/src/ui/react-blocks.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ export const REACT_BLOCKS: ReactBlockDef[] = [
318318
{ name: 'filters', type: "FilterArray e.g. ['status','=','active']", kind: 'controlled', description: 'ObjectQL base filter; drive from React state for tabbed/searched lists. ([field, op, value]; ops =, !=, >, <, contains, in; compound: [\"and\", […], […]]).' },
319319
{ name: 'navigation', type: "{ mode: 'page' | 'drawer' | 'modal' | 'split' | 'none' }", kind: 'binding', description: 'What a row click does. Use { mode: \"none\" } when you handle clicks via onRowClick.' },
320320
{ name: 'onRowClick', type: '(record) => void', kind: 'callback', description: "Called with the clicked row's record — the hook for master/detail." },
321-
{ name: 'onNavigate', type: "(recordId, action: 'view' | 'edit') => void", kind: 'callback', description: 'Called for page-level navigation.' },
321+
{ name: 'onNavigate', type: "(recordId, action: 'view' | 'new_window') => void", kind: 'callback', description: "Called for page-level navigation. The second argument is a navigation-MODE token from a CLOSED vocabulary — 'view' opens the record page, 'new_window' opens it in a new browser tab; the reference implementation emits no other value and reads no other branch." },
322322
{ name: 'fields', type: 'string[]', kind: 'binding', description: 'Limit/order the columns shown (defaults to the object list fields).' },
323323
{ name: 'options', type: 'Record<string, any>', kind: 'binding', description: 'View-type-specific options bag (kanban/calendar/gantt extras); prefer the typed spec props where they exist.' },
324324
],

‎skills/objectstack-ui/references/react-blocks.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ Server-connected object table with toolbar and switchable visualizations (grid/k
6464
| `options` | `Record<string, any>` | binding | | View-type-specific options bag (kanban/calendar/gantt extras); prefer the typed spec props where they exist. |
6565
| `filters` | `FilterArray e.g. ['status','=','active']` | controlled | | ObjectQL base filter; drive from React state for tabbed/searched lists. ([field, op, value]; ops =, !=, >, <, contains, in; compound: ["and", […], […]]). |
6666
| `onRowClick` | `(record) => void` | callback | | Called with the clicked row's record — the hook for master/detail. |
67-
| `onNavigate` | `(recordId, action: 'view' \| 'edit') => void` | callback | | Called for page-level navigation. |
67+
| `onNavigate` | `(recordId, action: 'view' \| 'new_window') => void` | callback | | Called for page-level navigation. The second argument is a navigation-MODE token from a CLOSED vocabulary — 'view' opens the record page, 'new_window' opens it in a new browser tab; the reference implementation emits no other value and reads no other branch. |
6868
| `columns` | `string[] \| object[]` | data | ✓ | Fields to display as columns |
6969
| `type` | `'grid' \| 'kanban' \| 'gallery' \| 'calendar' \| 'timeline' \| 'gantt' \| 'map' \| 'chart' \| 'tree'` | data | | |
7070
| `sort` | `object[]` | data | | |

0 commit comments

Comments
 (0)