ObjectMap copied the raw descriptionField value onto each marker, and
the popup, the mobile record sheet and the search box all treated it as
text. A lookup-typed description arrives expanded as { id, name } from
the map's own object fetch, so the first marker click threw "Objects
are not valid as a React child"; a number or a boolean made the search
box throw on the first keystroke.
The marker transform now derives the description once through
@object-ui/core's recordDisplayValueAt, the resolver the marker title
already uses for an authored titleField. A new pin file covers the
expanded lookup, the map's own fetch path, the bare-id control, the
mobile sheet, the search, and number, boolean and blank values.
Also drops three bare line addresses from the title comment above it.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BA3nKVUwKQJf8DBxrSVtNC
Fixes #10456
Clause-②: no
What changed
ObjectMapcopied the rawdescriptionFieldvalue onto each marker, and three readers treated it as text. The desktop popup and the mobile record sheet put it in JSX, and the search filter called.toLowerCase()on it. The map's own object fetch expands every declared relation (buildExpandFields(objectSchema?.fields), no column list), so a lookup-typed description arrives as{ id, name }, and the first marker click threwObjects are not valid as a React child. A number or a boolean description made the search box throw on the first keystroke.One line changes behaviour, in the marker transform in
packages/plugin-map/src/ObjectMap.tsx:recordDisplayValueAtis@object-ui/core's shared reader inutils/record-title. The marker TITLE already runs it for an authoredtitleField(step 0 ofgetRecordDisplayName), and@object-ui/plugin-detail's header and dedupe ladder use it too. So no new resolver is written, and the two text slots of one marker now share one rule.MarkerData.descriptionwas already typed as a string and now really is one, so the popup, the sheet and the search all read the same value.{ id: 'a1', name: 'Acme' }Acme; the search matchesacme'a1'(the control)a1a14242; the search throws42; the search matchesfalsefalse{ id: 'a1' }No spec key changes.
descriptionFieldstays out ofListView's$expandcollector: objectui#10454 excluded it on purpose, and that file is untouched.In the same comment block, three bare line addresses that pointed into other files (
(:301),(:356),(:600)) are dropped, and the symbol names beside them stay (AGENTS.md Commandment 11, repair when touched). The hunks are the import list, that comment and the description derivation. None is near the docblock paragraph that draft PR objectui#10508 edits.Tests (head
6134028a5)9eea12a2b. The pin file, run before the fix (it had 7 arms then), gaveTests 6 failed | 1 passed (7). The one pass was the bare-id control. The errors were:Objects are not valid as a React child (found: object with keys {id, name})for the popup, the reach path and the mobile sheet;TypeError: m.description?.toLowerCase is not a functionfor both search arms;expected null to be 'false'for the boolean arm.ObjectMap.descriptionDisplay-10456.test.tsxhas 8 arms:$expandnames the lookup, then clicks;pnpm exec vitest run packages/plugin-map/from the repo root gaveTest Files 26 passed (26)andTests 155 passed (155).turbo run build --filter="@object-ui/plugin-map^..." --concurrency=2, 13/13 tasks). Thenpnpm --filter @object-ui/plugin-map run type-check(tsc --noEmit && tsc -p tsconfig.test.json) exited 0.--listFilesOnlyon the test project lists the new pin file.--no-inline-config;--format jsoncounts 2 files):ObjectMap.tsxhas the same per-rule counts as base: 13no-explicit-anywarnings, 0 errors.no-explicit-anywarnings, all on thereact-map-glstub props. The siblingObjectMap.markerTitle.test.tsxcarries 7 of the same kind.eslint.config.jssets noparserOptions.projectorprojectService. There is no type-aware linting, so this diff cannot move an untouched file's result.check:control-bytescheck:new-line-citations(0 new)check-changeset-presence,check-changeset-no-majorandcheck-changeset-overwritecheck:changeset-claims: report-only. It names one pending changeset that mentionsObjectMap.tsx,6939-objectql-record-source-refinement.md. Its paragraph is about the record-source order ingetDataConfig, which this diff does not touch.check:vi-mock-specifiers,check:vi-mock-inheritandcheck:vi-mock-override-shapecheck:test-path-roots,check:pending-changeset-literalsandcheck:unreferenced-sourcespnpm testandpnpm lint, which CI runs.Ablation (head
6134028a5)This used objectstack's
scripts/ablation-replace.mjsin WRAP mode, under the shared verify lock. The mutation put the raw read back: the anchorrecordDisplayValueAt(record, mapConfig.descriptionField)becamemapConfig.descriptionField ? record[mapConfig.descriptionField] : undefined.6cac44151b68to161639bc651e.Tests 7 failed | 1 passed (8). The one pass isa bare id renders as text, so the expanded-row pin goes red with the throw and the control stays green.6cac44151b68), andgit diff HEADis empty.No rebuild was needed. The pin imports
./ObjectMapfrom source, and the root vitest config aliases@object-ui/coretopackages/core/src.Acceptance notes
recordDisplayValueAtrules a bare{ id }payload empty. The grid's lookup cell falls back to the id instead, but that fallback lives in a private helper in@object-ui/fields, which this package does not depend on. Copying it here would be the third spelling the card forbids. The arman expanded record with no display name, and a blank value, render no description linerecords this choice so it can be flipped deliberately.falseand0.falsenow shows asfalse.0now renders inside the description paragraph; before, the truthiness gate leaked a stray0text node beside the title.descriptionField("Field shown under the title in the marker popup.") stays true, so it is unchanged.ObjectTimelinehas the same shape. Its own fetch expands every relation, and it passesitem[descField]straight to the renderer as a React child. A one-shot uncommitted probe (a lookup-typeddescriptionField, the real renderer) threwObjects are not valid as a React child (found: object with keys {id, name}), with$expand: ["account"]. The bare-id control passed. It is reported to the seat for filing.domain:uiseat 1, sessionhttps://claude.ai/code/session_01BA3nKVUwKQJf8DBxrSVtNC.Generated by Claude Code