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
14 changes: 14 additions & 0 deletions .changeset/23610-record-page-seed-default.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
'@object-ui/plugin-detail': patch
'@object-ui/types': patch
---

A new record page seeded in Studio is saved with the `default` layout template instead of `full-width`, and the page node's `template` member is typed by reference to the spec's (part of objectstack#23610).

`@objectstack/spec` closes `PageSchema.template` to four layout names — `default`, `header-sidebar-main`, `three-column`, `dashboard` — and retires `full-width` by name, naming `default` as its replacement; the save path refuses a page that still carries it.

- **`buildDefaultPageSchema` seeds `template: 'default'`.** Studio's "new record page" seed takes its template from this synthesis, so with the old value the first save of a new record page was refused. The renderer maps `default` and `full-width` to the same `FullWidthTemplate`, so a seeded page lays out exactly as before.
- **`PageNodeSchema.template` is `SpecPage['template']`.** The member was re-declared as `string` over the spec's inherited one; once the spec closes the set, that widening is a TypeScript error (TS2430) on the interface. Against today's spec, whose member is still `string`, the type is unchanged.
- **The zod mirror's `template` is the spec's member too.** `PageNodeSchema` in the zod face declared `template: z.string()`, which accepts every name; once the spec closes the set it measures wider than the TS face, so `full-width` would still pass `objectui validate`. It now takes the spec's own member (defaults stripped, as for every imported member), and against today's spec it accepts exactly what it accepted before.

The renderer still recognises `full-width`, so pages already stored with it keep rendering.
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ describe('stripDiscussionNodes', () => {
type: 'record',
pageType: 'record',
object: 'account',
template: 'full-width',
template: 'default',
regions: [
{
name: 'main',
Expand All @@ -99,7 +99,7 @@ describe('stripDiscussionNodes', () => {
expect(types(stripped)).toEqual(['record', 'page:header', 'page:tabs', 'page:tab']);
// Everything else about the page survives, including the scalars that are
// not nodes at all.
expect(stripped.template).toBe('full-width');
expect(stripped.template).toBe('default');
expect(stripped.regions[0].name).toBe('main');
expect(stripped.regions[0].width).toBe('full');
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,12 +201,13 @@ describe('deriveHighlightFields', () => {
});

describe('buildDefaultPageSchema', () => {
it('emits a record Page with full-width template + main region', () => {
it('emits a record Page with the default template + main region', () => {
const page = buildDefaultPageSchema(leadDef);
expect(page.type).toBe('record');
expect(page.pageType).toBe('record');
expect(page.object).toBe('lead');
expect(page.template).toBe('full-width');
// The spec retires `full-width` by name; a seed carrying it is refused at save.
expect(page.template).toBe('default');
expect(page.regions).toHaveLength(1);
expect(page.regions[0].name).toBe('main');
});
Expand Down
8 changes: 6 additions & 2 deletions packages/plugin-detail/src/synth/buildDefaultPageSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -989,7 +989,7 @@ export function buildDefaultAttachments(): any {
* page.
*
* Shape:
* { type:'record', template:'full-width', regions:[ { name:'main',
* { type:'record', template:'default', regions:[ { name:'main',
* components: [page:header, record:highlights?, record:path?,
* page:tabs, record:discussion?] } ] }
* For `enable.files: true` objects the tabs node carries a peer
Expand Down Expand Up @@ -1172,7 +1172,11 @@ export function buildDefaultPageSchema(
type: 'record',
pageType: 'record',
object: def?.name,
template: 'full-width',
// `default`, not `full-width`: the spec's `PageSchema.template` names four
// layouts and retires `full-width` by name (its replacement is `default`),
// so a Studio seed carrying it is refused at save. The renderer maps both
// names to the same `FullWidthTemplate`, so the seed lays out unchanged.
template: 'default',
regions,
};
}
7 changes: 6 additions & 1 deletion packages/types/src/layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1643,8 +1643,13 @@ export interface PageNodeSchema extends BaseSchema, Omit<SpecPage, (typeof PAGE_
* `FullWidthTemplate`, the zod mirror's `.default('default')` made a PARSED
* page take the template branch and skip the `pageType` dispatch entirely —
* one authored page, two layouts. objectui#7735 removed the substitution.
*
* Typed by reference to the spec's member, so the closed set of layout
* names the spec declares is the set this node accepts — a re-declared
* `string` widens the inherited member, which TypeScript refuses once the
* spec closes it.
*/
template?: string;
template?: SpecPage['template'];
/**
* Local page state variables
* Initialized on mount and available to all components via context
Expand Down
7 changes: 6 additions & 1 deletion packages/types/src/zod/layout.zod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1305,7 +1305,12 @@ export const PageNodeSchema = BaseSchema.extend(SpecPageFields.shape).extend({
.optional()
.describe('Page override mode (full | slotted | html | react)'),
object: z.string().optional().describe('Bound object name (for record pages)'),
template: z.string().optional().describe('Layout template name'),
// objectstack#23610: the spec's own member, by reference, mirroring the TS
// face's `PageNodeSchema.template` (`SpecPage['template']`). A `z.string()`
// here accepts every name, so once the spec closes the set of layout names it
// measures WIDER than the TS face and accepts the `full-width` the spec
// retires. Against a spec whose member is still a string, it is unchanged.
template: stripImportedDefaults(SpecPageSchema).shape.template.describe('Layout template name'),
variables: z.array(PageVariableSchema).optional().describe('Local page state variables'),
regions: z.array(PageNodeRegionSchema).optional().describe('Page layout regions'),
body: aliasKeyRefusal(
Expand Down
Loading