You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit b87cbca
Browse filesBrowse the repository at this point in the historyBrowse files
fix(app-shell): a Studio pillar never saves one item's document into another while the second is loading, after a package switch, or after visiting a non-editable leaf
6
+
7
+
Studio's pillars keep the open item's document in an edit buffer and save it as a draft. Three
8
+
paths saved a buffer that was not the open item's. Each was reproduced through the real pillars on a writable package:
9
+
10
+
-**While the next item was loading**, the pillar went on showing the previous item's document
11
+
under the new one and took edits on it. A save from there wrote the previous item's document into
12
+
the new item. This affected the Interfaces page settings, the Automations enable switch and the
13
+
Data pillar's add field. Until the open item's own document is in, the pillars now show it as
14
+
loading, offer no editor over the previous item's document, and send no save. The enable switch,
15
+
add field, the column reorder, the field count and the "unpublished draft" badge wait for it too.
16
+
-**After a package switch**, the Automations and Data pillars kept the previous package's flow or
17
+
object open, and an edit saved it into the new package. Both pillars now start fresh on a package
18
+
switch and open the new package's own items, as the Interfaces pillar already does. An edit made
19
+
less than 1.5 s before the switch is dropped with it.
20
+
-**Visiting an object entry in the Interfaces navigation** and then reopening the page just edited
21
+
could send an empty document as that page's draft when the page was slow to reload. Opening such
22
+
an entry now drops the page's unsent edit, and the page's reload is what comes back.
Copy file name to clipboardExpand all lines: .changeset/7322-object-kanban-component-props.md
+2Lines changed: 2 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -95,3 +95,5 @@ boards (`columns` + inline `data`, no fetch) that author no `objectName`, which
95
95
`ObjectKanbanSchema` declared required **as of 2026-09-06** — objectui#7780's subject,
96
96
and that card made it a record-source presence rule on 2026-09-07; their casts stay,
97
97
now carrying the reason and the card number.
98
+
99
+
⏱ **Dated note, 2026-09-30 (objectui#8652).** 「`navigation` is declared on neither face」 in 「Casts this removes」 above was true when written and is falsified by objectui#8652, which declares `navigation` on `ObjectKanbanSchema` on both faces of `@object-ui/types`, by reference to the spec's `NavigationConfig`. The paragraph is kept as written, and is not restated in the present tense.
`navigation` is declared on the `object-kanban` and `object-calendar` blocks, by reference to `@objectstack/spec` (objectui#8652). This is the objectui half of the maintainer ruling on that card: the spec declares the key on both element entries from 17.5.0, and objectui now mirrors it.
8
+
9
+
-`@object-ui/types`: `ObjectKanbanSchema.navigation` and `ObjectCalendarSchema.navigation` are declared on both published faces with the spec's `NavigationConfig` type — the same type the grid, view, gantt and map schemas use. Until now the key was admitted and never examined: `navigation: { mode: 'not-a-mode' }` passed both faces while the spec refused it. It is now refused at `navigation.mode`, and a member the spec does not declare inside the block is refused too. A parse adds no default to the block, so a board or calendar without the key still opens a record in a drawer.
10
+
-`@object-ui/plugin-kanban`: the `object-kanban` registration publishes `navigation` in its `inputs`, so the designer offers it.
11
+
-`@object-ui/plugin-calendar`: the `object-calendar` and `calendar` registrations publish `navigation` in their `inputs`, and `ObjectCalendar` reads the key without a cast.
12
+
13
+
What a click does with each member is unchanged. One value has no effect on its own: `page` opens nothing on a board or calendar that no parent view navigates for, and a block written without `mode` resolves to `page`. The published input descriptions say so.
// `visible` stood here until objectui#11168 slice 1 measured each against its
1412
1412
// renderer through the real `SchemaRenderer` and DECLARED all four — the
1413
1413
// documented exit: a declaration retires its own cover.
1414
-
...owedEntries(
1415
-
'object-kanban',
1416
-
['navigation'],
1417
-
'objectui#8652',
1418
-
'A SPEC KEY NOT PUBLISHED: 17.5.0 declares `navigation` on this block; objectui#8652 met its unlock with that release and mirrors it as its ruled second step.',
1419
-
),
1420
-
...owedEntries(
1421
-
'object-calendar',
1422
-
['navigation'],
1423
-
'objectui#8652',
1424
-
'A SPEC KEY NOT PUBLISHED: 17.5.0 declares `navigation` on this block; objectui#8652 met its unlock with that release and mirrors it as its ruled second step.',
1425
-
),
1414
+
// objectui#8652's two entries — `navigation` on `object-kanban` and
1415
+
// `object-calendar` — are STRUCK: that card declared the key on both blocks'
1416
+
// `inputs` (and on both `@object-ui/types` faces), with a member pin apiece
1417
+
// in `MEMBER_PINS`, so each entry went stale and `carries no stale
1418
+
// unpublished-key exemption` would refuse it.
1426
1419
// objectui#8649's nine entries — the field-security triple on `record:details`,
1427
1420
// `record:highlights` and `record:related_list` — are STRUCK: that card
1428
1421
// declared all three keys on the three blocks' `inputs`, with the contract's
pins: 'The members are ObjectQL `$filter` elements, and this renderer adds nothing to that contract and subtracts nothing from it: the authored value reaches `dataSource.find` as `$filter` BY IDENTITY (`toBe`, so a normalising rewrite cannot pass), and the retired `filter.calendar` member spelling yields NO configuration — one authored key read twice with two incompatible meanings is the member-level defect objectui#7711 closed here and objectui#4034 closed on the map. Both member forms are covered (the object form and the array-of-arrays form). The spec side cannot supply this: the `object-calendar` `filter` row is `z.unknown()`, so the wire is the only member contract there is (objectui#7711, registered as a pin by objectui#8176 once objectui#8186 declared the key).',
pins: 'The members of the spec\'s `NavigationConfigSchema` block the calendar reads, each driven through a real event click on the real calendar and asserted on what the click DID: an ABSENT key opens a drawer (the renderer\'s own default, the lit control), `none` and `preventNavigation` open nothing with the flag outranking an overlay mode, `new_window` and `openNewTab` open the record page in a new tab with `openNewTab` outranking `page`, and `size` / `width` are one width decision (`width` wins, a bucket resolves through the size table, `auto` lands on the default). The precedence against a parent view is pinned both ways: an OVERLAY mode keeps the click from `onRowClick` / `onEventClick`, any other mode hands it to them. Two further rows pin the description\'s `page` warning — `page`, and a block without `mode`, open nothing on a calendar no parent view navigates for (objectui#8652).',
pins: 'The members are `{ field, order }` and those two keys are the whole of what this renderer reads inside one: `{ field, order }` lowers to the `field -> direction` map on `$orderby`, an omitted `order` is ascending rather than a dropped member (the objectui#4022 regression), every member arrives in authored order, and a member with no usable `field` is dropped rather than given an invented one — with an unauthored `sort` reaching the wire as `undefined` as the control. Also carries objectui#7711\'s case transposed: a sort on a field named `calendar` stays a sort and the config still comes from the declared `calendar` container. ⛔ NOT an identity pin, unlike the two `filter` entries: `ObjectCalendar.tsx` writes `$orderby: convertSortToQueryParams(schema.sort)`, which builds a new map, so `toBe` is false about this key — the pin asserts what is read inside the member instead, which is the sharper claim. The spec side cannot supply any of it: the `sort` row is unconstrained (an array, a bare string and a bare number all parse), so the wire is the whole member contract (objectui#8171).',
pins: 'ONE nested position and no more: `schema.grouping?.fields?.[0]?.field` is the FALLBACK source of `swimlaneField`, and that is the entire member contract this board carries for the key. Three rows make it a reading rather than a claim — the swimlane layout appears keyed by `fields[0].field` where without the key there is none; an explicit `swimlaneField` WINS over it; and a second `fields` entry changes nothing, which is what pins the read at `[0]` rather than at "the fields list". The declared description says the rest is inert precisely so the declaration does not recommend a write the renderer cannot honour — this file is what keeps that sentence true. The spec row is `z.unknown()`, so the read site is the whole member contract (objectui#8313).',
pins: 'The members of the spec\'s `NavigationConfigSchema` block the board reads, each driven through a real card click on the real board and asserted on what the click DID: an ABSENT key opens a drawer (the renderer\'s own default, the lit control), `none` and `preventNavigation` open nothing with the flag outranking an overlay mode, `new_window` and `openNewTab` open the record page in a new tab with `openNewTab` outranking `page`, and `size` / `width` are one width decision (`width` wins, a bucket resolves through the size table, `auto` lands on the default). A parent view\'s click handler outranks the whole key, overlay mode included. Two further rows pin the description\'s `page` warning — `page`, and a block without `mode`, open nothing on a board no parent view navigates for (objectui#8652).',
pins: 'The I18nLabel object arm, resolved at the read site: through the real `SchemaRenderer`, a `properties.cancelText` map under a `zh` UI renders the map\'s `zh-CN` entry on the Cancel button, with `en` listed FIRST so an `en` or first-entry fallback fails the row. Before objectui#10935 the map was a raw Button child and threw "Objects are not valid as a React child". The button renders only when the host supplies `onCancel`, so the row adds one beside the document and asserts the host handler still runs. A plain-string control and the nothing-authored control (`Cancel`) stay green, and the first row asserts the mounted documents pass `safeValidateSchema`. The manifest half (a map is not a `type-mismatch`) is `masterDetailFormI18nLabelManifest.test.ts` in this directory (objectui#10935).',
@@ -4638,9 +4639,8 @@ describe('registry `inputs` vs `@objectstack/spec` ComponentPropsMap (repo-wide)
4638
4639
// direction — greening a fresh divergence on these two blocks by writing a
4639
4640
// nineteenth entry instead of declaring the input.
4640
4641
//
4641
-
// TWO is the BOOKED backlog today — objectui#8652's `navigation` pair,
4642
-
// ruled at the 17.5.0 bump (the last row of the table below) — and ZERO
4643
-
// is what it measured before that bump, not a budget. `object-calendar`
4642
+
// ZERO again since objectui#8652 declared the `navigation` pair the 17.5.0
4643
+
// bump had BOOKED to it (the last two rows of the table below). `object-calendar`
4644
4644
// publishes all nine keys its spec row declares; `object-kanban` publishes
4645
4645
// thirteen of fourteen, and the fourteenth is RULED rather than owed —
4646
4646
// objectui#8313 emptied the board, objectui#8314 emptied the calendar, and
@@ -4676,6 +4676,9 @@ describe('registry `inputs` vs `@objectstack/spec` ComponentPropsMap (repo-wide)
4676
4676
// The only step UP this ceiling has taken, and it is a ruling, not
4677
4677
// headroom: the cap is exactly the two booked entries, and
4678
4678
// objectui#8652's landing takes it back to 0.
4679
+
// 0 objectui#8652 lands: `navigation` is declared on both blocks'
4680
+
// `inputs`, each with the `MEMBER_PINS` entry objectui#8212 made
4681
+
// part of the same obligation — the booked pair's paired exit.
4679
4682
//
4680
4683
// ⚠️ THE LAST STEP IS THE ONLY ONE THAT IS NOT A DECLARATION, and reading
4681
4684
// it as one would be the wrong lesson. Every step above was this ceiling's
@@ -4729,20 +4732,17 @@ describe('registry `inputs` vs `@objectstack/spec` ComponentPropsMap (repo-wide)
4729
4732
'a new unpublished-key exemption was added on a block objectui#8176 newly '+
4730
4733
'judged — declare the input at its registration site instead; the '+
4731
4734
'backlog list is shrink-only',
4732
-
).toBeLessThanOrEqual(2);
4735
+
).toBeLessThanOrEqual(0);
4733
4736
// Lower it here when the owning cards land, so the ceiling keeps ratcheting
4734
4737
// rather than banking the headroom their fixes free up.
4735
4738
expect(
4736
4739
backlog.length,
4737
4740
'the objectui#8176 backlog shrank — lower the ceiling above to match, in '+
4738
4741
'the same change that declared the input',
4739
-
).toBe(2);
4740
-
// …and the two it holds are exactly objectui#8652's booked `navigation`
4741
-
// pair, so the ruling's room cannot be spent on a different key.
4742
-
expect(backlog.sort(),'the backlog is not the two booked navigation keys').toEqual([
4743
-
'object-calendar.navigation',
4744
-
'object-kanban.navigation',
4745
-
]);
4742
+
).toBe(0);
4743
+
// …and it is empty BY NAME, so the room objectui#8652's landing freed
4744
+
// cannot be spent on a different key.
4745
+
expect(backlog.sort(),'the backlog is not empty').toEqual([]);
4746
4746
});
4747
4747
4748
4748
it('the one ruled carve-out on a newly judged block is real, and names its exit',()=>{
@@ -5598,6 +5598,6 @@ describe('registry `inputs` vs `@objectstack/spec` ComponentPropsMap (repo-wide)
0 commit comments