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 ( {