diff --git a/packages/block-editor/src/private-apis.js b/packages/block-editor/src/private-apis.js
index 92da4b8719632..762b3a96a87a9 100644
--- a/packages/block-editor/src/private-apis.js
+++ b/packages/block-editor/src/private-apis.js
@@ -29,6 +29,7 @@ import { useFlashEditableBlocks } from './components/use-flash-editable-blocks';
import { selectBlockPatternsKey } from './store/private-keys';
import { requiresWrapperOnCopy } from './components/writing-flow/utils';
import { PrivateRichText } from './components/rich-text/';
+import { BlockRenameModal } from './components/block-rename';
/**
* Private @wordpress/block-editor APIs.
@@ -62,4 +63,5 @@ lock( privateApis, {
selectBlockPatternsKey,
requiresWrapperOnCopy,
PrivateRichText,
+ BlockRenameModal,
} );
diff --git a/packages/editor/src/hooks/pattern-overrides.js b/packages/editor/src/hooks/pattern-overrides.js
index 442ce70a2bf71..3c90a32134540 100644
--- a/packages/editor/src/hooks/pattern-overrides.js
+++ b/packages/editor/src/hooks/pattern-overrides.js
@@ -14,7 +14,7 @@ import { store as editorStore } from '../store';
import { unlock } from '../lock-unlock';
const {
- useSetPatternBindings,
+ PatternOverridesControls,
ResetOverridesControl,
PATTERN_TYPES,
PARTIAL_SYNCING_SUPPORTED_BLOCKS,
@@ -38,7 +38,6 @@ const withPatternOverrideControls = createHigherOrderComponent(
return (
<>
- { isSupportedBlock && }
{ props.isSelected && isSupportedBlock && (
) }
@@ -47,15 +46,6 @@ const withPatternOverrideControls = createHigherOrderComponent(
}
);
-function BindingUpdater( props ) {
- const postType = useSelect(
- ( select ) => select( editorStore ).getCurrentPostType(),
- []
- );
- useSetPatternBindings( props, postType );
- return null;
-}
-
// Split into a separate component to avoid a store subscription
// on every block.
function ControlsWithStoreSubscription( props ) {
@@ -73,6 +63,8 @@ function ControlsWithStoreSubscription( props ) {
( binding ) => binding.source === 'core/pattern-overrides'
);
+ const shouldShowPatternOverridesControls =
+ isEditingPattern && blockEditingMode === 'default';
const shouldShowResetOverridesControl =
! isEditingPattern &&
!! props.attributes.metadata?.name &&
@@ -81,6 +73,9 @@ function ControlsWithStoreSubscription( props ) {
return (
<>
+ { shouldShowPatternOverridesControls && (
+
+ ) }
{ shouldShowResetOverridesControl && (
) }
diff --git a/packages/patterns/src/components/pattern-overrides-controls.js b/packages/patterns/src/components/pattern-overrides-controls.js
new file mode 100644
index 0000000000000..605eb92fd9ecc
--- /dev/null
+++ b/packages/patterns/src/components/pattern-overrides-controls.js
@@ -0,0 +1,122 @@
+/**
+ * WordPress dependencies
+ */
+import { useState } from '@wordpress/element';
+import {
+ InspectorControls,
+ privateApis as blockEditorPrivateApis,
+} from '@wordpress/block-editor';
+import { BaseControl, CheckboxControl } from '@wordpress/components';
+import { __ } from '@wordpress/i18n';
+
+/**
+ * Internal dependencies
+ */
+import { PARTIAL_SYNCING_SUPPORTED_BLOCKS } from '../constants';
+import { unlock } from '../lock-unlock';
+
+const { BlockRenameModal } = unlock( blockEditorPrivateApis );
+
+function removeBindings( bindings, syncedAttributes ) {
+ let updatedBindings = {};
+ for ( const attributeName of syncedAttributes ) {
+ // Omit any pattern override bindings from the `updatedBindings` object.
+ if (
+ bindings?.[ attributeName ]?.source !== 'core/pattern-overrides' &&
+ bindings?.[ attributeName ]?.source !== undefined
+ ) {
+ updatedBindings[ attributeName ] = bindings[ attributeName ];
+ }
+ }
+ if ( ! Object.keys( updatedBindings ).length ) {
+ updatedBindings = undefined;
+ }
+ return updatedBindings;
+}
+
+function addBindings( bindings, syncedAttributes ) {
+ const updatedBindings = { ...bindings };
+ for ( const attributeName of syncedAttributes ) {
+ if ( ! bindings?.[ attributeName ] ) {
+ updatedBindings[ attributeName ] = {
+ source: 'core/pattern-overrides',
+ };
+ }
+ }
+ return updatedBindings;
+}
+
+function PatternOverridesControls( { attributes, name, setAttributes } ) {
+ const [ showBlockNameModal, setShowBlockNameModal ] = useState( false );
+
+ const syncedAttributes = PARTIAL_SYNCING_SUPPORTED_BLOCKS[ name ];
+ const attributeSources = syncedAttributes.map(
+ ( attributeName ) =>
+ attributes.metadata?.bindings?.[ attributeName ]?.source
+ );
+ const isConnectedToOtherSources = attributeSources.every(
+ ( source ) => source && source !== 'core/pattern-overrides'
+ );
+
+ function updateBindings( isChecked, customName ) {
+ if ( isChecked && ! attributes.metadata?.name && ! customName ) {
+ setShowBlockNameModal( true );
+ return;
+ }
+
+ const prevBindings = attributes?.metadata?.bindings;
+ const updatedBindings = isChecked
+ ? addBindings( prevBindings, syncedAttributes )
+ : removeBindings( prevBindings, syncedAttributes );
+
+ const updatedMetadata = {
+ ...attributes.metadata,
+ bindings: updatedBindings,
+ };
+
+ if ( customName ) {
+ updatedMetadata.name = customName;
+ }
+
+ setAttributes( {
+ metadata: updatedMetadata,
+ } );
+ }
+
+ // Avoid overwriting other (e.g. meta) bindings.
+ if ( isConnectedToOtherSources ) return null;
+
+ return (
+ <>
+
+
+
+ { __( 'Pattern overrides' ) }
+
+ source === 'core/pattern-overrides'
+ ) }
+ onChange={ ( isChecked ) => {
+ updateBindings( isChecked );
+ } }
+ />
+
+
+
+ { showBlockNameModal && (
+ setShowBlockNameModal( false ) }
+ onSave={ ( newName ) => {
+ updateBindings( true, newName );
+ } }
+ />
+ ) }
+ >
+ );
+}
+
+export default PatternOverridesControls;
diff --git a/packages/patterns/src/components/use-set-pattern-bindings.js b/packages/patterns/src/components/use-set-pattern-bindings.js
deleted file mode 100644
index df16d2b2b0591..0000000000000
--- a/packages/patterns/src/components/use-set-pattern-bindings.js
+++ /dev/null
@@ -1,106 +0,0 @@
-/**
- * WordPress dependencies
- */
-import { usePrevious } from '@wordpress/compose';
-import { useEffect } from '@wordpress/element';
-
-/**
- * Internal dependencies
- */
-import { PARTIAL_SYNCING_SUPPORTED_BLOCKS } from '../constants';
-
-function removeBindings( bindings, syncedAttributes ) {
- let updatedBindings = {};
- for ( const attributeName of syncedAttributes ) {
- // Omit any pattern override bindings from the `updatedBindings` object.
- if (
- bindings?.[ attributeName ]?.source !== 'core/pattern-overrides' &&
- bindings?.[ attributeName ]?.source !== undefined
- ) {
- updatedBindings[ attributeName ] = bindings[ attributeName ];
- }
- }
- if ( ! Object.keys( updatedBindings ).length ) {
- updatedBindings = undefined;
- }
- return updatedBindings;
-}
-
-function addBindings( bindings, syncedAttributes ) {
- const updatedBindings = { ...bindings };
- for ( const attributeName of syncedAttributes ) {
- if ( ! bindings?.[ attributeName ] ) {
- updatedBindings[ attributeName ] = {
- source: 'core/pattern-overrides',
- };
- }
- }
- return updatedBindings;
-}
-
-export default function useSetPatternBindings(
- { name, attributes, setAttributes },
- currentPostType
-) {
- const metadataName = attributes?.metadata?.name ?? '';
- const prevMetadataName = usePrevious( metadataName ) ?? '';
- const bindings = attributes?.metadata?.bindings;
-
- useEffect( () => {
- // Bindings should only be created when editing a wp_block post type,
- // and also when there's a change to the user-given name for the block.
- if (
- currentPostType !== 'wp_block' ||
- metadataName === prevMetadataName
- ) {
- return;
- }
-
- const syncedAttributes = PARTIAL_SYNCING_SUPPORTED_BLOCKS[ name ];
- const attributeSources = syncedAttributes.map(
- ( attributeName ) =>
- attributes.metadata?.bindings?.[ attributeName ]?.source
- );
- const isConnectedToOtherSources = attributeSources.every(
- ( source ) => source && source !== 'core/pattern-overrides'
- );
-
- // Avoid overwriting other (e.g. meta) bindings.
- if ( isConnectedToOtherSources ) {
- return;
- }
-
- // The user-given name for the block was deleted, remove the bindings.
- if ( ! metadataName?.length && prevMetadataName?.length ) {
- const updatedBindings = removeBindings(
- bindings,
- syncedAttributes
- );
- setAttributes( {
- metadata: {
- ...attributes.metadata,
- bindings: updatedBindings,
- },
- } );
- }
-
- // The user-given name for the block was set, set the bindings.
- if ( ! prevMetadataName?.length && metadataName.length ) {
- const updatedBindings = addBindings( bindings, syncedAttributes );
- setAttributes( {
- metadata: {
- ...attributes.metadata,
- bindings: updatedBindings,
- },
- } );
- }
- }, [
- bindings,
- prevMetadataName,
- metadataName,
- currentPostType,
- name,
- attributes.metadata,
- setAttributes,
- ] );
-}
diff --git a/packages/patterns/src/private-apis.js b/packages/patterns/src/private-apis.js
index 54ad5a4aa47d1..817a3413405ee 100644
--- a/packages/patterns/src/private-apis.js
+++ b/packages/patterns/src/private-apis.js
@@ -13,7 +13,7 @@ import {
import RenamePatternModal from './components/rename-pattern-modal';
import PatternsMenuItems from './components';
import RenamePatternCategoryModal from './components/rename-pattern-category-modal';
-import useSetPatternBindings from './components/use-set-pattern-bindings';
+import PatternOverridesControls from './components/pattern-overrides-controls';
import ResetOverridesControl from './components/reset-overrides-control';
import { useAddPatternCategory } from './private-hooks';
import {
@@ -34,7 +34,7 @@ lock( privateApis, {
RenamePatternModal,
PatternsMenuItems,
RenamePatternCategoryModal,
- useSetPatternBindings,
+ PatternOverridesControls,
ResetOverridesControl,
useAddPatternCategory,
PATTERN_TYPES,
diff --git a/test/e2e/specs/editor/various/pattern-overrides.spec.js b/test/e2e/specs/editor/various/pattern-overrides.spec.js
index 4542e8c789ad1..e191a845492c4 100644
--- a/test/e2e/specs/editor/various/pattern-overrides.spec.js
+++ b/test/e2e/specs/editor/various/pattern-overrides.spec.js
@@ -87,6 +87,9 @@ test.describe( 'Pattern Overrides', () => {
await editorSettings
.getByRole( 'textbox', { name: 'Block Name' } )
.fill( editableParagraphName );
+ await editorSettings
+ .getByRole( 'checkbox', { name: 'Allow instance overrides' } )
+ .setChecked( true );
await expect.poll( editor.getBlocks ).toMatchObject( [
{