From 74f19969a6679fd4cf78d496ab624b37d3afee46 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 6 Sep 2026 02:06:17 +0000 Subject: [PATCH] =?UTF-8?q?docs(guide):=20repair=20the=20seven=20${?= =?UTF-8?q?=E2=80=A6}=20sites=20on=20keys=20with=20no=20carriage=20row=20(?= =?UTF-8?q?objectui#7872)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `SchemaRenderer` evaluates `content`, the `properties`/`props` bags, the eight condition keys, and the carriage rows `expressionBindableTextKeysFor` gives the node's own type. `badge`, `alert` and `page` have no rows, so every `${…}` these three pages wrote on `badge.text`, `badge.variant`, `alert.variant`, `alert.message` and `page.title` reached the renderer as the characters the author typed. Repaired per passage under the 2026-09-01 fork-B ruling on objectui#7115, not by one blanket rule: the passages demonstrating binding keep demonstrating it on a key that genuinely carries (`card.title`/`description`, `statistic.label`/ `value`/`description`, a nested `text` node's `content`), and the page-layout passage takes a literal plus prose naming the missing row. Two of the old shapes were broken beyond not evaluating, re-measured here through the real renderers rather than carried over from objectui#7847: `text` is not a `BadgeSchema` key (the badge's text is `label`, so the old shape rendered an empty badge with `text=${…}` left on the DOM), and `success` / `warning` / `info` are not badge variants. Newly measured on this card: `message` is not an `AlertSchema` key either — the alert's text keys are `title` and `description` — and `AlertSchema.variant` is the closed set `default` | `destructive`, so `info` / `warning` / `success` / `error` are all off-enum. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01KbJQ1y1J12nZxYzFWhP8Q3 --- content/docs/guide/architecture.md | 12 +++++-- content/docs/guide/layout.md | 4 +-- content/docs/guide/schema-rendering.md | 47 ++++++++++++++++++++------ 3 files changed, 47 insertions(+), 16 deletions(-) diff --git a/content/docs/guide/architecture.md b/content/docs/guide/architecture.md index 9bdb84df90..35c560cf50 100644 --- a/content/docs/guide/architecture.md +++ b/content/docs/guide/architecture.md @@ -253,12 +253,18 @@ ObjectUI includes a powerful expression engine for dynamic UIs: ```json { - "type": "badge", - "text": "${orders.length} Orders", - "variant": "${orders.length > 10 ? 'success' : 'warning'}" + "type": "statistic", + "label": "Orders", + "value": "${orders.length}", + "description": "${orders.length > 10 ? 'Above target' : 'On track'}" } ``` +`statistic` rather than `badge`: an expression is evaluated only on a key the node's own +type carries, and `expressionBindableTextKeysFor` gives `statistic` the rows `label`, +`value` and `description` while giving `badge` none. A badge's text is its `label`, and it +has to arrive already resolved. + See the [Expressions Guide](/docs/guide/expressions) for complete details. ## Data Flow diff --git a/content/docs/guide/layout.md b/content/docs/guide/layout.md index 7a8c7511c2..f54c7cd887 100644 --- a/content/docs/guide/layout.md +++ b/content/docs/guide/layout.md @@ -508,11 +508,11 @@ Omit `sidebar` and the content fills the width under the top bar. ```json { "type": "page", - "title": "${record.name}", + "title": "Acme Corporation", "breadcrumbs": [ { "label": "Home", "href": "/" }, { "label": "Customers", "href": "/customers" }, - { "label": "${record.name}" } + { "label": "Acme Corporation" } ], "actions": [ { diff --git a/content/docs/guide/schema-rendering.md b/content/docs/guide/schema-rendering.md index 70cdb10ae0..61257c6cb6 100644 --- a/content/docs/guide/schema-rendering.md +++ b/content/docs/guide/schema-rendering.md @@ -182,12 +182,19 @@ Object UI includes a powerful expression system for dynamic behavior: ```json { - "type": "badge", - "text": "${status === 'active' ? 'Active' : 'Inactive'}", - "variant": "${status === 'active' ? 'success' : 'default'}" + "type": "card", + "title": "${status === 'active' ? 'Active' : 'Inactive'}", + "description": "${status === 'active' ? 'This record is in use.' : 'This record is archived.'}" } ``` +`card` here rather than `badge`, because an expression is evaluated only on a key the +node's own type carries. `expressionBindableTextKeysFor` — the lookup `SchemaRenderer` +consumes out of `@objectstack/spec` — gives `card` the rows `title` and `description`, +and gives `badge` no rows at all, so a `${…}` written on a badge reaches the DOM as the +characters you typed. Resolve a badge's text before you hand the schema over, and author +it on `label`: `text` is not a `BadgeSchema` key. + ### Visibility Control ```json @@ -203,15 +210,26 @@ Object UI includes a powerful expression system for dynamic behavior: ```json { "type": "alert", - "message": "Welcome!", - "variant": "${ - user.isNew ? 'info' : - user.tasks.length === 0 ? 'warning' : - 'success' - }" + "variant": "default", + "title": "Welcome!", + "body": { + "type": "text", + "content": "${ + user.isNew ? 'Start with the quick tour.' : + user.tasks.length === 0 ? 'You are all caught up.' : + 'You have tasks waiting.' + }" + } } ``` +The branch sits on the nested `text` node's `content`, which `SchemaRenderer` evaluates +on every node type — the escape hatch for a component that carries no expression rows of +its own, and `alert` is one of those. Its severity could not be chosen by expression in +any case: `AlertSchema.variant` is the closed set `default` | `destructive`, so `info`, +`warning` and `success` are not values it accepts. Pick the variant in the host and +author it as a literal. + ## Event Handling Components can emit events that you handle in React: @@ -411,12 +429,19 @@ Always type your schemas for better IDE support and fewer runtime errors. ```json { "type": "alert", - "variant": "error", + "variant": "destructive", "visibleOn": "${error}", - "message": "${error.message}" + "title": "Something went wrong", + "body": { "type": "text", "content": "${error.message}" } } ``` +`visibleOn` is a condition key and is evaluated on every node type. The message text is a +nested `text` node because `alert` carries no expression rows — and `message` is not an +`AlertSchema` key at all: the alert's own text keys are `title` and `description`, and the +renderer falls back from `description` to `body`. `destructive` is the variant this state +wants; `error` is not in the closed set. + ## Next Steps - [Component Registry](./component-registry.md) - Learn about component registration