diff --git a/packages/block-library/src/block/edit.js b/packages/block-library/src/block/edit.js
index 6e6379571cc9e..2dd24a5fbd959 100644
--- a/packages/block-library/src/block/edit.js
+++ b/packages/block-library/src/block/edit.js
@@ -38,7 +38,7 @@ import { name as patternBlockName } from './index';
import { unlock } from '../lock-unlock';
const { useLayoutClasses } = unlock( blockEditorPrivateApis );
-const { PARTIAL_SYNCING_SUPPORTED_BLOCKS } = unlock( patternsPrivateApis );
+const { isOverridableBlock } = unlock( patternsPrivateApis );
const fullAlignments = [ 'full', 'wide', 'left', 'right' ];
@@ -90,21 +90,9 @@ const useInferredLayout = ( blocks, parentLayout ) => {
}, [ blocks, parentLayout ] );
};
-function hasOverridableAttributes( block ) {
- return (
- Object.keys( PARTIAL_SYNCING_SUPPORTED_BLOCKS ).includes(
- block.name
- ) &&
- !! block.attributes.metadata?.bindings &&
- Object.values( block.attributes.metadata.bindings ).some(
- ( binding ) => binding.source === 'core/pattern-overrides'
- )
- );
-}
-
function hasOverridableBlocks( blocks ) {
return blocks.some( ( block ) => {
- if ( hasOverridableAttributes( block ) ) return true;
+ if ( isOverridableBlock( block ) ) return true;
return hasOverridableBlocks( block.innerBlocks );
} );
}
@@ -133,7 +121,7 @@ function applyInitialContentValuesToInnerBlocks(
const metadataName =
legacyIdMap?.[ block.clientId ] ?? block.attributes.metadata?.name;
- if ( ! metadataName || ! hasOverridableAttributes( block ) ) {
+ if ( ! metadataName || ! isOverridableBlock( block ) ) {
return { ...block, innerBlocks };
}
@@ -184,7 +172,7 @@ function getContentValuesFromInnerBlocks( blocks, defaultValues, legacyIdMap ) {
}
const metadataName =
legacyIdMap?.[ block.clientId ] ?? block.attributes.metadata?.name;
- if ( ! metadataName || ! hasOverridableAttributes( block ) ) {
+ if ( ! metadataName || ! isOverridableBlock( block ) ) {
continue;
}
@@ -217,7 +205,7 @@ function setBlockEditMode( setEditMode, blocks, mode ) {
blocks.forEach( ( block ) => {
const editMode =
mode ||
- ( hasOverridableAttributes( block ) ? 'contentOnly' : 'disabled' );
+ ( isOverridableBlock( block ) ? 'contentOnly' : 'disabled' );
setEditMode( block.clientId, editMode );
setBlockEditMode(
diff --git a/packages/edit-post/src/components/sidebar/settings-sidebar/index.js b/packages/edit-post/src/components/sidebar/settings-sidebar/index.js
index 50e103cb8b444..2264dfaaa56d1 100644
--- a/packages/edit-post/src/components/sidebar/settings-sidebar/index.js
+++ b/packages/edit-post/src/components/sidebar/settings-sidebar/index.js
@@ -43,6 +43,7 @@ import { unlock } from '../../../lock-unlock';
const { PostCardPanel } = unlock( editorPrivateApis );
const { Tabs } = unlock( componentsPrivateApis );
+const { PatternOverridesPanel } = unlock( editorPrivateApis );
const SIDEBAR_ACTIVE_BY_DEFAULT = Platform.select( {
web: true,
@@ -123,6 +124,7 @@ const SidebarContent = ( {
+
>
) }
diff --git a/packages/edit-site/src/components/sidebar-edit-mode/template-panel/index.js b/packages/edit-site/src/components/sidebar-edit-mode/template-panel/index.js
index c19cf45e4551b..38e2ad4694906 100644
--- a/packages/edit-site/src/components/sidebar-edit-mode/template-panel/index.js
+++ b/packages/edit-site/src/components/sidebar-edit-mode/template-panel/index.js
@@ -9,8 +9,8 @@ import {
PostExcerptPanel,
PostLastRevisionPanel,
PostTaxonomiesPanel,
- store as editorStore,
privateApis as editorPrivateApis,
+ store as editorStore,
} from '@wordpress/editor';
import { store as coreStore } from '@wordpress/core-data';
import { __ } from '@wordpress/i18n';
@@ -31,7 +31,7 @@ import { TEMPLATE_PART_POST_TYPE } from '../../../utils/constants';
import { unlock } from '../../../lock-unlock';
const { PostCardPanel } = unlock( editorPrivateApis );
-
+const { PatternOverridesPanel } = unlock( editorPrivateApis );
const { useHistory } = unlock( routerPrivateApis );
function TemplatesList( { availableTemplates, onSelect } ) {
@@ -141,6 +141,7 @@ export default function TemplatePanel() {
+
>
);
}
diff --git a/packages/editor/src/components/pattern-overrides-panel/index.js b/packages/editor/src/components/pattern-overrides-panel/index.js
new file mode 100644
index 0000000000000..47cd6282b3fed
--- /dev/null
+++ b/packages/editor/src/components/pattern-overrides-panel/index.js
@@ -0,0 +1,26 @@
+/**
+ * WordPress dependencies
+ */
+import { useSelect } from '@wordpress/data';
+import { privateApis as patternsPrivateApis } from '@wordpress/patterns';
+
+/**
+ * Internal dependencies
+ */
+import { store as editorStore } from '../../store';
+import { unlock } from '../../lock-unlock';
+
+const { OverridesPanel } = unlock( patternsPrivateApis );
+
+export default function PatternOverridesPanel() {
+ const supportsPatternOverridesPanel = useSelect(
+ ( select ) => select( editorStore ).getCurrentPostType() === 'wp_block',
+ []
+ );
+
+ if ( ! supportsPatternOverridesPanel ) {
+ return null;
+ }
+
+ return ;
+}
diff --git a/packages/editor/src/private-apis.js b/packages/editor/src/private-apis.js
index 10143bdf00262..ea42d6ad5fde5 100644
--- a/packages/editor/src/private-apis.js
+++ b/packages/editor/src/private-apis.js
@@ -10,6 +10,7 @@ import DocumentTools from './components/document-tools';
import InserterSidebar from './components/inserter-sidebar';
import ListViewSidebar from './components/list-view-sidebar';
import ModeSwitcher from './components/mode-switcher';
+import PatternOverridesPanel from './components/pattern-overrides-panel';
import PluginPostExcerpt from './components/post-excerpt/plugin';
import PostPanelRow from './components/post-panel-row';
import PostViewLink from './components/post-view-link';
@@ -27,6 +28,7 @@ lock( privateApis, {
InserterSidebar,
ListViewSidebar,
ModeSwitcher,
+ PatternOverridesPanel,
PluginPostExcerpt,
PostPanelRow,
PostViewLink,
diff --git a/packages/patterns/src/api/index.js b/packages/patterns/src/api/index.js
new file mode 100644
index 0000000000000..07f3b2321a127
--- /dev/null
+++ b/packages/patterns/src/api/index.js
@@ -0,0 +1,23 @@
+/**
+ * Internal dependencies
+ */
+import { PARTIAL_SYNCING_SUPPORTED_BLOCKS } from '../constants';
+
+/**
+ * Determines whether a block is overridable.
+ *
+ * @param {WPBlock} block The block to test.
+ *
+ * @return {boolean} `true` if a block is overridable, `false` otherwise.
+ */
+export function isOverridableBlock( block ) {
+ return (
+ Object.keys( PARTIAL_SYNCING_SUPPORTED_BLOCKS ).includes(
+ block.name
+ ) &&
+ !! block.attributes.metadata?.bindings &&
+ Object.values( block.attributes.metadata.bindings ).some(
+ ( binding ) => binding.source === 'core/pattern-overrides'
+ )
+ );
+}
diff --git a/packages/patterns/src/components/overrides-panel.js b/packages/patterns/src/components/overrides-panel.js
new file mode 100644
index 0000000000000..b567b3c372daa
--- /dev/null
+++ b/packages/patterns/src/components/overrides-panel.js
@@ -0,0 +1,45 @@
+/**
+ * WordPress dependencies
+ */
+import {
+ privateApis as blockEditorPrivateApis,
+ store as blockEditorStore,
+} from '@wordpress/block-editor';
+import { PanelBody } from '@wordpress/components';
+import { useSelect } from '@wordpress/data';
+import { useMemo } from '@wordpress/element';
+import { __ } from '@wordpress/i18n';
+
+/**
+ * Internal dependencies
+ */
+import { isOverridableBlock } from '../api';
+import { unlock } from '../lock-unlock';
+
+const { BlockQuickNavigation } = unlock( blockEditorPrivateApis );
+
+export default function OverridesPanel() {
+ const allClientIds = useSelect(
+ ( select ) => select( blockEditorStore ).getClientIdsWithDescendants(),
+ []
+ );
+ const { getBlock } = useSelect( blockEditorStore );
+ const clientIdsWithOverrides = useMemo(
+ () =>
+ allClientIds.filter( ( clientId ) => {
+ const block = getBlock( clientId );
+ return isOverridableBlock( block );
+ } ),
+ [ allClientIds, getBlock ]
+ );
+
+ if ( ! clientIdsWithOverrides?.length ) {
+ return null;
+ }
+
+ return (
+
+
+
+ );
+}
diff --git a/packages/patterns/src/private-apis.js b/packages/patterns/src/private-apis.js
index 54ad5a4aa47d1..15ff161305f4a 100644
--- a/packages/patterns/src/private-apis.js
+++ b/packages/patterns/src/private-apis.js
@@ -2,6 +2,7 @@
* Internal dependencies
*/
import { lock } from './lock-unlock';
+import OverridesPanel from './components/overrides-panel';
import {
default as CreatePatternModal,
CreatePatternModalContents,
@@ -10,6 +11,7 @@ import {
default as DuplicatePatternModal,
useDuplicatePatternProps,
} from './components/duplicate-pattern-modal';
+import { isOverridableBlock } from './api';
import RenamePatternModal from './components/rename-pattern-modal';
import PatternsMenuItems from './components';
import RenamePatternCategoryModal from './components/rename-pattern-category-modal';
@@ -27,9 +29,11 @@ import {
export const privateApis = {};
lock( privateApis, {
+ OverridesPanel,
CreatePatternModal,
CreatePatternModalContents,
DuplicatePatternModal,
+ isOverridableBlock,
useDuplicatePatternProps,
RenamePatternModal,
PatternsMenuItems,