Repository navigation
Commit e7dda2c
fix(plugin-dashboard): a viewer whose every tile is refused sees one no-access panel, not a blank canvas (#27777)
domain:ui#2 · os-bill · session_01Utp6CY2n1JmZZKrHgtp9jC
<!-- claude-projects-attribution: {"github_login":"os-bill"} -->
_Requested by **Bill** · [project
thread](https://claude.ai/code/project/chan_016iMXWSZDzqWRWvWzdNo8wg?thread=cmsg_016iMXWSZDzqWRWvWzdNo8wgP6XkfpjCfyzzG2KwQPUZfy)_
Fixes #27491
Clause-②: no
Metadata-protocol: none
## 维护者速读
- 改了什么:观看者在仪表盘上的每个卡片都被服务器以读权限拒绝(403 /
PERMISSION_DENIED)时,画布里显示一个「无权访问」面板(锁图标 + 标题 +
一句说明),不再是标题下一片空白;部分卡片被拒、设计模式、非权限错误的表现都不变。
- 为什么改:CRM 的 Pipeline 仪表盘六个卡片共用一个数据集,普通成员打开只看到标题和「Refresh All」;维护者裁决
A(6508997538):保留逐卡隐藏,什么都不剩时说一次。
- 风险与回滚:只动渲染层和十个语言包,不动 spec、服务端、DatasetWidget、DashboardView;403
仍是唯一判据。回滚即 revert 本 PR。
-
先看:`ui/packages/plugin-dashboard/src/DashboardRenderer.tsx`(`nothingLeftForViewer`
与 `allTilesRefusedPanel`,桌面和移动两处挂载)→ 新测试
`DashboardRenderer.allTilesRefused-27491.test.tsx` →
`ui/packages/i18n/src/locales/en.ts` 新键。
- 两分钟验证:`pnpm --dir ui exec vitest run
packages/plugin-dashboard/src/__tests__/DashboardRenderer.allTilesRefused-27491.test.tsx`,预期
5/5 通过。
- 需要你做的:无,席位落地。
## What changes
Outside design mode, a dataset tile whose query the server refuses as a
read refusal is left out of the dashboard for that viewer (the rule the
spec states in the dashboard widget docblock). When that leaves nothing
to draw, the viewer saw the dashboard header over an empty canvas, with
no message. Ruling-ref: 6508997538 (maintainer ruling A).
- `DashboardRenderer` renders its tiles once per layout (`desktopTiles`;
`metricTiles` and `otherTiles` in the mobile stack) and asks
`nothingLeftForViewer(tiles)`: not design mode, at least one refused
tile, at least one widget, and every rendered tile is `null`. The only
`null` a tile renders is the refusal drop, so the trigger is exactly
"every widget was left out as refused". A dashboard that still has any
drawable widget (a non-dataset widget, a readable tile, a tile that
failed for another reason) draws it and no panel.
- The panel is the `Empty` composition the console draws for a dashboard
the viewer may not open (`dashboard-access-denied` in `DashboardView`):
lock icon, title `dashboard.widgetForbiddenTitle`, and one new message
`dashboard.allWidgetsForbiddenMessage`. It carries
`data-testid="dashboard-all-widgets-forbidden"` and spans the full grid
(`col-span-full`). The header, its description and the refresh control
are untouched.
- `@object-ui/i18n`: `dashboard.allWidgetsForbiddenMessage` in all ten
locales, beside `widgetForbiddenMessage`. en: "You don’t have permission
to view the data behind this dashboard." (the typographic apostrophe the
neighbouring `dashboard.*` strings use).
## Tests
New
`ui/packages/plugin-dashboard/src/__tests__/DashboardRenderer.allTilesRefused-27491.test.tsx`
(5 tests), with the refusal shape `AnalyticsForbiddenError` carries
(`httpStatus: 403`, `code: 'PERMISSION_DENIED'`):
- every tile refused: the panel with both strings, both tiles queried,
no tile title, no per-tile no-access face;
- the same in the mobile layout (non-vacuity: the 2-column metric grid
is drawn);
- partial refusal: no panel, the readable tile present, the refused ones
gone;
- CONTROL design mode, every tile refused: no panel, each tile's
objectui#10899 face present;
- CONTROL a 400 on every tile: no panel, every tile and its error alert
present.
`DashboardRenderer.refusedTile-26971.test.tsx` and
`DatasetWidget.forbiddenState-10899.test.tsx` pass unchanged.
<details><summary>Readings</summary>
- Premise (before the ruling, report 6508422494): the real
`examples/app-crm` `pipeline_dashboard` through the real
`ObjectStackAdapter` with fetch answering 403 `PERMISSION_DENIED`
rendered an empty canvas; ablating the refusal drop brought the six
frames back. Scratch test, never committed.
- Ablation of this change, through `scripts/ablation-replace.mjs` on
commit 76be307: the trigger's `tiles.length > 0 && tiles.every((tile)
=> tile == null)` replaced by `false` (anchor 1 → 0, blob c9adcbb →
eb6c069cbd78): the two all-refused tests red, the three others green (2
failed / 3 passed); restored, blob == HEAD c9adcbb, `git diff HEAD`
empty. The test imports the renderer by relative path, so no dist leg.
- After merging `main` (head 24bc0b9), closure `pnpm --filter
'@object-ui/plugin-dashboard^...' build` VERDICT command-exit 0; `pnpm
--dir ui exec vitest run --maxWorkers=2 packages/plugin-dashboard/
packages/i18n/`: Test Files 286 passed (286), Tests 3266 passed, 19
skipped; lock VERDICT command-exit 0.
- Gates: see the os-dev-report on #27491.
</details>
## Acceptance notes
- The trigger is "nothing left to draw", not "every dataset tile
refused", per the ruling: a dashboard that keeps any other widget draws
no panel.
- A refused tile is still not re-queried until remount; "Refresh All"
stays as #27065 left it (ruling item 2).
- Option C of the ruling (the CRM app declaring `requiredPermissions` on
`pipeline_dashboard` and its nav item) is a separate app-lane card, not
this PR.
## Verification
`pnpm --dir ui exec vitest run
packages/plugin-dashboard/src/__tests__/DashboardRenderer.allTilesRefused-27491.test.tsx
packages/plugin-dashboard/src/__tests__/DashboardRenderer.refusedTile-26971.test.tsx
packages/plugin-dashboard/src/__tests__/DatasetWidget.forbiddenState-10899.test.tsx`
→ 3 files, 11 tests passed.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01Utp6CXTsLkvW2GpvDwNQre
---
_Generated by [Claude
Code](https://claude.ai/code/session_01Utp6CXTsLkvW2GpvDwNQre)_
Co-authored-by: Claude <noreply@anthropic.com>1 parent 946395d commit e7dda2c
13 files changed
Lines changed: 227 additions & 5 deletions
File tree
- .changeset
- ui/packages
- i18n/src/locales
- plugin-dashboard/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 | + | |
| 17 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1612 | 1612 | | |
1613 | 1613 | | |
1614 | 1614 | | |
| 1615 | + | |
1615 | 1616 | | |
1616 | 1617 | | |
1617 | 1618 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1421 | 1421 | | |
1422 | 1422 | | |
1423 | 1423 | | |
| 1424 | + | |
1424 | 1425 | | |
1425 | 1426 | | |
1426 | 1427 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1806 | 1806 | | |
1807 | 1807 | | |
1808 | 1808 | | |
| 1809 | + | |
1809 | 1810 | | |
1810 | 1811 | | |
1811 | 1812 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1471 | 1471 | | |
1472 | 1472 | | |
1473 | 1473 | | |
| 1474 | + | |
1474 | 1475 | | |
1475 | 1476 | | |
1476 | 1477 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1467 | 1467 | | |
1468 | 1468 | | |
1469 | 1469 | | |
| 1470 | + | |
1470 | 1471 | | |
1471 | 1472 | | |
1472 | 1473 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1421 | 1421 | | |
1422 | 1422 | | |
1423 | 1423 | | |
| 1424 | + | |
1424 | 1425 | | |
1425 | 1426 | | |
1426 | 1427 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1421 | 1421 | | |
1422 | 1422 | | |
1423 | 1423 | | |
| 1424 | + | |
1424 | 1425 | | |
1425 | 1426 | | |
1426 | 1427 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1466 | 1466 | | |
1467 | 1467 | | |
1468 | 1468 | | |
| 1469 | + | |
1469 | 1470 | | |
1470 | 1471 | | |
1471 | 1472 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1521 | 1521 | | |
1522 | 1522 | | |
1523 | 1523 | | |
| 1524 | + | |
1524 | 1525 | | |
1525 | 1526 | | |
1526 | 1527 | | |
| |||
0 commit comments