Skip to content

fix(ui): group field not forwarding forceRender to nested fields - #17715

Open
vjymisal0 wants to merge 1 commit into
payloadcms:mainfrom
vjymisal0:fix/array-single-group-conditional-first-expand
Open

fix(ui): group field not forwarding forceRender to nested fields#17715
vjymisal0 wants to merge 1 commit into
payloadcms:mainfrom
vjymisal0:fix/array-single-group-conditional-first-expand

Conversation

@vjymisal0

Copy link
Copy Markdown

Which branch should this PR target?

main (v4 development) — the bug reproduces on the current main UI code as well as v3.

What?

The Group field component never forwarded its forceRender prop down to the RenderFields calls it renders internally for its own sub-fields. Sibling iterable field types (Row, Tabs) already correctly forward forceRender.

Why?

Fixes #15613 — "Array with a single group containing conditional fields does not render on first collapsible expand".

Top-level document fields are always rendered with forceRender (see DocumentFields), which flows down through ArrayArrayRow → the row's own fields. When an array row contains a single Group field, that forceRender reached the Group component but was silently dropped instead of being passed to the RenderFields call for the group's own child fields.

Without forceRender, those child fields fall back to lazy, IntersectionObserver-gated rendering (RenderIfInViewport). While an array row is collapsed, its content lives inside an ancestor with display: none (set by AnimateHeight), so the observer never reports an intersection. On first expand, AnimateHeight's height-animation helper (usePatchAnimateHeight) measures content.scrollHeight in the same effect flush in which the sibling display: none removal hasn't committed to the DOM yet, so it measures 0 and pins the row's container height to 0px. That keeps the (still not-yet-mounted) conditional fields clipped and unable to ever report an intersection, so they never mount — appearing "empty". Collapsing and expanding again works because, on the previous cycle, the fields (once mounted) stay mounted, so the height measurement is now accurate.

Forwarding forceRender through Group (matching Row/Tabs) makes the group's child fields mount immediately alongside the rest of the row's force-rendered content, avoiding the race entirely.

How?

  • packages/ui/src/fields/Group/index.tsx: destructure forceRender from props and pass it to both RenderFields calls (named-group and unnamed-group paths).
  • Added a regression test field collapsedGroupWithCondition to the Array fields test collection: an initCollapsed array with a default row containing a single group field with a conditional (admin.condition) text field.
  • Added an e2e test in test/fields/collections/Array/e2e.spec.ts that loads the create page, confirms the row starts collapsed, expands it once, and asserts the conditional field is visible on that very first expand.

Test status

I was unable to run the test suite in my environment (no disk space available to pnpm install the monorepo's dependencies — the working environment had under 3GB free). I've verified the fix by tracing the actual render/measurement code paths (Group, Row, Tabs, RenderFields, RenderIfInViewport, useIntersect, AnimateHeight, usePatchAnimateHeight) and confirmed Group was the only iterable field type not forwarding forceRender, which is a clear, minimal, and type-safe fix (forceRender is already part of GroupFieldClientProps via ClientFieldBase). I was not able to execute pnpm test:e2e locally to confirm the new test passes — please run it in CI.

Fixes #15613

The Group field component never forwarded its `forceRender` prop to
the RenderFields calls it renders internally, unlike sibling iterable
field types (Row, Tabs) which correctly forward it.

When an Array row is force-rendered (e.g. top-level document fields
are always force-rendered) but contains only a single Group field,
the Group's own child fields fell back to lazy IntersectionObserver
based rendering instead of rendering immediately. Combined with the
Collapsible/AnimateHeight height measurement (which measures
`scrollHeight` while the row content is still `display: none`),
conditional fields nested in that Group would race with the
IntersectionObserver callback and fail to render on the first expand
of a row that started collapsed. Collapsing and expanding again
worked because the fields, once mounted, stay mounted.

Fixes payloadcms#15613
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Array with a single group containing conditional fields does not render on first collapsible expand

1 participant