From 30ea8e1bac641b3ce685980f3ed6a929febe379f Mon Sep 17 00:00:00 2001 From: Patrik <35232443+PatrikKozak@users.noreply.github.com> Date: Mon, 18 Aug 2025 12:15:40 -0400 Subject: [PATCH] fix(ui): blocks field not respecting width styles in row layouts (#13502) ### What? This PR applies `mergeFieldStyles` to the `BlocksField` component, ensuring that custom admin styles such as `width` are correctly respected when Blocks fields are placed inside row layouts. ### Why? Previously, Blocks fields did not inherit or apply their `admin.width` (or other merged field styles). For example, when placing two Blocks fields side by side inside a row with `width: '50%'`, the widths were ignored, causing layout issues. ### How? - Imported and used `mergeFieldStyles` within `BlocksField`. - Applied the merged styles to the root `
` via the `style` prop, consistent with how other field components (like `TextField`) handle styles. Fixes #13498 --- packages/ui/src/fields/Blocks/index.tsx | 5 +++ test/fields/collections/Row/e2e.spec.ts | 37 ++++++++++++++++++++++ test/fields/collections/Row/index.ts | 41 +++++++++++++++++++++++++ test/fields/payload-types.ts | 38 +++++++++++++++++++++++ 4 files changed, 121 insertions(+) diff --git a/packages/ui/src/fields/Blocks/index.tsx b/packages/ui/src/fields/Blocks/index.tsx index 0981889951..1d6530222b 100644 --- a/packages/ui/src/fields/Blocks/index.tsx +++ b/packages/ui/src/fields/Blocks/index.tsx @@ -35,6 +35,7 @@ import './index.scss' import { FieldDescription } from '../FieldDescription/index.js' import { FieldError } from '../FieldError/index.js' import { FieldLabel } from '../FieldLabel/index.js' +import { mergeFieldStyles } from '../mergeFieldStyles.js' import { fieldBaseClass } from '../shared/index.js' import { BlockRow } from './BlockRow.js' import { BlocksDrawer } from './BlocksDrawer/index.js' @@ -45,6 +46,7 @@ const BlocksFieldComponent: BlocksFieldClientComponent = (props) => { const { i18n, t } = useTranslation() const { + field, field: { name, type, @@ -294,6 +296,8 @@ const BlocksFieldComponent: BlocksFieldClientComponent = (props) => { const showMinRows = rows.length < minRows || (required && rows.length === 0) const showRequired = readOnly && rows.length === 0 + const styles = useMemo(() => mergeFieldStyles(field), [field]) + return (
{ .filter(Boolean) .join(' ')} id={`field-${path?.replace(/\./g, '__')}`} + style={styles} > {showError && ( { expect(fieldABox.height).toEqual(fieldBBox.height) }).toPass() }) + + test('should respect admin.width for Blocks fields inside a row', async () => { + await page.goto(url.create) + + // Target the Blocks field wrappers + const left = page.locator('#field-leftColumn') + const right = page.locator('#field-rightColumn') + + 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(), + ) + + expect(leftVar).toBe('50%') + expect(rightVar).toBe('50%') + + // 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() + }) }) diff --git a/test/fields/collections/Row/index.ts b/test/fields/collections/Row/index.ts index db9aced88f..c25d470891 100644 --- a/test/fields/collections/Row/index.ts +++ b/test/fields/collections/Row/index.ts @@ -137,6 +137,47 @@ const RowFields: CollectionConfig = { }, ], }, + { + type: 'row', + fields: [ + { + name: 'leftColumn', + type: 'blocks', + blocks: [ + { + slug: 'leftTextBlock', + fields: [ + { + name: 'leftText', + type: 'text', + }, + ], + }, + ], + admin: { + width: '50%', + }, + }, + { + name: 'rightColumn', + type: 'blocks', + blocks: [ + { + slug: 'rightTextBlock', + fields: [ + { + name: 'rightText', + type: 'text', + }, + ], + }, + ], + admin: { + width: '50%', + }, + }, + ], + }, ], } diff --git a/test/fields/payload-types.ts b/test/fields/payload-types.ts index 8ee74af84f..d00c6c3497 100644 --- a/test/fields/payload-types.ts +++ b/test/fields/payload-types.ts @@ -1129,6 +1129,22 @@ export interface RowField { no_set_width_within_row_b?: string | null; no_set_width_within_row_c?: string | null; field_20_percent_width_within_row_d?: string | null; + leftColumn?: + | { + leftText?: string | null; + id?: string | null; + blockName?: string | null; + blockType: 'leftTextBlock'; + }[] + | null; + rightColumn?: + | { + rightText?: string | null; + id?: string | null; + blockName?: string | null; + blockType: 'rightTextBlock'; + }[] + | null; updatedAt: string; createdAt: string; } @@ -2760,6 +2776,28 @@ export interface RowFieldsSelect { no_set_width_within_row_b?: T; no_set_width_within_row_c?: T; field_20_percent_width_within_row_d?: T; + leftColumn?: + | T + | { + leftTextBlock?: + | T + | { + leftText?: T; + id?: T; + blockName?: T; + }; + }; + rightColumn?: + | T + | { + rightTextBlock?: + | T + | { + rightText?: T; + id?: T; + blockName?: T; + }; + }; updatedAt?: T; createdAt?: T; }