Skip to content

fix(plugin-detail): an empty body table of the record page keeps its header instead of folding into one line that reads the empty sentence before its title - #30378

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-eh0f41
Nov 5, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-eh0f41

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Bill · project thread
domain:ui#2 · os-bill · session_01Utp6Cb5R6JDdfyvtj3dPR2

Fixes #29944

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:记录页 v3 详情栏里的正文表格(body table)不再参与关联列表的「空列表折叠」;空的正文表格保留自己的表头(标题、0、新建),「暂无相关记录」画在表头下方、表格本该在的位置。
  • 为什么改:正文表格和兄弟区块同处详情栏一个容器,record page: the Related tab gives an empty related list ("客户 0 暂无相关记录") a full row, and record:path squeezes two terminal stages ("× 已取消 客户未到") into one cell at the right edge #25895 的折叠按「同一 DOM 父元素」认栈,把详情栏当成了关联栈,空表被收成一行「暂无相关记录 偏离 + 新建」,空态文字跑到表名前面(hotclm 合同页实测)。
  • 风险与回滚:只动 plugin-detail 三个渲染文件与一个测试文件,不动 spec、服务端或元数据;关联页签的折叠与右栏空面板行不变(只按页面的正文表格 id 集合判定)。回滚即 revert 本 PR。
  • 先看:ui/packages/plugin-detail/src/renderers/record-related-list.tsx(bodyTable 判定与 foldable)→ ui/packages/plugin-detail/src/RelatedList.tsx(空正文表格的表头与表头下空态)→ 测试 record-related-list.emptyFold-25895.test.tsx 末尾新 describe。
  • 两分钟验证:pnpm --dir ui exec vitest run packages/plugin-detail/src/renderers/__tests__/record-related-list.emptyFold-25895.test.tsx,预期 8/8 通过(在 main 源码上新用例失败:折叠行出现)。
  • 需要你做的:无,席位落地。

What changes

On the record page v3 (RecordPageColumns, and the tab-strip page whose Details tab carries the body tables), the record's body tables are drawn as siblings of the details sections in one container. The related-list empty fold (RelatedListEmptyFold.tsx, #25895) keys a stack by the DOM parent of each record:related_list root, so it took the details column for a Related tab stack: once one body table had rows, an empty one left its place and was drawn as one line after the last table, reading the empty sentence first (「暂无相关记录 偏离 + 新建」).

  • A body table never folds. record-related-list.tsx reads RecordBodyTablesContext (the page's body-table relationship ids, already provided by the record page host) and, outside the related column's panels, a list whose object.field id is in the set answers foldable: false to its stack. The body tables and the Related tab are a partition of the page's relationships (buildDefaultPageSchema: related.filter(isBody) vs the rest), so no Related tab list is in the set, and the fold rule itself (decideEmptyFold) is untouched.
  • An empty body table keeps its siblings' shape. In RelatedList's card face, a body table no longer writes 「暂无相关记录」 inline in its header row; while expanded and empty it draws the sentence in the card body under the header (a dashed box where the table would be, data-body-table-empty). The header keeps title, count 0, ⤢ and New. Lists that are not body tables keep the inline sentence. The card header gains a data-related-list-head attribute (the panel face already has data-related-panel-head).
  • RelatedListEmptyFold.tsx: the file header names the body-table exception; no code change there.

No new authoring key, no spec change, no change to the Related tab or the related column.

Tests

  • New top-level describe in ui/packages/plugin-detail/src/renderers/__tests__/record-related-list.emptyFold-25895.test.tsx (the fold's shared harness; it now takes an optional body-table set):
    • an empty body table beside tables with records: no fold line, its card visible, its header carries count 0 and a New that creates its own object, the empty sentence is not in the header and follows it in document order;
    • CONTROL: the Related tab stack (the existing ACCOUNT_STACK) under a body-table context that lists another relationship folds exactly as before.
  • Red on main's sources (the three source files restored to 13c0c5ba, test file from this branch): Tests 1 failed | 7 passed (8), the new case failing on expect(fold()).toBeNull(): the fold line (a div with two children) was drawn.
  • Green: the 116 plugin-detail test files that name RelatedList, record:related_list, related-list, RecordPageColumns or the empty sentence, plus three app-shell files that assert on the empty sentence or the New button: Test Files 118 passed | 1 skipped (119), Tests 1016 passed | 8 skipped (1024).
  • Ablation, three legs through scripts/ablation-replace.mjs, each landing verified and restored to the HEAD blob (tests import src, no build involved):
    1. !bodyTable && dropped from foldable → new case red (fold line drawn);
    2. the inline sentence drawn for body tables again → new case red (sentence inside the header);
    3. the under-header block disabled → new case red (no sentence found).
      A first attempt at leg 1 was refused by the tool before any run (its replacement text already occurred in the file), then re-run with a distinct replacement.
  • pnpm exec turbo run type-check --filter=@object-ui/plugin-detail --concurrency=2: 52/52 tasks.
  • pnpm lint in ui/packages/plugin-detail: 0 errors; per-file warning counts on the three source files equal main's (102 / 17 / 1), the test file 0.

Acceptance notes

  • The card's Expected says the empty state goes under the header, "the shape the non-empty tables and the pre-18.3 record:related_list both draw". Measured on the published @object-ui/plugin-detail@18.2.0: the pre-18.3 empty card wrote the sentence inline in its header row (title, 0, sentence, New), not under it. This PR follows the first half of the Expected (under the header, as the non-empty tables place their table) and keeps the inline sentence for every list that is not a body table. The second symptom on the card, 「合同 0 暂无相关记录」, is that inline header line on a body table; it is covered by the same change.

Verification

Derived gates (dispatch-gates --commands, final tree)

Head 4b04fd97 (after merging origin/main at 8589756a). Every command's exit code was captured before any pipe.

node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0
node scripts/check-adr-0087-registration.mjs --self-test :: exit 0
node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0
node scripts/check-closing-keyword-parity.mjs :: exit 0
node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0
node scripts/check-comment-mask-corpus.mjs :: exit 0
node scripts/check-empty-changeset.mjs --base origin/main :: exit 0
node scripts/check-empty-changeset.mjs --self-test :: exit 0
node scripts/check-sdui-manifest.mjs :: exit 0
node scripts/check-sdui-manifest.mjs --self-test :: exit 0
node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0
node scripts/release-pending-publish.mjs --self-test :: exit 0
pnpm --dir ui exec vitest run scripts/ :: exit 0
pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0
pnpm check:changeset-gate-self-tests :: exit 0
pnpm check:closed-ceremony-text :: exit 0
pnpm check:cross-package-test-inputs :: exit 0
pnpm check:dispatcher-error-vocabulary :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:engine-double-contract :: exit 0
pnpm check:gitlink-declared :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:published-files :: exit 0
pnpm check:query-options-erasure :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:registry-log-declared :: exit 0
pnpm check:rest-log-spy-declared :: exit 0
pnpm check:skill-identifier-liveness :: exit 0
pnpm check:test-source-alias :: exit 0
pnpm check:tier-file-adoption :: exit 0
pnpm check:type-check-coverage :: exit 0
pnpm check:type-check-debt :: exit 0
pnpm check:undeclared-dep-imports :: exit 0
pnpm check:watch-hint-literal :: exit 0
pnpm check:where-matcher :: exit 0

dispatch-gates --ran: Run reconciliation — 35 derived, 35 run, 0 NOT-MEASURED, 0 UNRUN (a derived zero: all 35 recorded an exit code).

check-sdui-manifest: sdui.manifest.json is present and intact (… 106 components).

Standing ui gates
  • pnpm --dir ui check:new-line-citations: VERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only -> exit 0.

Generated by Claude Code

…header instead of folding into the stack's empty line

A body table (its relationship id in RecordBodyTablesContext) shares the
details column with its siblings, so the related-list empty fold took the
column for a stack and folded the empty one into one line that read the
empty sentence before the table's title. A body table now answers
foldable: false, and an empty one draws its empty sentence under its
header, where the table of a non-empty one is.

Claude-Session: https://claude.ai/code/session_01Utp6CXft9FMzbd2ggz2oyp
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Nov 5, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#2 · os-bill · session_01Utp6Cb5R6JDdfyvtj3dPR2

Waits-on: main-red #30367

Lint UI on 4b04fd97 fails in @objectstack/spec check:liveness (the query/searchFields row cites engine.ts, which no longer names the key after #30341). This PR touches only ui/packages/plugin-detail and a changeset, so the red is main's, not this diff's: the p0 anchor is #30367 and its fix is PR #30368. Once #30368 merges, this branch merges origin/main and CI re-runs; the seat ACCEPTs on that head.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#2 · os-bill · session_01Utp6Cb5R6JDdfyvtj3dPR2

维护者速读

席位核对 — ACCEPT · domain:ui#2 · head 046d04c5ca17a319f8e6c8a6cbaf1a866f64b543

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

1 participant