Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Block editor: Self nesting and circular nesting block fix #66121

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions packages/block-editor/src/store/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -1552,19 +1552,26 @@ export function getTemplateLock( state, rootClientId ) {
* @param {string|Object} blockNameOrType The block type object, e.g., the response
* from the block directory; or a string name of
* an installed block type, e.g.' core/paragraph'.
* @param {Set} checkedBlocks Set of block names that have already been checked.
*
* @return {boolean} Whether the given block type is allowed to be inserted.
*/
const isBlockVisibleInTheInserter = ( state, blockNameOrType ) => {
const isBlockVisibleInTheInserter = (
state,
blockNameOrType,
checkedBlocks = new Set()
) => {
let blockType;
let blockName;

if ( blockNameOrType && 'object' === typeof blockNameOrType ) {
blockType = blockNameOrType;
blockName = blockNameOrType.name;
} else {
blockType = getBlockType( blockNameOrType );
blockName = blockNameOrType;
}

if ( ! blockType ) {
return false;
}
Expand All @@ -1580,11 +1587,22 @@ const isBlockVisibleInTheInserter = ( state, blockNameOrType ) => {
return false;
}

if ( checkedBlocks.has( blockName ) ) {
return false;
}

checkedBlocks.add( blockName );

// If parent blocks are not visible, child blocks should be hidden too.
if ( !! blockType.parent?.length ) {
return blockType.parent.some(
( name ) =>
isBlockVisibleInTheInserter( state, name ) ||
( blockName !== name &&
isBlockVisibleInTheInserter(
state,
name,
checkedBlocks
) ) ||
// Exception for blocks with post-content parent,
// the root level is often consider as "core/post-content".
// This exception should only apply to the post editor ideally though.
Expand Down
9 changes: 9 additions & 0 deletions packages/blocks/src/api/registration.js
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,15 @@ export function registerBlockType( blockNameOrMetadata, settings ) {
return;
}

if ( 1 === settings?.parent?.length && name === settings.parent[ 0 ] ) {
warning(
'Block "' +
name +
'" cannot be a parent of itself. Please remove the block name from the parent list.'
);
return;
}

if ( ! /^[a-z][a-z0-9-]*\/[a-z][a-z0-9-]*$/.test( name ) ) {
warning(
'Block names must contain a namespace prefix, include only lowercase alphanumeric characters or dashes, and start with a letter. Example: my-plugin/my-custom-block'
Expand Down
11 changes: 11 additions & 0 deletions packages/blocks/src/api/test/registration.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,17 @@ describe( 'blocks', () => {
expect( block ).toBeUndefined();
} );

it( 'Should reject blocks with the block name itself as the only parent attribute value', () => {
const block = registerBlockType( 'core/test-block', {
...defaultBlockSettings,
parent: [ 'core/test-block' ],
} );
expect( console ).toHaveWarnedWith(
'Block "core/test-block" cannot be a parent of itself. Please remove the block name from the parent list.'
);
expect( block ).toBeUndefined();
} );

it( 'should reject blocks with uppercase characters', () => {
const block = registerBlockType( 'Core/Paragraph' );
expect( console ).toHaveWarnedWith(
Expand Down
Loading