Skip to content

Migrate the remaining ListView legacy vocabulary to spec-canonical keys, and audit ObjectView/DetailView (#2231 phases 4–5) #2890

Description

@os-zhuang

Context

#2231 has been closing the @object-ui/types@objectstack/spec/ui fork in phases:

phase PR what it closed
1 #2622, #2636 ListViewSchema derived from the spec; the spec-bridge's third hand-written copy retired
2 #2882 every remaining "Mirrors @objectstack/spec/ui X" sub-schema (ViewDataSchema, ListColumnSchema, SelectionConfigSchema, PaginationConfigSchema, HttpRequestSchema, the whole theme family) reduced to spec re-exports, guarded by a reference-identity test
3 (in flight) the five per-view-type configs (kanban/calendar/gantt/gallery/timeline) derived from the spec, plus the two rendering bugs the fork had been hiding

What remains is the part that was never a reference swap: objectui still uses a different vocabulary from the protocol for the same concepts at the ListView top level, and two view schemas have never been audited at all.


Scope A — the remaining legacy ListView vocabulary

ListViewSchema's drift guard (packages/types/src/__tests__/list-view-spec-parity.test.ts) sanctions ~50 local keys. Most are renames of a spec key, not genuine objectui concerns:

objectui (legacy) spec (canonical)
viewType type (the view kind; objectui's type is the component discriminator)
fields columns
filters (tuple/CEL array) filter (ViewFilterRuleSchema[])
showSearch / showSort / showFilters / showGroup / showHideFields / showColor / showDensity userActions
densityMode rowHeight
objectName data: { provider: 'object', object }
aria: { label, describedBy, live } AriaPropsSchema (ariaLabel, ariaDescribedBy, role)
sharing: { visibility, enabled, … } ViewSharingSchema ({ type, lockedBy })
conditionalFormatting, exportOptions the spec shapes are narrower

Why this needs its own project. Unlike phases 1–3 this is not type-level. Read-sites consume the legacy names (packages/plugin-list/src/ListView.tsx, components/ViewSettingsPopover.tsx, packages/plugin-view/src/ObjectView.tsx, packages/app-shell/src/views/{ObjectDataPage,ObjectView,InterfaceListPage}.tsx, packages/react/src/hooks/useDensityMode.ts), and stored view metadata in user databases carries the legacy keys. A rename with no migration path silently invalidates or strips existing views.

Per AGENTS.md Commandment #0.1 the answer is not a renderer-side ?? dual-read. The sanctioned precedent is a normalizer at the contract, the way @objectstack/spec folds legacy filter-operator spellings on parse (VIEW_FILTER_OPERATOR_ALIASES + normalizeFilterOperator, documented there as "a migration bridge … may be dropped in a future major"). So each rename wants: spec-side preprocess → objectui read-sites moved to the canonical key → legacy key dropped from the sanctioned set.

Timing matters. @objectstack/spec is at 16.x RC. Vocabulary alignment is cheap inside the RC window and a breaking major afterwards.

Suggested order — one PR each, green build + consumer regression per step

  1. fieldscolumns (pure alias, smallest read-site surface)
  2. densityModerowHeight
  3. the show* family → userActions
  4. filtersfilter
  5. aria / sharing / conditionalFormatting / exportOptions → the spec sub-shapes
  6. objectNamedata.provider: 'object' (largest — touches every ObjectQL component)

Carried over from phase 3

  • Drop the four deprecated aliases phase 3 kept for back-compat once stored metadata has been migrated: kanban.groupField, kanban.cardFields, gallery.imageField, timeline.dateField.
  • Promote calendar.defaultView into @objectstack/spec — it is the one per-view-type field with no spec counterpart, so it is still a local extension.

Scope B — audit ObjectViewSchema / DetailViewSchema

Never touched by #2231. Both are still hand-written BaseSchema.extend({ … }):

  • ObjectViewSchema (packages/types/src/zod/objectql.zod.ts) — a composite (grid + form + toolbar flags). The nearest spec counterpart, ObjectListViewSchema (packages/spec/src/ui/view.zod.ts), is not 1:1.
  • DetailViewSchema (packages/types/src/zod/views.zod.ts) — nearest counterpart is spec FormViewSchema, also not 1:1.

The deliverable here is an audit, not a swap: field by field, decide promote-to-spec / keep-local / drop — the same triage that made the ListView derivation safe. Only then decide whether a derivation is even the right shape, or whether the spec should grow a genuine composite-view schema.


Also surfaced (small, independent)

useColumnSummary's computeAggregation (packages/plugin-grid/src/useColumnSummary.ts) implements only count / sum / avg / min / max. The spec's ColumnSummarySchema declares eleven functions, so count_empty, count_filled, count_unique, percent_empty, percent_filled and none currently compute null and render an empty footer — a declared ≠ enforced gap. Either implement the six or trim the spec enum.


Definition of done

  • The sanctioned-local set in list-view-spec-parity.test.ts contains only fields that are genuinely objectui renderer concerns (target: near-empty).
  • No renderer reads a legacy key; whatever legacy acceptance remains lives in one documented normalizer, not scattered ?? fallbacks.
  • ObjectViewSchema / DetailViewSchema have a written field-level verdict.

Related

  • objectstack#3761 promotes ListColumn.prefix and the { type, field } summary form into the spec. When it releases, @object-ui/types drops its last ListColumnSchema.extend() and that schema becomes a plain re-export.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions