Repository navigation
fix(grid): a lookup or master_detail cell draws no tooltip carrying the raw id (objectui#13377) - #13401
Conversation
…he raw id (objectui#13377) data-table's cell wrapper set its title tooltip to String(cellValue), and for a reference column that value is the referenced record's id, so hovering beside the drawn name, or beside "Restricted record", showed the id. - @object-ui/types: TableColumn gains optional `foreignKey` (zod mirror typed; the static table column tombstones it, like masked and chips). - @object-ui/components: data-table skips the title tooltip for it. - @object-ui/plugin-grid: ObjectGrid's last column pass stamps it on lookup / master_detail / tree / user columns (isExpandableFieldType over the authored and the object-declared type). Claude-Session: https://claude.ai/code/session_01WJozybDmRH8z2c5SD7vJkj Co-authored-by: Claude <noreply@anthropic.com>
…by (objectui#13377) Same precedence as the chip flag (authored type first, then the object's): a view column that authors `type: 'text'` over a lookup draws the id as its text, so it keeps its tooltip. Pinned as a second control in the grid test. Claude-Session: https://claude.ai/code/session_01WJozybDmRH8z2c5SD7vJkj Co-authored-by: Claude <noreply@anthropic.com>
…jectui#13377) Claude-Session: https://claude.ai/code/session_01WJozybDmRH8z2c5SD7vJkj Co-authored-by: Claude <noreply@anthropic.com>
✅ 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
|
Contract reviewServed-tier: Face: the ① Derived judgmentsThe diff adds one public member and changes no existing one:
The new member is a display hint, not an access control. The visible cell text and copy are governed as before, and the restricted-record rendering of objectui#13335 is unchanged. ② Semver levelThe changeset declares ③ Boundary flags
Implemented-by: VERDICT: PASS |
✅ 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
|
席位核对ACCEPT — objectui#13377 (p3, S), head Seat: domain:ui#1 · Clause-②: yes(合同复核见本 PR 上的
|
Closes #13377
Clause-②: yes (widening: TableColumn gains optional foreignKey; nothing is refused)
What changed
data-table's plain cell wrapper set itstitletooltip toString(cellValue)for every value that is not masked and not an object. For alookup/master_detailcolumn that value is the referenced record's raw id, so hovering beside the drawn name, or beside "Restricted record" (PR #13376), showed the id. The column reached the table untyped (normalizeTableColumnTypekeeps only the table's own types), so the table could not tell.@object-ui/types:TableColumngains one optional boolean,foreignKey, declared next tomaskedandchips. Doc: the column's raw value is a foreign key (the id of a referenced record: alookup,master_detail,treeoruserfield) while itscelldraws the record's name; the cell carries notitletooltip; unlikemaskedit withholds nothing else (copy, CSV export, inline edit, search and sort unchanged); written byObjectGridfrom the column's field type. Mirrored the waychipsis:TableColumnSchematypes itz.boolean().optional()(a non-strict object would strip it otherwise), and the statictablecolumn tombstones it in both faces (StaticTableColumn.foreignKey?: never,retirementTombstone(...)inStaticTableColumnSchema), which the_SameKeySetlockstep pin requires.@object-ui/components: the cell wrapper'stitleexpression adds!col.foreignKey. No other path reads the flag.@object-ui/plugin-grid:ObjectGrid's last column pass (the emit seam beside the masked and chip flags) stampsforeignKey: truewhenisExpandableFieldType(from@object-ui/core, the grid's existing reference family: lookup, master_detail, tree, user) holds for the column's type, read the same way as the chip flag (the authored type first, then the object's). Written only when true; a masked column carriesmaskedalone, as before.chipsandforeignKeyare stamped independently.@object-ui/typesminor (a new optional published member, as formaskedandchips),@object-ui/componentsand@object-ui/plugin-gridpatch.Tests
New pins:
packages/plugin-grid/src/__tests__/lookupCellTooltip-13377.test.tsx, through the realObjectGridwith a readable and a restricted reference in a lookup column (authoredtype: 'lookup') and a master_detail column (no authored type, typed by the object): notitlein any of those cells carries either id, the table's wrapper has notitle, and neither id is anywhere in the grid's DOM. Controls: the plain text column of the same grid keeps its value as its tooltip; a view column that authorstype: 'text'over the same lookup field draws the id as its text and keeps its tooltip (the flag follows the type the cell is drawn by).packages/components/src/renderers/complex/__tests__/data-table-foreign-key-tooltip-13377.test.tsx:foreignKey: truedrops the tooltip while an ordinary column keeps its own; Ctrl+C still copies the raw value (only the tooltip is withheld); the flag absent andfalseboth keep the tooltip; the zod mirror keeps the flag and refuses a non-boolean.packages/types/src/__tests__/static-table-narrow-surface.test.ts:foreignKeyjoins the rich key list, the retired-key fixture and the tombstone-message loop.lookupRestrictedRecord-13335.test.tsxthat said the wrapper still puts the raw value in itstitlenow points to the new pin.Results, at head
3114b300d:pnpm exec vitest run --maxWorkers=2 packages/types/ packages/components/src/renderers/complex/ packages/plugin-grid/pluslookupCell.restrictedRecord-13335,lookupCell.unresolvedReference-8695(fields),DetailSection.restrictedLookup-13335,RelatedList.maskedColumn-10657(plugin-detail) andObjectDataTable.maskedColumn-10657(plugin-dashboard), under the verify lock:Test Files 670 passed (670),Tests 13048 passed (13048), lockVERDICT command-exit 0.turbo run type-check --concurrency=2 --filter=@object-ui/types --filter=@object-ui/components --filter=@object-ui/plugin-grid:Tasks: 17 successful, 17 total,VERDICT command-exit 0.check:new-line-citations(0 new),check:control-bytes,check:spec-symbols,check:component-surface-parity,check:changeset-claims,check:pending-changeset-literals,check:test-path-roots,check:doc-types,check:handler-key-reads,check-changeset-no-major,check-changeset-presence: all exit 0.Cannot create components during render, one indata-table.tsx, one inObjectGrid.tsx) are on the base commit too (same count when the base blobs are linted), not on lines this PR touches.Ablation (fix committed first, each leg restored with
git checkout HEAD --and verified by blob hash):!col.foreignKey &&from thedata-tabletitle expression: the flagged data-table test and the grid pin go red (2 failed | 5 passed); the controls stay green.false: the grid pin goes red (1 failed | 1 passed).Acceptance notes
Docs not updated (outside the claimed file surface).
content/docs/components/complex/data-table.mdxlistsTableColumnmembers includingmaskedandchipsin its Schema block, and has "Chip columns" / "Masked columns" sections. It does not yet listforeignKey. A one-line Schema entry (plus, optionally, a sentence) is the follow-up; this PR does not touch docs because the claim did not declare them.Not measured: the other producers of
data-tablecolumns.RelatedList(@object-ui/plugin-detail) andObjectDataTable(@object-ui/plugin-dashboard) also stampmaskedon the columns they handdata-table, and do not stampforeignKey. If their lookup cells carry a raw id as the cell value, the same tooltip shows it there. This is read from source only, not reproduced.The object-type half of the stamp (
objectFields[...]?.typewhen the column has no type of its own) is not exercised by the grid pin: the master_detail column with no authored type is typed before this pass, so it is flagged by the producer type. The fallback is kept for parity with the chip flag.Not checked in a real browser; the observable is the DOM
titleattribute.Adds no product-specific widget, dialog, hook or string (see AGENTS.md › Product-neutral renderer)
🤖 Generated with Claude Code
https://claude.ai/code/session_01WJozybDmRH8z2c5SD7vJkj