From 7a224052af97fb51d41997fb8ab120e3fecedbad Mon Sep 17 00:00:00 2001 From: Vijay Misal Date: Fri, 7 Aug 2026 22:55:06 +0530 Subject: [PATCH] fix(ui): group field not forwarding forceRender to nested fields 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 #15613 --- packages/ui/src/fields/Group/index.tsx | 3 ++ test/fields/collections/Array/e2e.spec.ts | 26 +++++++++++++++++ test/fields/collections/Array/index.ts | 35 +++++++++++++++++++++++ 3 files changed, 64 insertions(+) diff --git a/packages/ui/src/fields/Group/index.tsx b/packages/ui/src/fields/Group/index.tsx index 37de60c5c04..2c229676926 100644 --- a/packages/ui/src/fields/Group/index.tsx +++ b/packages/ui/src/fields/Group/index.tsx @@ -29,6 +29,7 @@ export const GroupFieldComponent: GroupFieldClientComponent = (props) => { const { field, field: { admin: { className, description, hideGutter } = {}, fields, label }, + forceRender, indexPath, parentPath, parentSchemaPath, @@ -111,6 +112,7 @@ export const GroupFieldComponent: GroupFieldClientComponent = (props) => { {groupHasName(field) ? ( { ) : ( { await expect(page.locator(`#field-collapsedArray__0__text`)).toBeVisible() }) + test( + 'should render conditional fields inside a single group when an initially-collapsed array row is expanded for the first time', + async () => { + await page.goto(url.create) + + const row = page.locator('#collapsedGroupWithCondition-row-0') + await expect(row).toBeVisible() + + const toggler = row.locator('button.collapsible__toggle') + await expect(toggler).toHaveClass(/collapsible__toggle--collapsed/) + + await toggleBlockOrArrayRow({ + fieldName: 'collapsedGroupWithCondition', + page, + rowIndex: 0, + targetState: 'open', + }) + + // The conditional text field, nested inside the array row's single group field, + // must render immediately on the first expand rather than only after a second toggle + await expect( + page.locator('#field-collapsedGroupWithCondition__0__group__text'), + ).toBeVisible() + }, + ) + describe('sortable arrays', () => { test('should have disabled admin sorting', async () => { await loadCreatePage() diff --git a/test/fields/collections/Array/index.ts b/test/fields/collections/Array/index.ts index f09e6a8d6b9..23ed4f684d9 100644 --- a/test/fields/collections/Array/index.ts +++ b/test/fields/collections/Array/index.ts @@ -97,6 +97,41 @@ const ArrayFields: CollectionConfig = { ], type: 'array', }, + { + name: 'collapsedGroupWithCondition', + admin: { + initCollapsed: true, + }, + defaultValue: [ + { + group: { + text: 'conditional text', + toggle: true, + }, + }, + ], + fields: [ + { + name: 'group', + fields: [ + { + name: 'toggle', + defaultValue: true, + type: 'checkbox', + }, + { + name: 'text', + admin: { + condition: (_, siblingData) => Boolean(siblingData?.toggle), + }, + type: 'text', + }, + ], + type: 'group', + }, + ], + type: 'array', + }, { name: 'localized', defaultValue: arrayDefaultValue,