diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md
index 04710602d28f0b..320a9814e1c6ec 100644
--- a/docs/reference-guides/core-blocks.md
+++ b/docs/reference-guides/core-blocks.md
@@ -465,7 +465,7 @@ Show a block pattern. ([Source](https://github.com/WordPress/gutenberg/tree/trun
- **Name:** core/pattern
- **Category:** theme
- **Supports:** ~~html~~, ~~inserter~~
-- **Attributes:** slug, syncStatus
+- **Attributes:** content, slug, syncStatus
## Post Author
diff --git a/packages/block-library/src/pattern/block.json b/packages/block-library/src/pattern/block.json
index 82372fe1680984..d4f0fe3c18f94a 100644
--- a/packages/block-library/src/pattern/block.json
+++ b/packages/block-library/src/pattern/block.json
@@ -17,6 +17,9 @@
"syncStatus": {
"type": [ "string", "boolean" ],
"enum": [ "full", "partial" ]
+ },
+ "content": {
+ "type": "object"
}
}
}
diff --git a/packages/block-library/src/pattern/edit.js b/packages/block-library/src/pattern/edit.js
index c22536a59eb03f..dd345d52088645 100644
--- a/packages/block-library/src/pattern/edit.js
+++ b/packages/block-library/src/pattern/edit.js
@@ -2,13 +2,17 @@
* WordPress dependencies
*/
import { cloneBlock } from '@wordpress/blocks';
-import { useSelect, useDispatch } from '@wordpress/data';
-import { useEffect } from '@wordpress/element';
+import { createHigherOrderComponent } from '@wordpress/compose';
+import { useSelect, useDispatch, useRegistry } from '@wordpress/data';
+import { useEffect, useCallback } from '@wordpress/element';
import {
store as blockEditorStore,
useBlockProps,
useInnerBlocksProps,
} from '@wordpress/block-editor';
+import { addFilter } from '@wordpress/hooks';
+
+const EMPTY_OBJECT = {};
const PatternEdit = ( { attributes, clientId } ) => {
const { slug, syncStatus } = attributes;
@@ -81,3 +85,94 @@ const PatternEdit = ( { attributes, clientId } ) => {
};
export default PatternEdit;
+
+/**
+ * Override the default edit UI to include layout controls
+ *
+ * @param {Function} BlockEdit Original component.
+ *
+ * @return {Function} Wrapped component.
+ */
+export const withChildContentAttributes = createHigherOrderComponent(
+ ( BlockEdit ) => ( props ) => {
+ const { name: blockName, clientId, setAttributes } = props;
+
+ const registry = useRegistry();
+
+ const { patternClientId, patternSyncStatus, patternContent } =
+ useSelect(
+ ( select ) => {
+ const { getBlockParentsByBlockName, getBlockAttributes } =
+ select( blockEditorStore );
+
+ const patternBlockParents = getBlockParentsByBlockName(
+ clientId,
+ 'core/pattern'
+ );
+
+ const parentPatternClientId = patternBlockParents?.[ 0 ];
+
+ if ( ! parentPatternClientId ) {
+ return EMPTY_OBJECT;
+ }
+
+ const { syncStatus, content } = getBlockAttributes(
+ parentPatternClientId
+ );
+
+ return {
+ patternClientId: parentPatternClientId,
+ patternSyncStatus: syncStatus,
+ patternContent: content,
+ };
+ },
+ [ clientId ]
+ );
+
+ const { updateBlockAttributes } = useDispatch( blockEditorStore );
+
+ if ( patternSyncStatus !== 'partial' ) {
+ return ;
+ }
+
+ const setContentAttributes = useCallback(
+ ( newAttributes ) => {
+ registry.batch( () => {
+ // Sync any attribute updates to a partially synced pattern parent.
+ // todo
+ // - Only set `role: 'content' attributes.
+ // - Handle multiple blocks of the same type.
+ updateBlockAttributes( patternClientId, {
+ content: {
+ ...patternContent,
+ [ blockName ]: {
+ ...patternContent?.[ blockName ],
+ ...newAttributes,
+ },
+ },
+ } );
+ setAttributes( newAttributes );
+ } );
+ },
+ [
+ blockName,
+ patternClientId,
+ patternContent,
+ registry,
+ setAttributes,
+ updateBlockAttributes,
+ ]
+ );
+
+ return (
+
+ );
+ },
+ 'withInspectorControls'
+);
+
+addFilter(
+ 'editor.BlockEdit',
+ 'core/block-library/pattern/with-child-content-attributes',
+ withChildContentAttributes
+);