Repository navigation
fix(plugin-tree): tree cells draw the list cell faces, so a boolean no longer shows as true (objectui#11686) - #11721
Merged
objectstack-fleet[bot] merged 3 commits intoOct 6, 2026
Conversation
The tree's cell formatter knew option labels and referenced records and sent every other type to String(value), so the Org Chart's boolean Active column printed `true`, and dates, numbers and currency printed raw. Every cell whose column has a declared type now draws through the face the list surfaces resolve, getCellRenderer(resolveCellRendererType(field)), with the column key as `name`, the renderer key as `type`, translated options, and the header's label. Untyped columns keep the old string. Claude-Session: https://claude.ai/code/session_01DrKzdPdyLLBW3qpZ4vtk7z Co-authored-by: Claude <noreply@anthropic.com>
…roduce (objectui#11686) The pin file's helper waited for the boolean face's checkbox before reading any cell, so on a tree without the faces every test failed at that wait rather than at its own assertion. It now waits for the select column's option label, which the tree printed before this change as well. Claude-Session: https://claude.ai/code/session_01DrKzdPdyLLBW3qpZ4vtk7z Co-authored-by: Claude <noreply@anthropic.com>
…jectui#11686) Replaces the three `as any` casts with `DataSource` and the face's own `CellRendererProps['field']`, and drops an unused parameter. Claude-Session: https://claude.ai/code/session_01DrKzdPdyLLBW3qpZ4vtk7z Co-authored-by: Claude <noreply@anthropic.com>
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
|
objectstack-fleet
Bot
deleted the
claude/issue-11686-tree-cell-display-faces
branch
October 6, 2026 15:25
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 free
to 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 #11686
Clause-②: no — tree cells start drawing through the faces list cells already use; no export, prop, type member or i18n key is added to anything a package entry reaches.
What was wrong
Setup → Business Units, Org Chart tab. That tab is
sys_business_unit'sorg_chartlist view (type: 'tree'), whichListView'scase 'tree'branch hands toobject-tree, so it is drawn byObjectTreein this package. Its cell formatterformatCellValuehandled two families (fields carryingoptions, and expanded references) and ended inreturn String(value);for everything else. So the booleanactivecolumn printedtrue, and dates, numbers and currency printed their stored form.What changed
packages/plugin-tree/src/ObjectTree.tsx: newrenderTreeCell. A cell whose column has a DECLARED type now draws throughgetCellRenderer(resolveCellRendererType(field))from@object-ui/fields. That is the two-step the grid (resolveGridCellRendering), the kanban card, the gallery card and the report viewer use. The face receives the column definition plus four values:name: the column key, whichBooleanCellRendererand the date faces read;type: the renderer key, as the grid'sfieldMeta.typecarries it;options: translated throughtranslateOptions, the same call the grid makes;label: the label the tree's header prints.A column with no declared type (one the object does not define, or a schema-less mount) keeps
formatCellValue's string. The label column and the other columns both go throughrenderTreeCell. The record overlay's plain-reading fallback is unchanged.packages/plugin-tree/package.json: declares@object-ui/fieldsasworkspace:*, as plugin-kanban, plugin-grid and plugin-report do. There is no cycle, because fields depends on nothing that depends on plugin-tree. plugin-tree already reached fields at runtime: it imports@object-ui/plugin-detailstatically, and plugin-detail's built entry imports@object-ui/fieldsstatically. So this edge adds no module to any bundle closure.pnpm-lock.yaml: only the matching three-line importer entry..changeset/11686-tree-cell-display-faces.md: a patch for@object-ui/plugin-tree.Behaviour a reviewer will see beyond the card:
lookupcell now drawsLookupCellRenderer. It links to the referenced record when the host publishes a record href; that link stops propagation, so the row click does not also fire. An id that was not expanded is resolved by name on demand.usercell now draws the avatar face.title. An empty label draws the shared empty-value affordance: the same dash, now with an accessible name.Measured before editing (dispatch hypotheses H1–H4)
return String(value);, and three call sites print its result: the label column, the other columns, and the overlay fallback. The Org Chart is drawn by this package (path above).getCellRenderer+resolveCellRendererType, published by@object-ui/fields, which plugin-tree did not declare.true, with no words. Forfalseon a status-named field (active,enabledand so on) it draws a badge reading "FIELD-LABEL — Off". The label half is localized here, because the header's translated label is handed to the face. The wordOff, and the completion indicator'sCompleted/Not completedaccessible names, are hard-coded English inBooleanCellRendereritself; see Acceptance notes.Tests
New
ObjectTree.cellFaces-11686.test.tsx. It mounts the tree the wayListViewdoes (live dataSource plus inline data) withsys_business_unitcolumns, and pins:truedraws the checkbox (aria-checked="true"), nottrue. Booleanfalseonactivedraws the face's badge, notfalse.innerHTML) to the list face rendered on its own for the same field and value.zhprovider withfields.sys_business_unit.activeset to 启用, the Active column draws the checkbox fortrue. Forfalseit draws a badge naming 启用 (notActive), the same word as the header.The six objectui#6014 pins in
ObjectTree.fieldFormatting.test.tsx(lookup name,$expand, option label, case-insensitive match,fieldOptionsi18n, plain value) pass unchanged through the faces.Reverse check (ablation), run from committed state with
ablation-replace.mjsin wrap mode:renderTreeCellalways returns the old string. Anchor hits went 1 → 0; blob9a76f60b→b395115b.expected 'true' not to contain 'true',expected 'false' not to contain 'false',expected '2024-03-05' not to be '2024-03-05', and the zh pin'strue.9a76f60b== HEAD, andgit diff HEADis empty.506a6e5), and the result above is from the rerun.Gates — local head
142121epnpm --filter '@object-ui/plugin-tree^...' build(closure, 13 projects)VERDICT command-exit 0pnpm --filter @object-ui/plugin-tree buildVERDICT command-exit 0; dts built, fields externalpnpm --filter @object-ui/plugin-tree testTest Files 24 passed (24),Tests 178 passed (178), at142121epnpm --filter @object-ui/plugin-tree type-check(echoestsc --noEmit && tsc -p tsconfig.test.json; the test config lists the new file)VERDICT command-exit 0vitest runofObjectTree.cellFaces-11686.test.tsx+ package type-check, on the bytes committed as142121eTests 5 passed (5)pnpm check:control-bytescheck-control-bytes: OKpnpm check:new-line-citationsVERDICT new-cross-file-line-citations: 0 new citation(s)pnpm check:changeset-claimsNo pending changeset names a file this change touches.pnpm check:pending-changeset-literalsNo test source names a pending changeset.pnpm check:phantom-depsEvery in-scope import is declared by the package that publishes it.pnpm check:unused-depsEvery gated declaration has a consumer in the package that declares it.pnpm check:lockfile-integrityVERDICT cleanpnpm check:lockfile-dedupe(added: lockfile touched)VERDICT dedupedpnpm check:test-path-roots(added: new test file)check-test-path-roots: OKnode scripts/check-changeset-presence.mjs(added: changeset)2 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)node scripts/check-changeset-no-major.mjs(added: changeset)No changeset declares a major bump.node scripts/check-governed-queue-guard.mjs --test(the five paths)NOT GOVERNEDFull package suite at
142121e: measured under the lock on the fourth attempt. Result:Test Files 24 passed (24),Tests 178 passed (178), plustype-checkgreen. The first three attempts timed out in the queue (exit 99) behind another holder. (Paragraph corrected by thedomain:uiseat 1 PM at review; the dev's report carries the measurement.)Lint narrowed to the two touched source files:
eslint --no-inline-config --format jsonread 2 files, with 0 errors.ObjectTree.tsxhas 24 warnings, the same count as its base. The new test file has 0. Invariance: the flat config enables no type-aware linting (emptyparserOptions, noprojectand noprojectService), noimport/*rules, and no customobject-ui/*rule reads the filesystem or type services. So this diff cannot move any untouched file's verdict. The repo-widepnpm lintstays with CI.NOT MEASURED locally:
check:eager-closure: it needs a full consolevite build. See the closure argument above.Acceptance notes (not changed here)
BooleanCellRenderer(packages/fields/src/index.tsx), the badge wordOffand the completion indicator'sCompleted/Not completedaccessible names are hard-coded English on every list surface, and now the tree too. They are the same family as the read-onlyYes/Nowords carded in objectui#11689, but sit in a file that card does not list. They are named for that card's scope, not changed here (this claim adds no i18n key).fieldDef.labelwhile its header resolves the translated label. So under a non-en pack the grid's Off badge names the column in the authored language, while the tree's (after this PR) names it like its header. Same family as above.vite.config.tsnames no UMD global for@object-ui/fields(new here),@object-ui/plugin-detail,@object-ui/permissionsorreact/jsx-runtime. The build warnsMISSING_GLOBAL_NAMEand guesses a name for each. ESM output is unaffected.Implemented by an
os-devagent dispatched by thedomain:uiseat 1 PM; sessionhttps://claude.ai/code/session_01DrKzdPdyLLBW3qpZ4vtk7z.Generated by Claude Code