Skip to content

Commit 143b6e3

Browse files
authored
feat: allow hiding the blockName field visible in blocks' headers via admin.disableBlockName (#11301)
Adds a new `admin.disableBlockName` property that allows you to disable the blockName field entirely in the admin view. It defaults to false for backwards compatibility.
1 parent 4ebe673 commit 143b6e3

File tree

9 files changed

+198
-12
lines changed

9 files changed

+198
-12
lines changed

docs/fields/blocks.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ The Blocks Field inherits all of the default options from the base [Field Admin
8484
| **`group`** | Text or localization object used to group this Block in the Blocks Drawer. |
8585
| **`initCollapsed`** | Set the initial collapsed state |
8686
| **`isSortable`** | Disable order sorting by setting this value to `false` |
87+
| **`disableBlockName`** | Hide the blockName field by setting this value to `true` |
8788

8889
#### Customizing the way your block is rendered in Lexical
8990

@@ -165,7 +166,7 @@ The `blockType` is saved as the slug of the block that has been selected.
165166

166167
**`blockName`**
167168

168-
The Admin Panel provides each block with a `blockName` field which optionally allows editors to label their blocks for better editability and readability.
169+
The Admin Panel provides each block with a `blockName` field which optionally allows editors to label their blocks for better editability and readability. This can be visually hidden via `admin.disableBlockName`.
169170

170171
## Example
171172

packages/payload/src/fields/config/client.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,15 @@ export const createClientBlocks = ({
126126
clientBlock.jsx = jsxResolved
127127
}
128128

129+
if (block?.admin?.disableBlockName) {
130+
// Check for existing admin object, this way we don't have to spread it in
131+
if (clientBlock.admin) {
132+
clientBlock.admin.disableBlockName = block.admin.disableBlockName
133+
} else {
134+
clientBlock.admin = { disableBlockName: block.admin.disableBlockName }
135+
}
136+
}
137+
129138
if (block.labels) {
130139
clientBlock.labels = {} as unknown as LabelsClient
131140

packages/payload/src/fields/config/sanitize.spec.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,4 +362,71 @@ describe('sanitizeFields', () => {
362362
expect(sanitizedFields).toStrictEqual([])
363363
})
364364
})
365+
describe('blocks', () => {
366+
it('should maintain admin.blockName true after sanitization', async () => {
367+
const fields: Field[] = [
368+
{
369+
name: 'noLabelBlock',
370+
type: 'blocks',
371+
blocks: [
372+
{
373+
slug: 'number',
374+
admin: {
375+
disableBlockName: true,
376+
},
377+
fields: [
378+
{
379+
name: 'testNumber',
380+
type: 'number',
381+
},
382+
],
383+
},
384+
],
385+
label: false,
386+
},
387+
]
388+
const sanitizedField = (
389+
await sanitizeFields({
390+
config,
391+
fields,
392+
validRelationships: [],
393+
})
394+
)[0] as BlocksField
395+
396+
const sanitizedBlock = sanitizedField.blocks[0]
397+
398+
expect(sanitizedBlock.admin?.disableBlockName).toStrictEqual(true)
399+
})
400+
it('should default admin.disableBlockName to true after sanitization', async () => {
401+
const fields: Field[] = [
402+
{
403+
name: 'noLabelBlock',
404+
type: 'blocks',
405+
blocks: [
406+
{
407+
slug: 'number',
408+
fields: [
409+
{
410+
name: 'testNumber',
411+
type: 'number',
412+
},
413+
],
414+
},
415+
],
416+
label: false,
417+
},
418+
]
419+
const sanitizedField = (
420+
await sanitizeFields({
421+
config,
422+
fields,
423+
validRelationships: [],
424+
})
425+
)[0] as BlocksField
426+
427+
const sanitizedBlock = sanitizedField.blocks[0]
428+
429+
expect(sanitizedBlock.admin?.disableBlockName).toStrictEqual(undefined)
430+
})
431+
})
365432
})

packages/payload/src/fields/config/types.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1378,6 +1378,12 @@ export type Block = {
13781378
}
13791379
/** Extension point to add your custom data. Available in server and client. */
13801380
custom?: Record<string, any>
1381+
/**
1382+
* Hides the block name field from the Block's header
1383+
*
1384+
* @default false
1385+
*/
1386+
disableBlockName?: boolean
13811387
group?: Record<string, string> | string
13821388
jsx?: PayloadComponent
13831389
}
@@ -1407,7 +1413,7 @@ export type Block = {
14071413
}
14081414

14091415
export type ClientBlock = {
1410-
admin?: Pick<Block['admin'], 'custom' | 'group'>
1416+
admin?: Pick<Block['admin'], 'custom' | 'disableBlockName' | 'group'>
14111417
fields: ClientField[]
14121418
labels?: LabelsClient
14131419
} & Pick<Block, 'imageAltText' | 'imageURL' | 'jsx' | 'slug'>

packages/ui/src/fields/Blocks/BlockRow.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ export const BlockRow: React.FC<BlocksFieldProps> = ({
8080

8181
const fieldHasErrors = hasSubmitted && errorCount > 0
8282

83+
const showBlockName = !block.admin?.disableBlockName
84+
8385
const classNames = [
8486
`${baseClass}__row`,
8587
fieldHasErrors ? `${baseClass}__row--has-errors` : `${baseClass}__row--no-errors`,
@@ -155,7 +157,7 @@ export const BlockRow: React.FC<BlocksFieldProps> = ({
155157
>
156158
{getTranslation(block.labels.singular, i18n)}
157159
</Pill>
158-
<SectionTitle path={`${path}.blockName`} readOnly={readOnly} />
160+
{showBlockName && <SectionTitle path={`${path}.blockName`} readOnly={readOnly} />}
159161
{fieldHasErrors && <ErrorPill count={errorCount} i18n={i18n} withMessage />}
160162
</Fragment>
161163
)}

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

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { AdminUrlUtil } from '../../../helpers/adminUrlUtil.js'
1616
import { initPayloadE2ENoConfig } from '../../../helpers/initPayloadE2ENoConfig.js'
1717
import { reInitializeDB } from '../../../helpers/reInitializeDB.js'
1818
import { RESTClient } from '../../../helpers/rest.js'
19-
import { TEST_TIMEOUT_LONG } from '../../../playwright.config.js'
19+
import { POLL_TOPASS_TIMEOUT, TEST_TIMEOUT_LONG } from '../../../playwright.config.js'
2020

2121
const filename = fileURLToPath(import.meta.url)
2222
const currentFolder = path.dirname(filename)
@@ -82,7 +82,7 @@ describe('Block fields', () => {
8282
const addedRow = page.locator('#field-blocks .blocks-field__row').last()
8383
await expect(addedRow).toBeVisible()
8484
await expect(addedRow.locator('.blocks-field__block-header')).toHaveText(
85-
'Custom Block Label: Content 04',
85+
'Custom Block Label: Content 05',
8686
)
8787
})
8888

@@ -156,7 +156,7 @@ describe('Block fields', () => {
156156
await duplicateButton.click()
157157

158158
const blocks = page.locator('#field-blocks > .blocks-field__rows > div')
159-
expect(await blocks.count()).toEqual(4)
159+
expect(await blocks.count()).toEqual(5)
160160
})
161161

162162
test('should save when duplicating subblocks', async () => {
@@ -171,7 +171,7 @@ describe('Block fields', () => {
171171
await duplicateButton.click()
172172

173173
const blocks = page.locator('#field-blocks > .blocks-field__rows > div')
174-
expect(await blocks.count()).toEqual(4)
174+
expect(await blocks.count()).toEqual(5)
175175

176176
await page.click('#action-save')
177177
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
@@ -379,6 +379,33 @@ describe('Block fields', () => {
379379
})
380380
})
381381

382+
describe('blockNames', () => {
383+
test('should show blockName field', async () => {
384+
await page.goto(url.create)
385+
386+
const blockWithBlockname = page.locator('#field-blocks .blocks-field__rows #blocks-row-1')
387+
388+
const blocknameField = blockWithBlockname.locator('.section-title')
389+
390+
await expect(async () => await expect(blocknameField).toBeVisible()).toPass({
391+
timeout: POLL_TOPASS_TIMEOUT,
392+
})
393+
394+
await expect(blocknameField).toHaveAttribute('data-value', 'Second block')
395+
})
396+
397+
test("should not show blockName field when it's disabled", async () => {
398+
await page.goto(url.create)
399+
const blockWithBlockname = page.locator('#field-blocks .blocks-field__rows #blocks-row-3')
400+
401+
await expect(
402+
async () => await expect(blockWithBlockname.locator('.section-title')).toBeHidden(),
403+
).toPass({
404+
timeout: POLL_TOPASS_TIMEOUT,
405+
})
406+
})
407+
})
408+
382409
describe('block groups', () => {
383410
test('should render group labels', async () => {
384411
await page.goto(url.create)

test/fields/collections/Blocks/index.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,19 @@ export const getBlocksField = (prefix?: string): BlocksField => ({
3030
},
3131
],
3232
},
33+
{
34+
slug: prefix ? `${prefix}NoBlockname` : 'noBlockname',
35+
interfaceName: prefix ? `${prefix}NoBlockname` : 'NoBlockname',
36+
admin: {
37+
disableBlockName: true,
38+
},
39+
fields: [
40+
{
41+
name: 'text',
42+
type: 'text',
43+
},
44+
],
45+
},
3346
{
3447
slug: prefix ? `${prefix}Number` : 'number',
3548
interfaceName: prefix ? `${prefix}NumberBlock` : 'NumberBlock',

test/fields/collections/Blocks/shared.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ export const getBlocksFieldSeedData = (prefix?: string): any => [
3232
},
3333
],
3434
},
35+
{
36+
blockType: prefix ? `${prefix}NoBlockname` : 'noBlockname',
37+
text: 'Hello world',
38+
},
3539
]
3640

3741
export const blocksDoc: Partial<BlockField> = {

0 commit comments

Comments
 (0)