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
6 changes: 3 additions & 3 deletions .changeset/5293-view-sort-order-spelling.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ described above no longer finds a live `direction` read on this package's publis
path.

`order` is the spelling every other sort surface already uses (`SortConfig`,
`NamedListView.sort`, `ObjectGridSchema.sort` / `.defaultSort`, and the shared
`QuerySortEntry` sink), so the prop now has one spelling repo-wide and declared equals
enforced.
`NamedListView.sort`, `ObjectGridSchema.sort` (and, at this change, `.defaultSort` —
since retired, objectui#5861), and the shared `QuerySortEntry` sink), so the prop now has
one spelling repo-wide and declared equals enforced.

⛔ Deliberately **not** a tolerant dual-read (`direction ?? order`): that is the tolerance
layer objectui#4869 ruled against, and admitting the old key as an alias would rebuild the
Expand Down
46 changes: 46 additions & 0 deletions .changeset/5861-retire-table-defaultsort.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
'@object-ui/types': minor
'@object-ui/plugin-grid': minor
'@object-ui/plugin-view': minor
---

Retire `object-grid`'s `defaultSort` and `object-view`'s `table.defaultSort`
(objectui#5861 — ADR-0049 enforce-or-remove, the "C half" of the 2026-08-22
ruling on objectui#4869).

**BREAKING for the legacy spelling, deliberately.** `defaultSort` was the
single-entry `{ field, order }` spelling of `sort`. `@objectstack/spec` 17.3.0
turned `ObjectGridProps.defaultSort` into a retired-key tombstone, so the
protocol already refuses an authored value by name; the renderers were the last
place that still honoured it. Every read goes in this one change, so no path
honours the key while another ignores it:

- `@object-ui/plugin-grid` — `ObjectGrid` no longer lowers `defaultSort` into
`$orderby` and no longer falls back to it for the header sort arrows. The
registered `sort` input's description no longer advertises it as a fallback.
- `@object-ui/plugin-view` — `ObjectView` no longer forwards `table.defaultSort`
to the grid, no longer lowers it on the non-grid fetch (calendar / kanban /
gallery / timeline / gantt / map), and no longer wraps it into the sort
handed to a host's `renderListView`.
- `@object-ui/types` — the published faces narrow to match:
`ObjectGridSchema.defaultSort` is now `?: never` (a TypeScript author who
writes it gets a compile error), and the `ObjectGridSchema` zod mirror refuses
it by name with a migration message. `ObjectViewSchema.table` inherits the
`never` member. A tombstone rather than a deletion: `BaseSchema`'s index
signature and `.passthrough()` would otherwise absorb the key silently.

A document that still carries `defaultSort` renders **unsorted** — it is not
re-routed into `sort`. Migrate by renaming the key and wrapping the value:
`defaultSort: { field: 'name', order: 'asc' }` becomes
`sort: [{ field: 'name', order: 'asc' }]`. `sort` already outranked
`defaultSort` wherever both were written, so documents that carry both keep
their current ordering.

This supersedes the `defaultSort` handling described by five earlier
changesets still pending in the same release, each of which now carries a dated
note naming this card: objectui#4869 (the non-grid lowering, and "both
spellings of the pair keep working"), objectui#6235 (the delegated-slot wrap),
objectui#8973 (the normalized grid arm), objectui#4082 (the grid's legacy
`defaultSort` leg, graded as needing no change) and objectui#5293 (which cites
`ObjectGridSchema.defaultSort` as a live `order` spelling). Those describe how
the legacy key was honoured; from this release it is not read at all.
22 changes: 15 additions & 7 deletions .changeset/6235-mergedsort-wrap.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,16 @@
'@object-ui/plugin-view': patch
---

`ObjectView` wraps `table.defaultSort` before handing it to a delegated list view, so a
view whose only ordering is the deprecated key actually sorts (objectui#6235).
At this change `ObjectView` wrapped `table.defaultSort` before handing it to a delegated
list view, so a view whose only ordering was the deprecated key actually sorted
(objectui#6235).

⚠️ **Dated note, 2026-09-25 — this wrap has since been retired — objectui#5861.** Later in
this same release `table.defaultSort` became an ADR-0049 retirement tombstone and
`mergedSort` lost the branch this entry changed: it now ends at `table.sort`, so a
`table.defaultSort` alone hands the delegated slot no sort at all and the view renders
unsorted. The rest of this entry is kept as the reading of this change; the objectui#5861
entry states what ships and the migration (`sort: [{ field, order }]`).

`ObjectGridSchema.defaultSort` is declared a SINGLE `{ field, order }` object — the zod
mirror agrees (`z.object({ field, order })`, not a union) — while the `list-view` node's
Expand All @@ -21,16 +29,16 @@ Studio's `renderStudioGridList`), so the symptom was an unsorted list with no er
while the SAME metadata sorted correctly as a grid, because `ObjectGrid` performs this
lowering for the same pair.

The wrap is verbatim the one the non-grid fetch path in this same file already applies
(`|| (schema.table?.defaultSort ? [schema.table.defaultSort] : undefined)`), so all three
consumers now agree and no fourth dialect is introduced. The shared sink is deliberately
The wrap was verbatim the one the non-grid fetch path in this same file then applied
(`|| (schema.table?.defaultSort ? [schema.table.defaultSort] : undefined)`), so at this
change all three consumers agreed and no fourth dialect was introduced. The shared sink is deliberately
NOT widened to accept a bare `{ field, order }`: that is the widening the maintainer ruling
of 2026-08-22 rejected on the merits, because the same slot legitimately carries
`$orderby`'s own `Record<field, direction>` map, in which `{ field: 'desc' }` is a legal
ordering by a column literally named `field`.

Precedence is unchanged — a named view's sort still outranks `table.sort`, which still
outranks `table.defaultSort`. Only the final branch changes shape.
Precedence was unchanged — a named view's sort still outranked `table.sort`, which still
outranked `table.defaultSort`. Only the final branch changed shape.

One behaviour note for hosts writing off-schema metadata: an ARRAY in `table.defaultSort`
was previously forwarded verbatim by this path alone and is now lowered like every other
Expand Down
9 changes: 7 additions & 2 deletions .changeset/8973-grid-orderby-normalize.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,13 @@ params.$orderby = schemaSort.map((s) => `${s.field} ${s.order}`).join(', ');

So an entry missing `field` or `order` reached the wire as the literal text
`undefined`. The legacy `defaultSort` arm one `else` down had the identical defect on a
single object, and is fixed with it — leaving it would keep the class open inside the
same `if`/`else` chain.
single object, and was fixed with it at this change — leaving it would have kept the class
open inside the same `if`/`else` chain.

⚠️ **Dated note, 2026-09-25 — that legacy arm has since been retired — objectui#5861.**
Later in this same release `ObjectGrid` stopped reading `defaultSort` at all (an ADR-0049
retirement tombstone), so the chain has two arms, header sort and `sort`. Everything else
in this entry, including the table below, concerns `sort` and still holds.

This is a wire failure, not a cosmetic one. `normalizeSortNodes` — the one normalizer
every `@objectstack` server ingress funnels through — validates the direction token, so
Expand Down
9 changes: 7 additions & 2 deletions .changeset/grid-default-filters-lowering-4082.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,10 @@ Not in scope, and deliberately not done: retiring `defaultFilters`. This is
consumer-side only — the key the schema admits is unchanged, and its precedence behind
the canonical `filter` is unchanged.

The sibling legacy `defaultSort` leg was graded and needs no change; see the PR for the
measurements.
At this change the sibling legacy `defaultSort` leg was graded and needed no change; see
the PR for the measurements.

⚠️ **Dated note, 2026-09-25 — that leg has since been retired — objectui#5861.** Later in
this same release `ObjectGrid` stopped reading `defaultSort` at all (an ADR-0049 retirement
tombstone), so there is no longer a legacy sort leg beside the `defaultFilters` one this
entry lowers.
26 changes: 18 additions & 8 deletions .changeset/objectview-sort-sink-4869.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,31 @@ swallows the error, and the view rendered with no records — while the *same*
metadata sorted correctly as a grid, because `ObjectGrid` lowers that pair
before using it (objectui#4869, maintainer ruling 2026-08-22).

`ObjectView` now performs the same legacy-to-canonical lowering `ObjectGrid`
already does (`sort ?? (defaultSort ? [defaultSort] : undefined)`) and routes
the whole chain — named view sort, `views` prop sort, `table.sort`,
At this change `ObjectView` performed the same legacy-to-canonical lowering
`ObjectGrid` then did (`sort ?? (defaultSort ? [defaultSort] : undefined)`) and
routed the whole chain — named view sort, `views` prop sort, `table.sort`,
`table.defaultSort` — through the shared `convertSortToQueryParams` sink. This
was the last object-bound read site sending an authored sort to `$orderby`
unlowered; every other block (gantt, map, calendar, timeline,
`record:line_items`) already did, so an adapter that implements `find` itself
now receives one normalized `Record<field, direction>` from all of them instead
of two different dialects.

Precedence is unchanged, and both spellings of the pair keep working. The
shared sink was deliberately **not** widened to accept a bare `{ field, order }`:
its input slot also legitimately carries `$orderby`'s own map, in which
`{ field: 'desc' }` is a valid ordering by a column named `field`, so widening
it would make one shared function guess.
Precedence was unchanged, and at this change both spellings of the pair kept
working. The shared sink was deliberately **not** widened to accept a bare
`{ field, order }`: its input slot also legitimately carries `$orderby`'s own
map, in which `{ field: 'desc' }` is a valid ordering by a column named `field`,
so widening it would make one shared function guess.

⚠️ **Dated note, 2026-09-25 — the legacy half of that pair has since been
retired — objectui#5861.** Later in this same release `table.defaultSort` became
an ADR-0049 retirement tombstone: `ObjectView` no longer lowers or forwards it on
any path and `ObjectGrid` no longer reads it, so a view whose only sort is
`table.defaultSort` now renders **unsorted** (no longer empty, and no longer
sorted). The paragraphs above are kept as the reading of this change. What still
ships from it is the canonical chain — named view sort, `views` prop sort,
`table.sort` — reaching the shared sink; the objectui#5861 entry states the
migration (`sort: [{ field, order }]`).

Two visible shape changes on the wire, both semantically identical to before: a
string `table.sort` such as `'name desc'` now serializes as `-name` rather than
Expand Down
16 changes: 13 additions & 3 deletions content/docs/api/schema-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -589,8 +589,9 @@ than one node. Build it from the shapes that do render:

The `defaultSort` and `defaultSortOrder` keys documented here were `CRUDSchema`'s
own — a flat field name plus a separate direction. They are gone with it.
[ObjectGridSchema](#objectgridschema) declares its own, differently shaped
`defaultSort` (an object with `field` and `order`); that key is unaffected.
[ObjectGridSchema](#objectgridschema) used to declare its own, differently shaped
`defaultSort` (an object with `field` and `order`); that key has since been
retired too — see the note under [ObjectGridSchema](#objectgridschema).

Authoring `crud` is now refused by name: `validateSchema` from `@object-ui/core`
returns a `RETIRED_TYPE` error on `schema.type` naming the migration above, and
Expand Down Expand Up @@ -745,7 +746,7 @@ A data grid that auto-fetches from an ObjectQL object definition. Includes searc
{ "field": "phone" },
{ "field": "status", "label": "Status", "sortable": true }
],
"defaultSort": { "field": "name", "order": "asc" },
"sort": [{ "field": "name", "order": "asc" }],
"operations": {
"create": true,
"read": true,
Expand Down Expand Up @@ -782,6 +783,15 @@ A data grid that auto-fetches from an ObjectQL object definition. Includes searc
| `frozenColumns` | `number` | Number of columns frozen on scroll. |
| `navigation` | `ViewNavigationConfig` | SPA navigation configuration. |

> **`defaultSort` is retired (objectui#5861).** `ObjectGridSchema` used to accept a
> legacy single-entry `defaultSort: { field, order }` beside `sort`. The installed
> `@objectstack/spec` protocol refuses it by name (a retired-key tombstone), and no
> renderer reads it any more: a grid that still carries it renders **unsorted**, and
> `@object-ui/types` refuses it on both faces (a `?: never` member and a named zod
> refusal). Rename the key to `sort` and wrap the value in an array —
> `"defaultSort": { "field": "name", "order": "asc" }` becomes
> `"sort": [{ "field": "name", "order": "asc" }]`, as in the example above.

**Related:** [ObjectViewSchema](#objectviewschema), [TableSchema](#tableschema)

---
Expand Down
50 changes: 29 additions & 21 deletions content/docs/plugins/plugin-view.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,8 @@ declared member of `ObjectViewSchema`, and none is read anywhere in
`packages/plugin-view/src`. Because `type: 'object-view'` *is* registered, a
node built from those keys still resolves to a renderer; it just never receives
an `objectName`, and the component's data effects are all guarded on it
(`ObjectView.tsx:403`, `:420`), so the result is a silent empty view rather than
an error.
(`ObjectView.tsx`'s `if (!dataSource || !schema.objectName) return;` guards), so
the result is a silent empty view rather than an error.

Three structural facts about this node:

Expand Down Expand Up @@ -128,41 +128,48 @@ object through. Anything else you put in them is ignored:

| Sub-config | Keys `ObjectView` forwards |
| --- | --- |
| `table` | `columns`, `fields`, `title`, `description`, `filter`, `defaultFilters`, `sort`, `defaultSort`, `pagination`, `pageSize`, `selection`, `selectable`, `operations`, `className` |
| `table` | `columns`, `fields`, `title`, `description`, `filter`, `defaultFilters`, `sort`, `pagination`, `pageSize`, `selection`, `selectable`, `operations`, `className` |
| `form` | `fields`, `customFields`, `sections`, `groups`, `layout`, `columns`, `title`, `description`, `subforms`, `buttons`, `defaults`, `initialValues`, `readOnly`, `showSubmit`, `submitText`, `showCancel`, `cancelText`, `showReset`, `className` |

The forwarding is literal, key by key, spread across three sites in
`ObjectView.tsx` — the non-grid data fetch (around `:604-611`), the grid
schema (around `:1041-1078`), and the schema handed to a host-supplied list
renderer (around `:1201-1209`) — so the four keys below behave the same on
every rendering path.
`ObjectView.tsx` — the non-grid data fetch, the `gridSchema` memo, and the
`mergedFilters` / `mergedSort` pair handed to a host-supplied list renderer —
so the keys below behave the same on every rendering path.

> **`table.defaultSort` is retired (objectui#5861).** It was the legacy
> single-entry spelling of `table.sort`. The installed `@objectstack/spec`
> protocol refuses `object-grid`'s `defaultSort` by name (a retired-key
> tombstone), and `ObjectView` no longer reads it on any of the three
> paths above (nor does `ObjectGrid`): a view that still carries it renders
> **unsorted**. Rename the key to `sort` and wrap the value in an array —
> `defaultSort: { field: 'name', order: 'asc' }` becomes
> `sort: [{ field: 'name', order: 'asc' }]`.

#### Canonical keys now take effect (objectui#5102)

Four of the forwarded `table` keys are pairs — a canonical `ObjectGridSchema`
key and the `@deprecated` legacy spelling it replaced. Both now work; write
the canonical one:
Three of the forwarded `table` keys are pairs — a canonical `ObjectGridSchema`
key and the `@deprecated` legacy spelling it replaced. Both work; write the
canonical one. (A fourth pair, `sort` / `defaultSort`, used to be listed here;
its legacy half is retired — see the note above.)

| write this (canonical) | not this (legacy alias — still works) |
| --- | --- |
| `pagination: { pageSize, pageSizeOptions? }` | `pageSize: number` |
| `selection: { type: 'single' \| 'multiple' \| 'none' }` | `selectable: boolean \| 'single' \| 'multiple'` |
| `filter: [{ field, operator, value }, …]` (same shape as a named view's `filter`) | `defaultFilters: Record<field, value>` (equality-only) |
| `sort: SortConfig[]` (`[{ field, order }]`) | `defaultSort: { field, order }` (a single entry, not an array) |

Before objectui#5102, `pagination` / `selection` / `filter` / `sort` had **no
read point at all** in this file: an author who wrote the canonical shape
`ObjectGridSchema`'s own JSDoc recommends got a view that compiled, read
correctly, and silently did nothing. That is fixed — the legacy spellings on
the right are not going away, they are simply no longer the ones to reach for.
the right are simply no longer the ones to reach for.

**When a key is written both ways, the canonical spelling wins** — that is
`ObjectGrid`'s own existing resolution (`schema.pagination?.pageSize ||
schema.pageSize`; `if (schema.selection?.type) … else if (schema.selectable
!== undefined)`; `schemaFilter !== undefined ? … : schema.defaultFilters`;
`schemaSort ?? (schema.defaultSort ? [schema.defaultSort] : undefined)`), and
`ObjectView` defers to it by forwarding both slots rather than re-resolving
the pair itself:
!== undefined)`; `schemaFilter !== undefined ? … : schema.defaultFilters`), and `ObjectView`
defers to it by forwarding both slots rather than re-resolving the pair
itself:

```typescript
import type { ObjectViewSchema } from '@object-ui/types';
Expand All @@ -183,9 +190,9 @@ predates this change: an **active named view's own** `filter` / `sort`
`table` — `table.filter` does **not** universally win over the legacy
`table.defaultFilters`, it wins only when no active named view supplies its
own filter. In order, highest first: the active named view's `filter`/`sort`,
then `table.filter`/`table.sort`, then
`table.defaultFilters`/`table.defaultSort`. If you never write `listViews`,
that first tier never applies.
then `table.filter`/`table.sort`, then `table.defaultFilters` (filter only —
`sort` has no legacy tier since `table.defaultSort` was retired). If you never
write `listViews`, that first tier never applies.

`pagination` and `selection` have no such tier, and no effect outside the
grid: `defaultViewType: 'kanban' | 'gallery' | 'calendar' | 'timeline' |
Expand Down Expand Up @@ -223,8 +230,9 @@ types:

Both spellings resolve here: `ComponentRegistry.register` publishes
`namespace:type` and, unless the call passes `skipFallback: true`, the bare
`type` as a back-compat fallback (`packages/core/src/registry/Registry.ts:194`,
fallback at `:226`). No call in this package sets `skipFallback`. Note the
`type` as a back-compat fallback (`ComponentRegistry.register` in
`packages/core/src/registry/Registry.ts`, its `!meta?.skipFallback` branch). No
call in this package sets `skipFallback`. Note the
namespaces are not uniform — `object-view`, `view` and `view:simple` register
under `plugin-view`, the four control components under `view`.

Expand Down
Loading
Loading