Repository navigation
Commit dfafae8
domain:ui#2 · os-justin · session_01QSeKYnE9YNkSZen7sijLA3
Fixes #30077
Clause-②: no
Metadata-protocol: none
## 维护者速读
-
改了什么:列表的「隐藏字段」面板(工具栏的和「显示」面板里的同一节)在视图自带的列之后,列出该对象里读者有权读取的其余字段,默认不勾选;勾选就把它加成读者自己列表里的一列,取消勾选就拿掉。
- 为什么改:按本卡裁决 A(「其他全部同意」),业务员想在自己的列表里看负责人、折扣等字段时,不必再请管理员替全公司改视图。
- 风险与回滚:只存在读者个人偏好里(`sys_user_preference` 的 `ui.list.OBJECT` 行新增
`addedFields`),不写视图、不改元数据和 spec;回滚即 revert,旧版本的清洗函数会丢掉
`addedFields`,没有残留副作用。
- 先看:`ui/packages/plugin-list/src/ListView.tsx`(`addableFields` 的取数与
`updateListedFields`
里「取消勾选已加列」的分支);`ui/packages/app-shell/src/views/objectViewListRenderer.tsx`(`withAddedListColumns`
与 `columns`
那一级的拼接);`ui/packages/app-shell/src/context/ListPreferencesProvider.tsx`(`addedFields`
的清洗)。
- 两分钟验证:`pnpm --dir ui exec vitest run
packages/plugin-list/src/__tests__/ListView.fieldListRead-11984.test.tsx
packages/app-shell/src/views/ObjectView.userListState-12418.test.tsx`,预期两个文件全部通过。
- 需要你做的:无,席位落地。
## What changes
- **The offer (`@object-ui/plugin-list`).** Two new `ListView` host
props: `addedFields` (the names among `schema.columns` the reader added)
and `onAddedFieldsChange` (the door that stores them). When the door is
wired, both hide-fields lists — the wide toolbar's Hide fields popover
and the Hide Fields section of the Display panel (`ViewSettingsPopover`,
the same door: it writes through the same `updateHiddenFields` callback)
— list, after the view's columns, every other field of the object's
definition that the caller may read (`canReadField`, the predicate every
toolbar field list asks), that the definition does not mark `hidden:
true`, and that is not already a column; business fields first, then
system fields (`isSystemManagedField`), unticked. Ticking one calls
`onAddedFieldsChange` with the whole new list; unticking an added column
calls it without that name instead of hiding it; adding a name a stale
hidden entry carries clears that entry too. A reader-added column is
labelled from the definition in the popover. Without the door the
popover is byte-for-byte today's.
- **The store (`@object-ui/app-shell`).** `ListViewPreference` gains
`addedFields` beside `hiddenFields` in the reader's `ui.list.OBJECT`
row: sanitized to unique names, and an empty list is dropped (nothing
added is the view's own). "Reset to view" removes it with the rest of
the view's entry (`resetViewPreference`), unchanged code path.
- **The host (`objectViewListRenderer`).** `withAddedListColumns`
appends the stored added names to the `columns` rung in the view's own
column shape (bare names beside bare names, `{ field }` beside column
objects), skipping any name the view itself declares; the list therefore
fetches (`$select`), expands (`$expand`, through `buildExpandFields`
over `schema.columns`) and draws an added column exactly as a declared
one. The door is wired where a Hide Fields choice is stored (a view that
declares `columns`, or a saved view); on a system view or fallback tab
that declares no columns it is not wired, matching the session-only hide
there.
- **The marker (`objectViewListUrlState`).** A stored `addedFields`
reports the existing `columns` part, so the Personalized marker draws
and names "Column layout" with no new copy.
- `ui/packages/plugin-list/README.md` documents the two host props
(ui/AGENTS.md #2).
## Tests
All runs on this branch's worktree, under
`scripts/pm/os-verify-lock.sh`; head at the final run: `916241ae1`.
- New pins, `ListView.fieldListRead-11984.test.tsx` (top-level describe,
objectstack#30077): wide and compact, restricted caller is offered
`Title, Status, Priority` and not the two unreadable fields; full-read
control offers them; door absent offers the view's columns alone; tick
adds through the door and leaves hide/show of a view column unchanged;
untick of an added column removes it and writes no hidden list; a stale
hidden entry is cleared on add; the Display panel adds through the same
door; a `hidden: true` field is not offered and system fields follow
business fields; an added lookup is in the next `$select` and `$expand`.
- New pins, `ObjectView.userListState-12418.test.tsx` (top-level
describe): adding writes no view and lands as one save of `addedFields`;
the list is handed the column appended; a fresh browser opens with it;
another user does not see it; "Reset to view" drops it (the stored
addition alone draws the marker); a name the view declares is not
duplicated; a saved view that declares columns stores it the same way.
- New pins, `ListPreferencesProvider.slot-12418.test.ts`: sanitize,
patch/clear and reset of `addedFields`.
<details><summary>Readings</summary>
- Three touched test files on `916241ae1`: see Verification.
- `pnpm --dir ui exec vitest run --maxWorkers=2 packages/plugin-list/`
(pre-merge head): `Test Files 1 failed | 188 passed (189)`, `Tests 1
failed | 1805 passed (1806)`; the one failure was this PR's own new
lookup pin, which asserted a grid header this harness does not render
(the grid registers lazily here). The pin was narrowed to the
`$select`/`$expand` and popover assertions and re-run:
`ListView.fieldListRead-11984.test.tsx` `Tests 31 passed (31)`.
- app-shell, 101 files touching list preferences, the relay or the
marker (every `ObjectView*` and `InterfaceListPage*` test plus the `git
grep` hits for
`ListPreferences`/`listPrefs`/`personaliz`/`onHiddenFieldsChange`):
`Test Files 101 passed (101)`, `Tests 880 passed (880)`. The full
app-shell suite (2172 files) is declared to CI's sharded Test UI.
- `pnpm --filter @object-ui/plugin-list type-check` exit 0; `pnpm
--filter @object-ui/app-shell type-check` exit 0 (pre-merge, and again
post-merge, see Verification).
- Reverse verification (one-shot, no permanent file):
`addedFields={listColumns.addedFields}` replaced by `addedFields={42}`
with `scripts/ablation-replace.mjs` (anchor 1 to 0, blob `91185df42a4e`
to `8287f4f432e4`), `tsc --noEmit` in app-shell: `TSC_EXIT=2`, `error
TS2322: Type 'number' is not assignable to type 'readonly string[]'`;
restored, blob equal to HEAD (`91185df42a4e`) and `git diff HEAD` empty.
The rebuilt `dist/ListView.d.ts` carries both props and `ListViewProps`
declares no index signature.
</details>
## Acceptance notes
- **File surface.** The claim named the store path under
`ui/packages/app-shell/src/views/**`; the store is
`ui/packages/app-shell/src/context/ListPreferencesProvider.tsx` (its
sanitizer drops any undeclared part, so an added list cannot persist
without it). `objectViewListUrlState.ts` (under `views/**`) carries the
marker part. `ui/packages/plugin-list/README.md` is added for
ui/AGENTS.md #2.
- **Lookup columns (card "Not measured").** No server change needed:
`$expand` is built from `schema.columns` by `buildExpandFields`, so an
added lookup is expanded as a declared one; pinned. No field type is
excluded from the offer.
- **Where it is not offered.** A system view or fallback tab that
declares no `columns` (its hide is session-only by ruling 5839344270 on
objectui#10694), and every host that does not wire `onAddedFieldsChange`
(InterfaceListPage, page blocks).
- **Copy unchanged.** The trigger still reads "Hide fields" and the
section "Hide Fields"; the marker reports added fields as "Column
layout", and the reset sentence already names "column layout". The
Display trigger's count does not count added fields. Renaming the
control would touch ten locale packs and is left as a note, not filed.
- **Shared list.** The Group editor and Row color select read the same
`allFields`, so they also list a column the reader added (it is a column
of their list); they never list a field not on the list.
- **Query width.** An added column widens `$select` by one field; the
page size and existing caps are unchanged.
- **Grid header of an added column** was not measured in a browser: this
harness does not render the lazily registered grid. The popover label is
pinned.
## Verification
<details><summary>Post-merge head and gates</summary>
Head `916241ae1` (this branch merged with `origin/main`; the incoming
commits touch none of this diff's files):
- `pnpm turbo run build --filter='@object-ui/app-shell^...'
--concurrency=2`: exit 0, `Tasks: 67 successful, 67 total`.
- `pnpm --filter @object-ui/app-shell type-check`: exit 0.
- The three touched test files: `Test Files 3 passed (3)`, `Tests 84
passed (84)`.
- `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` derived 36 commands on this head; 35 re-run
here on `916241ae1`, each exit 0; `pnpm --dir ui exec vitest run
scripts/` exit 0 (`Test Files 170 passed | 2 skipped (172)`) on the
pre-merge head, and the merge brings no `ui/scripts` change. `--ran`
reconciliation: `36 derived, 36 run, 0 NOT-MEASURED, 0 UNRUN` (a derived
zero, every line with its exit code).
- Also run: `pnpm check:changeset-fixed` exit 0; `pnpm
check:new-line-citations` (under `ui/`) exit 0, `0 new citation(s)`;
`check:doc-types`, `check:doc-json`, `check:doc-fences`,
`check:doc-example-readers` exit 0 over the README change. NOT MEASURED:
`check:doc-snippets` / `check:doc-examples`, reason: each needs its own
scoped 33-package build first (exit 2, prerequisite), left to CI.
- Not run locally, declared to CI: the full app-shell suite (2172 files,
sharded Test UI), Lint UI, Type Check UI, and the lint/type-check lanes
the derivation lists as CI-only.
</details>
## Browser check
Real Chromium (Playwright, real clicks and real typing), head
`916241ae1`. Example CRM backend (`objectstack dev --seed-admin --fresh
-p 4377` in `examples/app-crm`), Console built from this branch's `ui/`
source (`vite --port 5377 --strictPort`,
`DEV_PROXY_TARGET=http://localhost:4377`), locale en-US, signed in as
the seeded admin, on Opportunities / All Opportunities
(`crm_opportunity.all`). The organization's first-run "Set the default
currency" dialog was answered once. 14 of 14 assertions passed. The
screenshots are kept in the seat's scratchpad, not uploaded; the seat
viewed `04-grid-added.png` and `11-labels.png`.
1. **List opens on the view's columns.** Headers: Opportunity Name,
Account, Stage, Deal Value, Win Probability (%), Expected Revenue,
Expected Close. PASS
2. **Offered unticked.** In Display, Hide Fields, Owner (`owner_id`, a
lookup) and Days to Close (a formula field) are listed after the view's
columns, unticked. Discount (%), Approval Status, Renewal Of and
Products Total are offered too. PASS
3. **Tick both.** The grid header gains Owner and Days to Close. Owner
cells read "Dev Admin" (the lookup's display name, not the id) and Days
to Close cells carry values. The reader's preference
`ui.list.crm_opportunity` reads `perView.crm_opportunity.all.addedFields
= ["owner_id","days_to_close"]`. PASS
4. **Reload.** Both columns are still there. PASS
5. **Reset to view.** The Personalized marker names "Column layout".
After the reset both columns are gone and the stored row is `{}`. PASS
6. **Untick an added column.** Days to Close goes back to the add list
unticked; the stored row is `addedFields = ["owner_id"]` with no
`hiddenFields`. After a reload it is still absent and still offered, so
it was removed, not hidden. PASS
**Rework round 2: labels** (head `cfcc48813`). The first run listed two
of the view's own label-less columns under their machine names
(`account`, `expected_revenue`) while the grid header read Account and
Expected Revenue. `allFields` in `ListView.tsx` now falls back to the
object definition's field label for every column with no label of its
own. Order: translation, then the column's own label, then the
definition's label, then the field name.
7. **Labels.** Hide Fields lists Opportunity Name, Account, Stage, Deal
Value, Win Probability (%), Expected Revenue, Expected Close, equal item
for item to the grid header row. 3 of 3 assertions passed. PASS
Pin: `ListView.fieldListRead-11984.test.tsx`, "a column with no label of
its own is listed under the definition's label, not its machine name";
ablated red (`expected [ 'title', 'status', 'Priority ] to include
'Status'`), restored green. The Group and Row color pickers share this
list; the one pin that spelled the old raw name
(`ListView.rowColorSelect-12376.test.tsx`, `subject` → `Subject`) was
updated with its intent unchanged.
Not changed here: the list puts a translation before a column's own
label, while the grid header puts the column's own label first and
prettifies a bare name. They agree whenever a column has no label, which
is the case fixed here.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01QSeKYnE9YNkSZen7sijLA3)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: objectstack-fleet[bot] <332303061+objectstack-fleet[bot]@users.noreply.github.com>
1 parent bedb96e commit dfafae8
11 files changed
Lines changed: 600 additions & 20 deletions
File tree
- .changeset
- ui/packages
- app-shell/src
- context
- __tests__
- views
- plugin-list
- src
- __tests__
- components
| 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 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
7 | | - | |
8 | | - | |
| 7 | + | |
| 8 | + | |
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
| |||
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
22 | | - | |
| 22 | + | |
| 23 | + | |
23 | 24 | | |
24 | 25 | | |
25 | 26 | | |
| |||
102 | 103 | | |
103 | 104 | | |
104 | 105 | | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
105 | 115 | | |
106 | 116 | | |
107 | 117 | | |
| |||
172 | 182 | | |
173 | 183 | | |
174 | 184 | | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
175 | 191 | | |
176 | 192 | | |
177 | 193 | | |
| |||
185 | 201 | | |
186 | 202 | | |
187 | 203 | | |
| 204 | + | |
| 205 | + | |
188 | 206 | | |
189 | 207 | | |
190 | 208 | | |
| |||
210 | 228 | | |
211 | 229 | | |
212 | 230 | | |
213 | | - | |
| 231 | + | |
| 232 | + | |
214 | 233 | | |
215 | 234 | | |
216 | 235 | | |
| |||
222 | 241 | | |
223 | 242 | | |
224 | 243 | | |
| 244 | + | |
225 | 245 | | |
226 | 246 | | |
227 | | - | |
| 247 | + | |
228 | 248 | | |
229 | 249 | | |
230 | 250 | | |
| |||
251 | 271 | | |
252 | 272 | | |
253 | 273 | | |
254 | | - | |
| 274 | + | |
| 275 | + | |
255 | 276 | | |
256 | 277 | | |
257 | 278 | | |
| |||
493 | 514 | | |
494 | 515 | | |
495 | 516 | | |
496 | | - | |
| 517 | + | |
497 | 518 | | |
498 | 519 | | |
499 | 520 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
159 | 159 | | |
160 | 160 | | |
161 | 161 | | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
52 | 52 | | |
53 | 53 | | |
54 | 54 | | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
55 | 60 | | |
56 | 61 | | |
57 | 62 | | |
| |||
889 | 894 | | |
890 | 895 | | |
891 | 896 | | |
| 897 | + | |
| 898 | + | |
| 899 | + | |
| 900 | + | |
| 901 | + | |
| 902 | + | |
| 903 | + | |
| 904 | + | |
| 905 | + | |
| 906 | + | |
| 907 | + | |
| 908 | + | |
| 909 | + | |
| 910 | + | |
| 911 | + | |
| 912 | + | |
| 913 | + | |
| 914 | + | |
| 915 | + | |
| 916 | + | |
| 917 | + | |
| 918 | + | |
| 919 | + | |
| 920 | + | |
| 921 | + | |
| 922 | + | |
| 923 | + | |
| 924 | + | |
| 925 | + | |
| 926 | + | |
| 927 | + | |
| 928 | + | |
| 929 | + | |
| 930 | + | |
| 931 | + | |
| 932 | + | |
| 933 | + | |
| 934 | + | |
| 935 | + | |
| 936 | + | |
| 937 | + | |
| 938 | + | |
| 939 | + | |
| 940 | + | |
| 941 | + | |
| 942 | + | |
| 943 | + | |
| 944 | + | |
| 945 | + | |
| 946 | + | |
| 947 | + | |
| 948 | + | |
| 949 | + | |
| 950 | + | |
| 951 | + | |
| 952 | + | |
| 953 | + | |
| 954 | + | |
| 955 | + | |
| 956 | + | |
| 957 | + | |
| 958 | + | |
| 959 | + | |
| 960 | + | |
| 961 | + | |
| 962 | + | |
| 963 | + | |
| 964 | + | |
| 965 | + | |
| 966 | + | |
| 967 | + | |
| 968 | + | |
| 969 | + | |
| 970 | + | |
| 971 | + | |
| 972 | + | |
| 973 | + | |
| 974 | + | |
| 975 | + | |
| 976 | + | |
| 977 | + | |
| 978 | + | |
| 979 | + | |
| 980 | + | |
| 981 | + | |
| 982 | + | |
| 983 | + | |
| 984 | + | |
| 985 | + | |
| 986 | + | |
| 987 | + | |
| 988 | + | |
| 989 | + | |
| 990 | + | |
| 991 | + | |
| 992 | + | |
892 | 993 | | |
893 | 994 | | |
894 | 995 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
12 | | - | |
| 12 | + | |
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
| |||
43 | 43 | | |
44 | 44 | | |
45 | 45 | | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
46 | 69 | | |
47 | 70 | | |
48 | 71 | | |
| |||
383 | 406 | | |
384 | 407 | | |
385 | 408 | | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
| 418 | + | |
| 419 | + | |
| 420 | + | |
| 421 | + | |
| 422 | + | |
| 423 | + | |
| 424 | + | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
| 429 | + | |
386 | 430 | | |
387 | 431 | | |
388 | 432 | | |
| |||
457 | 501 | | |
458 | 502 | | |
459 | 503 | | |
460 | | - | |
| 504 | + | |
| 505 | + | |
| 506 | + | |
461 | 507 | | |
462 | 508 | | |
463 | 509 | | |
| |||
946 | 992 | | |
947 | 993 | | |
948 | 994 | | |
| 995 | + | |
| 996 | + | |
| 997 | + | |
| 998 | + | |
949 | 999 | | |
950 | 1000 | | |
951 | 1001 | | |
| |||
0 commit comments