Skip to content

fix(plugin-list): every export entry in the list Display popover stays inside the panel - #29252

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-mccp47
Nov 2, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-mccp47

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Steedos · project thread

Fixes #29175

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:列表「显示」弹窗底栏里的导出按钮单独占一行,且这一行可以换行;「重置为视图默认」和刷新按钮留在上一行。
  • 为什么改:视图提供 CSV、XLSX、JSON 三种导出时,刷新按钮和三个导出按钮挤在一个不换行的组里,比弹窗宽,「Export as JSON」被裁到弹窗外,窄窗口下直接出屏,用户看不到 JSON 导出。
  • 风险与回滚:只改一个组件底栏的布局类名,导出行为、文案、权限门控和 test id 都不变,弹窗宽度不变;回滚即还原 ViewSettingsPopover.tsx 这一处。
  • 先看:ui/packages/plugin-list/src/components/ViewSettingsPopover.tsx 的 footer(display-footer 下的 display-export-group),再看新测试 ViewSettingsPopover.exportFooter-29175.test.tsx。
  • 两分钟验证:showcase 以管理员登录,打开 Tasks › In Progress 列表,点「显示」按钮:底栏第一行是重置和刷新,下面是 CSV、XLSX 一行、JSON 换到下一行,三个按钮都在弹窗内且可点。
  • 需要你做的:无,席位落地。

What changes

The Display popover's footer no longer puts Refresh and the export entries in one non-wrapping group.

  • Chosen shape: the export entries get their own row. The footer keeps Reset (left) and Refresh (right, ml-auto) on its first row; the export entries sit in a new full-width row (basis-full, test id display-export-group) below it.
  • That row is itself flex-wrap, because three "Export as …" entries (about 400 px together) are wider than the panel's 304 px content box at sm:w-80, so on its own the row still could not hold them. With CSV, XLSX and JSON, CSV and XLSX share the first export line and JSON wraps to the next, right-aligned like the old group.
  • Unchanged: the popover width, the export behaviour (onExport(format)), the list.exportAs labels, the export grant gating (the host still decides whether onExport and exportFormats are passed), and every existing test id (display-footer, display-reset-button, refresh-button, display-export-FORMAT). No i18n key, no metadata key, no gate.

Tests

  • New ui/packages/plugin-list/src/__tests__/ViewSettingsPopover.exportFooter-29175.test.tsx (3 cases): renders the popover with CSV, XLSX and JSON and asserts every entry is reachable by role ("Export as CSV/XLSX/JSON"), is the display-export-FORMAT element, sits inside display-export-group, which carries flex flex-wrap basis-full, and calls onExport with its format; Refresh and Reset stay on the footer's first row, outside the export row, and the export row holds the export entries only; no export row is drawn when no format is offered.
  • Ablation: with main's ViewSettingsPopover.tsx (ea150b7) put back on disk, the first two cases go red (2 failed, 1 passed); restored by git checkout HEAD, hash-matched to HEAD's blob.
  • Full packages/plugin-list/ suite: see Verification.

Acceptance notes

  • Phone width (375 px): the popover is w-[calc(100vw-2rem)], 343 px. Measured in a real browser, the footer fits (scrollWidth 341 = clientWidth 341) and all three entries are inside the panel; before the fix the footer's scrollWidth was 440 against 341 and "Export as JSON" ran to x 441 on a 375 px viewport. Nothing else in the footer overflows there.
  • The measurement used a temporary Vite entry under ui/apps/console that renders the real component (before = main's file, after = this branch) with the Console stylesheet, not a showcase boot; it was deleted afterwards and is not in the diff. Its "before" numbers at 1440x900 match the card's own measurement exactly (popover 1073..1393, footer 440 vs 318, JSON 1380..1514).

Verification

Bounding boxes in Chromium, Display popover open, CSV/XLSX/JSON offered, Reset and Refresh present:

Viewport Popover x Footer scrollWidth / clientWidth Export as CSV Export as XLSX Export as JSON
1440x900 before 1073..1393 440 / 318 1114..1240 inside 1244..1376 inside 1380..1514, outside, not hit-testable
1440x900 after 1073..1393 318 / 318 1122..1248 inside 1252..1384 inside 1250..1384 (second line) inside
1024x768 before 657..977 440 / 318 inside inside 964..1098, outside
1024x768 after 657..977 318 / 318 inside inside 834..968 inside
375x812 before 0..343 440 / 341 inside inside 307..441, past the viewport
375x812 after 0..343 341 / 341 inside inside 200..334 inside

"Inside" means the button's box lies within the popover's box; every "after" entry is also the topmost element at its own centre (hit-tested with elementFromPoint).

Local runs (head fb87f20 unless noted)
  • pnpm --dir ui exec vitest run --maxWorkers=2 packages/plugin-list/ under os-verify-lock: 183 files, 1752 tests passed on fb87f20 (merge of main); 183 files, 1750 tests on 9ec8995 before the merge. VERDICT command-exit 0 both times.
  • pnpm exec turbo run type-check --filter=@object-ui/plugin-list (tsc --noEmit && tsc -p tsconfig.test.json, so the new test is compiled): 50 tasks, exit 0.
  • pnpm --dir ui exec vitest run scripts/: 169 files passed, 2 skipped; exit 0.
  • dispatch-gates --commands derived 34 families for this diff; all 34 ran with exit 0 (--ran reconciliation: 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN). The PR-body gates (check-changeset-no-major, check-adr-0087-registration, check-closing-keyword-parity, check-partof-closing-keyword) ran against this exact body. The ratchet families (check:type-check-coverage, check:type-check-debt, check:query-options-erasure, check:test-source-alias, check:published-files) were re-run on fb87f20, exit 0.
  • node scripts/check-sdui-manifest.mjs: exit 0 (no registry change).
  • Diff size: 3 files, +145 / -19, under the 3000-line threshold.

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic. (not touched)
  • 2. Every name I added uses the casing for its kind.
  • 3. No formatter ran; pnpm lint passes. (CI's Lint UI is the authority)
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset.
  • 6. packages/spec touched: its generated artifacts are regenerated and committed. (not touched)
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English (the 维护者速读 block is Chinese by the repo's os-dev contract).
  • The Before Submitting list in CONTRIBUTING.md is done.

Generated by Claude Code

…g row of their own

Refresh and the export entries shared one non-wrapping group in the
popover footer; with three formats it outgrew the fixed-width panel and
the last entry spilled past its edge. The entries now take a full-width
row that wraps, and Refresh stays on the first row beside Reset.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsR2jRbeD6sscgnYJiqhny
@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 2, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#4 · zhuangjianguo · session_01QsR2jR7ZFkpqmm4U8S1f9y

席位核对 · ACCEPT · 2026-11-02T10:35Z

Head-sha: fb87f20e

  • Shape: draft → main, Fixes #29175 in the body only. 3 files, +145 / −19 (164 lines ≤ 3000). No governed path: check-governed-merges --pr 29252 hit 0 of 3 paths. Author objectstack-fleet[bot]. Clause-②: no and Metadata-protocol: none are line-initial in the body and the changeset. No contract review is owed.
  • File surface: ui/packages/plugin-list/src/components/ViewSettingsPopover.tsx, its new test file, and the changeset, exactly the claim. main was merged after feat(ui)!: a record opened from a list opens in the drawer by default, and the split pane retires #29108, and no open PR touches the file.
  • Read in the diff:
    • Refresh leaves the shared wrapper and takes ml-auto itself, so Reset and Refresh stay on row 1.
    • The export entries move into a basis-full flex-wrap justify-end group (data-testid="display-export-group"). The footer is already flex flex-wrap, so the group takes a row of its own and wraps inside it.
    • Labels, onExport, grant gating, the panel width and the existing test ids are unchanged, and there is no new i18n key.
  • Deviation, accepted: the card offered "wrap" or "own row". The PR does both, because three entries (about 400 px) do not fit one 304 px row. The PR body names this.
  • Run record (in the body): at 1440×900, before, JSON spans 1380 to 1514 px, past the popover edge at 1393 px (footer content 440 px in 318 px). After, all three are inside (318 / 318). At 375 px the footer fits (341 / 341).
  • Tests: the new exportFooter-29175 file passes 3 of 3, and 2 of 3 go red with main's file. The plugin-list suite passes 183 files, 1752 tests. 34 of 34 derived gates exited 0.
  • Gates (check-runs on this head): 44 success, 11 skipped, 0 failed or pending, including all 16 Test UI shards. All ten required contexts succeeded, or were skipped by their path filter (Build Core).
  • Landing: ordinary queue landing, pr_ready then automerge_enable. Fixes #29175 closes the card on merge.

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 2, 2026 10:37
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge November 2, 2026 10:37
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 2, 2026
Merged via the queue into main with commit c2a5be0 Nov 2, 2026
56 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-mccp47 branch November 2, 2026 11:29
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

Development

Successfully merging this pull request may close these issues.

List Display popover: with three export formats the footer overflows and "Export as JSON" is clipped outside the popover

1 participant