Skip to content

Commit 43e6d25

Browse files
xuyushun441-sysos-zhuangclaude
authored
fix(ui): tidy interface-page studio affordance (#1685)
Two UX papercuts on ADR-0047 interface pages, reported from the console: 1. The 'Edit in studio' page button was a long text+icon pill (and the label fell back to English in localized UIs). Now an icon-only pencil button (28x28) with the label kept as tooltip/aria-label. 2. Clicking it dropped the author on the page designer's empty canvas ('No regions yet / Add region') — misleading, since interface pages are config-driven and have no regions to compose. The canvas now detects interface pages (interfaceConfig, or a list page with no regions) and explains they're configured in the Properties → Interface panel, with no Add-region call to action. Locked by PageBlockCanvas.test.tsx (3 cases: interfaceConfig, bare list, and a region-composed page keeping the original empty state). Browser-verified on the showcase Task Workbench: icon button + the new canvas hint both render. Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent 6164366 commit 43e6d25

3 files changed

Lines changed: 66 additions & 2 deletions

File tree

packages/app-shell/src/views/PageView.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,12 @@ export function PageView() {
8484
<button
8585
type="button"
8686
onClick={openInStudio}
87-
className="absolute right-3 top-3 z-30 inline-flex items-center gap-1.5 rounded-md border border-input bg-background/90 px-2.5 py-1.5 text-xs font-medium text-muted-foreground shadow-sm backdrop-blur hover:bg-accent hover:text-accent-foreground"
87+
className="absolute right-3 top-3 z-30 inline-flex h-7 w-7 items-center justify-center rounded-md border border-input bg-background/90 text-muted-foreground shadow-sm backdrop-blur hover:bg-accent hover:text-accent-foreground"
8888
data-testid="page-edit-in-studio-button"
8989
title={t('common.editInStudio', { defaultValue: 'Edit in studio' })}
90+
aria-label={t('common.editInStudio', { defaultValue: 'Edit in studio' })}
9091
>
9192
<Pencil className="h-3.5 w-3.5" />
92-
{t('common.editInStudio', { defaultValue: 'Edit in studio' })}
9393
</button>
9494
)}
9595
{(page as any).interfaceConfig?.source ? (
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2+
3+
import { describe, it, expect, afterEach } from 'vitest';
4+
import { render, screen, cleanup } from '@testing-library/react';
5+
import { PageBlockCanvas } from './PageBlockCanvas';
6+
7+
afterEach(cleanup);
8+
9+
/**
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.
14+
*/
15+
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', () => {
35+
render(<PageBlockCanvas draft={{ name: 'home', type: 'home', regions: [] }} onPatch={() => {}} />);
36+
expect(screen.getByText(/No regions yet/i)).toBeInTheDocument();
37+
expect(screen.getByText(/Add region/i)).toBeInTheDocument();
38+
expect(screen.queryByText(/configured in Properties/i)).not.toBeInTheDocument();
39+
});
40+
});

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,30 @@ export function PageBlockCanvas({
307307

308308
// Empty draft → empty canvas with hint.
309309
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+
}
310334
return (
311335
<div className="h-full overflow-auto bg-muted/20" onClick={handleBgClick}>
312336
<div className="mx-auto max-w-3xl px-6 py-8">

0 commit comments

Comments
 (0)