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.
This commit is contained in:
Paul
2025-03-05 18:24:39 +00:00
committed by GitHub
parent 4ebe67324a
commit 143b6e3b8e
9 changed files with 198 additions and 12 deletions

View File

@@ -84,6 +84,7 @@ The Blocks Field inherits all of the default options from the base [Field Admin
| **`group`** | Text or localization object used to group this Block in the Blocks Drawer. |
| **`initCollapsed`** | Set the initial collapsed state |
| **`isSortable`** | Disable order sorting by setting this value to `false` |
| **`disableBlockName`** | Hide the blockName field by setting this value to `true` |
#### Customizing the way your block is rendered in Lexical
@@ -165,7 +166,7 @@ The `blockType` is saved as the slug of the block that has been selected.
**`blockName`**
The Admin Panel provides each block with a `blockName` field which optionally allows editors to label their blocks for better editability and readability.
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`.
## Example

View File

@@ -126,6 +126,15 @@ export const createClientBlocks = ({
clientBlock.jsx = jsxResolved
}
if (block?.admin?.disableBlockName) {
// Check for existing admin object, this way we don't have to spread it in
if (clientBlock.admin) {
clientBlock.admin.disableBlockName = block.admin.disableBlockName
} else {
clientBlock.admin = { disableBlockName: block.admin.disableBlockName }
}
}
if (block.labels) {
clientBlock.labels = {} as unknown as LabelsClient

View File

@@ -362,4 +362,71 @@ describe('sanitizeFields', () => {
expect(sanitizedFields).toStrictEqual([])
})
})
describe('blocks', () => {
it('should maintain admin.blockName true after sanitization', async () => {
const fields: Field[] = [
{
name: 'noLabelBlock',
type: 'blocks',
blocks: [
{
slug: 'number',
admin: {
disableBlockName: true,
},
fields: [
{
name: 'testNumber',
type: 'number',
},
],
},
],
label: false,
},
]
const sanitizedField = (
await sanitizeFields({
config,
fields,
validRelationships: [],
})
)[0] as BlocksField
const sanitizedBlock = sanitizedField.blocks[0]
expect(sanitizedBlock.admin?.disableBlockName).toStrictEqual(true)
})
it('should default admin.disableBlockName to true after sanitization', async () => {
const fields: Field[] = [
{
name: 'noLabelBlock',
type: 'blocks',
blocks: [
{
slug: 'number',
fields: [
{
name: 'testNumber',
type: 'number',
},
],
},
],
label: false,
},
]
const sanitizedField = (
await sanitizeFields({
config,
fields,
validRelationships: [],
})
)[0] as BlocksField
const sanitizedBlock = sanitizedField.blocks[0]
expect(sanitizedBlock.admin?.disableBlockName).toStrictEqual(undefined)
})
})
})

View File

@@ -1378,6 +1378,12 @@ export type Block = {
}
/** Extension point to add your custom data. Available in server and client. */
custom?: Record<string, any>
/**
* Hides the block name field from the Block's header
*
* @default false
*/
disableBlockName?: boolean
group?: Record<string, string> | string
jsx?: PayloadComponent
}
@@ -1407,7 +1413,7 @@ export type Block = {
}
export type ClientBlock = {
admin?: Pick<Block['admin'], 'custom' | 'group'>
admin?: Pick<Block['admin'], 'custom' | 'disableBlockName' | 'group'>
fields: ClientField[]
labels?: LabelsClient
} & Pick<Block, 'imageAltText' | 'imageURL' | 'jsx' | 'slug'>

View File

@@ -80,6 +80,8 @@ export const BlockRow: React.FC<BlocksFieldProps> = ({
const fieldHasErrors = hasSubmitted && errorCount > 0
const showBlockName = !block.admin?.disableBlockName
const classNames = [
`${baseClass}__row`,
fieldHasErrors ? `${baseClass}__row--has-errors` : `${baseClass}__row--no-errors`,
@@ -155,7 +157,7 @@ export const BlockRow: React.FC<BlocksFieldProps> = ({
>
{getTranslation(block.labels.singular, i18n)}
</Pill>
<SectionTitle path={`${path}.blockName`} readOnly={readOnly} />
{showBlockName && <SectionTitle path={`${path}.blockName`} readOnly={readOnly} />}
{fieldHasErrors && <ErrorPill count={errorCount} i18n={i18n} withMessage />}
</Fragment>
)}

View File

@@ -16,7 +16,7 @@ import { AdminUrlUtil } from '../../../helpers/adminUrlUtil.js'
import { initPayloadE2ENoConfig } from '../../../helpers/initPayloadE2ENoConfig.js'
import { reInitializeDB } from '../../../helpers/reInitializeDB.js'
import { RESTClient } from '../../../helpers/rest.js'
import { TEST_TIMEOUT_LONG } from '../../../playwright.config.js'
import { POLL_TOPASS_TIMEOUT, TEST_TIMEOUT_LONG } from '../../../playwright.config.js'
const filename = fileURLToPath(import.meta.url)
const currentFolder = path.dirname(filename)
@@ -82,7 +82,7 @@ describe('Block fields', () => {
const addedRow = page.locator('#field-blocks .blocks-field__row').last()
await expect(addedRow).toBeVisible()
await expect(addedRow.locator('.blocks-field__block-header')).toHaveText(
'Custom Block Label: Content 04',
'Custom Block Label: Content 05',
)
})
@@ -156,7 +156,7 @@ describe('Block fields', () => {
await duplicateButton.click()
const blocks = page.locator('#field-blocks > .blocks-field__rows > div')
expect(await blocks.count()).toEqual(4)
expect(await blocks.count()).toEqual(5)
})
test('should save when duplicating subblocks', async () => {
@@ -171,7 +171,7 @@ describe('Block fields', () => {
await duplicateButton.click()
const blocks = page.locator('#field-blocks > .blocks-field__rows > div')
expect(await blocks.count()).toEqual(4)
expect(await blocks.count()).toEqual(5)
await page.click('#action-save')
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
@@ -379,6 +379,33 @@ describe('Block fields', () => {
})
})
describe('blockNames', () => {
test('should show blockName field', async () => {
await page.goto(url.create)
const blockWithBlockname = page.locator('#field-blocks .blocks-field__rows #blocks-row-1')
const blocknameField = blockWithBlockname.locator('.section-title')
await expect(async () => await expect(blocknameField).toBeVisible()).toPass({
timeout: POLL_TOPASS_TIMEOUT,
})
await expect(blocknameField).toHaveAttribute('data-value', 'Second block')
})
test("should not show blockName field when it's disabled", async () => {
await page.goto(url.create)
const blockWithBlockname = page.locator('#field-blocks .blocks-field__rows #blocks-row-3')
await expect(
async () => await expect(blockWithBlockname.locator('.section-title')).toBeHidden(),
).toPass({
timeout: POLL_TOPASS_TIMEOUT,
})
})
})
describe('block groups', () => {
test('should render group labels', async () => {
await page.goto(url.create)

View File

@@ -30,6 +30,19 @@ export const getBlocksField = (prefix?: string): BlocksField => ({
},
],
},
{
slug: prefix ? `${prefix}NoBlockname` : 'noBlockname',
interfaceName: prefix ? `${prefix}NoBlockname` : 'NoBlockname',
admin: {
disableBlockName: true,
},
fields: [
{
name: 'text',
type: 'text',
},
],
},
{
slug: prefix ? `${prefix}Number` : 'number',
interfaceName: prefix ? `${prefix}NumberBlock` : 'NumberBlock',

View File

@@ -32,6 +32,10 @@ export const getBlocksFieldSeedData = (prefix?: string): any => [
},
],
},
{
blockType: prefix ? `${prefix}NoBlockname` : 'noBlockname',
text: 'Hello world',
},
]
export const blocksDoc: Partial<BlockField> = {

View File

@@ -699,16 +699,29 @@ export interface ArrayField {
*/
export interface BlockField {
id: string;
blocks: (ContentBlock | NumberBlock | SubBlocksBlock | TabsBlock)[];
duplicate: (ContentBlock | NumberBlock | SubBlocksBlock | TabsBlock)[];
blocks: (ContentBlock | NoBlockname | NumberBlock | SubBlocksBlock | TabsBlock)[];
duplicate: (ContentBlock | NoBlockname | NumberBlock | SubBlocksBlock | TabsBlock)[];
collapsedByDefaultBlocks: (
| LocalizedContentBlock
| LocalizedNoBlockname
| LocalizedNumberBlock
| LocalizedSubBlocksBlock
| LocalizedTabsBlock
)[];
disableSort: (
| LocalizedContentBlock
| LocalizedNoBlockname
| LocalizedNumberBlock
| LocalizedSubBlocksBlock
| LocalizedTabsBlock
)[];
localizedBlocks: (
| LocalizedContentBlock
| LocalizedNoBlockname
| LocalizedNumberBlock
| LocalizedSubBlocksBlock
| LocalizedTabsBlock
)[];
disableSort: (LocalizedContentBlock | LocalizedNumberBlock | LocalizedSubBlocksBlock | LocalizedTabsBlock)[];
localizedBlocks: (LocalizedContentBlock | LocalizedNumberBlock | LocalizedSubBlocksBlock | LocalizedTabsBlock)[];
i18nBlocks?:
| {
text?: string | null;
@@ -883,6 +896,16 @@ export interface ContentBlock {
blockName?: string | null;
blockType: 'content';
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "NoBlockname".
*/
export interface NoBlockname {
text: string;
id?: string | null;
blockName?: string | null;
blockType: 'noBlockname';
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "NumberBlock".
@@ -939,6 +962,16 @@ export interface LocalizedContentBlock {
blockName?: string | null;
blockType: 'localizedContent';
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "localizedNoBlockname".
*/
export interface LocalizedNoBlockname {
text: string;
id?: string | null;
blockName?: string | null;
blockType: 'localizedNoBlockname';
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "localizedNumberBlock".
@@ -1801,7 +1834,7 @@ export interface TabsField {
text: string;
id?: string | null;
}[];
blocks: (ContentBlock | NumberBlock | SubBlocksBlock | TabsBlock)[];
blocks: (ContentBlock | NoBlockname | NumberBlock | SubBlocksBlock | TabsBlock)[];
group: {
number: number;
};
@@ -2461,6 +2494,7 @@ export interface BlockFieldsSelect<T extends boolean = true> {
| T
| {
content?: T | ContentBlockSelect<T>;
noBlockname?: T | NoBlocknameSelect<T>;
number?: T | NumberBlockSelect<T>;
subBlocks?: T | SubBlocksBlockSelect<T>;
tabs?: T | TabsBlockSelect<T>;
@@ -2469,6 +2503,7 @@ export interface BlockFieldsSelect<T extends boolean = true> {
| T
| {
content?: T | ContentBlockSelect<T>;
noBlockname?: T | NoBlocknameSelect<T>;
number?: T | NumberBlockSelect<T>;
subBlocks?: T | SubBlocksBlockSelect<T>;
tabs?: T | TabsBlockSelect<T>;
@@ -2477,6 +2512,7 @@ export interface BlockFieldsSelect<T extends boolean = true> {
| T
| {
localizedContent?: T | LocalizedContentBlockSelect<T>;
localizedNoBlockname?: T | LocalizedNoBlocknameSelect<T>;
localizedNumber?: T | LocalizedNumberBlockSelect<T>;
localizedSubBlocks?: T | LocalizedSubBlocksBlockSelect<T>;
localizedTabs?: T | LocalizedTabsBlockSelect<T>;
@@ -2485,6 +2521,7 @@ export interface BlockFieldsSelect<T extends boolean = true> {
| T
| {
localizedContent?: T | LocalizedContentBlockSelect<T>;
localizedNoBlockname?: T | LocalizedNoBlocknameSelect<T>;
localizedNumber?: T | LocalizedNumberBlockSelect<T>;
localizedSubBlocks?: T | LocalizedSubBlocksBlockSelect<T>;
localizedTabs?: T | LocalizedTabsBlockSelect<T>;
@@ -2493,6 +2530,7 @@ export interface BlockFieldsSelect<T extends boolean = true> {
| T
| {
localizedContent?: T | LocalizedContentBlockSelect<T>;
localizedNoBlockname?: T | LocalizedNoBlocknameSelect<T>;
localizedNumber?: T | LocalizedNumberBlockSelect<T>;
localizedSubBlocks?: T | LocalizedSubBlocksBlockSelect<T>;
localizedTabs?: T | LocalizedTabsBlockSelect<T>;
@@ -2690,6 +2728,15 @@ export interface ContentBlockSelect<T extends boolean = true> {
id?: T;
blockName?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "NoBlockname_select".
*/
export interface NoBlocknameSelect<T extends boolean = true> {
text?: T;
id?: T;
blockName?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "NumberBlock_select".
@@ -2739,6 +2786,15 @@ export interface LocalizedContentBlockSelect<T extends boolean = true> {
id?: T;
blockName?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "localizedNoBlockname_select".
*/
export interface LocalizedNoBlocknameSelect<T extends boolean = true> {
text?: T;
id?: T;
blockName?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "localizedNumberBlock_select".
@@ -3404,6 +3460,7 @@ export interface TabsFieldsSelect<T extends boolean = true> {
| T
| {
content?: T | ContentBlockSelect<T>;
noBlockname?: T | NoBlocknameSelect<T>;
number?: T | NumberBlockSelect<T>;
subBlocks?: T | SubBlocksBlockSelect<T>;
tabs?: T | TabsBlockSelect<T>;