Skip to content

Commit 62d31bc

Browse files
fix(studio): interface-page Design tab renders the live list, not a hint (#1695)
The page editor's Design tab showed a static 'Interface page — configured in Properties' placeholder for interface pages (config-driven, no regions to drag). Authors edit config on the right but the left didn't reflect it until switching to Preview. Now PagePreview renders the live InterfaceListPage for interface pages in BOTH design and preview modes — right-panel edits reflect live on the left, no tab switch, no placeholder. Removes the now-dead interface empty-state from PageBlockCanvas (interface pages never reach it) and flips the PagePreview/PageBlockCanvas tests to the new behavior. Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com>
1 parent 1496e6d commit 62d31bc

4 files changed

Lines changed: 19 additions & 56 deletions

File tree

packages/app-shell/src/views/metadata-admin/previews/PageBlockCanvas.test.tsx

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,13 @@ import { PageBlockCanvas } from './PageBlockCanvas';
77
afterEach(cleanup);
88

99
/**
10-
* Empty-canvas messaging (ADR-0047). An interface page (config-driven, no
11-
* regions) must NOT be invited to "Add region" — it points the author at the
12-
* Properties → Interface panel instead. A region-composed page keeps the
13-
* normal "No regions yet / Add region" empty state.
10+
* Empty-canvas messaging. Region-composed pages with no regions yet invite
11+
* the author to add one. (ADR-0047 interface pages never reach this canvas —
12+
* PagePreview renders them as a live InterfaceListPage in both modes — so
13+
* there is no interface-specific empty state here.)
1414
*/
1515
describe('PageBlockCanvas — empty state', () => {
16-
it('interface page (interfaceConfig present) shows the Properties hint, not Add region', () => {
17-
render(
18-
<PageBlockCanvas
19-
draft={{ name: 'wb', type: 'list', regions: [], interfaceConfig: { source: 'task' } }}
20-
onPatch={() => {}}
21-
/>,
22-
);
23-
expect(screen.getByText(/configured in Properties/i)).toBeInTheDocument();
24-
expect(screen.queryByText(/Add region/i)).not.toBeInTheDocument();
25-
expect(screen.queryByText(/No regions yet/i)).not.toBeInTheDocument();
26-
});
27-
28-
it('a list page without regions is treated as interface mode', () => {
29-
render(<PageBlockCanvas draft={{ name: 'wb', type: 'list', regions: [] }} onPatch={() => {}} />);
30-
expect(screen.getByText(/configured in Properties/i)).toBeInTheDocument();
31-
expect(screen.queryByText(/Add region/i)).not.toBeInTheDocument();
32-
});
33-
34-
it('a region-composed page keeps the "No regions yet / Add region" empty state', () => {
16+
it('a region-composed page shows the "No regions yet / Add region" empty state', () => {
3517
render(<PageBlockCanvas draft={{ name: 'home', type: 'home', regions: [] }} onPatch={() => {}} />);
3618
expect(screen.getByText(/No regions yet/i)).toBeInTheDocument();
3719
expect(screen.getByText(/Add region/i)).toBeInTheDocument();

packages/app-shell/src/views/metadata-admin/previews/PageBlockCanvas.tsx

Lines changed: 3 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -305,32 +305,10 @@ export function PageBlockCanvas({
305305
[onSelectionChange, selectedId],
306306
);
307307

308-
// Empty draft → empty canvas with hint.
308+
// Empty draft → empty canvas with hint. (ADR-0047 interface pages never
309+
// reach this canvas — PagePreview renders them as a live InterfaceListPage
310+
// in both design and preview modes — so no interface-specific hint here.)
309311
if (regions.length === 0) {
310-
// ADR-0047 interface pages are config-driven, not composed from regions:
311-
// the list surface is generated from `interfaceConfig` (source view +
312-
// user filters + visualizations). Inviting the author to "add a region"
313-
// here is misleading — point them at the Properties panel instead.
314-
const isInterfacePage =
315-
!!(draft as any)?.interfaceConfig ||
316-
((draft as any)?.type === 'list' && !(draft as any)?.regions?.length);
317-
if (isInterfacePage) {
318-
return (
319-
<div className="h-full overflow-auto bg-muted/20" onClick={handleBgClick}>
320-
<div className="mx-auto max-w-3xl px-6 py-8">
321-
<div className="rounded-lg border-2 border-dashed bg-background py-16 px-6 text-center space-y-3">
322-
<div className="text-sm font-medium">Interface page — configured in Properties</div>
323-
<div className="text-xs text-muted-foreground">
324-
This page renders a list from a source view; there are no regions
325-
to design. Use the <span className="font-medium">Properties</span> panel
326-
<span className="font-medium">Interface</span> section to set the data
327-
source, user filters, visualizations and toolbar actions.
328-
</div>
329-
</div>
330-
</div>
331-
</div>
332-
);
333-
}
334312
return (
335313
<div className="h-full overflow-auto bg-muted/20" onClick={handleBgClick}>
336314
<div className="mx-auto max-w-3xl px-6 py-8">

packages/app-shell/src/views/metadata-admin/previews/PagePreview.test.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ describe('PagePreview — interface-page routing (ADR-0047)', () => {
3939
expect(screen.queryByTestId('mock-schema-renderer')).not.toBeInTheDocument();
4040
});
4141

42-
it('does NOT use InterfaceListPage in design mode (keeps the canvas hint)', () => {
42+
it('also renders the live InterfaceListPage in design mode (no canvas hint)', () => {
4343
render(
4444
<PagePreview
4545
draft={interfaceDraft}
@@ -48,8 +48,10 @@ describe('PagePreview — interface-page routing (ADR-0047)', () => {
4848
onPatch={() => {}}
4949
/>,
5050
);
51-
expect(screen.queryByTestId('mock-interface-list')).not.toBeInTheDocument();
52-
expect(screen.getByTestId('mock-page-canvas')).toBeInTheDocument();
51+
// Interface pages are config-driven: the design tab shows the live list
52+
// (mirroring the runtime), not the region canvas/placeholder.
53+
expect(screen.getByTestId('mock-interface-list')).toBeInTheDocument();
54+
expect(screen.queryByTestId('mock-page-canvas')).not.toBeInTheDocument();
5355
});
5456

5557
it('renders the generic SchemaRenderer for a region-composed page (not an interface page)', () => {

packages/app-shell/src/views/metadata-admin/previews/PagePreview.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,12 @@ export function PagePreview({ draft, editing, selection, onSelectionChange, onPa
9797
onSelectionChange?.({ kind: 'block', id: `children[${next.length - 1}]`, label: newBlock.type });
9898
}, [canEdit, draft, onPatch, onSelectionChange, shape]);
9999

100-
// Interface page in preview mode → mirror the runtime (InterfaceListPage)
101-
// so the Preview tab shows the real source view + user filters + data,
102-
// live from the edited draft. Design mode falls through to the canvas,
103-
// which shows the "configured in Properties" hint instead.
104-
if (isInterfacePage && !designMode) {
100+
// Interface page → always mirror the runtime (InterfaceListPage), in BOTH
101+
// design and preview modes. These pages are config-driven, not region-
102+
// composed, so there is nothing to drag on a canvas: the author edits the
103+
// Properties panel on the right and sees the real list (source view + user
104+
// filters + data) update live on the left — no tab switch, no placeholder.
105+
if (isInterfacePage) {
105106
return (
106107
<PreviewShell hint="page · interface">
107108
<PreviewErrorBoundary fallbackHint="The interface page references a source object/view that isn't available.">

0 commit comments

Comments
 (0)