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
90 changes: 90 additions & 0 deletions .changeset/7664-kanban-arm-plugin-dialect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
---
'@object-ui/types': minor
---

**BREAKING** — the `'kanban'` validator arm now accepts the shape the registered
renderer reads, and the six `DeclarativeKanban*` exports retire (objectui#7664,
maintainer ruling (a), 2026-09-05).

For an authored `type: 'kanban'` document two different types were
authoritative depending on who asked. `safeValidateSchema` — what the CLI's
`validate` / `check` commands apply — honoured `DeclarativeKanbanSchema`
(`columns` with `color`, `draggable`, cards with `labels` / `assignees` /
`priority`), while the renderer registered for the key, `ObjectKanbanRenderer`
in `@object-ui/plugin-kanban`, consumed that package's own `KanbanSchema`
(`objectName` / `groupBy` / `cardTitle` / `cardFields`, cards with `badges`).
The two were unrelated dialects, so a board could pass `objectui validate` and
render **empty**. The ruling: the plugin dialect is authoritative.

**What changes on this package's published surface:**

- **The `'kanban'` arm's accept set is replaced.** `ComplexSchema` →
`AnyComponentSchema` → `safeValidateSchema` now validate the plugin dialect,
declared here as `KanbanSchema` / `KanbanColumn` / `KanbanCard` /
`CardTemplate` / `ColumnWidthConfig` (TypeScript) and `KanbanSchema` /
`KanbanColumnSchema` / `KanbanCardSchema` / `CardTemplateSchema` /
`ColumnWidthConfigSchema` (`@object-ui/types/zod`). An `objectName` /
`groupBy` board passes. A static `columns[].cards[]` board passes — that
spelling is the same document in both dialects and always rendered. A board
in the retired dialect is **refused by name** at the keys that betray it: a
board-level `draggable` and a column `color` are `?: never` tombstones on the
TypeScript face and named refusal arms on the mirror, each message naming the
retired `DeclarativeKanbanSchema` shape and the spelling to write instead.
Both were measured inert (zero read sites in the plugin). The retired card
keys are deliberately *not* refused: a card is an open record
(`[key: string]: any`), and `priority` or `dueDate` are legitimate record
fields.
- **Every handler key the retired arm refused is still refused, and one more
joins them.** The successor arm carries all five `#6124` refusal arms under
the same `'kanban'` key — `onCardMove`, `onCardClick` and `onQuickAdd` as
RUNTIME SLOTS (callable on the TypeScript face, refused by name on the
mirror: `KanbanRenderer` forwards all three off `schema.*` in one block),
`onColumnAdd` and `onCardAdd` as `?: never` tombstones. `onQuickAdd` is the
one that is newly refused — the plugin dialect declared it, the retired
declarative face did not. ⚠️ `onCardClick` is the key this arm must never
drop rather than refuse: the plugin dialect it is modelled on never declared
the member (the renderer read it undeclared), and because `BaseSchema` is
`.passthrough()`, leaving it out does not refuse it — it stops being judged
and the value is kept. Measured on the built dist, `{ type: 'kanban',
columns: [], onCardClick: { action: 'toast' } }` is REFUSED, beside the same
document at `onCardMove` / `onQuickAdd` / `onColumnAdd` / `draggable`.
- **Six exports retire — the second step of the objectui#6172 rename.**
objectui#6172 (PR #7643, same release line) renamed this package's trio from
the bare names to `DeclarativeKanbanSchema` / `DeclarativeKanbanColumn` /
`DeclarativeKanbanCard` and the three Zod mirrors to `DeclarativeKanban*Schema`
so the bare names could belong to the renderer's dialect. objectui#6172's own
stop condition was "if the renamed copy has no retained value, escalate", and
the retained value it cited was precisely the validator arm. This ruling moves
that arm to the plugin dialect, so the renamed copies have no consumer left
and retire under ADR-0049 (enforce-or-remove): `DeclarativeKanbanSchema`,
`DeclarativeKanbanColumn`, `DeclarativeKanbanCard` from `@object-ui/types` and
`DeclarativeKanbanSchema`, `DeclarativeKanbanColumnSchema`,
`DeclarativeKanbanCardSchema` from `@object-ui/types/zod` are gone.
Importing any of them is a compile error (TS2305).
- **`SchemaRegistry['kanban']` is `KanbanSchema`.** objectui#7645 (PR #7662)
weakened the entry to `BaseSchema & { type: 'kanban' }` because this layer
could not name the plugin's type; it now names the declaration the plugin
itself imports. `keyof SchemaRegistry` — the published `ComponentType` union —
is unchanged.
- **The bare names return to this package with a different shape than they had
before objectui#6172.** `KanbanSchema` here is now the plugin dialect, not the
declarative one the pre-rename `KanbanSchema` was. A consumer that never
migrated off the old bare name and expected `columns` to be required, or
`draggable` to exist, gets a type error rather than a silent change.
- `KanbanConditionalFormattingRuleSchema` is newly exported from
`@object-ui/types/zod`: the rule union the `'object-kanban'` arm already
applied, now shared with the `'kanban'` arm.

**Migration.** Author boards in the plugin dialect — `objectName` + `groupBy`
for an object-bound board, or `columns[].cards[]` with `badges` for a static
one. Replace `DeclarativeKanbanSchema` imports with `KanbanSchema` (from
`@object-ui/types`, or the Zod `KanbanSchema` from `@object-ui/types/zod`;
`@object-ui/plugin-kanban` re-exports the same `KanbanSchema` type). Delete
`draggable` (drag-and-drop is always on) and column `color` (style a lane
through `className`). `content/docs/api/schema-reference.md`'s kanban section
now documents this dialect.

This is a breaking change shipped as `minor`: this repository's
version-alignment rule keeps objectui's major pinned to `@objectstack`'s and
ships objectui's own breaking changes as `minor` with the break spelled out in
the changeset body, which is what the bullets above are.
30 changes: 30 additions & 0 deletions .changeset/7664-plugin-kanban-declared-schema.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
'@object-ui/plugin-kanban': patch
---

`KanbanSchema` / `KanbanColumn` / `KanbanCard` / `CardTemplate` /
`ColumnWidthConfig` are now the `@object-ui/types` declarations, re-exported
from this package rather than declared in it (objectui#7664, maintainer ruling
(a)). Nothing this package renders changed and every existing import keeps
resolving; what changed is that `safeValidateSchema` in `@object-ui/types` now
validates an authored `type: 'kanban'` document against this very shape, so a
board that validates is a board these renderers draw.

**The shape is not member-for-member what this package declared — it is that
shape plus four members**, counted off `origin/main`'s
`plugin-kanban/src/types.ts` (19 members on `KanbanSchema`, 6 on
`KanbanColumn`, 7 on `KanbanCard`) against the `@object-ui/types`
declarations:

- **`onCardClick` is DECLARED for the first time.** This package's dialect
never had the member, while `KanbanRenderer` has always forwarded
`onCardClick={schema.onCardClick}` — an undeclared read (objectui#7742). It
is declared here as a `#6124` RUNTIME SLOT: callable on the TypeScript face,
refused by name on the mirror, like the `onCardMove` and `onQuickAdd` beside
it in the same forward block.
- **Four `?: never` tombstones** carry the retired declarative face's keys
under the same `'kanban'` key so those spellings keep being refused by name:
`draggable`, `onColumnAdd` and `onCardAdd` on `KanbanSchema`, and `color` on
`KanbanColumn`. None of the four was ever a member of this package's dialect;
each is refused, not silently accepted, because the retired face taught it.
The full accept-set statement is on the sibling `@object-ui/types` entry.
2 changes: 1 addition & 1 deletion content/docs/api/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@ Complete reference for every ObjectUI schema type with annotated JSON examples c
- **Data Display** — `TableSchema`, `ChartSchema`, `TreeViewSchema`
- **CRUD** — `ActionSchema`, `DetailSchema`, `CRUDDialogSchema`
- **ObjectQL** — `ObjectGridSchema`, `ObjectFormSchema`, `ObjectViewSchema`
- **Complex** — `DeclarativeKanbanSchema`, `DashboardSchema`, `CalendarViewSchema`
- **Complex** — `KanbanSchema`, `DashboardSchema`, `CalendarViewSchema`
- **Views** — `DetailViewSchema`, `ViewSwitcherSchema`
57 changes: 44 additions & 13 deletions content/docs/api/schema-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -896,36 +896,51 @@ A complete object management interface combining grid, form, search, filters, an

## Complex Schemas

### DeclarativeKanbanSchema
### KanbanSchema

A drag-and-drop Kanban board with columns and cards.
A drag-and-drop Kanban board. The `kanban` type key validates the shape the registered renderer (`@object-ui/plugin-kanban`) reads: bind the board to an object with `objectName` + `groupBy` (the lanes come from the group field's options), or author it statically with `columns`, each carrying its `cards`.

```json
{
"type": "kanban",
"objectName": "tasks",
"groupBy": "status",
"cardTitle": "title",
"cardFields": ["assignee", "due_date"],
"quickAdd": true
}
```

A static board carries its cards inline:

```json
{
"type": "kanban",
"draggable": true,
"columns": [
{
"id": "todo",
"title": "To Do",
"color": "#6366f1",
"cards": [
{ "id": "task-1", "title": "Design mockups", "description": "Create wireframes for new feature" },
{
"id": "task-1",
"title": "Design mockups",
"description": "Create wireframes for new feature",
"badges": [{ "label": "High", "variant": "destructive" }]
},
{ "id": "task-2", "title": "Write tests", "description": "Unit tests for auth module" }
]
},
{
"id": "in-progress",
"title": "In Progress",
"color": "#f59e0b",
"limit": 3,
"cards": [
{ "id": "task-3", "title": "API integration", "description": "Connect to payment gateway" }
]
},
{
"id": "done",
"title": "Done",
"color": "#22c55e",
"cards": []
}
]
Expand All @@ -934,10 +949,26 @@ A drag-and-drop Kanban board with columns and cards.

| Property | Type | Description |
|----------|------|-------------|
| `columns` | `DeclarativeKanbanColumn[]` | **Required.** Board columns, each with `id`, `title`, `color`, and `cards`. |
| `draggable` | `boolean` | Enable drag-and-drop between columns. |
| `onCardMove` | `function` | Callback when a card is moved: `(cardId, fromColumn, toColumn, position)`. |
| `onCardClick` | `function` | Callback when a card is clicked. |
| `objectName` | `string` | Object to fetch records from. |
| `groupBy` | `string` | Field whose values become the lanes (maps to column ids). |
| `swimlaneField` | `string` | Field for swimlane rows (2D grouping). |
| `cardTitle` | `string` | Field used as the card title. |
| `cardFields` | `string[]` | Fields rendered on each card. |
| `data` | `any[]` | Inline records, bucketed into lanes by `groupBy`. |
| `limit` | `number` | Fetch window for the board (default 100). |
| `columns` | `KanbanColumn[]` | Lanes, each with `id`, `title`, `cards`, and optional `limit` / `className` / `collapsed`. A card has `id`, `title`, optional `description` and `badges`. |
| `quickAdd` | `boolean` | Show a Quick Add button at the bottom of each column. |
| `coverImageField` | `string` | Field whose URL renders as the card cover image. |
| `allowCollapse` | `boolean` | Allow columns to be collapsed. |
| `conditionalFormatting` | `KanbanConditionalFormattingRule[]` | Card colouring rules — native `{ field, operator, value }` or spec `{ condition, style }`. |
| `cardTemplates` | `CardTemplate[]` | Predefined quick-add templates. |
| `columnWidths` | `ColumnWidthConfig` | Column width configuration. |
| `grouping` | `GroupingConfig` | ListView grouping config; its first field is the swimlane fallback. |
| `onCardMove` | `function` | Runtime slot supplied by a React host, `(cardId, fromColumnId, toColumnId, newIndex)`; not authorable in JSON. |
| `onCardClick` | `function` | Runtime slot supplied by a React host, `(card, event?)`; not authorable in JSON. On the object-bound board the host's handler runs alongside the record-detail overlay. |
| `onQuickAdd` | `function` | Runtime slot supplied by a React host, `(columnId, title)`; not authorable in JSON. |

> The former `@object-ui/types` kanban dialect — `DeclarativeKanbanSchema`, with a board-level `draggable`, a column `color` and card `labels` / `priority` — was retired in objectui#7664: no registered renderer read it, so a board written that way validated and rendered empty. `draggable` and a column `color` are now refused by name; a static board written with `columns[].cards[]` as above is the same document in both dialects and renders every card.

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

Expand Down Expand Up @@ -1211,7 +1242,7 @@ A toggle control that switches between different view types (list, grid, kanban,
| `storageKey` | `string` | Storage key for persisting the preference. |
| `onViewChange` | `string` | Expression or callback invoked on view change. |

**Related:** [ObjectViewSchema](#objectviewschema), [DeclarativeKanbanSchema](#declarativekanbanschema), [CalendarViewSchema](#calendarviewschema)
**Related:** [ObjectViewSchema](#objectviewschema), [KanbanSchema](#kanbanschema), [CalendarViewSchema](#calendarviewschema)

---

Expand Down Expand Up @@ -1286,7 +1317,7 @@ import type { ActionSchema, DetailSchema } from '@object-ui/types';
import type { ObjectGridSchema, ObjectFormSchema, ObjectViewSchema } from '@object-ui/types';

// Complex
import type { DeclarativeKanbanSchema, DashboardComponentSchema, CalendarViewSchema } from '@object-ui/types';
import type { KanbanSchema, DashboardComponentSchema, CalendarViewSchema } from '@object-ui/types';

// Views
import type { DetailViewSchema, ViewSwitcherSchema } from '@object-ui/types';
Expand Down
2 changes: 1 addition & 1 deletion packages/plugin-kanban/src/ObjectKanban.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import {
} from '@object-ui/core';
import { getBadgeColorClasses, getBadgeHexAppearance, getCellRenderer, resolveCellRendererType } from '@object-ui/fields';
import { KanbanRenderer, KANBAN_UNCOLUMNED_ID } from './index';
import { KanbanSchema } from './types';
import type { KanbanSchema } from './types';
import {
collectRequiredWhenPromptFields,
type RequiredWhenPromptField,
Expand Down
Loading
Loading