Uh oh!
There was an error while loading. Please reload this page.
fix(detail): lookup field values link to the referenced record (#4336) - #4489
Merged
Conversation
A valued lookup on a record detail page rendered as plain text plus a copy
button — the referenced document's name was visible and unreachable, so users
copied the number and searched for it from the list page instead. Lookup cells
in a related list that pointed at a third object were dead the same way.
Measurement put both halves on ONE path: `DetailSection`'s read branch and
`RelatedList.makeCell` each resolve the cell through `getCellRenderer('lookup')`
→ `LookupCellRenderer`, so the affordance goes there, once.
The URL is not assembled in the renderer, which has no router and no business
knowing the console's route shape. `RelatedRecordActionsContext` — already the
channel the related list's row navigation uses — gains an optional
`recordHref` / `openRecord` pair, published by `RelatedRecordActionsBridge`
from the SAME builder `onView` navigated with (its inner `detailUrl` hoisted,
`?from=` trail included). One record-route shape, not a second one. A host
that provides neither (Studio designer, embedded renderers, standalone grids)
renders exactly what it rendered before.
A real `<a href>` rather than a click handler, so middle-click / ⌘-click /
"copy link address" work; a plain left click is handed to the host so
navigation stays in-app, and the click never reaches the row underneath (which
would otherwise also fire the detail row's copy or the related row's own open).
Red-first: pre-fix the value region contains no `<a>` (the reporter's own DOM
check) on the detail body, the related-list cell and the renderer itself;
green both sides for display-name resolution, the copy button, non-lookup
fields, and a lookup with no value.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Qqyix2QcnpUC9XeYVDzx3The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
yinlianghui
commented
Aug 12, 2026
CollaboratorAuthor
ACCEPT — step-7 复核 by PM session
Flipping ready + arming auto-merge. Generated by Claude Code Generated by Claude Code |
yinlianghui
marked this pull request as ready for review
August 12, 2026 20:42
Uh oh!
There was an error while loading. Please reload this page.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes#4336
The defect
A valued lookup on a record detail page rendered as plain text plus a copy button: the referenced document's display name was right there, and there was no way to reach it. The reporter's own check — no
aelement anywhere over the field's value region, everyhrefnull — held for readonly and non-readonly lookups alike, across several objects, which is what makes this display-mode rendering rather than an editability gate.The follow-up comment widened it: on a related list, the ROW navigates to its own record, but lookup cells pointing at a THIRD object were dead in the same way (
执行责任人,管理责任人,来源维保派工单— "逐格取 href 全部为 null").Measurement: one path, so both halves land here
DetailSection's read branch andRelatedList.makeCelleach resolve the cell throughgetCellRenderer('lookup')→LookupCellRenderer(packages/fields/src/index.tsx). Same renderer, same path — so the related-list half is covered here, not split into a second card. Pinned separately all the same: the detail body being green is not evidence for the related-list cell.The URL resolver — measured, and what I found
The ruling asked me to reuse the shared mechanism the list page's
link: truecolumns and related-row navigation build record URLs with. Measured, that mechanism is not a URL helper — the repo has none reachable from a low-level renderer:ObjectGrid'slink: true/ primary column renders aspan role="link"and callsnavigation.handleClick(row);useNavigationOverlaydelegates to the host'sonNavigate.useRelatedRecordActions().resolve({objectName}).onView(id)./apps/:app/:object/record/:idis written out in ~15 host-side places, and the one exported helper (deriveRecordPageHref) derives its prefix by finding/{objectName}in the current path, so it cannot address a different object's record.So the shared mechanism is "host-provided callbacks through React context, host owns the route shape". I reused exactly that, and added no new assembly:
RelatedRecordActionsContext— already mounted around the whole detail page body, already the channel the related list navigates through — gains an optionalrecordHref/openRecordpair.RelatedRecordActionsBridgepublishes them from the same builderonViewalready navigated with: its innerdetailUrlis hoisted torecordHref(objectName, id),?from=breadcrumb trail included, andonViewnow calls it. One route shape, one place; nothing re-derived renderer-side.A real anchor with an
hrefrather than a click handler, so middle-click / Cmd-click / "copy link address" behave the way a link should; a plain left click is handed toopenRecordso navigation stays in-app, and the click is stopped from reaching the row underneath (which would otherwise ALSO fire the detail row's copy-to-clipboard or the related row's own open).No host, or a host that cannot route to the target object → no link, and the value renders exactly as before (Studio designer, embedded renderers, standalone grids).
Red-first
Pre-fix, over the three surfaces, verbatim:
Post-fix:
Test Files 3 passed (3) · Tests 18 passed (18)— the anchor'shrefis the host-built record URL (/apps/demo/mtc_work_order/record/wo-1).Must-not-change (green on both sides): display-name resolution unchanged (the anchor carries the same text); the copy button still sits beside the linked value; non-lookup fields untouched; a lookup with no value keeps its placeholder and grows no empty link; no host → plain text.
Edge cases
recordHrefreturns null → plain value, exactly as before{"externalId":…})Verification
pnpm exec vitest run packages/fields/ packages/plugin-detail/ packages/react/ packages/plugin-grid/ packages/plugin-dashboard/ packages/plugin-kanban/ packages/plugin-list/→ 350 files / 4166 tests passedpnpm exec vitest run packages/app-shell/ apps/console/→ 404 files / 3945 passed, 1 skippedtype-check(bothtsccommands where the package has them) for@object-ui/fields,@object-ui/react,@object-ui/app-shell→ all exit 0check:control-bytes,check:changeset-presence,check:changeset-no-major,check:spec-symbols,check:phantom-deps,check:i18n-keys,eslinton touched files (0 errors) → greenGrading
.d.tsmeasured after a dependency-closure build:@object-ui/react's emitted declaration gains the two optional members → minor.@object-ui/fields' declaration is unchanged (LookupCellRenderer's signature identical; the link component and id helper are module-private) and@object-ui/app-shell's bridge props are unchanged → patch for both.Not in scope
The
${param._rowRecord.«lookupField»}shape inconsistency from the follow-up's item 4 (expand-object in header actions vs bare id in row actions) is untouched, per the ruling.Generated by Claude Code