diff --git a/packages/ui/src/fields/Array/index.tsx b/packages/ui/src/fields/Array/index.tsx index 848971467..0358f459a 100644 --- a/packages/ui/src/fields/Array/index.tsx +++ b/packages/ui/src/fields/Array/index.tsx @@ -6,7 +6,7 @@ import type { } from 'payload' import { getTranslation } from '@payloadcms/translations' -import React, { Fragment, useCallback } from 'react' +import React, { Fragment, useCallback, useMemo } from 'react' import { toast } from 'sonner' import type { ClipboardPasteData } from '../../elements/ClipboardAction/types.js' @@ -36,6 +36,7 @@ import { useDocumentInfo } from '../../providers/DocumentInfo/index.js' import { useLocale } from '../../providers/Locale/index.js' import { useTranslation } from '../../providers/Translation/index.js' import { scrollToID } from '../../utilities/scrollToID.js' +import { mergeFieldStyles } from '../mergeFieldStyles.js' import { fieldBaseClass } from '../shared/index.js' import { ArrayRow } from './ArrayRow.js' import './index.scss' @@ -44,6 +45,7 @@ const baseClass = 'array-field' export const ArrayFieldComponent: ArrayFieldClientComponent = (props) => { const { + field, field: { name, type, @@ -299,6 +301,8 @@ export const ArrayFieldComponent: ArrayFieldClientComponent = (props) => { const showRequired = (readOnly || disabled) && rows.length === 0 const showMinRows = (rows.length && rows.length < minRows) || (required && rows.length === 0) + const styles = useMemo(() => mergeFieldStyles(field), [field]) + return (
{ .filter(Boolean) .join(' ')} id={`field-${path.replace(/\./g, '__')}`} + style={styles} > {showError && ( { getComputedStyle(el).getPropertyValue('--field-width').trim(), ) - expect(leftVar).toBe('50%') - expect(rightVar).toBe('50%') + await expect(() => { + expect(leftVar).toBe('50%') + expect(rightVar).toBe('50%') + }).toPass() + + // Also assert inline style contains the var (robust to other inline styles) + await expect(left).toHaveAttribute('style', /--field-width:\s*50%/) + await expect(right).toHaveAttribute('style', /--field-width:\s*50%/) + + // 2) Layout reflects the widths (same row, equal widths) + const leftBox = await left.boundingBox() + const rightBox = await right.boundingBox() + + await expect(() => { + // Same row + expect(Math.round(leftBox.y)).toEqual(Math.round(rightBox.y)) + // Equal width (tolerate sub-pixel differences) + expect(Math.round(leftBox.width)).toEqual(Math.round(rightBox.width)) + }).toPass() + }) + + test('should respect admin.width for array fields inside a row', async () => { + await page.goto(url.create) + + // Target the Array field wrappers + const left = page.locator('#field-arrayLeftColumn') + const right = page.locator('#field-arrayRightColumn') + + await expect(left).toBeVisible() + await expect(right).toBeVisible() + + // 1) CSS variable is applied (via mergeFieldStyles) + const leftVar = await left.evaluate((el) => + getComputedStyle(el).getPropertyValue('--field-width').trim(), + ) + const rightVar = await right.evaluate((el) => + getComputedStyle(el).getPropertyValue('--field-width').trim(), + ) + + await expect(() => { + expect(leftVar).toBe('50%') + expect(rightVar).toBe('50%') + }).toPass() // Also assert inline style contains the var (robust to other inline styles) await expect(left).toHaveAttribute('style', /--field-width:\s*50%/) diff --git a/test/fields/collections/Row/index.ts b/test/fields/collections/Row/index.ts index c25d47089..1142a68af 100644 --- a/test/fields/collections/Row/index.ts +++ b/test/fields/collections/Row/index.ts @@ -178,6 +178,37 @@ const RowFields: CollectionConfig = { }, ], }, + { + type: 'row', + fields: [ + { + name: 'arrayLeftColumn', + type: 'array', + admin: { + width: '50%', + }, + fields: [ + { + name: 'leftArrayChild', + type: 'text', + }, + ], + }, + { + name: 'arrayRightColumn', + type: 'array', + fields: [ + { + name: 'rightArrayChild', + type: 'text', + }, + ], + admin: { + width: '50%', + }, + }, + ], + }, ], } diff --git a/test/fields/payload-types.ts b/test/fields/payload-types.ts index 6de3e8529..50b383869 100644 --- a/test/fields/payload-types.ts +++ b/test/fields/payload-types.ts @@ -1194,6 +1194,18 @@ export interface RowField { blockType: 'rightTextBlock'; }[] | null; + arrayLeftColumn?: + | { + leftArrayChild?: string | null; + id?: string | null; + }[] + | null; + arrayRightColumn?: + | { + rightArrayChild?: string | null; + id?: string | null; + }[] + | null; updatedAt: string; createdAt: string; } @@ -2904,6 +2916,18 @@ export interface RowFieldsSelect { blockName?: T; }; }; + arrayLeftColumn?: + | T + | { + leftArrayChild?: T; + id?: T; + }; + arrayRightColumn?: + | T + | { + rightArrayChild?: T; + id?: T; + }; updatedAt?: T; createdAt?: T; }