fix(form): a split form keeps BOTH panels' values (#2153) - #3012
Merged
Conversation
SplitForm rendered one SchemaRenderer — one react-hook-form instance and one
<form> element — PER section, and its two groups of sections live in separate
ResizablePanel children. So each panel owned isolated form state: submitting
from one panel's action bar sent only that section's fields and silently
dropped everything typed on the other side of the divider. Filling both panels
and clicking Create persisted { subject } alone.
The same isolation killed cross-panel field rules: a visibleWhen in the right
panel referencing a left-panel field never saw that field in its rule record,
so the predicate faulted and failed OPEN — the field the author meant to hide
was always shown.
Both panels are now ONE form, the same way #2959 fixed the tabbed hosts. A
single <form> cannot straddle two panels from INSIDE them, so the panel group
became a layout the form renderer owns: a new FormSchema.fieldPanes
(+ fieldPanesOrientation, fieldPanesResizable) mirroring fieldTabs, where the
<form> wraps the whole ResizablePanelGroup and each pane holds only fields.
Sections inside a pane render behind the inline section-divider header, each at
its own declared column density within the form's shared grid.
Also fixed by moving the panels into the renderer: splitResizable: false now
actually pins the divider (it only hid the grip before, leaving the separator
draggable — nothing passed the panel library's `disabled`).
Each pane is its own @container, so a multi-column section collapses to fewer
columns as its panel is dragged narrower instead of overflowing.
Verified in a real browser (zero-backend console harness): one <form> holding
both panes, typing 'urgent' in the left pane reveals the right pane's
conditional field, and the submit payload carries every field from both sides.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
This was referenced Jul 30, 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 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.
The defect
SplitFormhad the data-loss defect that #2987 fixed inModalForm/TabbedFormfor #2959 / #2153.renderSectionsrendered oneSchemaRenderer— one react-hook-form instance and one<form>element — per section, and its two groups of sections live in separateResizablePanelchildren. Each panel therefore owned isolated form state:visibleWhenin the right panel referencing a left-panel field never saw that field in its rule record, so the predicate faulted and failed open — the field the author meant to hide was always shown.The regression test proves the first one exactly. Against
main, filling all four fields across both panels and submitting produced:The fix
A single
<form>cannot straddle two panels from inside them, so the panel group became a layout the form renderer owns — option (b) from the issue discussion. NewFormSchema.fieldPanes(+fieldPanesOrientation,fieldPanesResizable) mirrorsfieldTabs: the<form>wraps the wholeResizablePanelGroupand each pane holds only fields, so one react-hook-form instance spans the divider.Sections inside a pane render behind the inline
section-dividerheader, each at its own declared column density within the form's shared grid (applyAutoColSpan(fields, formColumns, section.columns)) — the same arrangement the stacked/tabbed hosts use.Two behaviours fall out of moving the panels into the renderer:
splitResizable: falsenow actually pins the divider. It previously only hid the grip; nothing passed the panel library'sdisabled, so the separator stayed draggable.@container, so a multi-column section collapses to fewer columns as its panel is dragged narrower instead of overflowing.A single-section split form is not a split, so the renderer falls back to a plain field list rather than a one-panel resizable group (same
> 1guardfieldTabsuses).Tests
packages/plugin-form/src/sectionedFormValues.test.tsx— 3 new SplitForm cases (both panels' values in the payload; section headers staying in the pane that owns them; a right-pane condition reading a left-pane field). Verified failing againstmainfirst, all three for the right reason.packages/components/src/renderers/form/__tests__/form-field-panes.test.tsx— 9 renderer-level cases pinning thefieldPanescontract: one<form>, both panes' values, cross-pane rules, unclaimed fields still rendered, single-pane fallback,fieldTabsprecedence, the divider's pinned/draggable states, and that the pane schema keys never leak onto the<form>DOM element (the follow-up bug fix(form): a tabbed/sectioned modal keeps every tab's values (#2959, #2153) #2987 hit).51 test files / 508 tests green across
plugin-form, the form renderer andtypes;turbo type-checkgreen for all three packages.Browser verification
Ran the split form in a real browser against a zero-backend harness (throwaway console Vite entry, deleted before commit):
<form>, containing both panes;urgentinto the left pane's Subject revealed the right pane's conditionalEscalation contactfield — the cross-panel rule working live;{subject, reporter, status, priority, description, escalation};One correction worth recording: I initially believed numeric pane sizes were being read as pixels by
react-resizable-panelsv4 (its own docs say so) and wrote that up as a third fix. A same-frame A/B in the browser showednumeric 40/60 → 40/60%andstring "40"/"60" → 40/60%— identical. There was no pixel bug; that claim is out of the changeset. The renderer still passes percentage strings so the unit is explicit, and the comment says only that.🤖 Generated with Claude Code