diff --git a/packages/block-editor/src/components/block-actions/index.js b/packages/block-editor/src/components/block-actions/index.js index c10d03a5c060f..ffb14ffca8c5e 100644 --- a/packages/block-editor/src/components/block-actions/index.js +++ b/packages/block-editor/src/components/block-actions/index.js @@ -117,8 +117,14 @@ export default compose( [ return; } + const { + getGroupingBlockName, + } = select( 'core/blocks' ); + + const groupingBlockName = getGroupingBlockName(); + // Activate the `transform` on `core/group` which does the conversion - const newBlocks = switchToBlockType( blocks, 'core/group' ); + const newBlocks = switchToBlockType( blocks, groupingBlockName ); if ( ! newBlocks ) { return; diff --git a/packages/blocks/src/api/factory.js b/packages/blocks/src/api/factory.js index 92074c35a6baf..8d659b1c87886 100644 --- a/packages/blocks/src/api/factory.js +++ b/packages/blocks/src/api/factory.js @@ -25,7 +25,7 @@ import { createHooks, applyFilters } from '@wordpress/hooks'; /** * Internal dependencies */ -import { getBlockType, getBlockTypes } from './registration'; +import { getBlockType, getBlockTypes, getGroupingBlockName } from './registration'; import { normalizeBlockType } from './utils'; /** @@ -147,8 +147,8 @@ const isPossibleTransformForSource = ( transform, direction, blocks ) => { return false; } - // Don't allow single 'core/group' blocks to be transformed into - // a 'core/group' block. + // Don't allow single Grouping blocks to be transformed into + // a Grouping block. if ( ! isMultiBlock && isContainerGroupBlock( sourceBlock.name ) && isContainerGroupBlock( transform.blockName ) ) { return false; } @@ -252,7 +252,7 @@ export const isWildcardBlockTransform = ( t ) => t && t.type === 'block' && Arra * * @return {boolean} whether or not the Block is the container Block type */ -export const isContainerGroupBlock = ( name ) => name === 'core/group'; +export const isContainerGroupBlock = ( name ) => name === getGroupingBlockName(); /** * Determines whether the provided Blocks are of the same type @@ -374,7 +374,7 @@ export function switchToBlockType( blocks, name ) { const firstBlock = blocksArray[ 0 ]; const sourceName = firstBlock.name; - // Unless it's a `core/group` Block then for multi block selections + // Unless it's a Grouping Block then for multi block selections // check that all Blocks are of the same type otherwise // we can't run a conversion if ( ! isContainerGroupBlock( name ) && isMultiBlock && ! isBlockSelectionOfSameType( blocksArray ) ) { diff --git a/packages/blocks/src/api/test/factory.js b/packages/blocks/src/api/test/factory.js index 973964bfe05ba..bb34cb60cf2cd 100644 --- a/packages/blocks/src/api/test/factory.js +++ b/packages/blocks/src/api/test/factory.js @@ -23,6 +23,7 @@ import { getBlockTypes, registerBlockType, unregisterBlockType, + setGroupingBlockName, } from '../registration'; describe( 'block factory', () => { @@ -1554,12 +1555,34 @@ describe( 'block factory', () => { } ); describe( 'isContainerGroupBlock', () => { - it( 'should return true when passed block name matches "core/group"', () => { - expect( isContainerGroupBlock( 'core/group' ) ).toBe( true ); + beforeEach( () => { + registerBlockType( 'core/registered-grouping-block', { + attributes: { + value: { + type: 'string', + }, + }, + transforms: { + from: [ { + type: 'block', + blocks: [ '*' ], + transform: noop, + } ], + }, + save: noop, + category: 'common', + title: 'A Block with InnerBlocks that supports grouping', + } ); + } ); + + it( 'should return true when passed block name that matches the registered "Grouping" Block', () => { + setGroupingBlockName( 'registered-grouping-block' ); + expect( isContainerGroupBlock( 'registered-grouping-block' ) ).toBe( true ); } ); - it( 'should return false when passed block name does not match "core/group"', () => { - expect( isContainerGroupBlock( 'core/some-test-name' ) ).toBe( false ); + it( 'should return false when passed block name does not match the registered "Grouping" Block', () => { + setGroupingBlockName( 'registered-grouping-block' ); + expect( isContainerGroupBlock( 'core/group' ) ).toBe( false ); } ); } ); diff --git a/packages/e2e-tests/plugins/custom-grouping-block.php b/packages/e2e-tests/plugins/custom-grouping-block.php new file mode 100644 index 0000000000000..da29a788ff7bc --- /dev/null +++ b/packages/e2e-tests/plugins/custom-grouping-block.php @@ -0,0 +1,27 @@ + { + return wp.blocks.createBlock( name, attributes, innerBlocks ); + } ); + + return wp.blocks.createBlock( 'test/alternative-group-block', {}, groupInnerBlocks ); + }, + } ], + }, + } ); +}() ); diff --git a/packages/e2e-tests/specs/__snapshots__/block-grouping.test.js.snap b/packages/e2e-tests/specs/__snapshots__/block-grouping.test.js.snap index e41b27cdf3aa1..4b786e99fdb23 100644 --- a/packages/e2e-tests/specs/__snapshots__/block-grouping.test.js.snap +++ b/packages/e2e-tests/specs/__snapshots__/block-grouping.test.js.snap @@ -97,3 +97,19 @@ exports[`Block Grouping Preserving selected blocks attributes preserves width al " `; + +exports[`Block Grouping Registering alternative Blocks to handle Grouping interactions should use registered grouping block for grouping interactions 1`] = ` +" + +

First Paragraph

+ + + +

Second Paragraph

+ + + +

Third Paragraph

+ +" +`; diff --git a/packages/e2e-tests/specs/block-grouping.test.js b/packages/e2e-tests/specs/block-grouping.test.js index a1b25f055edde..3a42f3f6e43f6 100644 --- a/packages/e2e-tests/specs/block-grouping.test.js +++ b/packages/e2e-tests/specs/block-grouping.test.js @@ -10,6 +10,8 @@ import { transformBlockTo, getAllBlocks, getAvailableBlockTransforms, + activatePlugin, + deactivatePlugin, } from '@wordpress/e2e-test-utils'; async function insertBlocksOfSameType() { @@ -111,7 +113,7 @@ describe( 'Block Grouping', () => { } ); } ); - describe( 'Container Block availability', () => { + describe( 'Grouping Block availability', () => { beforeEach( async () => { // Disable the Group block await page.evaluate( () => { @@ -133,7 +135,7 @@ describe( 'Block Grouping', () => { } ); } ); - it( 'does not show group transform if container block is disabled', async () => { + it( 'does not show group transform if Grouping block is disabled', async () => { const availableTransforms = await getAvailableBlockTransforms(); expect( @@ -141,7 +143,7 @@ describe( 'Block Grouping', () => { ).not.toContain( 'Group' ); } ); - it( 'does not show group option in the options toolbar if container block is disabled ', async () => { + it( 'does not show group option in the options toolbar if Grouping block is disabled ', async () => { await clickBlockToolbarButton( 'More options' ); const blockOptionsDropdownHTML = await page.evaluate( () => document.querySelector( '.block-editor-block-settings-menu__content' ).innerHTML ); @@ -180,4 +182,37 @@ describe( 'Block Grouping', () => { expect( await getEditedPostContent() ).toMatchSnapshot(); } ); } ); + + describe( 'Registering alternative Blocks to handle Grouping interactions', () => { + beforeAll( async () => { + await activatePlugin( 'gutenberg-test-custom-grouping-block' ); + } ); + + afterAll( async () => { + await deactivatePlugin( 'gutenberg-test-custom-grouping-block' ); + } ); + + it( 'should use registered grouping block for grouping interactions', async () => { + // Set custom Block as the Block to use for Grouping + await page.evaluate( () => { + window.wp.blocks.setGroupingBlockName( 'test/alternative-group-block' ); + } ); + + // Creating test blocks + await insertBlocksOfSameType(); + + // Multiselect via keyboard. + await pressKeyWithModifier( 'primary', 'a' ); + await pressKeyWithModifier( 'primary', 'a' ); + + // Group - this will use whichever Block is registered as the Grouping Block + // as opposed to "transformTo()" which uses whatever is passed to it. To + // ensure this test is meaningful we must rely on what is registered. + await clickBlockToolbarButton( 'More options' ); + const groupButton = await page.waitForXPath( '//button[text()="Group"]' ); + await groupButton.click(); + + expect( await getEditedPostContent() ).toMatchSnapshot(); + } ); + } ); } ); diff --git a/packages/editor/src/components/convert-to-group-buttons/convert-button.js b/packages/editor/src/components/convert-to-group-buttons/convert-button.js index 2aa2fc9dcd6b1..8b024d325ae35 100644 --- a/packages/editor/src/components/convert-to-group-buttons/convert-button.js +++ b/packages/editor/src/components/convert-to-group-buttons/convert-button.js @@ -55,33 +55,40 @@ export default compose( [ canInsertBlockType, } = select( 'core/block-editor' ); - const containerBlockAvailable = canInsertBlockType( 'core/group' ); + const { + getGroupingBlockName, + } = select( 'core/blocks' ); + + const groupingBlockName = getGroupingBlockName(); + + const groupingBlockAvailable = canInsertBlockType( groupingBlockName ); const blocksSelection = getBlocksByClientId( clientIds ); - const isSingleContainerBlock = blocksSelection.length === 1 && blocksSelection[ 0 ] && blocksSelection[ 0 ].name === 'core/group'; + const isSingleGroupingBlock = blocksSelection.length === 1 && blocksSelection[ 0 ] && blocksSelection[ 0 ].name === groupingBlockName; // Do we have - // 1. Container block available to be inserted? + // 1. Grouping block available to be inserted? // 2. One or more blocks selected // (we allow single Blocks to become groups unless // they are a soltiary group block themselves) const isGroupable = ( - containerBlockAvailable && + groupingBlockAvailable && blocksSelection.length && - ! isSingleContainerBlock + ! isSingleGroupingBlock ); // Do we have a single Group Block selected and does that group have inner blocks? - const isUngroupable = isSingleContainerBlock && !! blocksSelection[ 0 ].innerBlocks.length; + const isUngroupable = isSingleGroupingBlock && !! blocksSelection[ 0 ].innerBlocks.length; return { isGroupable, isUngroupable, blocksSelection, + groupingBlockName, }; } ), - withDispatch( ( dispatch, { clientIds, onToggle = noop, blocksSelection = [] } ) => { + withDispatch( ( dispatch, { clientIds, onToggle = noop, blocksSelection = [], groupingBlockName } ) => { const { replaceBlocks, } = dispatch( 'core/block-editor' ); @@ -92,8 +99,8 @@ export default compose( [ return; } - // Activate the `transform` on `core/group` which does the conversion - const newBlocks = switchToBlockType( blocksSelection, 'core/group' ); + // Activate the `transform` on the Grouping Block which does the conversion + const newBlocks = switchToBlockType( blocksSelection, groupingBlockName ); if ( newBlocks ) { replaceBlocks(