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

Update to allow alternative Blocks to handle Grouping interactions #16278

Merged
merged 10 commits into from
Jul 9, 2019
8 changes: 7 additions & 1 deletion packages/block-editor/src/components/block-actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
10 changes: 5 additions & 5 deletions packages/blocks/src/api/factory.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';

/**
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 ) ) {
Expand Down
31 changes: 27 additions & 4 deletions packages/blocks/src/api/test/factory.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
getBlockTypes,
registerBlockType,
unregisterBlockType,
setGroupingBlockName,
} from '../registration';

describe( 'block factory', () => {
Expand Down Expand Up @@ -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( () => {
getdave marked this conversation as resolved.
Show resolved Hide resolved
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 );
} );
} );

Expand Down
27 changes: 27 additions & 0 deletions packages/e2e-tests/plugins/custom-grouping-block.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php
/**
* Plugin Name: Gutenberg Test Custom Grouping Block
* Plugin URI: https://github.com/WordPress/gutenberg
* Author: Gutenberg Team
*
* @package gutenberg-test-custom-grouping-block
*/

/**
* Registers a custom script for the plugin.
*/
function enqueue_custom_grouping_block_plugin_script() {
wp_enqueue_script(
'gutenberg-test-custom-grouping-block',
plugins_url( 'custom-grouping-block/index.js', __FILE__ ),
array(
'wp-blocks',
'wp-element',
'wp-block-editor',
),
filemtime( plugin_dir_path( __FILE__ ) . 'custom-grouping-block/index.js' ),
true
);
}

add_action( 'init', 'enqueue_custom_grouping_block_plugin_script' );
28 changes: 28 additions & 0 deletions packages/e2e-tests/plugins/custom-grouping-block/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
( function() {
wp.blocks.registerBlockType( 'test/alternative-group-block', {
title: 'Alternative Group Block',
category: 'layout',
icon: 'yes',
edit() {
return wp.element.createElement( wp.blockEditor.InnerBlocks );
},

save() {
return wp.element.createElement( wp.blockEditor.InnerBlocks.Content );
},
transforms: {
from: [ {
type: 'block',
blocks: [ '*' ],
isMultiBlock: true,
__experimentalConvert( blocks ) {
const groupInnerBlocks = blocks.map( ( { name, attributes, innerBlocks } ) => {
return wp.blocks.createBlock( name, attributes, innerBlocks );
} );

return wp.blocks.createBlock( 'test/alternative-group-block', {}, groupInnerBlocks );
},
} ],
},
} );
}() );
16 changes: 16 additions & 0 deletions packages/e2e-tests/specs/__snapshots__/block-grouping.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,19 @@ exports[`Block Grouping Preserving selected blocks attributes preserves width al
<!-- /wp:paragraph --></div></div>
<!-- /wp:group -->"
`;

exports[`Block Grouping Registering alternative Blocks to handle Grouping interactions should use registered grouping block for grouping interactions 1`] = `
"<!-- wp:test/alternative-group-block -->
<!-- wp:paragraph -->
<p>First Paragraph</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Second Paragraph</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Third Paragraph</p>
<!-- /wp:paragraph -->
<!-- /wp:test/alternative-group-block -->"
`;
41 changes: 38 additions & 3 deletions packages/e2e-tests/specs/block-grouping.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import {
transformBlockTo,
getAllBlocks,
getAvailableBlockTransforms,
activatePlugin,
deactivatePlugin,
} from '@wordpress/e2e-test-utils';

async function insertBlocksOfSameType() {
Expand Down Expand Up @@ -111,7 +113,7 @@ describe( 'Block Grouping', () => {
} );
} );

describe( 'Container Block availability', () => {
describe( 'Grouping Block availability', () => {
beforeEach( async () => {
// Disable the Group block
await page.evaluate( () => {
Expand All @@ -133,15 +135,15 @@ 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(
availableTransforms
).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 );
Expand Down Expand Up @@ -180,4 +182,37 @@ describe( 'Block Grouping', () => {
expect( await getEditedPostContent() ).toMatchSnapshot();
} );
} );

describe( 'Registering alternative Blocks to handle Grouping interactions', () => {
getdave marked this conversation as resolved.
Show resolved Hide resolved
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();
} );
} );
} );
Original file line number Diff line number Diff line change
Expand Up @@ -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' );
Expand All @@ -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(
Expand Down