diff --git a/packages/block-editor/src/components/block-card/index.js b/packages/block-editor/src/components/block-card/index.js index 988dcfb2216b2a..e8dd2c987038ce 100644 --- a/packages/block-editor/src/components/block-card/index.js +++ b/packages/block-editor/src/components/block-card/index.js @@ -14,7 +14,7 @@ import { privateApis as componentsPrivateApis, } from '@wordpress/components'; import { chevronLeft, chevronRight } from '@wordpress/icons'; -import { __, isRTL } from '@wordpress/i18n'; +import { __, _x, isRTL } from '@wordpress/i18n'; import { useSelect, useDispatch } from '@wordpress/data'; /** @@ -35,22 +35,35 @@ function BlockCard( { title, icon, description, blockType, className, name } ) { ( { title, icon, description } = blockType ); } - const { parentNavBlockClientId } = useSelect( ( select ) => { - const { getSelectedBlockClientId, getBlockParentsByBlockName } = - select( blockEditorStore ); + const { parentNavBlockClientId, isUsingBindings } = useSelect( + ( select ) => { + const { + getSelectedBlockClientId, + getSelectedBlockClientIds, + getBlockParentsByBlockName, + getBlockAttributes, + } = select( blockEditorStore ); - const _selectedBlockClientId = getSelectedBlockClientId(); + const _selectedBlockClientId = getSelectedBlockClientId(); + const _selectedBlockClientIds = getSelectedBlockClientIds(); - return { - parentNavBlockClientId: getBlockParentsByBlockName( - _selectedBlockClientId, - 'core/navigation', - true - )[ 0 ], - }; - }, [] ); + return { + parentNavBlockClientId: getBlockParentsByBlockName( + _selectedBlockClientId, + 'core/navigation', + true + )[ 0 ], + isUsingBindings: _selectedBlockClientIds.every( + ( clientId ) => + !! getBlockAttributes( clientId )?.metadata?.bindings + ), + }; + }, + [] + ); const { selectBlock } = useDispatch( blockEditorStore ); + const hasCustomName = !! name?.length; return (
@@ -68,15 +81,26 @@ function BlockCard( { title, icon, description, blockType, className, name } ) { /> ) } - +

- { !! name?.length ? name : title } + { hasCustomName ? name : title } - { !! name?.length && { title } } + { hasCustomName && { title } } + { isUsingBindings && ( + + { _x( + 'Connected', + 'block connected to a bound source' + ) } + + ) }

{ description && ( - + { description } ) } diff --git a/packages/block-editor/src/components/block-card/style.scss b/packages/block-editor/src/components/block-card/style.scss index a5cb675597908b..553c0a8ef1517a 100644 --- a/packages/block-editor/src/components/block-card/style.scss +++ b/packages/block-editor/src/components/block-card/style.scss @@ -34,4 +34,3 @@ .block-editor-block-card.is-synced .block-editor-block-icon { color: var(--wp-block-synced-color); } - diff --git a/packages/block-editor/src/components/block-switcher/index.js b/packages/block-editor/src/components/block-switcher/index.js index 285581578ead43..3c0710eb113161 100644 --- a/packages/block-editor/src/components/block-switcher/index.js +++ b/packages/block-editor/src/components/block-switcher/index.js @@ -1,14 +1,12 @@ /** * WordPress dependencies */ -import { __, _n, sprintf, _x } from '@wordpress/i18n'; +import { __, _n, sprintf } from '@wordpress/i18n'; import { DropdownMenu, ToolbarButton, ToolbarGroup, ToolbarItem, - __experimentalText as Text, - MenuGroup, } from '@wordpress/components'; import { switchToBlockType, @@ -35,45 +33,16 @@ function BlockSwitcherDropdownMenuContents( { onClose, clientIds, hasBlockStyles, - canRemove, + patterns, + blocks, + possibleBlockTransformations, + blockVariationTransformations, + hasPatternTransformation, + hasBlockOrBlockVariationTransforms, } ) { const { replaceBlocks, multiSelect, updateBlockAttributes } = useDispatch( blockEditorStore ); - const { possibleBlockTransformations, patterns, blocks, isUsingBindings } = - useSelect( - ( select ) => { - const { - getBlockAttributes, - getBlocksByClientId, - getBlockRootClientId, - getBlockTransformItems, - __experimentalGetPatternTransformItems, - } = select( blockEditorStore ); - const rootClientId = getBlockRootClientId( clientIds[ 0 ] ); - const _blocks = getBlocksByClientId( clientIds ); - return { - blocks: _blocks, - possibleBlockTransformations: getBlockTransformItems( - _blocks, - rootClientId - ), - patterns: __experimentalGetPatternTransformItems( - _blocks, - rootClientId - ), - isUsingBindings: clientIds.every( - ( clientId ) => - !! getBlockAttributes( clientId )?.metadata - ?.bindings - ), - }; - }, - [ clientIds ] - ); - const blockVariationTransformations = useBlockVariationTransforms( { - clientIds, - blocks, - } ); + function selectForMultipleBlocks( insertedBlocks ) { if ( insertedBlocks.length > 1 ) { multiSelect( @@ -100,42 +69,6 @@ function BlockSwitcherDropdownMenuContents( { replaceBlocks( clientIds, transformedBlocks ); selectForMultipleBlocks( transformedBlocks ); } - /** - * The `isTemplate` check is a stopgap solution here. - * Ideally, the Transforms API should handle this - * by allowing to exclude blocks from wildcard transformations. - */ - const isSingleBlock = blocks.length === 1; - const isTemplate = isSingleBlock && isTemplatePart( blocks[ 0 ] ); - const hasPossibleBlockTransformations = - !! possibleBlockTransformations.length && canRemove && ! isTemplate; - const hasPossibleBlockVariationTransformations = - !! blockVariationTransformations?.length; - const hasPatternTransformation = !! patterns?.length && canRemove; - const hasBlockOrBlockVariationTransforms = - hasPossibleBlockTransformations || - hasPossibleBlockVariationTransformations; - const hasContents = - hasBlockStyles || - hasBlockOrBlockVariationTransforms || - hasPatternTransformation; - if ( ! hasContents ) { - return ( -

- { __( 'No transforms.' ) } -

- ); - } - - const connectedBlockDescription = isSingleBlock - ? _x( - 'This block is connected.', - 'block toolbar button label and description' - ) - : _x( - 'These blocks are connected.', - 'block toolbar button label and description' - ); return (
@@ -175,13 +108,6 @@ function BlockSwitcherDropdownMenuContents( { onSwitch={ onClose } /> ) } - { isUsingBindings && ( - - - { connectedBlockDescription } - - - ) }
); } @@ -196,6 +122,9 @@ export const BlockSwitcher = ( { clientIds } ) => { isReusable, isTemplate, isDisabled, + possibleBlockTransformations, + patterns, + blocks, } = useSelect( ( select ) => { const { @@ -204,6 +133,9 @@ export const BlockSwitcher = ( { clientIds } ) => { getBlockAttributes, canRemoveBlocks, getBlockEditingMode, + getBlockRootClientId, + getBlockTransformItems, + __experimentalGetPatternTransformItems, } = select( blockEditorStore ); const { getBlockStyles, getBlockType, getActiveBlockVariation } = select( blocksStore ); @@ -238,6 +170,8 @@ export const BlockSwitcher = ( { clientIds } ) => { _icon = isSelectionOfSameType ? blockType.icon : copy; } + const rootClientId = getBlockRootClientId( clientIds[ 0 ] ); + return { canRemove: canRemoveBlocks( clientIds ), hasBlockStyles: @@ -250,6 +184,15 @@ export const BlockSwitcher = ( { clientIds } ) => { _isSingleBlockSelected && isTemplatePart( _blocks[ 0 ] ), hasContentOnlyLocking: _hasTemplateLock, isDisabled: editingMode !== 'default', + blocks: _blocks, + possibleBlockTransformations: getBlockTransformItems( + _blocks, + rootClientId + ), + patterns: __experimentalGetPatternTransformItems( + _blocks, + rootClientId + ), }; }, [ clientIds ] @@ -264,6 +207,11 @@ export const BlockSwitcher = ( { clientIds } ) => { [] ); + const blockVariationTransformations = useBlockVariationTransforms( { + clientIds, + blocks, + } ); + if ( invalidBlocks ) { return null; } @@ -278,18 +226,34 @@ export const BlockSwitcher = ( { clientIds } ) => { ? blockTitle : undefined; + const hideTooltip = blockSwitcherLabel === blockIndicatorText; + + const hasPossibleBlockTransformations = + !! possibleBlockTransformations?.length && canRemove && ! isTemplate; + const hasPossibleBlockVariationTransformations = + !! blockVariationTransformations?.length; + const hasPatternTransformation = !! patterns?.length && canRemove; + const hasBlockOrBlockVariationTransforms = + hasPossibleBlockTransformations || + hasPossibleBlockVariationTransformations; + const hasContents = + hasBlockStyles || + hasBlockOrBlockVariationTransforms || + hasPatternTransformation; + const hideDropdown = isDisabled || ( ! hasBlockStyles && ! canRemove ) || - hasContentOnlyLocking; + hasContentOnlyLocking || + ! hasContents; if ( hideDropdown ) { return ( - + { onClose={ onClose } clientIds={ clientIds } hasBlockStyles={ hasBlockStyles } - canRemove={ canRemove } + patterns={ patterns } + blocks={ blocks } + possibleBlockTransformations={ + possibleBlockTransformations + } + blockVariationTransformations={ + blockVariationTransformations + } + hasPatternTransformation={ + hasPatternTransformation + } + hasBlockOrBlockVariationTransforms={ + hasBlockOrBlockVariationTransforms + } /> ) } diff --git a/packages/block-editor/src/components/block-switcher/style.scss b/packages/block-editor/src/components/block-switcher/style.scss index 62a7bebe95d278..dee93435b8837d 100644 --- a/packages/block-editor/src/components/block-switcher/style.scss +++ b/packages/block-editor/src/components/block-switcher/style.scss @@ -160,8 +160,3 @@ padding: 6px $grid-unit; margin: 0; } - -.block-editor-block-switcher__binding-indicator { - display: block; - padding: $grid-unit; -} diff --git a/packages/block-editor/src/components/block-switcher/test/index.js b/packages/block-editor/src/components/block-switcher/test/index.js index bc1b7d1a4a3640..092be30b3bc723 100644 --- a/packages/block-editor/src/components/block-switcher/test/index.js +++ b/packages/block-editor/src/components/block-switcher/test/index.js @@ -149,7 +149,7 @@ describe( 'BlockSwitcher', () => { expect( blockSwitcher ).toHaveAttribute( 'aria-disabled', 'true' ); } ); - test( 'should render message for no available transforms', async () => { + test( 'should render accessibly disabled block switcher when there are no available transforms', async () => { useSelect.mockImplementation( () => ( { possibleBlockTransformations: [], blocks: [ headingBlock1 ], @@ -157,26 +157,10 @@ describe( 'BlockSwitcher', () => { canRemove: true, } ) ); render( ); - const user = userEvent.setup(); - await user.type( - screen.getByRole( 'button', { - name: 'Block Name', - expanded: false, - } ), - '[ArrowDown]' - ); - await waitFor( () => - expect( - screen.getByRole( 'button', { - name: 'Block Name', - expanded: true, - } ) - ).toBeVisible() - ); - expect( - screen.getByRole( 'menu', { - name: 'Block Name', - } ) - ).toHaveTextContent( 'No transforms.' ); + const blockSwitcher = screen.getByRole( 'button', { + name: 'Block Name', + } ); + expect( blockSwitcher ).toBeEnabled(); + expect( blockSwitcher ).toHaveAttribute( 'aria-disabled', 'true' ); } ); } ); diff --git a/packages/block-editor/src/components/multi-selection-inspector/index.js b/packages/block-editor/src/components/multi-selection-inspector/index.js index 23d890d79fff4d..73eeed087808e3 100644 --- a/packages/block-editor/src/components/multi-selection-inspector/index.js +++ b/packages/block-editor/src/components/multi-selection-inspector/index.js @@ -1,36 +1,66 @@ /** * WordPress dependencies */ -import { sprintf, _n } from '@wordpress/i18n'; +import { sprintf, _n, _x } from '@wordpress/i18n'; import { useSelect } from '@wordpress/data'; import { copy } from '@wordpress/icons'; -import { __experimentalHStack as HStack } from '@wordpress/components'; +import { + __experimentalHStack as HStack, + __experimentalVStack as VStack, + privateApis as componentsPrivateApis, +} from '@wordpress/components'; /** * Internal dependencies */ import BlockIcon from '../block-icon'; import { store as blockEditorStore } from '../../store'; +import { unlock } from '../../lock-unlock'; + +const { Badge } = unlock( componentsPrivateApis ); export default function MultiSelectionInspector() { - const selectedBlockCount = useSelect( - ( select ) => select( blockEditorStore ).getSelectedBlockCount(), - [] - ); + const { selectedBlockCount, isUsingBindings } = useSelect( ( select ) => { + const { + getSelectedBlockCount, + getBlockAttributes, + getSelectedBlockClientIds, + } = select( blockEditorStore ); + + return { + selectedBlockCount: getSelectedBlockCount(), + isUsingBindings: getSelectedBlockClientIds().every( + ( clientId ) => + !! getBlockAttributes( clientId )?.metadata?.bindings + ), + }; + }, [] ); + return ( -
- { sprintf( - /* translators: %d: number of blocks */ - _n( '%d Block', '%d Blocks', selectedBlockCount ), - selectedBlockCount - ) } -
+ +

+ { sprintf( + /* translators: %d: number of blocks */ + _n( '%d Block', '%d Blocks', selectedBlockCount ), + selectedBlockCount + ) } + { isUsingBindings && ( + + { _x( + 'Connected', + 'multiple blocks connected to a bound source' + ) } + + ) } +

+
); } diff --git a/packages/block-editor/src/components/multi-selection-inspector/style.scss b/packages/block-editor/src/components/multi-selection-inspector/style.scss index e37245d58f5dd5..4b17906b613b51 100644 --- a/packages/block-editor/src/components/multi-selection-inspector/style.scss +++ b/packages/block-editor/src/components/multi-selection-inspector/style.scss @@ -4,11 +4,22 @@ .block-editor-multi-selection-inspector__card-title { font-weight: 500; + display: flex; + align-items: center; + flex-wrap: wrap; + gap: calc($grid-unit-10 / 2) $grid-unit-10; + + &.block-editor-multi-selection-inspector__card-title { + font-size: $default-font-size; + line-height: $default-line-height; + margin: 0; + } } .block-editor-multi-selection-inspector__card .block-editor-block-icon { - margin-left: -2px; - padding: 0 3px; - width: $button-size; + flex: 0 0 $button-size-small; + margin-left: 0; + margin-right: $grid-unit-15; + width: $button-size-small; height: $button-size-small; } diff --git a/test/e2e/specs/editor/various/block-switcher.spec.js b/test/e2e/specs/editor/various/block-switcher.spec.js index cb95c4d395c664..8eb2516ed5c6ba 100644 --- a/test/e2e/specs/editor/various/block-switcher.spec.js +++ b/test/e2e/specs/editor/various/block-switcher.spec.js @@ -114,7 +114,7 @@ test.describe( 'Block Switcher', () => { await expect( button ).toBeDisabled(); } ); - test( 'Should show a message if there are no transforms or styles available', async ( { + test( 'Should be disabled when there are no transforms or styles available', async ( { editor, page, pageUtils, @@ -126,11 +126,11 @@ test.describe( 'Block Switcher', () => { await page.keyboard.type( '- List content' ); await pageUtils.pressKeys( 'alt+F10' ); - await page + const button = page .getByRole( 'toolbar', { name: 'Block tools' } ) - .getByRole( 'button', { name: 'List item' } ) - .click(); - await expect( page.getByText( 'No transforms.' ) ).toBeVisible(); + .getByRole( 'button', { name: 'List item' } ); + // Verify the block switcher isn't enabled. + await expect( button ).toBeDisabled(); } ); test( 'Should show Columns block only if selected blocks are between limits (1-6)', async ( {