diff --git a/content/docs/guide/architecture.md b/content/docs/guide/architecture.md index 35c560cf5..0d1192fbb 100644 --- a/content/docs/guide/architecture.md +++ b/content/docs/guide/architecture.md @@ -243,12 +243,18 @@ ObjectUI includes a powerful expression engine for dynamic UIs: ```json { "type": "button", - "text": "Submit", + "label": "Submit", "visible": "${form.isValid && !form.isSubmitting}", "disabled": "${form.isSubmitting}" } ``` +A button's text key is `label`, and `text` is not a `ButtonSchema` key at all. Nothing +refuses the misspelling either: `BaseSchema` is `.passthrough()`, so the validator KEEPS +the unknown key, and the renderer — which reads `schema.label` — never looks at it. +Measured on the node above with `text`: the button renders with an empty `textContent`, +so it appears on screen as a blank rectangle with no text. + ### Data Transformations ```json diff --git a/content/docs/guide/layout.md b/content/docs/guide/layout.md index f54c7cd88..12b73b130 100644 --- a/content/docs/guide/layout.md +++ b/content/docs/guide/layout.md @@ -152,13 +152,13 @@ The `Page` component provides a consistent wrapper for individual pages with opt "actions": [ { "type": "button", - "text": "Add Product", + "label": "Add Product", "variant": "default", "icon": "plus" }, { "type": "button", - "text": "Export", + "label": "Export", "variant": "outline", "icon": "download" } @@ -170,6 +170,10 @@ The `Page` component provides a consistent wrapper for individual pages with opt } ``` +`label` is the button's text key — `text` is not a `ButtonSchema` key, and because +`BaseSchema` is `.passthrough()` nothing refuses it: the validator keeps the unknown key +and `button.tsx`, which reads `schema.label`, renders a button with no text. + ### Schema API @@ -516,18 +520,20 @@ Omit `sidebar` and the content fills the width under the top bar. ], "actions": [ { - "type": "button", - "text": "Edit", + "type": "action:button", + "name": "edit_record", + "label": "Edit", "variant": "default", "icon": "pencil", - "onClick": "editRecord" + "actionType": "editRecord" }, { - "type": "button", - "text": "Delete", + "type": "action:button", + "name": "delete_record", + "label": "Delete", "variant": "destructive", "icon": "trash", - "onClick": "deleteRecord" + "actionType": "deleteRecord" } ], "body": { @@ -539,6 +545,19 @@ Omit `sidebar` and the content fills the width under the top bar. } ``` +A button that RUNS something is an `action:button` node, not a `button` carrying an +`onClick`. `ButtonSchema.onClick` is declared as a runtime slot for a host-supplied +function and the zod mirror refuses it BY NAME — JSON has no function value, and no +handler key consumes a declarative action object. The refusal is not the whole cost: +`onClick` is on `SDUI_DOM_PASS_THROUGH_KEYS`, so an authored string or object is +forwarded to the real DOM listener slot, and React throws the moment anyone clicks. +Measured, React's own error: "Expected `onClick` listener to be a function, instead got +a value of `object` type." + +The handler name goes in `actionType`, which `action:button` forwards to the action +runner as the action's type; the runner dispatches to the handler registered under it. +Same spelling as [Record Edit Modes](./record-edit-modes.md). + ## Responsive Behavior The shell has exactly **one** layout breakpoint, at **768px** — Tailwind's `md`, and @@ -650,7 +669,7 @@ Place primary actions in page headers: "type": "page", "title": "Orders", "actions": [ - { "type": "button", "text": "New Order", "variant": "default" } + { "type": "button", "label": "New Order", "variant": "default" } ] } ``` diff --git a/content/docs/guide/schema-rendering.md b/content/docs/guide/schema-rendering.md index 61257c6cb..3d51712e8 100644 --- a/content/docs/guide/schema-rendering.md +++ b/content/docs/guide/schema-rendering.md @@ -252,15 +252,26 @@ Reference actions in schemas: ```json { - "type": "button", + "type": "action:button", + "name": "call_api", "label": "Click Me", - "onClick": { - "actionType": "ajax", - "api": "/api/action" - } + "actionType": "api", + "endpoint": "/api/action", + "method": "POST" } ``` +Three things about the shape this replaces. A declarative action is its own NODE TYPE, +`action:button` — a plain `button` has no authorable handler: `ButtonSchema.onClick` is a +runtime slot for a host-supplied function, refused by name by the zod mirror, and (being +on `SDUI_DOM_PASS_THROUGH_KEYS`) forwarded straight to the DOM listener slot, where React +throws on the first click: "Expected `onClick` listener to be a function, instead got a +value of `object` type." The execution type is `actionType`, and the built-in vocabulary +is `script` | `url` | `modal` | `flow` | `api` | `form` (plus objectui's `navigation` +alias) — anything else must be a handler your host registered on `ActionProvider`. `ajax` +is neither. And the endpoint key is `endpoint`, with `method`; `api` is not a key any +action renderer forwards. + ## Performance Optimization ### Lazy Loading