Repository navigation
Commit 14208f2
fix(core): a row menu's url action reads the row's ctx.recordId and ctx.record (#29127)
<!-- claude-projects-attribution: {"github_login":"zhuangjianguo"} -->
_Requested by **Steedos** · [project
thread](https://claude.ai/code/project/chan_018ETKaNwMZCNRYD7D8Q9Ubb?thread=cmsg_018ETKaNwMZCNRYD7D8Q9UbbN1EehYQ4h5a3GavPuVMmrb)_
Fixes #28995
Clause-②: no
Metadata-protocol: none
Declarations as set by the claim (card comment 6571768622) and its
amendment (card comment 6571831284), which fixes the file surface:
`ActionRunner.ts`, the new test beside it and a root changeset.
## 维护者速读
- **改了什么**:列表视图行菜单(`list_item`)里的 `type: 'url'` 动作,目标地址中的
`${ctx.recordId}` 与 `${ctx.record.*}`
现在取被点开的那一行;此前在行菜单里插值为空,在记录头部菜单里正常。同一规则也作用于行菜单里 `type: 'api'` 动作的 target
和 `onSuccess.navigate` 跳转。`${ctx.selection.ids}` 不变,仍是列表勾选集。
- **为什么改**:duly 在 `sys_user` 上声明的「打开成员页」url 动作,从行菜单点击落到
`/apps/…/sys_user/record/page/duly_member`(缺记录 id),只能退回仅在记录头部提供。规范把
`ctx.recordId` 写成 url 动作可用的上下文,一行就是一条记录的上下文。
- **风险与回滚**:低。只改动作运行器拼装 `${ctx.*}`
插值作用域的一处:分发携带行(`params._rowRecord`)时,行绑定为 `ctx.record`,其 `id` 为
`ctx.recordId`,且压过页面级记录(相关列表行在记录页上读子行)。不带行的分发与此前逐字节相同。回滚即还原本 PR。
- **先看**:`ui/packages/core/src/actions/ActionRunner.ts` 的
`buildInterpolationContext`(末尾新增的行绑定);测试
`ui/packages/core/src/actions/__tests__/ActionRunner.rowTargetContext.test.ts`。
- **两分钟验证**:`pnpm --dir ui exec vitest run
packages/core/src/actions/__tests__/ActionRunner.rowTargetContext.test.ts`,预期
1 个文件、5 个测试全绿。
- **需要你做的**:无,席位落地。
## What changes
- **One rule for the row, now on targets too.** A list view's row menu
(`ObjectGrid`'s row actions) dispatches a `list_item` action as `{
...def, params: { _rowRecord: row } }` through the page's action runner,
whose context names the page, not the row.
`ActionRunner.buildInterpolationContext`, the `${ctx.*}` scope behind
`interpolateTarget`, read that context only, so on a list page
`${ctx.recordId}` and `${ctx.record.*}` printed empty. It now takes the
action and, when the dispatch carries a row, binds `ctx.record` to the
row and `ctx.recordId` to its `id`, after the `ctx` mirror
`ActionProvider` publishes, so the row wins over a page-level record.
This is the rule `templateEvaluatorFor` already applies to a row
action's copy templates (objectui#12967), via the same `dispatchRow`
helper.
- **Reach.** `interpolateTarget` serves `type: 'url'` targets (and the
`navigation` alias), `type: 'api'` targets before a registered api
handler receives them, and the `onSuccess.navigate` hop, so all three
read the row from a row menu.
- **Unchanged.** `ctx.selection.ids` / `ctx.selection.count` stay the
grid's checked selection: opening a row's menu does not select the row.
A dispatch with no row reads exactly what it read before. No other row
surface or host was touched; `ListView.tsx` and `plugin-grid` are not in
the diff.
## Tests
- New `ActionRunner.rowTargetContext.test.ts` drives the same `type:
'url'` action through two doors: a runner carrying `record` (flat and in
the `ctx` mirror, as `ActionProvider` publishes it) and a list-page
runner with the row stashed as `ObjectGrid` stashes it. It pins
`${ctx.recordId}` and `${ctx.record.FIELD}` filled identically on both
doors, the row winning over a page-level record, `${ctx.selection.*}`
staying the checked selection under a row dispatch, and a CONTROL with
no row on a list page still reading no record.
## Acceptance notes
- **Live browser not measured.** The pin runs the real runner in vitest
with a recording navigation handler; the console's own url path is that
same runner (`useConsoleActionRuntime` registers no url handler), so the
click on a live row menu goes through the changed function.
- **Selection by design.** The card names `${ctx.selection.ids}` among
the empty holes. In a row menu with nothing checked, empty is the
documented answer (`view.zod` documents it as the toolbar selection,
empty when nothing is selected), so it is left as is.
## Verification
<details><summary>New pin · 5/5 here, 3 red against main's
runner</summary>
`pnpm --dir ui exec vitest run --maxWorkers=2
packages/core/src/actions/__tests__/ActionRunner.rowTargetContext.test.ts`
under `os-verify-lock` at `45dbb5d8`: Test Files 1 passed (1), Tests 5
passed (5).
Reverse check at `45dbb5d8`: `ActionRunner.ts` restored from
`origin/main` (`5d3667e2`; the row binding's count on disk 0), same
command: Tests 3 failed, 2 passed. The `${ctx.recordId}`,
`${ctx.record.FIELD}` and row-over-page cases failed; the selection case
and the CONTROL passed, as designed. Restored with `git checkout HEAD
--`; blob hash back to `76bfe046`, `git diff HEAD` empty.
</details>
<details><summary>Core package, type check and lint</summary>
- `pnpm --dir ui exec vitest run --maxWorkers=2 packages/core/`
(locked): Test Files 230 passed (230), Tests 4236 passed, 27 skipped.
- `turbo run type-check --filter=@object-ui/core` (`tsc --noEmit && tsc
-p tsconfig.test.json`): exit 0.
- `eslint` on the two changed `.ts` files: 0 errors, 15 warnings, all on
lines outside this diff's hunks.
- `pnpm --dir ui run check:new-line-citations`: 0 new citations.
- No other ui test file interpolates `${ctx.recordId}` or
`${ctx.record.*}` through a row dispatch (`git grep` over ui test files:
four app-shell files name the holes, none with a row stash); a dispatch
without a row reads exactly as before. CI's Test UI runs the rest.
</details>
<details><summary>Derived gates</summary>
`node scripts/pm/dispatch-gates.mjs --commands` derived 34 commands
against merge base `5d3667e29` at head `45dbb5d8`. All 34 ran and exited
0, including `check-sdui-manifest.mjs` and its `--self-test`,
`check:type-check-debt`, `check:nul-bytes`,
`check:cross-package-test-inputs`, `check:test-source-alias` and `pnpm
--dir ui exec vitest run scripts/` (Test Files 169 passed, 2 skipped;
Tests 4900 passed, 2 skipped). `dispatch-gates --ran` reconciled 34
derived, 34 run, 0 NOT-MEASURED, 0 UNRUN. `origin/main` moved 5 commits
since the base, none touching `ActionRunner.ts`.
</details>
---
_Generated by [Claude
Code](https://claude.ai/code/session_01QsR2jV25i2Lc1VE2P98L8u)_
Co-authored-by: Claude <noreply@anthropic.com>1 parent af8448f commit 14208f2
3 files changed
Lines changed: 119 additions & 4 deletions
File tree
- .changeset
- ui/packages/core/src/actions
- __tests__
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2997 | 2997 | | |
2998 | 2998 | | |
2999 | 2999 | | |
3000 | | - | |
| 3000 | + | |
| 3001 | + | |
| 3002 | + | |
3001 | 3003 | | |
3002 | 3004 | | |
3003 | 3005 | | |
| |||
3059 | 3061 | | |
3060 | 3062 | | |
3061 | 3063 | | |
3062 | | - | |
| 3064 | + | |
3063 | 3065 | | |
3064 | 3066 | | |
3065 | 3067 | | |
| |||
3068 | 3070 | | |
3069 | 3071 | | |
3070 | 3072 | | |
3071 | | - | |
3072 | | - | |
| 3073 | + | |
| 3074 | + | |
| 3075 | + | |
| 3076 | + | |
| 3077 | + | |
| 3078 | + | |
| 3079 | + | |
| 3080 | + | |
| 3081 | + | |
| 3082 | + | |
| 3083 | + | |
| 3084 | + | |
| 3085 | + | |
3073 | 3086 | | |
3074 | 3087 | | |
3075 | 3088 | | |
| |||
3102 | 3115 | | |
3103 | 3116 | | |
3104 | 3117 | | |
| 3118 | + | |
| 3119 | + | |
| 3120 | + | |
| 3121 | + | |
| 3122 | + | |
3105 | 3123 | | |
3106 | 3124 | | |
3107 | 3125 | | |
| |||
| 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 | + | |
0 commit comments