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;
}