Uh oh!
There was an error while loading. Please reload this page.
fix(plugin-tree): format tree-grid cells like the flat table - #6097
Merged
Conversation
…i#6014) The 「组织架构」 tree tab rendered a lookup column as a bare record id and a select column as its raw stored value, while the flat-table tab on the same page rendered the display name and the translated label over the same records. The defect was in the fetch, not the formatter. ObjectTree treated "the host passed inline `data`" as "I do not need the object schema" and skipped getObjectSchema, but its record-fetch branch prefers a live object dataSource over any inline data. On ListView's real mount shape (objectName + dataSource + its own pre-fetched data) the tree therefore ran its own query with buildExpandFields(undefined) -> [] -> no $expand, and had no field definitions to format cells from. Both symptoms fall out of that one gap. - Fetch the object schema whenever the dataSource can serve one. - Hold the record fetch until that schema has SETTLED (not necessarily succeeded), which also drops a wasted first query whose lookup columns came back as bare ids and were painted before the real query landed. - Route cells through a field-aware formatter that delegates both decisions: option labels via the same `translateOptions` seam ObjectGrid uses, expanded references via getRecordDisplayName (ADR-0079), family judged by isExpandableFieldType -- the same predicate that built $expand. No new exports, no new package dependencies. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CSoz9uGhaaSgiq3hshtN7L
Contributor
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
yinlianghui
marked this pull request as ready for review
August 24, 2026 16:52
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.
Fixes#6014
Verified at
773c6463e(the branch head; every gate result below was produced on this tree).What was wrong — and it was not the formatter
The card's own control pointed at the fetch, not at the cell renderer.
ObjectTreetreated "the host passed inlinedata" as "I do not need the object schema" and skippedgetObjectSchema. But its record-fetch branch prefers a live object dataSource over any inline data. On the one mount shapeListViewactually uses —objectName+ a dataSource + its own pre-fetcheddata— the tree therefore issued its own query withbuildExpandFields(undefined)→[]→ no$expandat all, and had no field definitions to format cells from.Both reported symptoms fall out of that single gap. That is why the flat tab was unaffected (it renders from
ListView's own expanded fetch) and why the tree's existing tests could not see it (they pass inline data with no dataSource — a path that never runs the tree's own fetch).Measured with a dataSource that expands only what it is asked to expand:
getObjectSchemafinddata(the repro)$expand: nullAcme │ u1 │ department$expand: [parent_id, manager_user_id]Acme │ Zhang San │ departmentAcme │ Zhang San │ departmentRow 2 is the counter-probe: same component, same formatter, schema present → the lookup already resolved.
formatValuewas never the lookup defect. Row 2 also shows the select half surviving a correct expand — it is a genuinely separate mechanism, and the only half a formatter change fixes.The fix
Three changes, all in
packages/plugin-tree/src/ObjectTree.tsx:find1-of-2 in row 2 above, whose lookup columns came back as bare ids and were painted for a moment before the real query landed.Which seam, measured rather than assumed
The dispatch asked for "the same chain as the flat table" and warned there may not be one reachable chain. Measured:
ObjectGrid.tsx:3125 renderFieldValueis the record-detail drawer panel, not the flat table's cells. The flat table's column cells go throughresolveCellRendererType+getCellRenderer(@object-ui/fields), with afieldMetabuilt from the object schema — and, for the select half,fieldMeta.options = translateOptions(objectName, field, options).translateOptionscomes fromuseSafeFieldLabel()— which this component already calls for its column headers. No new dependency, no new export; the tree and the flat tab now read onefieldOptions.*i18n key.getRecordDisplayName(@object-ui/core) is the unified display-name resolver (ADR-0079), and the family is judged byisExpandableFieldType— the same predicate that decided what to put in$expanda few lines up, so "what we expanded" and "what we unwrap as a reference" cannot drift apart.localizeFieldOptions/buildDimensionLabelMap(core/utils/chart-series.ts) are ruled out — not because they are chart-shaped (their mechanism is generic) but because they implement a different matching contract from the one the flat tab uses: no case-insensitive fallback,null-when-no-op, and extra authored-label keys. Adopting them would make tree cells disagree with flat cells in exactly the mixed-case seed-data casesSelectCellRenderer's own comment calls out.What I did not do: take on
@object-ui/fieldsand render cells throughgetCellRenderer. That is the fuller convergence, but it turns every tree cell into the grid's chrome (badges, links, per-cell lookup-resolution hooks) — a redesign of the tree-grid rather than a patch-level bug fix. Flagging it rather than deciding it unilaterally.Verification
Every new assertion was red before the fix for a value reason, not a mount error:
The select fixture's labels are deliberately not derivable from their values:
humanizeLabel('department')returns'Department', so an assertion expectingDepartmentwould have passed with option matching completely broken. The fixture usesBusiness Department, and the test pins the fallback out explicitly.Ablation (two legs, each mutation proven on disk by grepping the injected and the removed text, restored under
trap … EXIT INT TERM,git diff HEAD --statempty after each). No rebuild leg applies: the tests import./ObjectTreeas a relative source path andvitest.config.mtsaliases@object-ui/core→packages/core/src, so nothing resolves throughdist/.buildExpandFields(undefined)(schema still fetched)Each leg reds exactly the tests bound to the half it ablates — so neither half's green is phantom, and the two mechanisms are independent as claimed.
Gates
pnpm --filter @object-ui/plugin-tree type-checkTS2550onArray.prototype.at, since removed). Log shows the realtscinvocation, so not a zero-match silent green.pnpm exec vitest run packages/plugin-tree/src(root form)pnpm exec eslint(plain, no--no-inline-config) on both changed filesno-explicit-any(this file isany-typed throughout) plus two pre-existing hook rules on untouched lines;pnpm lintsets no--max-warnings.check:phantom-deps·check:self-import·check:esm-specifiers·check:spec-symbols·check:action-forward-parity·check:designer-field-key-parity·check:icon-record-names·check:i18n-keys·check:i18n-drift·check:control-bytesType Checkjob's own step list — derived from the job steps, not from top-level script namescheck:changeset-presence·check:published-dist·check:eager-closure·check-lint-coverage·check-type-check-coveragepackages/**/.changeset/**pnpm --filter '...@object-ui/plugin-tree' type-checkThe dependent set is exactly two projects:
packages/plugin-tree(Done, green) andapps/console. Everyapps/consoleerror is the unbuilt-closure class, confirmed mechanically rather than asserted: all 105TS2307+ 36TS2882name modules whosedist/index.d.tsis MISSING (the caret form'@object-ui/plugin-tree^...'builds the package's dependencies, not the package itself nor console's other deps), the list includes packages entirely unrelated to this diff (@object-ui/auth,@object-ui/app-shell,@object-ui/permissions,@object-ui/providers) and 14 sibling plugins untouched here, and zero console errors name any file this PR changes. CI installs and builds properly, so it type-checks console for real.(The lock wrapper reported
141for that run — SIGPIPE from aheadin my own reporting pipeline, not the gate. The gate's exit was captured before the pipe:2.)Scope
packages/coreandpackages/componentswere flagged as contended — neither is touched. No new exports, no new package dependencies, no changes to the flat-table renderers. Changeset ispatch(runtime bug fix; no public type or export changes).One visible consequence beyond the report, called out in the changeset: an expanded record that comes back with no name-ish field now reads as ADR-0079's
Record #+ the record id floor — the string every other surface shows for it — rather than as the bare id.Generated by Claude Code