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
5 changes: 5 additions & 0 deletions .changeset/10392-registration-record-source-inputs.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,8 @@ on the registrations disagreed with that rule in two ways:
The renderers' read order and the zod schemas are unchanged. The
`@object-ui/plugin-gantt` README sentence that listed the registration's inputs
is updated.

**Correction, 2026-10-01 (objectui#11117).** "One of the three" now has an exception on all
three blocks: a block that declares none of them is accepted when its `dataSource.object` names
the object, because the registration's `ElementDataSourceGate` lands that name on `objectName`.
The `objectName` input description now says so.
49 changes: 49 additions & 0 deletions .changeset/11117-datasource-objectname-waiver.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
---
'@object-ui/types': minor
---

feat(types): a node bound through `dataSource.object` needs no `objectName` on the validator (objectui#11117)

`safeValidateSchema` (and `objectui validate`, which runs it) refused the documented
`object-grid`, `list-view` and `object-kanban` bindings —
`{ "type": "object-grid", "dataSource": { "object": "product", "limit": 20 } }`, the same on
`list-view`, and `object-kanban`'s documented board — although each of those blocks renders
it: their registrations are wrapped in `ElementDataSourceGate`, which lands the
binding's `object` on `objectName` before the renderer reads the node, and `@objectstack/spec`
accepts the node as written. `object-grid` and `list-view` required `objectName`, and the
record-source rule of `object-kanban`, `object-calendar` and `object-gantt`, and of the flat
`object-map` mirror, had no rung for the binding. (The authored `object-map` node has been judged by
its `properties`-bag arm since objectui#10859 batch 5, which already counted a binding; see the
last bullet below.)

**A widening.** On those six blocks a node whose `dataSource.object` is a non-empty name now
validates with no `objectName` and no other record source. Nothing that validated before is
refused, except the one `object-map` shape named last:

- `objectName` alone still validates, an empty one included.
- A node with neither is still refused, with `params.code = 'RECORD_SOURCE_REQUIRED'`: at
`objectName` on `object-grid` and `list-view` (a `custom` issue now, where it was an
`invalid_type`), and at the node's root on the four ladder blocks, as before. Its message names
`dataSource.object` beside the keys the block reads.
- `dataSource: { object: '' }` supplies nothing, as at runtime, so that node is still refused.
- A wrong-typed `objectName` beside a binding is still refused at `objectName`.
- **One narrowing, on the `object-map` bag arm.** objectui#10859 batch 5 counted that arm's
binding by presence, so `{ "type": "object-map", "dataSource": { "object": "" } }` with no
`properties.data`, `properties.staticData` or `properties.objectName` parsed, and rendered no
records: the runtime's `isElementDataSourceConfig` refuses an empty name and the gate lands
nothing. The bag arm now reads the binding with the same predicate as the other arms, so that node
is refused with `RECORD_SOURCE_REQUIRED`. A named binding still parses there.

One refinement carries the rule for all six blocks, reading the rungs on the node or, for the
authored `object-map`, in its `properties` bag. It counts the binding with the same predicate
`element:number`'s spec waiver uses, so the validator, the spec's props gate and the runtime agree
on what a binding is.

The TypeScript `ObjectGridSchema` keeps `objectName: string`: it is the type `ObjectGrid` reads
after the gate has run. `ListViewSchema` is derived from its validator, so its `objectName` is
now optional.

Docs: in `content/docs/utilities/data-objectstack.mdx`, the "Narrowing a saved view" example
writes its `filter` as the rule array `[{ field, operator, value }]` the spec takes, and the
`ElementDataSource` excerpt types `filter` as `ViewFilterRule[]`. The data-binding guide and the
schema reference say that a bound node needs no `objectName`.
16 changes: 16 additions & 0 deletions .changeset/11117-registration-objectname-description.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
'@object-ui/plugin-gantt': patch
'@object-ui/plugin-calendar': patch
---

fix(plugin-gantt, plugin-calendar): the `objectName` input description names the `dataSource.object` binding (objectui#11117)

The designer-facing `objectName` input on `object-gantt` and `object-calendar` (and
`view:calendar`, which shares the calendar's list) said the block's schema refuses a block that
declares none of `data`, `staticData` and `objectName`. Since objectui#11117 the schema also
accepts a block whose `dataSource.object` names the object, because the registration's
`ElementDataSourceGate` lands that name on `objectName` before the renderer reads the node. The
sentence now says so. No input's name, type, `required` flag or shape changed. (`object-map`'s
input already names the binding: objectui#10859 batch 5 rewrote it.)

The `plugin-gantt` and `plugin-kanban` READMEs carry the same correction.
6 changes: 6 additions & 0 deletions .changeset/6939-objectql-record-source-refinement.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,9 @@ including an empty one, still parses, because presence is `!== undefined` and
not the renderer's truthiness. The one shape the refinement refuses (none of
the three) was refused before too, when `objectName` was required. Documents
the renderers already draw start validating.

**Correction, 2026-10-01 (objectui#11117).** "The one shape the refinement refuses (none of the
three)" is narrower now: a node with none of the three that names its object in a
`dataSource` binding (`dataSource: { object }`, a non-empty name) parses, because the
registration's `ElementDataSourceGate` lands that object on `objectName`. The refusal now names
the binding beside the three keys.
6 changes: 6 additions & 0 deletions .changeset/7313-object-calendar-record-source.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,9 @@ refinement refuses (none of the three) was refused before too, at
`objectName`. The two static-data examples in
`content/docs/plugins/plugin-calendar.mdx` are now annotated
`ObjectCalendarSchema` and compile under the doc-snippet gate.

**Correction, 2026-10-01 (objectui#11117).** A node authoring none of the three is still
refused, unless it names its object in a `dataSource` binding (`dataSource: { object }`, a
non-empty name): objectui#11117 counts that binding as a record source, because the
registration's `ElementDataSourceGate` lands it on `objectName`. The refusal message now names
the binding beside `data`, `staticData` and `objectName`.
5 changes: 5 additions & 0 deletions .changeset/7470-designer-objectname-not-required.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,8 @@ schema refuses a block that declares none of them. No one-of form was added to t
input declaration; the zod refinement stays the place that enforces it. The
renderers' read order is unchanged. The `@object-ui/plugin-gantt` README sentence
that called `objectName` required is corrected (objectui#7470).

**Correction, 2026-10-01 (objectui#11117).** The rule this entry describes gained an exception:
the `object-map` and `object-gantt` schemas also accept a block that declares none of the three
when its `dataSource.object` names the object, because the registration's `ElementDataSourceGate`
lands that name on `objectName`. The `objectName` input description now says so.
9 changes: 9 additions & 0 deletions .changeset/7780-object-kanban-record-source.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,12 @@ compiles, which is the assertion.

Marked `minor` per this repo's version-alignment rule: a published accept set widens, and
no declared shape narrows.

**Correction, 2026-10-01 (objectui#11117).** Two statements above no longer hold. The
`dataSource` json fragment in `content/docs/utilities/data-objectstack.mdx` is no longer
refused: objectui#11117 counts a node's `dataSource.object` binding as a record source, because
the registration's `ElementDataSourceGate` lands it on `objectName` before the board reads the
node, so `{ type, dataSource }` parses and the pin now asserts that. And the refinement is no
longer a board-specific `requireKanbanRecordSource`: it is the shared `requireRecordSource`,
given this board's own rungs (`bind`, `data`, `objectName`). The ladder and its presence rule are
otherwise unchanged.
6 changes: 6 additions & 0 deletions .changeset/8990-object-kanban-groupby-optional.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,9 @@ objectui#8913 and recorded there as unreachable, is now reachable: it fires only
`if (!schema.groupBy)`, which no schema-valid document could satisfy while the key was
required. Pinned with a firing control in
`packages/plugin-kanban/src/__tests__/laneLessBoard-8990.test.tsx`.

**Correction, 2026-10-01 (objectui#11117).** The second bullet above says the
`data-objectstack.mdx` fragment `{ type, dataSource }` is still refused at
`RECORD_SOURCE_REQUIRED` because `dataSource` is not a rung. That no longer holds: objectui#11117
counts a node's `dataSource.object` binding as a record source, as the registration's
`ElementDataSourceGate` already did, so the fragment now parses.
4 changes: 2 additions & 2 deletions content/docs/api/schema-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -774,7 +774,7 @@ presence enables paging.

| Property | Type | Description |
|----------|------|-------------|
| `objectName` | `string` | **Required.** ObjectQL object API name. |
| `objectName` | `string` | **Required**, unless a `dataSource` binding names the object (`"dataSource": { "object": "Contact" }`), which the registration lands here (objectui#11117). ObjectQL object API name. |
| `label` | `string \| I18nLabel` | Grid label: the table caption, the export file title and the record-detail overlay heading. The canonical spelling; the deprecated `title` is read only when `label` is absent. A per-locale map (`{ "en": "Accounts", "zh-CN": "客户" }`) resolves against the display locale (the workspace's regional default when one is configured, otherwise the active UI language) (objectui#10993). |
| `columns` | `string[] \| ListColumn[]` | Columns to display. Either a plain array of field names (`["name", "email"]`), which auto-resolve from object metadata, or an array of `ListColumn` objects whose identity key is `field` (`{ "field": "status", "label": "Status" }`) — never `name`. **Do not mix the two forms in one array:** the array is dispatched on its first entry, so column objects sitting behind a bare string are dropped. |
| `filter` | `any[]` | Pre-applied filter conditions. |
Expand Down Expand Up @@ -1004,7 +1004,7 @@ A drag-and-drop Kanban board. The `object-kanban` type key validates the shape t

> **Handler keys are not authorable in JSON, and all three now say so by name.** Since objectui#7804 this face declares `onCardClick` as an objectui#6124 **runtime slot**: a React host supplies the function through the TypeScript interface or as a React prop, and this validator **refuses the key by name** with a message pointing at the node-type spelling (`{ "type": "toast", … }`, an `action:button` node). Until then an authored `onCardClick: { "action": "toast" }` parsed **green** — `BaseSchema` is `.passthrough()`, so a key no arm declares is not refused, it stops being judged and the value is kept, then reaches a call site expecting a function. ⭐ The other two keys are **tombstones**, not runtime slots, so their TypeScript twins are `?: never` rather than callable. `onCardMove` has been one since objectui#9342: an authored one reached **nothing** even as a function, because an object-bound board substitutes its own mover, and the mover lives on `KanbanRenderer`'s React prop of the same name, a sibling of its `schema`. `onQuickAdd` has been one since objectui#11234: a supplied one reached the board and was never called, because its partner `quickAdd` is retired here. `ObjectKanban` renders an internal board that takes the Quick Add pair only as explicit props and supplies neither half; the pair lives on `KanbanRenderer`'s `schema`, for a React host.

> `data` and `bind` are [`BaseSchema`](#baseschema) members, not narrowed here, but this face requires **one of** `bind`, `data`, `objectName` — the renderer's own record-source ladder (an external `data` prop → `bind` via `useDataScope` → this schema's own `data` → a fetch keyed by `objectName`). A purely static board (lanes carrying their own cards, no record source) authors `"groupBy"` and `"data": []`. ⚠️ The record-source rule is **separate** from the lane key and is unaffected by objectui#8990: omitting `groupBy` is fine, omitting all of `bind` / `data` / `objectName` is still refused, at the refinement rather than at `groupBy`.
> `data` and `bind` are [`BaseSchema`](#baseschema) members, not narrowed here, but this face requires **one of** `bind`, `data`, `objectName` — the renderer's own record-source ladder (an external `data` prop → `bind` via `useDataScope` → this schema's own `data` → a fetch keyed by `objectName`) — or a `dataSource` binding whose `object` names the object, which the registration's `ElementDataSourceGate` lands on `objectName` before the board reads it (objectui#11117). A purely static board (lanes carrying their own cards, no record source) authors `"groupBy"` and `"data": []`. ⚠️ The record-source rule is **separate** from the lane key and is unaffected by objectui#8990: omitting `groupBy` is fine, omitting all of `bind` / `data` / `objectName` with no binding is still refused, at the refinement rather than at `groupBy`.

**Related:** [ObjectViewSchema](#objectviewschema), [ObjectGridSchema](#objectgridschema)

Expand Down
7 changes: 7 additions & 0 deletions content/docs/guide/data-source.md
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,13 @@ of the binding and stays the author's — it has to name a field on the bound ch
object, so rebinding `object` without updating it is an authoring error the panel
cannot paper over.

Because the binding lands on `objectName`, a node bound this way needs no
`objectName` of its own. The schema validator (`safeValidateSchema`, which
`objectui validate` runs) counts the binding as a record source of `list-view`,
`object-grid`, `object-kanban`, `object-calendar`, `object-gantt` and `object-map`,
and still refuses one of those nodes that declares no other record source and names
its object in neither place. A binding with an empty `object` names nothing.

The two `element:*` rows keep their configuration in the node's `properties` bag,
so the binding does not land on a schema key there: each reads it directly, and
`dataSource.object` wins over `properties.object`. They differ on `filter`.
Expand Down
11 changes: 6 additions & 5 deletions content/docs/utilities/data-objectstack.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ A schema node's `dataSource` key is the spec's `ElementDataSource` (validated by
`ElementDataSourceSchema` in `@objectstack/spec`) — plain JSON metadata, **not**
the adapter object:

{/* doc-snippet: fragment — a SHAPE excerpt restating `@objectstack/spec`'s own `ElementDataSource`, whose `filter` member is typed `FilterCondition` — a spec type that is NOT re-exported from `@objectstack/spec`'s root entry, so neither this block nor a reader can import the name (measured: TS2304 x1). ⚠️ This gate never compiles a DECLARED block, so nothing here re-verifies this excerpt against `ElementDataSourceSchema` in the installed `@objectstack/spec` — and this reason may not claim it does (objectui#7505). The schema is the authority on which members are required and whether it is strict; read it there */}
{/* doc-snippet: fragment — a SHAPE excerpt restating `@objectstack/spec`'s own `ElementDataSource` as a bare interface, so its `filter` member names the spec's `ViewFilterRule` without importing it (measured: TS2304 x1); the type is exported from `@objectstack/spec/ui`. ⚠️ This gate never compiles a DECLARED block, so nothing here re-verifies this excerpt against `ElementDataSourceSchema` in the installed `@objectstack/spec` — and this reason may not claim it does (objectui#7505). The schema is the authority on which members are required and whether it is strict; read it there */}
```typescript
interface ElementDataSource {
/** Object to query (required) */
Expand All @@ -271,8 +271,8 @@ interface ElementDataSource {
/** Saved view name — supplies columns, filter, sort and page size */
view?: string

/** Additional filter criteria; AND-combines with the view's filter */
filter?: FilterCondition
/** Additional filter rules, one `{ field, operator, value }` per condition; they AND together and with the view's filter */
filter?: ViewFilterRule[]

/** Sort order — overrides the view's */
sort?: Array<{ field: string; order: 'asc' | 'desc' }>
Expand Down Expand Up @@ -304,15 +304,16 @@ adapter itself is injected once, at the renderer boundary, as shown in Quick Sta
### Narrowing a saved view

`filter` AND-combines with the view's own filter, so a binding can only narrow
what the view already restricts:
what the view already restricts. It is a list of `{ field, operator, value }`
rules, which AND together:

```json
{
"type": "object-grid",
"dataSource": {
"object": "order",
"view": "open_orders",
"filter": { "total": { "$gt": 100 } },
"filter": [{ "field": "total", "operator": "greater_than", "value": 100 }],
"limit": 50
}
}
Expand Down
6 changes: 4 additions & 2 deletions packages/plugin-calendar/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,9 @@ export const ObjectCalendarRenderer: React.FC<{ schema: any; [key: string]: any
* The record source is ONE OF `data`, `staticData` and `objectName`: the
* shared ladder reads them in that order, and `ObjectCalendarSchema`
* (`@object-ui/types`) enforces "one of the three" with
* `requireRecordSource('object-calendar')`. A `required: true` flag here made
* `requireRecordSource`, which since objectui#11117 also counts a
* `dataSource.object` binding, because the gate lands it on `objectName`.
* A `required: true` flag here made
* `sdui-parser`'s `validateTree` raise a `missing-required-prop` ERROR on a
* `staticData`-only calendar that the schema accepts and the renderer draws.
* Same repair as objectui#7470 made for map and gantt: the flag is gone, the
Expand All @@ -428,7 +430,7 @@ export const ObjectCalendarRenderer: React.FC<{ schema: any; [key: string]: any
* `__tests__/recordSourceInput-10392.test.ts`.
*/
const OBJECT_CALENDAR_INPUTS: ComponentInput[] = [
{ name: 'objectName', type: 'string', description: 'ObjectQL object name. The record source is one of `data`, `staticData` and `objectName`; the `object-calendar` schema refuses a block that declares none of them.' },
{ name: 'objectName', type: 'string', description: 'ObjectQL object name. The record source is one of `data`, `staticData` and `objectName`; the `object-calendar` schema refuses a block that declares none of them, unless the node\'s `dataSource.object` names the object, which then lands on this key.' },
{ name: 'calendar', type: 'object', description: 'startDateField, endDateField, titleField, colorField' },
{ name: 'filter', type: 'array', description: 'Filter criteria in JSON-rules form, narrowing the records the calendar fetches. Lowered to `$filter` on the query.' },
{ name: 'sort', type: 'array', description: 'Sort order in `[{ field, order }]` form, ordering the records the calendar fetches. Lowered to `$orderby` on the query.' },
Expand Down
6 changes: 4 additions & 2 deletions packages/plugin-gantt/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,10 @@ Both spellings of the surviving key resolve — `register` stores the namespaced
configuration object, and the two other record sources, `data` and `staticData`.
`objectName` is not a required input: the record source is one of `data`,
`staticData` and `objectName` (read in that order), and the `object-gantt` schema
refuses a block that declares none of them. `data` is a `{ provider, … }`
data-source configuration, never a bare array; inline rows go under `staticData`.
refuses a block that declares none of them, unless the node's `dataSource.object`
names the object, which the registration then lands on `objectName`. `data` is a
`{ provider, … }` data-source configuration, never a bare array; inline rows go
under `staticData`.

> **The bare `gantt` key is retired** (objectui#8008, ruled 2026-09-09). This
> table used to carry a second row, `gantt` / `view:gantt`, on the same renderer.
Expand Down
Loading
Loading