Skip to content

Commit accd95e

Browse files
authored
fix(ui): array fields not respecting width styles in row layouts (#13986)
### What? This PR applies `mergeFieldStyles` to the `ArrayFieldComponent` component, ensuring that custom admin styles such as `width` are correctly respected when Array fields are placed inside row layouts. ### Why? Previously, Array fields did not inherit or apply their `admin.width` (or other merged field styles). For example, when placing two array fields side by side inside a row with `width: '50%'`, the widths were ignored, causing layout issues. ### How? - Imported and used `mergeFieldStyles` within `ArrayFieldComponent`. - Applied the merged styles to the root `<div>` via the `style` prop, consistent with how other field components (like `TextField`) handle styles. Fixes #13973 --- - To see the specific tasks where the Asana app for GitHub is being used, see below: - https://app.asana.com/0/0/1211511898438801
1 parent 54b6f15 commit accd95e

File tree

4 files changed

+104
-3
lines changed

4 files changed

+104
-3
lines changed

packages/ui/src/fields/Array/index.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import type {
66
} from 'payload'
77

88
import { getTranslation } from '@payloadcms/translations'
9-
import React, { Fragment, useCallback } from 'react'
9+
import React, { Fragment, useCallback, useMemo } from 'react'
1010
import { toast } from 'sonner'
1111

1212
import type { ClipboardPasteData } from '../../elements/ClipboardAction/types.js'
@@ -36,6 +36,7 @@ import { useDocumentInfo } from '../../providers/DocumentInfo/index.js'
3636
import { useLocale } from '../../providers/Locale/index.js'
3737
import { useTranslation } from '../../providers/Translation/index.js'
3838
import { scrollToID } from '../../utilities/scrollToID.js'
39+
import { mergeFieldStyles } from '../mergeFieldStyles.js'
3940
import { fieldBaseClass } from '../shared/index.js'
4041
import { ArrayRow } from './ArrayRow.js'
4142
import './index.scss'
@@ -44,6 +45,7 @@ const baseClass = 'array-field'
4445

4546
export const ArrayFieldComponent: ArrayFieldClientComponent = (props) => {
4647
const {
48+
field,
4749
field: {
4850
name,
4951
type,
@@ -299,6 +301,8 @@ export const ArrayFieldComponent: ArrayFieldClientComponent = (props) => {
299301
const showRequired = (readOnly || disabled) && rows.length === 0
300302
const showMinRows = (rows.length && rows.length < minRows) || (required && rows.length === 0)
301303

304+
const styles = useMemo(() => mergeFieldStyles(field), [field])
305+
302306
return (
303307
<div
304308
className={[
@@ -310,6 +314,7 @@ export const ArrayFieldComponent: ArrayFieldClientComponent = (props) => {
310314
.filter(Boolean)
311315
.join(' ')}
312316
id={`field-${path.replace(/\./g, '__')}`}
317+
style={styles}
313318
>
314319
{showError && (
315320
<RenderCustomComponent

test/fields/collections/Row/e2e.spec.ts

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,8 +224,49 @@ describe('Row', () => {
224224
getComputedStyle(el).getPropertyValue('--field-width').trim(),
225225
)
226226

227-
expect(leftVar).toBe('50%')
228-
expect(rightVar).toBe('50%')
227+
await expect(() => {
228+
expect(leftVar).toBe('50%')
229+
expect(rightVar).toBe('50%')
230+
}).toPass()
231+
232+
// Also assert inline style contains the var (robust to other inline styles)
233+
await expect(left).toHaveAttribute('style', /--field-width:\s*50%/)
234+
await expect(right).toHaveAttribute('style', /--field-width:\s*50%/)
235+
236+
// 2) Layout reflects the widths (same row, equal widths)
237+
const leftBox = await left.boundingBox()
238+
const rightBox = await right.boundingBox()
239+
240+
await expect(() => {
241+
// Same row
242+
expect(Math.round(leftBox.y)).toEqual(Math.round(rightBox.y))
243+
// Equal width (tolerate sub-pixel differences)
244+
expect(Math.round(leftBox.width)).toEqual(Math.round(rightBox.width))
245+
}).toPass()
246+
})
247+
248+
test('should respect admin.width for array fields inside a row', async () => {
249+
await page.goto(url.create)
250+
251+
// Target the Array field wrappers
252+
const left = page.locator('#field-arrayLeftColumn')
253+
const right = page.locator('#field-arrayRightColumn')
254+
255+
await expect(left).toBeVisible()
256+
await expect(right).toBeVisible()
257+
258+
// 1) CSS variable is applied (via mergeFieldStyles)
259+
const leftVar = await left.evaluate((el) =>
260+
getComputedStyle(el).getPropertyValue('--field-width').trim(),
261+
)
262+
const rightVar = await right.evaluate((el) =>
263+
getComputedStyle(el).getPropertyValue('--field-width').trim(),
264+
)
265+
266+
await expect(() => {
267+
expect(leftVar).toBe('50%')
268+
expect(rightVar).toBe('50%')
269+
}).toPass()
229270

230271
// Also assert inline style contains the var (robust to other inline styles)
231272
await expect(left).toHaveAttribute('style', /--field-width:\s*50%/)

test/fields/collections/Row/index.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,37 @@ const RowFields: CollectionConfig = {
178178
},
179179
],
180180
},
181+
{
182+
type: 'row',
183+
fields: [
184+
{
185+
name: 'arrayLeftColumn',
186+
type: 'array',
187+
admin: {
188+
width: '50%',
189+
},
190+
fields: [
191+
{
192+
name: 'leftArrayChild',
193+
type: 'text',
194+
},
195+
],
196+
},
197+
{
198+
name: 'arrayRightColumn',
199+
type: 'array',
200+
fields: [
201+
{
202+
name: 'rightArrayChild',
203+
type: 'text',
204+
},
205+
],
206+
admin: {
207+
width: '50%',
208+
},
209+
},
210+
],
211+
},
181212
],
182213
}
183214

test/fields/payload-types.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1194,6 +1194,18 @@ export interface RowField {
11941194
blockType: 'rightTextBlock';
11951195
}[]
11961196
| null;
1197+
arrayLeftColumn?:
1198+
| {
1199+
leftArrayChild?: string | null;
1200+
id?: string | null;
1201+
}[]
1202+
| null;
1203+
arrayRightColumn?:
1204+
| {
1205+
rightArrayChild?: string | null;
1206+
id?: string | null;
1207+
}[]
1208+
| null;
11971209
updatedAt: string;
11981210
createdAt: string;
11991211
}
@@ -2904,6 +2916,18 @@ export interface RowFieldsSelect<T extends boolean = true> {
29042916
blockName?: T;
29052917
};
29062918
};
2919+
arrayLeftColumn?:
2920+
| T
2921+
| {
2922+
leftArrayChild?: T;
2923+
id?: T;
2924+
};
2925+
arrayRightColumn?:
2926+
| T
2927+
| {
2928+
rightArrayChild?: T;
2929+
id?: T;
2930+
};
29072931
updatedAt?: T;
29082932
createdAt?: T;
29092933
}

0 commit comments

Comments
 (0)