Repository navigation
Commit 8c93af7
feat(ui): one Personalized marker beside the list title holds the one reset to the view (#29266)
<!-- claude-projects-attribution: {"github_login":"zhuangjianguo"} -->
_Requested by **Steedos** · [project
thread](https://claude.ai/code/project/chan_018ETKaNwMZCNRYD7D8Q9Ubb?thread=cmsg_018ETKaNwMZCNRYD7D8Q9UbbJXD4Zm2YJkpU7fqLKKwUzp)_
Fixes #28950
Clause-②: yes (widening)
Metadata-protocol: none
## 维护者速读
-
**改了什么**:列表与视图不一致时,标题旁出现一个「已个性化」标记;点开列出哪里不同(筛选、搜索、排序、分组、列布局、隐藏字段、行高、布局、行颜色、行号、行内编辑),并给出唯一的「恢复为视图设置」。页头
⋯ 里的「重置视图」和 Display 面板的「恢复视图默认设置」去掉;排序弹层的重置和筛选面板的「全部清除」保留。
- **为什么改**:原来有两个重置入口,各管一部分,而且刷新或打开链接后 `uf_*`
参数会把旧列表带回来,搜索词也会从缓存回填。读者看不出列表被改过,也找不到一个能彻底恢复的地方。
- **风险与回滚**:只加不删:`ListView` 多一个可选宿主属性 `onPersonalizationChange`,i18n
多一组键,没有元数据键或选项。未传该属性的宿主(InterfaceListPage、ObjectDataPage、嵌入列表)保留原来的
Display 重置。回滚即 revert 本 PR,无数据迁移。
-
**先看**:`ui/packages/app-shell/src/views/objectViewListUrlState.ts`(`handleResetView`
与 `withoutListStateParams`:清存储、清搜索缓存、剥离整组
`uf_*`)、`ui/packages/plugin-list/src/ListView.tsx`(各部分与视图的比对)、`ui/packages/app-shell/src/views/ListPersonalizedMarker.tsx`。
- **两分钟验证**:`pnpm dev:crm` 打开 Accounts 列表,按名称倒序并搜索
`a`:标题旁出现「已个性化」;点开看到「搜索」「排序」,点「恢复为视图设置」:列表回到视图排序、搜索清空、地址栏不再有
`uf_*`,刷新后仍然干净,标记消失。
- **需要你做的**:无,席位落地。
## What changes
While a reader's list differs from its view, the Console draws one
**Personalized** marker beside the list title. Opening it names what
differs and holds the one **Reset to view**, with a sentence saying what
it covers and what it leaves alone. The marker is not configurable: no
metadata key and no option reach it.
- `ListView` (`@object-ui/plugin-list`): one new optional host prop,
`onPersonalizationChange(parts)`. It reports, on change, which parts of
the list on screen differ from the view: `sort`, `filters` (panel
conditions, user-filter chips, the queue toggle), `search`, `grouping`,
`hiddenFields`, `density`, `layout`, `rowColor`, `rowNumbers`,
`inlineEdit`. The callback is read through a ref (AGENTS.md #10), and
the report is keyed on the joined part list, so it fires only when the
set changes. With the prop wired, the Display popover draws no reset of
its own (`onReset` is left undefined; `ViewSettingsPopover.tsx` is
untouched). `viewDefaults` also accepts `grouping`, the view's own
grouping under one the host laid over it; without that key, the grouping
the list opened with counts as the view's.
- `ListPersonalizedMarker.tsx` (new, app-shell): an outline pill with a
popover. Below `sm` it is the icon alone, named by its `aria-label`, so
it never pushes the view name out. Each part label is a literal i18n
key.
- `objectViewListUrlState.ts`: the reset now drops the view's stored
entry, clears the cached search term, and strips the whole `uf_*` family
from the address (`uf__filter`, `uf__search`, `uf__sort`, `uf__group`
and every `uf_<field>` chip) through the new `withoutListStateParams`. A
pending-reset flag makes the next seed read storage, not the URL, so a
reload or an opened link cannot bring the old list back. The host adds
`columns` to the parts when the stored entry carries a column state.
- `ObjectView.tsx`, `objectViewPageHeader.tsx`: the marker sits in the
existing PageHeader `badge` slot (both header faces) and beside the
phone title. `PageHeader.tsx` is not edited.
- `objectViewPageHeader.tsx`, `objectViewHeaderChrome.tsx`: the ⋯ entry
"Reset view" and the folded bar's `object_view_reset_view` action are
gone; ⋯ keeps favorites and is drawn only when favorites are.
- i18n, all ten packs: new `console.objectView.personalized`,
`personalizedHeading` and `personalizedParts.*`; `resetView` and
`resetViewTitle` are reworded to label the marker's reset and say
everything it covers. No key is removed.
- Docs: `content/docs/ui/views.mdx`, the list personalization paragraph.
- Changeset `.changeset/28950-list-personalized-marker.md`, patch on
plugin-list, app-shell and i18n.
## Why
Measured on main a4015ab before the change (code reading):
- The header ⋯ "Reset view" was offered only while a stored entry
existed. It dropped the entry and remounted the list, but it left the
search term (re-seeded from the search cache as `uf__search`) and the
`uf_<field>` chips. After a reload or an opened link it left everything
the URL carried, because the remount re-seeded sort, filters, search and
grouping from the same `uf_*` params. A reader who changed only search,
grouping, chips or a Display setting was never offered it.
- The Display popover's "Reset to view default" restored the Display
settings only.
So a reader had two partial resets and no sign the list was theirs. The
card asks for one marker and one whole reset.
## Tests
-
`ui/packages/plugin-list/src/__tests__/ListView.personalization-28950.test.tsx`
(new, 12): nothing reported on the view's own list; sort compared
against `viewDefaults`; search and filters; an incomplete filter row is
not a filter; grouping against `viewDefaults` plus its control; hidden
fields and density; a row-numbers toggle reported and then reverted;
typed search; no Display reset when the callback is wired, kept without
it (control); the sort popover's own reset stays.
-
`ui/packages/app-shell/src/views/ObjectView.listPersonalized-28950.test.tsx`
(new, 5): `withoutListStateParams` strips the family and keeps
`filter[owner]`; the marker shows only while parts are reported and ⋯
has no reset; after a reload the reset leaves no `uf_*` param, keeps
`filter[owner]`, and the list opens with no search, filters or chips and
the view's sort; in session the reset clears the cached search; a stored
column state alone draws the marker and the reset clears it (control).
-
`ui/packages/app-shell/src/views/ObjectView.userListState-12418.test.tsx`:
its ⋯ "Reset view" pins move to the marker.
Ablations (each restored, tree byte-identical to HEAD):
- Removing `&& !reset` from the seed condition turns the reload test red
(1 of 5 failed).
- Making the Display `onReset` unconditional turns the "no Display
reset" test red (1 of 12 failed).
## Acceptance notes
- **Entries removed:** the header ⋯ "Reset view" (both header faces).
The Display popover's whole-Display reset is hidden on the Console list,
where the marker carries the reset; hosts that do not wire
`onPersonalizationChange` keep it.
- **Per-control resets kept:** the sort popover's reset and the Filter
panel's "Clear all".
- **Not touched by the reset, by design:** the record-open preference,
pinned views and their order, favorites, a "View all" link's
`filter[...]` scope, and the gallery card size (an object-wide setting,
not this view's state, so it is not a marker part either).
**Screenshots** (examples/app-crm, Accounts, `?uf__sort=[name
desc]&uf__search=a` opened as a link, then the reset, then a reload).
The 16 files are in the project files under `issue-28950/shots/`
(`before-` and `after-`, `1440-` and `390-`):
| | before (main dedc350) | after (this branch) |
|---|---|---|
| 1440, personalized link | no sign the list differs; ⋯ holds only "Add
to favorites" (no stored entry, so "Reset view" was never offered) |
**Personalized** pill beside the view name; popover names Search and
Sort |
| 1440, Display popover | "Reset to view default" footer | no
whole-Display reset; the Open records, Group, Density, Row numbers and
Edit inline controls stay |
| 390, personalized link | nothing | icon-only marker beside the phone
title, same popover |
| after **Reset to view** | n/a | list back to the view's sort, search
empty, address bar has no query; after a reload still none, marker gone
(1440 and 390) |
## Verification
Readings at b2d090d (merge of main dedc350, which carries #29047 and
#29108 as ea150b7):
- `pnpm exec turbo run type-check` for plugin-list, app-shell and i18n
with dependencies: 67/67.
- `pnpm build` (whole workspace): 117/117.
- vitest from `ui/`: plugin-list 183 files, 1759 passed; app-shell
`ObjectView*` / `objectView*` set 88 files, 735 passed (351 + 384); i18n
95 files, 1443 passed, 13 skipped; the two new files plus 12418, 37
passed.
- `node scripts/pm/dispatch-gates.mjs --ran`: 66 derived families, all
run, all exit 0 (includes `check-sdui-manifest` and `vitest run
scripts/`).
- PR-body gates with this body: `check-changeset-no-major`,
`check-adr-0087-registration`, `check-closing-keyword-parity`,
`check-partof-closing-keyword` all exit 0.
- ui eslint on the touched files: 0 errors.
- Live run (app-crm + console dev server): the reset leaves
`location.search` empty and a reload keeps it empty at 1440 and 390 px.
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
(not touched)
- [x] 2. Every name I added uses the casing for its kind.
- [x] 3. No formatter ran; `pnpm lint` passes. (ui eslint on the touched
files: 0 errors)
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset, and both
declaration lines are filled in here and in the changeset.
- [x] 6. `packages/spec` touched: its generated artifacts are
regenerated and committed. (not touched)
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English.
- [x] The *Before Submitting* list in CONTRIBUTING.md is done.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01QsR2jQki8itS3sC2eZCj7J
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 38a278b commit 8c93af7
22 files changed
Lines changed: 1141 additions & 128 deletions
File tree
- .changeset
- content/docs/ui
- ui/packages
- app-shell/src/views
- i18n/src/locales
- plugin-list/src
- __tests__
| 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
551 | 551 | | |
552 | 552 | | |
553 | 553 | | |
554 | | - | |
| 554 | + | |
| 555 | + | |
| 556 | + | |
| 557 | + | |
| 558 | + | |
| 559 | + | |
| 560 | + | |
| 561 | + | |
555 | 562 | | |
556 | 563 | | |
557 | 564 | | |
| |||
| 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 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
0 commit comments