Repository navigation
Commit 42afac7
feat(studio): pick an object list view from the Interfaces header title, and keep it in the address (#30175)
Part of #30046 — this PR ships Scope 1–5. Scope 6–8 (fold the
`surface=view:` page into this one, the 不在导航中 → 视图 filter, readable
form-view names and their 视图类型) are left for a decision: see the size
reading in Acceptance notes.
Clause-②: no
Metadata-protocol: none
## 维护者速读
- 改了什么:Studio「界面」里对象入口的标题变成「对象 / 视图
▾」,用运行态列表同一个下拉切换视图、搜索、新建视图;右侧面板的视图选择器、「添加视图」和「返回…」去掉了;当前视图写进地址栏
`?view=`。
-
为什么改:维护者要求「在预览区域顶部下拉选择视图。在对象的位置」,与前端运行态一致;原来切换视图后标题、地址都不变,刷新就回到默认视图,无法分享链接。
- 风险与回滚:只动 Studio 界面支柱(`ui/packages/app-shell`)与 studio 文案;不改
spec、不改服务端、不改元数据。运行态列表的下拉只多了「📌 可省略」一项,运行态照传,行为不变。回滚即 revert 本 PR。
- 先看:`ObjectListViewTitle.tsx`(新标题与新建视图)、`ObjectListViewInspector.tsx` 中
`useObjectListViewDraft` 的 `initialView`
一段(链接带来的视图何时生效、何时失效)、`StudioItemHeader.tsx` 的
`readableTitleWidth`(组合标题声明可读宽度)。
- 两分钟验证:`pnpm --dir ui exec vitest run
packages/app-shell/src/views/studio-design/StudioDesignSurface.listViewSwitch-27896.test.tsx`,预期
10 passed;或在 Studio 打开 CRM 的「商机」,标题下拉选「商机看板」,地址出现
`&view=crm_opportunity.pipeline`,刷新后仍是看板。
- 需要你做的:无,席位落地。
## What changes
- **The view switcher is the header's title.** An Interfaces `object`
entry's header reads object / view ▾. The view part is the running
list's own `ObjectViewTitleSwitcher`, fed the object's list views (the
merged `listViews` plus this package's drafts, as the panel picker
listed them): rows by label, a search box, and New view, which opens the
existing Create View dialog. Studio hands it no ☆, no 📌, no recent views
and no Save as my view. It shows when the object has one list view too,
so New view stays reachable. On a read-only package it switches views
and offers no New view (`canCreate = !readOnly`, as the panel's Add view
was gated). New file `ObjectListViewTitle.tsx`; the create path
(`createView`, the taken-name toast) moved there unchanged.
- **The panel's picker, Add view and "Back to …" are gone.** The panel
keeps the open view's settings. Its title is the view's name exactly as
the switcher gives it (`listViewNameOf`: the served `listViews` label
first, else the item's own), so the CRM kanban's panel reads 商机看板 like
the switcher, not the raw `Pipeline (Kanban)`. A label-less view is
"Default view" or its key; the object's name moved to the header's
prefix.
- **The open view is in the address.**
`?surface=object:OBJECT&nav=NAV&view=OBJECT.KEY`, written only while a
view other than the entry's own is open. `useSurfaceDeepLink` is split
into its capture and mirror halves (the composed hook is unchanged for
the other pillars), so the Interfaces pillar writes the leaf and its
view in one replace. `useObjectListViewDraft` takes `initialView`: shown
once the linked leaf opens, dropped as soon as another leaf opens or the
author picks a view, and taken only for a view of the open object. A
pending edit is still sent before the switch (`switchListView`,
unchanged), and a held edit still asks.
- **`ObjectViewTitleSwitcher`: the pin is optional.** `pinnedViewId`,
`onPinnedViewChange` and `recentViewIds` may be left out; without
`onPinnedViewChange` no row draws 📌 and no Pinned section shows. The
running list passes all three, so its behaviour is unchanged (its
existing pins stay green, and a new pin holds both directions).
- **The title keeps its view name on a narrow row** (measured, see
Verification). A composed title shrinks inside its box instead of
overflowing, so #29693's fold never saw it as cut: at 1280 on the
read-only CRM it showed `Op… / Pipeline (K…` with room to spare in the
labelled actions. The title now declares its whole width
(`data-studio-title-readable`, read by `readableTitleWidth`) and carries
a zero-height sizer whose text is a pseudo-element (no reader or tooltip
reads it twice), and "Open in Data" folds to its glyph like the other
labelled actions. Order on a narrow row: chips and tools fold, then the
object prefix truncates (down to 1.5em), then the view name, with its
whole text in the trigger's tooltip.
- **Strings (`@object-ui/i18n` studio namespace):** adds
`engine.studio.inspector.listView.defaultView`; removes `defaultTitle`,
`namedTitle`, `addView`, `backToEntry`, `switchView`, `switchViewDraft`,
which only the removed panel controls read. The switcher's own words
(搜索视图, 新建视图) are the console's existing `view.*` keys.
## Tests
All on head `ba9af0941`.
<details><summary>Affected suites: 140 files, 1843 passed</summary>
`pnpm --dir ui exec vitest run --maxWorkers=2` over every studio-design
test that reads the Interfaces header, the list-view panel or the deep
link (40 files), the switcher's own tests and the running list's
switcher pins (`objectViewTitleSwitcher.test.tsx`,
`objectViewTitleSwitcher.phoneWidth-29074`,
`ObjectView.titleViewSwitcher-27594`, `ObjectView.starredView-27594`,
`ObjectView.personalViews-29635`) and `packages/i18n/`: `Test Files 140
passed (140) · Tests 1843 passed | 13 skipped`. Typecheck: `pnpm
--filter @object-ui/app-shell type-check` exit 0 (both programs).
</details>
<details><summary>New and rewritten pins</summary>
- `StudioDesignSurface.listViewSwitch-27896.test.tsx` (rewritten onto
the header switcher, 10 tests): the switcher lists all three views by
label, the panel title is the same name, no ☆ / 📌 / recent / Save as my
view, New view and search present; the panel holds no picker, Add view
or Back; a drafted view's edit saves to that view; a pending edit is
sent before the switch with no prompt; a held edit asks, declined stays,
accepted switches; one list view still shows the switcher with New view;
a read-only package switches and offers no New view; a pick writes
`?view=` and the entry's view writes none; a link carrying `view` opens
that view (a drafted one); CONTROL: a link naming another object's view
is not taken.
- `ObjectListViewInspector.addView-27836.test.tsx`: New view from the
switcher creates the kanban and the calendar, refuses a taken name; the
switcher lists the new view and picking the entry's view shows it again.
- `objectViewTitleSwitcher.test.tsx`: new top-level describe — without
the pin door no row draws 📌 and a handed pinned id draws no Pinned
section; a row still switches.
- `useSurfaceDeepLink.test.ts`: `view` parses beside `surface` and
`nav`, back-compat without it, `view` alone names nothing.
- `ObjectListViewInspector.displayTitle-13151`,
`StudioDesignSurface.listViewInspector-11823`,
`InterfacesPillar.customize-28562`: expectations moved to the view-only
title and to the switcher's New view.
</details>
<details><summary>Ablations (one per changed behaviour, restored and
proven by blob hash)</summary>
Through `scripts/ablation-replace.mjs`; the suites import `src`
directly, so no build sits in the path.
- Mirror passes `null` for the view: `a pick writes ?view=` and `a link
carrying the view` go red (2 failed / 8 passed); restored blob
`b26a265eff49` == HEAD, `git diff HEAD` empty, 10/10 again.
- `initialView: null`: `a link carrying the view` goes red (1 failed / 9
passed); restored == HEAD.
- Pin always drawn: `no row draws 📌` goes red (1 failed / 14 passed);
restored == HEAD.
The first run of the mirror ablation turned only one pin red: the link
pin read the address before the mirror's write landed. The pin now waits
for the writes to land, and the re-run turned both red.
</details>
## Acceptance notes
- **Scope 6–8 are not in this PR.** The size reading: this PR is 1059
changed lines (712 + / 347 −, 16 files) for Scope 1–5. Scope 6 means
folding 条件格式化 (with its CEL verdict wired into the list buffer's
autosave hold) and the spec-form sections into the list panel, and
sending `surface=view:` for a list view to the object page, including
for an object no nav entry opens. Scope 7 is the 不在导航中 filter (a draft
header carries no body, so the view's kind and object are known only
from published rows or the name). Scope 8 is readable form-view names, a
heading that says what remains, and the form 视图类型. Together they are an
estimated 600–800 more lines with their pins. Also, the 视图类型 fix lives
in `metadata-admin/inspectors/ViewVariantInspector.tsx` (its type
options come from the list-variant enum, so a form's `simple` reads
未找到), which is outside this claim's file surface.
- **The header-width table on the card does not hold on the read-only
CRM.** With Customize, 只读 and Open in Data in the row, the title's room
at 1280 is about 208 px, not ≥ 222 px. Before the fold fix the title
clipped at 1280 too. After it, 1280 shows both names whole, and 1024
folds the tools and keeps the view name (`Pipeline (Kanban)` / 商机看板)
whole with the object prefix cut.
- **Seen, not fixed (pre-existing, not changed here):** on a draft-only
object, an entry with no `viewName` opens `OBJECT.default` even when the
package drafts an `isDefault` view, because the entry's view is resolved
from the shell's merged `listViews` only. The switcher therefore lists
"Default view" beside the drafted default, and Create View seeds no
columns there. Both behaved the same with the old panel picker. Reported
to the seat.
- No spec, server or metadata change, and no new metadata key.
## Verification
Browser, latest `main` + this branch, isolated stack (CRM `--fresh`
backend on its own port, Console dev server pointed at it, Chromium from
`/opt/pw-browsers`). The screenshots are in the session's scratchpad.
They are not attached here because the relay carries no uploads.
<details><summary>Read-only package (com.example.crm), en and zh, 1280
and 1024</summary>
- 1280, 商机: title `商机 / 全部商机 ▾`; the dropdown lists 全部商机, 商机看板; picking
商机看板 writes `&view=crm_opportunity.pipeline`; reload keeps it; the panel
title reads 商机看板.
- 1280, en: `Opportunities / Pipeline (Kanban) ▾`, both whole (title 315
px).
- 1024, en: tools folded to glyphs, `Op… / Pipeline (Kanban) ▾`, view
name whole (label 151 px, not cut).
- 1024, zh: `商机 / 商机看板 ▾`, both whole.
- No New view in the dropdown; the panel's controls stay disabled.
</details>
<details><summary>Writable package (a fresh package with one object
entry)</summary>
- New view from the dropdown, named "Open opportunities this quarter":
saved to the package draft, the URL gains
`&view=…open_opportunities_this_quarter`, the panel and the title show
it.
- Switch to another view and back: the dropdown lists the new view.
- Reload: the same view opens. A shared link opened in a second tab at
1024 opens the same view: chips folded, the long name truncated with its
whole text in the trigger's tooltip.
- The pending-edit-before-switch path was not driven in the browser (the
drafted object's new view had no column to remove). It is pinned by `a
pending edit is sent to its own view before the switch, and nothing
asks`.
</details>
<details><summary>Local gates (on ba9af09)</summary>
`node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` derived 35 families; each was run and exited
0, `pnpm check:type-check-debt` included (`0 raw tsc error(s) total`)
and `pnpm --dir ui exec vitest run scripts/` (172 files, 4985 passed).
`--ran` reconciliation: `35 derived, 35 run, 0 NOT-MEASURED, 0 UNRUN`.
`node scripts/check-sdui-manifest.mjs` exit 0; `pnpm --dir ui run
check:new-line-citations`: `0 new citation(s)`; `node
scripts/check-platform-checklist.mjs --changed-from origin/main` exit 0;
eslint over the changed files: 0 errors. Repo-wide scans and the full UI
test battery are CI's.
</details>
---
_Generated by [Claude
Code](https://claude.ai/code/session_01QSeKYnE9YNkSZen7sijLA3)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 7e9fa4b commit 42afac7
17 files changed
Lines changed: 716 additions & 347 deletions
File tree
- .changeset
- ui/packages
- app-shell/src
- console/__tests__
- views
- studio-design
- i18n/src/namespaces/studio
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
115 | 115 | | |
116 | 116 | | |
117 | 117 | | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
118 | 122 | | |
119 | 123 | | |
120 | 124 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
190 | 190 | | |
191 | 191 | | |
192 | 192 | | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
77 | 77 | | |
78 | 78 | | |
79 | 79 | | |
80 | | - | |
81 | | - | |
82 | | - | |
83 | | - | |
84 | | - | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
85 | 89 | | |
86 | 90 | | |
87 | 91 | | |
| |||
123 | 127 | | |
124 | 128 | | |
125 | 129 | | |
126 | | - | |
| 130 | + | |
127 | 131 | | |
128 | | - | |
| 132 | + | |
129 | 133 | | |
130 | 134 | | |
131 | 135 | | |
| |||
144 | 148 | | |
145 | 149 | | |
146 | 150 | | |
147 | | - | |
| 151 | + | |
148 | 152 | | |
149 | 153 | | |
150 | 154 | | |
| |||
227 | 231 | | |
228 | 232 | | |
229 | 233 | | |
230 | | - | |
231 | | - | |
232 | | - | |
233 | | - | |
234 | | - | |
235 | | - | |
236 | | - | |
237 | | - | |
238 | | - | |
239 | | - | |
240 | | - | |
241 | | - | |
242 | | - | |
243 | | - | |
244 | | - | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
245 | 251 | | |
246 | 252 | | |
247 | 253 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
568 | 568 | | |
569 | 569 | | |
570 | 570 | | |
| 571 | + | |
| 572 | + | |
| 573 | + | |
| 574 | + | |
| 575 | + | |
| 576 | + | |
| 577 | + | |
| 578 | + | |
| 579 | + | |
| 580 | + | |
571 | 581 | | |
572 | 582 | | |
573 | 583 | | |
574 | 584 | | |
575 | 585 | | |
576 | 586 | | |
577 | 587 | | |
578 | | - | |
579 | | - | |
| 588 | + | |
| 589 | + | |
580 | 590 | | |
581 | 591 | | |
582 | 592 | | |
| |||
591 | 601 | | |
592 | 602 | | |
593 | 603 | | |
594 | | - | |
| 604 | + | |
595 | 605 | | |
596 | 606 | | |
597 | 607 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
4 | | - | |
5 | | - | |
6 | | - | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
7 | 8 | | |
8 | | - | |
9 | | - | |
10 | | - | |
11 | | - | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
12 | 13 | | |
13 | 14 | | |
14 | 15 | | |
15 | 16 | | |
16 | 17 | | |
17 | 18 | | |
18 | | - | |
| 19 | + | |
| 20 | + | |
19 | 21 | | |
20 | 22 | | |
21 | 23 | | |
| |||
121 | 123 | | |
122 | 124 | | |
123 | 125 | | |
| 126 | + | |
124 | 127 | | |
125 | 128 | | |
126 | 129 | | |
| |||
143 | 146 | | |
144 | 147 | | |
145 | 148 | | |
146 | | - | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
147 | 157 | | |
148 | 158 | | |
149 | 159 | | |
| |||
159 | 169 | | |
160 | 170 | | |
161 | 171 | | |
162 | | - | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
163 | 181 | | |
164 | 182 | | |
165 | 183 | | |
| |||
174 | 192 | | |
175 | 193 | | |
176 | 194 | | |
177 | | - | |
| 195 | + | |
| 196 | + | |
178 | 197 | | |
179 | 198 | | |
180 | 199 | | |
| |||
221 | 240 | | |
222 | 241 | | |
223 | 242 | | |
224 | | - | |
| 243 | + | |
225 | 244 | | |
226 | 245 | | |
227 | 246 | | |
| |||
236 | 255 | | |
237 | 256 | | |
238 | 257 | | |
239 | | - | |
| 258 | + | |
240 | 259 | | |
241 | 260 | | |
242 | 261 | | |
243 | 262 | | |
244 | | - | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
245 | 267 | | |
246 | 268 | | |
247 | | - | |
248 | 269 | | |
249 | 270 | | |
250 | 271 | | |
| |||
0 commit comments