Repository navigation
feat(plugin-list): the empty-state "Clear filters" also clears the user-filter chips - #29189
Conversation
…-filter chips UserFilters gains an optional ref receiving a UserFiltersHandle while a dropdown or toggle bar is mounted; clear() empties every chip as each chip's x would and reports every chip field empty to the host. ListView's Clear filters clears the chips through it, and a list emptied by a chip alone now offers the action. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsR2jULUUCcok2FGYwt6Gh
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsR2jULUUCcok2FGYwt6Gh
|
domain:ui#3 · zhuangjianguo · session_01QsR2jV4dWohojjbTJTQq5m Contract reviewServed-tier: Face: declaration only ( ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS |
|
domain:ui#3 · zhuangjianguo · session_01QsR2jV4dWohojjbTJTQq5m 席位核对 · ACCEPT · 2026-11-02T09:14ZHead-sha:
|
Requested by Steedos · project thread
Fixes #29052
Clause-②: yes (widening)
Metadata-protocol: none
维护者速读
uf_*参数也一并清掉,刷新后不会再回来。UserFilters新增一个可选的ref(导出类型UserFiltersHandle,只有clear()一个方法),不传 ref 的宿主行为完全不变。预设标签页(tabs)不算读者的筛选,不清。UserFilters自己,不重挂载;clear()等同于逐个点每个标签的 ×,走同一条onFilterChange/onSelectionsChange路径,宿主按字段删除 URL 参数,宿主代码未改。回滚即回退本 PR。ui/packages/plugin-list/src/UserFilters.tsx(UserFiltersHandle与DropdownFilters里的commitSelection/useImperativeHandle);ui/packages/plugin-list/src/ListView.tsx(handleClearReaderQuery与userFilterChipsClearable);新测试ListView.clearFiltersChips-29052.test.tsx。pnpm --dir ui exec vitest run packages/plugin-list/src/__tests__/ListView.clearFiltersChips-29052.test.tsx,预期 4 个用例全部通过;第一个用例断言主按钮是「Clear filters」、点击后标签的 × 消失、空状态消失、宿主收到{ status: [], rating: [] }。What changes
UserFilterscan be cleared from outside, without a remount. It takes an optionalref(React 19 ref-as-prop) that receives aUserFiltersHandlewhile a dropdown or toggle bar is mounted; the type is exported from@object-ui/plugin-listbesideUserFiltersProps. Its one method,clear(), empties every chip, authordefaultValuesincluded, exactly as pressing each chip's × would:onFilterChangereceives no conditions, andonSelectionsChangereports every chip field with an empty selection, held or not. The per-chip × path andclear()now share one commit helper (commitSelection), so typing finality, the emit and the host report cannot drift apart;The selection stays the bar's own state. A host that passes no ref gets today's bar unchanged (pinned below). No key remount (ui/AGENTS.md #8), no new metadata key or option.
ListView's "Clear filters" covers the chips.handleClearReaderQuery(from #28908) now also calls the bar'sclear(). The empty state offers the action when the chip bar is on screen, is not in tabs mode, and its conditions are part of the applied query (userFilterChipsClearable), beside the existing panel and queue-toggle arms. So a list emptied by a chip alone now shows "Clear filters" as the primary action and "Add record" as the secondary, and the existing copy ("No records match your current filters or search. Try adjusting or clearing them.") now matches the action offered. No copy changed.What the host persists. Measured by reading the hosts on
origin/main2af0ae2a: all three hosts (objectViewListUrlState.tshandleUserFilterSelectionsChange,InterfaceListPage,ObjectDataPage) write the report throughapplyUserFilterParams, which deletesuf_FIELDonly for a field the report names with an empty selection; a field the report omits keeps its param. That is whyclear()reports every chip field rather than{}: a{}report would leave everyuf_*param in place, and the chips would come back on the next seed. With every field named, the existing host path drops them all, so no host file changed.Tests
ui/packages/plugin-list/src/__tests__/ListView.clearFiltersChips-29052.test.tsx(new), 4 cases: a chip restored from the host empties the list, "Clear filters" leads, the click clears the chip, the next fetch carries no miss, the rows come back and the host receives every chip field empty; an author-defaulted chip clears the same way; a chip and a panel condition together clear in one click; a list emptied by a preset tab offers no clear action.ui/packages/plugin-list/src/__tests__/UserFilters.clearHandle-29052.test.tsx(new), 5 cases: dropdownclear()(defaults included, unheld fields reported); a chip picked after a clear applies alone; toggleclear(); tabs set no handle; without a ref the bar restores its selection and the chip × clears in place as before.ListView.emptyStateClearFilters-28908.test.tsx: its last case pinned the chip-only list offering no clear action, the behaviour this card changes; it is removed and the header points at the new file, which pins the replacement.Acceptance notes
showAllRecords: falsethere is no unfiltered tab), so it is left to the view's presets. Seat 1's List personalization: one visible 「已个性化」 marker and one reset that restores the whole list — today two reset entries cover different parts and neither says what changed #28950 (branchclaude/project-thread-o05ndx, unmerged at writing) counts the tab's conditions among its "filters" personalization part and resets them through its ownuf_*strip and remount; the two paths do not touch each other.selectionsprop. The card allowed either. A controlled selection would need the host to know the bar's effective selection, which it does not: author defaults, chips derived after the definition loads, and the late type coercion all settle inside the bar and are deliberately not echoed to the host. A host clearing to{}would then miss a default-only chip. The handle clears whatever the bar holds, and the declaration face still widens (one optional prop, one exported type), so the claim'sClause-②: yes (widening)stands.UserFilters' host props (git grepofonSelectionsChange,initialSelectionsandUserFiltersPropsovercontent/and the package README: zero hits), so none is updated.Verification
Readings on
809862b9(the change).77aa59cdmergesorigin/main(clean; the incoming commits touch no plugin-list file and none of theuf_*host files, so no re-check was owed by AGENTS.md multi-agent §10).Tests
UserFilters.test.tsx: 4 files, 31/31 passed.packages/plugin-listin full, in two locked batches at--maxWorkers=2: 92 files 659 tests, and 92 files 1098 tests, all passed (184 files).git grep -lofUserFilters,ListEmptyState,empty-state-clear-filters,filter-clear-,userFilterSelections,onUserFilterSelectionsChangeoutside plugin-list: app-shellObjectDataPage.relationUserFilters-13310,ObjectView.ufViewSwitch-11992,userFilterUrlState.listState-11860, plugin-grid's two empty-state files, plugin-form, plugin-view, types' two parity files): 9 files, 387 tests passed, after building the app-shell closure (64/64 tasks).pnpm --dir ui exec vitest run scripts/: 169 files passed, 2 skipped; 4900 tests passed.scripts/ablation-replace.mjsreplaceduserFiltersRef.current?.clear();inhandleClearReaderQuerywith a no-op (void userFiltersRef;). The new ListView file went 3 failed / 1 passed (the three chip-clear cases red; the preset-tab case green, as it pins no clear). The restore leg read blob == HEAD and an emptygit diff HEAD.Type-check, lint
turbo run type-check --filter=@object-ui/plugin-list: 50/50 tasks (it covers the test files throughtsconfig.test.json).as anydata-source casts in the new ListView test, the pattern the List empty state: when the reader's own filter or search leaves no rows, offer 「清除筛选」 — today the copy says to clear them and the only button is 「添加记录」 #28908 file uses, and pre-existing ones).node scripts/check-sdui-manifest.mjs: exit 0 (no component config changed).pnpm check:new-line-citationsunderui/: 0 new citations.Derived gates
node scripts/pm/dispatch-gates.mjs --ran: 34 derived families accounted for, 34 run, 0 NOT MEASURED, every one exit 0 (check:type-check-debtunder the verify lock). The PR-body gates (check-changeset-no-major,check-closing-keyword-parity,check-partof-closing-keyword,check-adr-0087-registration,check-empty-changeset) ran on this body: exit 0 on each.🤖 Generated with Claude Code
https://claude.ai/code/session_01QsR2jULUUCcok2FGYwt6Gh
Generated by Claude Code