From 383c87d1fd1d78fe2cce77fbd12d94882f69cbaa Mon Sep 17 00:00:00 2001 From: ntsekouras Date: Thu, 27 Apr 2023 15:55:38 +0300 Subject: [PATCH 1/7] Add block variations transformation in block switcher --- .../block-variation-transformations-menu.js | 114 ++++++++++++++++++ .../src/components/block-switcher/index.js | 45 ++++++- 2 files changed, 156 insertions(+), 3 deletions(-) create mode 100644 packages/block-editor/src/components/block-switcher/block-variation-transformations-menu.js diff --git a/packages/block-editor/src/components/block-switcher/block-variation-transformations-menu.js b/packages/block-editor/src/components/block-switcher/block-variation-transformations-menu.js new file mode 100644 index 0000000000000..f541f31e74a82 --- /dev/null +++ b/packages/block-editor/src/components/block-switcher/block-variation-transformations-menu.js @@ -0,0 +1,114 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { MenuGroup, MenuItem } from '@wordpress/components'; +import { + getBlockMenuDefaultClassName, + cloneBlock, + store as blocksStore, +} from '@wordpress/blocks'; +import { useSelect } from '@wordpress/data'; +import { useState, useMemo } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import { store as blockEditorStore } from '../../store'; +import BlockIcon from '../block-icon'; +import PreviewBlockPopover from './preview-block-popover'; + +export function useBlockVariationTransforms( { clientIds, blocks } ) { + const { activeBlockVariation, blockVariationTransformations } = useSelect( + ( select ) => { + const { + getBlockRootClientId, + getBlockAttributes, + canRemoveBlocks, + } = select( blockEditorStore ); + const { getActiveBlockVariation, getBlockVariations } = + select( blocksStore ); + const rootClientId = getBlockRootClientId( + Array.isArray( clientIds ) ? clientIds[ 0 ] : clientIds + ); + const canRemove = canRemoveBlocks( clientIds, rootClientId ); + // Only handle single selected blocks for now. + if ( blocks.length !== 1 || ! canRemove ) { + return; + } + const [ firstBlock ] = blocks; + return { + blockVariationTransformations: getBlockVariations( + firstBlock.name, + 'transform' + ), + activeBlockVariation: getActiveBlockVariation( + firstBlock.name, + getBlockAttributes( firstBlock.clientId ) + ), + }; + }, + [ clientIds, blocks ] + ); + const transformations = useMemo( () => { + return blockVariationTransformations?.filter( + ( { name } ) => name !== activeBlockVariation?.name + ); + }, [ blockVariationTransformations, activeBlockVariation ] ); + return transformations; +} + +const BlockVariationTransformationsMenu = ( { + transformations, + onSelect, + blocks, +} ) => { + const [ hoveredTransformItemName, setHoveredTransformItemName ] = + useState(); + return ( + + { hoveredTransformItemName && ( + name === hoveredTransformItemName + ).attributes + ) } + /> + ) } + { transformations?.map( ( item ) => ( + + ) ) } + + ); +}; + +function BlockVariationTranformationItem( { + item, + onSelect, + setHoveredTransformItemName, +} ) { + const { name, icon, title } = item; + return ( + { + event.preventDefault(); + onSelect( name ); + } } + onMouseLeave={ () => setHoveredTransformItemName( null ) } + onMouseEnter={ () => setHoveredTransformItemName( name ) } + > + + { title } + + ); +} + +export default BlockVariationTransformationsMenu; diff --git a/packages/block-editor/src/components/block-switcher/index.js b/packages/block-editor/src/components/block-switcher/index.js index 696b097757d33..a397d7c87c01a 100644 --- a/packages/block-editor/src/components/block-switcher/index.js +++ b/packages/block-editor/src/components/block-switcher/index.js @@ -24,12 +24,17 @@ import { store as blockEditorStore } from '../../store'; import useBlockDisplayInformation from '../use-block-display-information'; import BlockIcon from '../block-icon'; import BlockTransformationsMenu from './block-transformations-menu'; +import { + useBlockVariationTransforms, + default as BlockVariationTransformationsMenu, +} from './block-variation-transformations-menu'; import BlockStylesMenu from './block-styles-menu'; import PatternTransformationsMenu from './pattern-transformations-menu'; import useBlockDisplayTitle from '../block-title/use-block-display-title'; export const BlockSwitcherDropdownMenu = ( { clientIds, blocks } ) => { - const { replaceBlocks, multiSelect } = useDispatch( blockEditorStore ); + const { replaceBlocks, multiSelect, updateBlockAttributes } = + useDispatch( blockEditorStore ); const blockInformation = useBlockDisplayInformation( blocks[ 0 ].clientId ); const { possibleBlockTransformations, @@ -43,9 +48,9 @@ export const BlockSwitcherDropdownMenu = ( { clientIds, blocks } ) => { getBlockRootClientId, getBlockTransformItems, __experimentalGetPatternTransformItems, + canRemoveBlocks, } = select( blockEditorStore ); const { getBlockStyles, getBlockType } = select( blocksStore ); - const { canRemoveBlocks } = select( blockEditorStore ); const rootClientId = getBlockRootClientId( Array.isArray( clientIds ) ? clientIds[ 0 ] : clientIds ); @@ -82,6 +87,11 @@ export const BlockSwitcherDropdownMenu = ( { clientIds, blocks } ) => { [ clientIds, blocks, blockInformation?.icon ] ); + const blockVariationTransformations = useBlockVariationTransforms( { + clientIds, + blocks, + } ); + const blockTitle = useBlockDisplayTitle( { clientId: Array.isArray( clientIds ) ? clientIds[ 0 ] : clientIds, maximumLength: 35, @@ -105,6 +115,14 @@ export const BlockSwitcherDropdownMenu = ( { clientIds, blocks } ) => { selectForMultipleBlocks( newBlocks ); } + function onBlockVariationTransform( name ) { + updateBlockAttributes( blocks[ 0 ].clientId, { + ...blockVariationTransformations.find( + ( { name: variationName } ) => variationName === name + ).attributes, + } ); + } + // Pattern transformation through the `Patterns` API. function onPatternTransform( transformedBlocks ) { replaceBlocks( clientIds, transformedBlocks ); @@ -118,8 +136,14 @@ export const BlockSwitcherDropdownMenu = ( { clientIds, blocks } ) => { */ const hasPossibleBlockTransformations = !! possibleBlockTransformations.length && canRemove && ! isTemplate; + const hasPossibleBlockVariationTransformations = + !! blockVariationTransformations?.length; const hasPatternTransformation = !! patterns?.length && canRemove; - if ( ! hasBlockStyles && ! hasPossibleBlockTransformations ) { + if ( + ! hasBlockStyles && + ! hasPossibleBlockTransformations && + ! hasPossibleBlockVariationTransformations + ) { return ( { const showDropDown = hasBlockStyles || hasPossibleBlockTransformations || + hasPossibleBlockVariationTransformations || hasPatternTransformation; return ( @@ -226,6 +251,20 @@ export const BlockSwitcherDropdownMenu = ( { clientIds, blocks } ) => { } } /> ) } + { hasPossibleBlockVariationTransformations && ( + { + onBlockVariationTransform( + name + ); + onClose(); + } } + /> + ) } { hasBlockStyles && ( Date: Thu, 11 May 2023 10:32:40 +0300 Subject: [PATCH 2/7] move variation transforms to the top of the list --- .../block-transformations-menu.js | 12 +++++++++ ....js => block-variation-transformations.js} | 11 ++++---- .../src/components/block-switcher/index.js | 26 +++++++------------ 3 files changed, 27 insertions(+), 22 deletions(-) rename packages/block-editor/src/components/block-switcher/{block-variation-transformations-menu.js => block-variation-transformations.js} (91%) diff --git a/packages/block-editor/src/components/block-switcher/block-transformations-menu.js b/packages/block-editor/src/components/block-switcher/block-transformations-menu.js index 6b88fddb1163f..033201d7facad 100644 --- a/packages/block-editor/src/components/block-switcher/block-transformations-menu.js +++ b/packages/block-editor/src/components/block-switcher/block-transformations-menu.js @@ -14,6 +14,7 @@ import { useState, useMemo } from '@wordpress/element'; */ import BlockIcon from '../block-icon'; import PreviewBlockPopover from './preview-block-popover'; +import BlockVariationTransformations from './block-variation-transformations'; /** * Helper hook to group transformations to display them in a specific order in the UI. @@ -65,7 +66,9 @@ function useGroupedTransforms( possibleBlockTransformations ) { const BlockTransformationsMenu = ( { className, possibleBlockTransformations, + possibleBlockVariationTransformations, onSelect, + onSelectVariation, blocks, } ) => { const [ hoveredTransformItemName, setHoveredTransformItemName ] = @@ -95,6 +98,15 @@ const BlockTransformationsMenu = ( { ) } /> ) } + { !! possibleBlockVariationTransformations?.length && ( + + ) } { priorityTextTransformations.map( ( item ) => ( + <> { hoveredTransformItemName && ( ) ) } - + ); }; @@ -111,4 +110,4 @@ function BlockVariationTranformationItem( { ); } -export default BlockVariationTransformationsMenu; +export default BlockVariationTransformations; diff --git a/packages/block-editor/src/components/block-switcher/index.js b/packages/block-editor/src/components/block-switcher/index.js index a397d7c87c01a..a4c15a9e17062 100644 --- a/packages/block-editor/src/components/block-switcher/index.js +++ b/packages/block-editor/src/components/block-switcher/index.js @@ -24,10 +24,7 @@ import { store as blockEditorStore } from '../../store'; import useBlockDisplayInformation from '../use-block-display-information'; import BlockIcon from '../block-icon'; import BlockTransformationsMenu from './block-transformations-menu'; -import { - useBlockVariationTransforms, - default as BlockVariationTransformationsMenu, -} from './block-variation-transformations-menu'; +import { useBlockVariationTransforms } from './block-variation-transformations'; import BlockStylesMenu from './block-styles-menu'; import PatternTransformationsMenu from './pattern-transformations-menu'; import useBlockDisplayTitle from '../block-title/use-block-display-title'; @@ -184,10 +181,12 @@ export const BlockSwitcherDropdownMenu = ( { clientIds, blocks } ) => { blocks.length ); + const hasBlockOrBlockVariationTransforms = + hasPossibleBlockTransformations || + hasPossibleBlockVariationTransformations; const showDropDown = hasBlockStyles || - hasPossibleBlockTransformations || - hasPossibleBlockVariationTransformations || + hasBlockOrBlockVariationTransforms || hasPatternTransformation; return ( @@ -238,26 +237,21 @@ export const BlockSwitcherDropdownMenu = ( { clientIds, blocks } ) => { } } /> ) } - { hasPossibleBlockTransformations && ( + { hasBlockOrBlockVariationTransforms && ( { onBlockTransform( name ); onClose(); } } - /> - ) } - { hasPossibleBlockVariationTransformations && ( - { + onSelectVariation={ ( name ) => { onBlockVariationTransform( name ); From c053a1e07f4efddd78aa856dcdf52ac2a5516932 Mon Sep 17 00:00:00 2001 From: ntsekouras Date: Thu, 11 May 2023 10:41:46 +0300 Subject: [PATCH 3/7] fix error on multi select --- .../block-switcher/block-variation-transformations.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/block-switcher/block-variation-transformations.js b/packages/block-editor/src/components/block-switcher/block-variation-transformations.js index 6207f6824e6af..c2ec449dc61f9 100644 --- a/packages/block-editor/src/components/block-switcher/block-variation-transformations.js +++ b/packages/block-editor/src/components/block-switcher/block-variation-transformations.js @@ -17,6 +17,8 @@ import { store as blockEditorStore } from '../../store'; import BlockIcon from '../block-icon'; import PreviewBlockPopover from './preview-block-popover'; +const EMPTY_OBJECT = {}; + export function useBlockVariationTransforms( { clientIds, blocks } ) { const { activeBlockVariation, blockVariationTransformations } = useSelect( ( select ) => { @@ -33,7 +35,7 @@ export function useBlockVariationTransforms( { clientIds, blocks } ) { const canRemove = canRemoveBlocks( clientIds, rootClientId ); // Only handle single selected blocks for now. if ( blocks.length !== 1 || ! canRemove ) { - return; + return EMPTY_OBJECT; } const [ firstBlock ] = blocks; return { From 5625984ce36a490f4d662330f239cf7790e88b69 Mon Sep 17 00:00:00 2001 From: ntsekouras Date: Thu, 11 May 2023 12:17:57 +0300 Subject: [PATCH 4/7] add e2e test --- .../various/block-switcher-test.spec.js | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 test/e2e/specs/editor/various/block-switcher-test.spec.js diff --git a/test/e2e/specs/editor/various/block-switcher-test.spec.js b/test/e2e/specs/editor/various/block-switcher-test.spec.js new file mode 100644 index 0000000000000..c0a948598e14a --- /dev/null +++ b/test/e2e/specs/editor/various/block-switcher-test.spec.js @@ -0,0 +1,53 @@ +/** + * WordPress dependencies + */ +const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' ); + +test.describe( 'Block Switcher', () => { + test.beforeEach( async ( { admin } ) => { + await admin.createNewPost(); + } ); + + test( 'Block variation transforms', async ( { editor, page } ) => { + // This is the `stack` Group variation. + await editor.insertBlock( { + name: 'core/group', + attributes: { + layout: { + type: 'flex', + orientation: 'vertical', + }, + }, + innerBlocks: [ + { + name: 'core/paragraph', + attributes: { content: '1' }, + }, + ], + } ); + expect( await editor.getEditedPostContent() ).toBe( + ` +
+

1

+
+` + ); + // Transform to `Stack` variation. + await editor.clickBlockToolbarButton( 'Stack' ); + await expect( + page.locator( 'role=menuitem[name="Stack"i]' ) + ).toBeHidden(); + await page.click( 'role=menuitem[name="Row"i]' ); + expect( await editor.getEditedPostContent() ).toBe( + ` +
+

1

+
+` + ); + await editor.clickBlockToolbarButton( 'Row' ); + await expect( + page.locator( 'role=menuitem[name="Stack"i]' ) + ).toBeVisible(); + } ); +} ); From 44108f758780d664a7b1ab7383414729ed8a5f9e Mon Sep 17 00:00:00 2001 From: Nik Tsekouras Date: Thu, 11 May 2023 15:39:22 +0300 Subject: [PATCH 5/7] Update test/e2e/specs/editor/various/block-switcher-test.spec.js Co-authored-by: Kai Hao --- test/e2e/specs/editor/various/block-switcher-test.spec.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/e2e/specs/editor/various/block-switcher-test.spec.js b/test/e2e/specs/editor/various/block-switcher-test.spec.js index c0a948598e14a..c7a98d3de09ff 100644 --- a/test/e2e/specs/editor/various/block-switcher-test.spec.js +++ b/test/e2e/specs/editor/various/block-switcher-test.spec.js @@ -34,10 +34,12 @@ test.describe( 'Block Switcher', () => { ); // Transform to `Stack` variation. await editor.clickBlockToolbarButton( 'Stack' ); + const variations = page.getByRole( 'menu', { name: 'Stack' } ) + .getByRole( 'group', { name: 'variations' } ); await expect( - page.locator( 'role=menuitem[name="Stack"i]' ) + variations.getByRole( 'menuitem', { name: 'Stack' } ) ).toBeHidden(); - await page.click( 'role=menuitem[name="Row"i]' ); + await variations.getByRole( 'menuitem', { name: 'Row' } ).click(); expect( await editor.getEditedPostContent() ).toBe( `
From a71e09dbf541299b15dd7dd60f35019070030220 Mon Sep 17 00:00:00 2001 From: ntsekouras Date: Thu, 11 May 2023 17:08:49 +0300 Subject: [PATCH 6/7] update e2e test --- .../various/block-switcher-test.spec.js | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/test/e2e/specs/editor/various/block-switcher-test.spec.js b/test/e2e/specs/editor/various/block-switcher-test.spec.js index c7a98d3de09ff..de7eb9b167b29 100644 --- a/test/e2e/specs/editor/various/block-switcher-test.spec.js +++ b/test/e2e/specs/editor/various/block-switcher-test.spec.js @@ -25,28 +25,33 @@ test.describe( 'Block Switcher', () => { }, ], } ); - expect( await editor.getEditedPostContent() ).toBe( - ` -
-

1

-
-` - ); // Transform to `Stack` variation. await editor.clickBlockToolbarButton( 'Stack' ); - const variations = page.getByRole( 'menu', { name: 'Stack' } ) - .getByRole( 'group', { name: 'variations' } ); + const variations = page + .getByRole( 'menu', { name: 'Stack' } ) + .getByRole( 'group' ); await expect( variations.getByRole( 'menuitem', { name: 'Stack' } ) ).toBeHidden(); await variations.getByRole( 'menuitem', { name: 'Row' } ).click(); - expect( await editor.getEditedPostContent() ).toBe( - ` -
-

1

-
-` - ); + expect( await editor.getBlocks() ).toMatchObject( [ + { + name: 'core/group', + attributes: expect.objectContaining( { + layout: { + type: 'flex', + flexWrap: 'nowrap', + orientation: undefined, + }, + } ), + innerBlocks: [ + { + name: 'core/paragraph', + attributes: { content: '1' }, + }, + ], + }, + ] ); await editor.clickBlockToolbarButton( 'Row' ); await expect( page.locator( 'role=menuitem[name="Stack"i]' ) From 675c66d0216ff248c1786cebc9bd12dc7806c74e Mon Sep 17 00:00:00 2001 From: Nik Tsekouras Date: Mon, 15 May 2023 12:09:22 +0300 Subject: [PATCH 7/7] Update test/e2e/specs/editor/various/block-switcher-test.spec.js Co-authored-by: Kai Hao --- test/e2e/specs/editor/various/block-switcher-test.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/specs/editor/various/block-switcher-test.spec.js b/test/e2e/specs/editor/various/block-switcher-test.spec.js index de7eb9b167b29..12fd843ed2ed1 100644 --- a/test/e2e/specs/editor/various/block-switcher-test.spec.js +++ b/test/e2e/specs/editor/various/block-switcher-test.spec.js @@ -34,7 +34,7 @@ test.describe( 'Block Switcher', () => { variations.getByRole( 'menuitem', { name: 'Stack' } ) ).toBeHidden(); await variations.getByRole( 'menuitem', { name: 'Row' } ).click(); - expect( await editor.getBlocks() ).toMatchObject( [ + await expect.poll( editor.getBlocks ).toMatchObject( [ { name: 'core/group', attributes: expect.objectContaining( {