Uh oh!
There was an error while loading. Please reload this page.
fix(detail): inline-edit an address as sub-fields, not a text box reading [Object] - #4222
Merged
Merged
Conversation
…ding [Object] InlineFieldInput's type switch routed the scalar and relational families to dedicated widgets; every structured-object type matched nothing and fell through to the terminal raw text input. That fallback stringifies through coerceToSafeValue, whose general-object case returns the literal [Object] for a value carrying none of name/label/externalId/id/_id — and then emits what the user types as a STRING that replaces the whole object on save. Route address, location and geolocation to the widgets the create/edit dialog already uses, so the object round-trips and the two entry points cannot diverge on the value shape they write back. coerceToSafeValue is untouched: the routing is what stops these values from ever reaching it. Fixes#4216
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
This was referenced Aug 11, 2026
yinlianghui
marked this pull request as ready for review
August 11, 2026 04:16
Uh oh!
There was an error while loading. Please reload this page.
yinlianghui pushed a commit
that referenced
this pull request
Aug 11, 2026
Three residual reds on main after the rc.5→rc.6 spec bump (#4169), none of which the train cars themselves covered. QUICK_REFERENCE.md's "Current Release" block still quoted `^17.0.0-rc.5` for both `@objectstack/spec` and `@objectstack/client` while every manifest it names as its anchor declares `rc.6`. The pin test for that block (quick-reference-current-release-4143) landed on main AFTER the train was built, so no car ever ran against it. recordDetailsInputs.spec-parity carried `layout: 'custom'` in both fixtures of its object-sections case. `layout` was removed in @objectstack/spec 17.0.0 (objectstack#6946, ADR-0087 D2) and rc.6 gave it a named `never` rejection, so the object-form fixture failed on `layout` while its `sections` were valid — and the id-list fixture above it kept PASSING on `layout`'s own `invalid_type` without ever reaching `sections`. Dropping the key puts both verdicts back on `sections`; the id-list case now also pins the issue PATH, since a bare code could not tell the two rejections apart. AddressField re-exports `AddressValue` through its declaring module rather than bare. main's #4222 moved the declaration into the pure `address-format` module while this train re-sourced the type from `@objectstack/spec/data`; the merge keeps both by importing the spec type in `address-format` (where the definition now lives) and letting the widget re-export from there, which is the form check:spec-symbols resolves to a declaration site instead of reading as a second local declaration of a name the spec owns.
This was referenced Aug 11, 2026
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#4216
Inline-editing an
addresson the record detail page collapsed it to one text box reading[Object], and saving wrote a bare string over the structured value.locationandgeolocationare fixed with it — same cause, same two signatures, one defect in three spellings.Cause
InlineFieldInput's type switch routes the scalar and relational families to dedicated widgets. Every structured-object type matched nothing and fell through to the terminal raw text input at the end of the component, which stringifies throughcoerceToSafeValue. That helper's general-object case extractsname || label || externalId || id || _idand otherwise returns the literal[Object]— and an address ({ street, city, state, postalCode, country }) or a coordinate pair ({ latitude, longitude }) carries none of those keys.The defect has two halves and the second is the damaging one:
[Object], so[Object]is what the user sees as the value they are correcting, which makes typing over it the natural gesture.onChange(v), so whatever is typed is emitted as a string replacing the whole object on save. An ordinary double-click inline edit destroys the sub-field structure.Input path only. #4037 / PR #4215 fixed the display registry, so read mode — including the inline-edit read state before editing starts — already renders a formatted address; once a field is actively being edited,
DetailSectionrendersInlineFieldInputinstead, which is why that PR does not touch this.The fix
Route
address,locationandgeolocationto the widgets the create/edit dialog already uses —AddressField,LocationField,GeolocationField, the form's own structured-value editors (fieldWidgetMap, andFieldEditWidget's "structured-value editors" block). No new editor, and no consumer-side tolerance: the two entry points now cannot diverge on the value shape they write back.coerceToSafeValueis untouched. Adding an address branch there would only have improved the display half while leaving the write half intact; the routing is what stops these values from reaching it at all, which makes the branch unnecessary rather than merely unwritten.autoFocusfollows the numeric branches' convention — each widget forwards the DOM pass-through set onto its first focusable sub-input (street / the coordinate box / latitude), so entering inline edit lands the caret where a single-input type would. Commit/cancel semantics are unchanged:InlineFieldInputis a controlled input in every branch, and the hosts commit throughInlineEditSaveBar, so there is no per-input keyboard contract to mirror. Both hosts (DetailSectionbody and theHeaderHighlightstrip) share the component and get the fix identically; the highlights strip renders a taller editor for these types, as it already does for the routedImageField/FileField.Composite fall-through sweep
Every field type reaching the terminal text input before this change, and the verdict for each. Fixed here: the object-valued composites, which are the same-shape routing the card scopes. Everything else is filed.
address,location,geolocation[Object], saves a stringtags,checkboxes,vector, options-lessmultiselectobject,composite,record,grid,repeater,json[Object], saves a string over the embedded structuretoggle,slider,progress,rating,radioradiofree-types over its option setpassword,secrettext,textarea,markdown,html,richtext,email,phone,url,time,color,code,qrcode, options-lessselectformula,summary,rollup,auto_numberautonumberspelling filed as #4219The array / container / scalar rows are grouped in #4220 rather than ridden along here because each needs a design call (route vs. exclude; what an options-less
checkboxesdoes), not a one-line routing. #4220 also records the root cause behind the whole tail:@object-ui/fieldsalready exportsFieldEditWidgetwithINLINE_EXCLUDED_FIELD_TYPESand a drift-guard test against the form's type list, andInlineFieldInputhand-rolls a parallel switch that consults neither.Evidence
Red first. The pin asserts both halves for all three types and failed on both signatures before the fix — 11 failed, 2 passed, the 2 passes being the string-type controls that legitimately keep the terminal input:
The write-half pin is deliberately query-independent (it types into the first box the editor renders, whatever that is). Addressing a named sub-input instead would have failed with "unable to find an element" — a signature naming the missing sub-input, not the missing structured write, which are two different defects.
After the fix:
Test Files 1 passed (1) / Tests 13 passed (13).Affected-package suites — repo root, path-filtered per AGENTS.md:
Reverse verification. Predicted RED with both signatures returning. Removed only the routing (
git checkout origin/main -- packages/plugin-detail/src/InlineFieldInput.tsx), keeping the test: 11 failed / 2 passed — the identical split and test set as the pre-fix run, both signatures back. Restored withgit checkout HEAD -- ..., green again at 13/13.Gates:
type-checkgreen for@object-ui/plugin-detailand@object-ui/fields(after building the dependency closure);eslinton both changed files 0 errors (32 pre-existingno-explicit-anywarnings, the same pattern the sibling branches already use);check-control-bytesOK across 3917 tracked files;check-changeset-presenceOK. No copy was touched, so no i18n gate applies — the widgets' sub-labels are the form's existing ones and this change adds no strings.Changeset:
.changeset/inline-address-input-4216.md, patch on@object-ui/plugin-detail. Not askip-changesetcandidate — the change is user-visible (objectui#3724).Generated by Claude Code