From aedb39b4bdb1949a40e8c4dcf9d3b908fc9459a8 Mon Sep 17 00:00:00 2001 From: Kai Hao Date: Fri, 24 Nov 2023 15:41:31 +0800 Subject: [PATCH 1/4] Automatically assign block ids --- lib/experimental/blocks.php | 26 ++++-- lib/experimental/connection-sources/index.php | 4 +- package-lock.json | 30 ++++++- .../block-editor/src/hooks/custom-fields.js | 90 +++++++------------ packages/block-library/src/block/edit.js | 36 ++++---- packages/edit-site/src/hooks/index.js | 1 + .../src/hooks/pattern-partial-syncing.js | 69 ++++++++++++++ packages/patterns/package.json | 3 +- .../components/partial-syncing-controls.js | 74 +++++++++++++++ packages/patterns/src/private-apis.js | 2 + 10 files changed, 246 insertions(+), 89 deletions(-) create mode 100644 packages/edit-site/src/hooks/pattern-partial-syncing.js create mode 100644 packages/patterns/src/components/partial-syncing-controls.js diff --git a/lib/experimental/blocks.php b/lib/experimental/blocks.php index 73c23999cc3c46..31c5d314eecf28 100644 --- a/lib/experimental/blocks.php +++ b/lib/experimental/blocks.php @@ -142,17 +142,25 @@ function gutenberg_render_block_connections( $block_content, $block, $block_inst continue; } - // If the attribute does not specify the name of the custom field, skip it. - if ( ! isset( $attribute_value['value'] ) ) { - continue; + if ( 'pattern_attributes' === $attribute_value['source'] ) { + if ( ! _wp_array_get( $block_instance->attributes, array( 'metadata', 'id' ), false ) ) { + continue; + } + + $custom_value = $connection_sources[ $attribute_value['source'] ]( $block_instance ); + } else { + // If the attribute does not specify the name of the custom field, skip it. + if ( ! isset( $attribute_value['value'] ) ) { + continue; + } + + // Get the content from the connection source. + $custom_value = $connection_sources[ $attribute_value['source'] ]( + $block_instance, + $attribute_value['value'] + ); } - // Get the content from the connection source. - $custom_value = $connection_sources[ $attribute_value['source'] ]( - $block_instance, - $attribute_value['value'] - ); - if ( false === $custom_value ) { continue; } diff --git a/lib/experimental/connection-sources/index.php b/lib/experimental/connection-sources/index.php index 4bfc9f89d9adf5..043bb9d38bddda 100644 --- a/lib/experimental/connection-sources/index.php +++ b/lib/experimental/connection-sources/index.php @@ -12,7 +12,7 @@ // if it doesn't, `get_post_meta()` will just return an empty string. return get_post_meta( $block_instance->context['postId'], $meta_field, true ); }, - 'pattern_attributes' => function ( $block_instance, $meta_field ) { - return _wp_array_get( $block_instance->context, array( 'overrides', $meta_field ), false ); + 'pattern_attributes' => function ( $block_instance ) { + return _wp_array_get( $block_instance->context, array( 'overrides', $block_instance->attributes['metadata']['id'] ), false ); }, ); diff --git a/package-lock.json b/package-lock.json index b2deb50ef559fa..f37fe7a47d40bc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -56046,7 +56046,8 @@ "@wordpress/icons": "file:../icons", "@wordpress/notices": "file:../notices", "@wordpress/private-apis": "file:../private-apis", - "@wordpress/url": "file:../url" + "@wordpress/url": "file:../url", + "nanoid": "^4.0.2" }, "engines": { "node": ">=16.0.0" @@ -56056,6 +56057,23 @@ "react-dom": "^18.0.0" } }, + "packages/patterns/node_modules/nanoid": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-4.0.2.tgz", + "integrity": "sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^14 || ^16 || >=18" + } + }, "packages/plugins": { "name": "@wordpress/plugins", "version": "6.14.0", @@ -70916,7 +70934,15 @@ "@wordpress/icons": "file:../icons", "@wordpress/notices": "file:../notices", "@wordpress/private-apis": "file:../private-apis", - "@wordpress/url": "file:../url" + "@wordpress/url": "file:../url", + "nanoid": "^4.0.2" + }, + "dependencies": { + "nanoid": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-4.0.2.tgz", + "integrity": "sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw==" + } } }, "@wordpress/plugins": { diff --git a/packages/block-editor/src/hooks/custom-fields.js b/packages/block-editor/src/hooks/custom-fields.js index 60c924f40c9408..adb9df15824a77 100644 --- a/packages/block-editor/src/hooks/custom-fields.js +++ b/packages/block-editor/src/hooks/custom-fields.js @@ -2,7 +2,7 @@ * WordPress dependencies */ import { addFilter } from '@wordpress/hooks'; -import { PanelBody, TextControl, SelectControl } from '@wordpress/components'; +import { PanelBody, TextControl } from '@wordpress/components'; import { __, sprintf } from '@wordpress/i18n'; import { hasBlockSupport } from '@wordpress/blocks'; import { createHigherOrderComponent } from '@wordpress/compose'; @@ -47,71 +47,45 @@ function CustomFieldsControl( props ) { if ( props.name === 'core/paragraph' ) attributeName = 'content'; if ( props.name === 'core/image' ) attributeName = 'url'; - const connectionSource = - props.attributes?.connections?.attributes?.[ attributeName ]?.source || - ''; - const connectionValue = - props.attributes?.connections?.attributes?.[ attributeName ]?.value || - ''; - - function updateConnections( source, value ) { - if ( value === '' ) { - props.setAttributes( { - connections: undefined, - placeholder: undefined, - } ); - } else { - props.setAttributes( { - connections: { - attributes: { - // The attributeName will be either `content` or `url`. - [ attributeName ]: { - // Source will be variable, could be post_meta, user_meta, term_meta, etc. - // Could even be a custom source like a social media attribute. - source, - value, - }, - }, - }, - placeholder: sprintf( - 'This content will be replaced on the frontend by the value of "%s" custom field.', - value - ), - } ); - } - } - return ( - { - updateConnections( nextSource, connectionValue ); - } } - /> { - updateConnections( connectionSource, nextValue ); + if ( nextValue === '' ) { + props.setAttributes( { + connections: undefined, + [ attributeName ]: undefined, + placeholder: undefined, + } ); + } else { + props.setAttributes( { + connections: { + attributes: { + // The attributeName will be either `content` or `url`. + [ attributeName ]: { + // Source will be variable, could be post_meta, user_meta, term_meta, etc. + // Could even be a custom source like a social media attribute. + source: 'meta_fields', + value: nextValue, + }, + }, + }, + [ attributeName ]: undefined, + placeholder: sprintf( + 'This content will be replaced on the frontend by the value of "%s" custom field.', + nextValue + ), + } ); + } } } /> diff --git a/packages/block-library/src/block/edit.js b/packages/block-library/src/block/edit.js index 5a8e153eb6033b..e86ed9b59c62b2 100644 --- a/packages/block-library/src/block/edit.js +++ b/packages/block-library/src/block/edit.js @@ -46,14 +46,11 @@ function isPartiallySynced( block ) { ); } function getPartiallySyncedAttributes( block ) { - const attributes = {}; - for ( const [ attribute, connection ] of Object.entries( - block.attributes.connections.attributes - ) ) { - if ( connection.source !== 'pattern_attributes' ) continue; - attributes[ attribute ] = connection.value; - } - return attributes; + return Object.entries( block.attributes.connections.attributes ) + .filter( + ( [ , connection ] ) => connection.source === 'pattern_attributes' + ) + .map( ( [ attributeKey ] ) => attributeKey ); } const fullAlignments = [ 'full', 'wide', 'left', 'right' ]; @@ -94,13 +91,15 @@ function applyInitialOverrides( blocks, overrides = {}, defaultValues ) { overrides, defaultValues ); - if ( ! isPartiallySynced( block ) ) return { ...block, innerBlocks }; + const blockId = block.attributes.metadata?.id; + if ( ! isPartiallySynced( block ) || ! blockId ) + return { ...block, innerBlocks }; const attributes = getPartiallySyncedAttributes( block ); const newAttributes = { ...block.attributes }; - for ( const [ attributeKey, id ] of Object.entries( attributes ) ) { - defaultValues[ id ] = block.attributes[ attributeKey ]; - if ( overrides[ id ] ) { - newAttributes[ attributeKey ] = overrides[ id ]; + for ( const attributeKey of attributes ) { + defaultValues[ blockId ] = block.attributes[ attributeKey ]; + if ( overrides[ blockId ] ) { + newAttributes[ attributeKey ] = overrides[ blockId ]; } } return { @@ -119,11 +118,14 @@ function getOverridesFromBlocks( blocks, defaultValues ) { overrides, getOverridesFromBlocks( block.innerBlocks, defaultValues ) ); - if ( ! isPartiallySynced( block ) ) continue; + const blockId = block.attributes.metadata?.id; + if ( ! isPartiallySynced( block ) || ! blockId ) continue; const attributes = getPartiallySyncedAttributes( block ); - for ( const [ attributeKey, id ] of Object.entries( attributes ) ) { - if ( block.attributes[ attributeKey ] !== defaultValues[ id ] ) { - overrides[ id ] = block.attributes[ attributeKey ]; + for ( const attributeKey of attributes ) { + if ( + block.attributes[ attributeKey ] !== defaultValues[ blockId ] + ) { + overrides[ blockId ] = block.attributes[ attributeKey ]; } } } diff --git a/packages/edit-site/src/hooks/index.js b/packages/edit-site/src/hooks/index.js index 4e871f4e3824eb..9be1160f7f3672 100644 --- a/packages/edit-site/src/hooks/index.js +++ b/packages/edit-site/src/hooks/index.js @@ -5,3 +5,4 @@ import './components'; import './push-changes-to-global-styles'; import './template-part-edit'; import './navigation-menu-edit'; +import './pattern-partial-syncing'; diff --git a/packages/edit-site/src/hooks/pattern-partial-syncing.js b/packages/edit-site/src/hooks/pattern-partial-syncing.js new file mode 100644 index 00000000000000..c9927afba1d1a1 --- /dev/null +++ b/packages/edit-site/src/hooks/pattern-partial-syncing.js @@ -0,0 +1,69 @@ +/** + * WordPress dependencies + */ +import { addFilter } from '@wordpress/hooks'; +import { privateApis } from '@wordpress/patterns'; +import { createHigherOrderComponent } from '@wordpress/compose'; +import { useBlockEditingMode } from '@wordpress/block-editor'; +import { hasBlockSupport } from '@wordpress/blocks'; +import { useSelect } from '@wordpress/data'; + +/** + * Internal dependencies + */ +import { store as editSiteStore } from '../store'; +import { unlock } from '../lock-unlock'; + +const { PartialSyncingControls, PATTERN_TYPES } = unlock( privateApis ); + +/** + * Override the default edit UI to include a new block inspector control for + * assigning a partial syncing controls to supported blocks in the pattern editor. + * Currently, only the `core/paragraph` block is supported. + * + * @param {Component} BlockEdit Original component. + * + * @return {Component} Wrapped component. + */ +const withPartialSyncingControls = createHigherOrderComponent( + ( BlockEdit ) => ( props ) => { + const blockEditingMode = useBlockEditingMode(); + const hasCustomFieldsSupport = hasBlockSupport( + props.name, + '__experimentalConnections', + false + ); + const isEditingPattern = useSelect( + ( select ) => + select( editSiteStore ).getEditedPostType() === + PATTERN_TYPES.user, + [] + ); + + // Check if editing a pattern and the current block is a paragraph block. + // Currently, only the paragraph block is supported. + const shouldShowPartialSyncingControls = + hasCustomFieldsSupport && + props.isSelected && + isEditingPattern && + blockEditingMode === 'default' && + [ 'core/paragraph' ].includes( props.name ); + + return ( + <> + + { shouldShowPartialSyncingControls && ( + + ) } + + ); + } +); + +if ( window.__experimentalConnections ) { + addFilter( + 'editor.BlockEdit', + 'core/edit-site/with-partial-syncing-controls', + withPartialSyncingControls + ); +} diff --git a/packages/patterns/package.json b/packages/patterns/package.json index bab11059bf92c9..8172ad2a435547 100644 --- a/packages/patterns/package.json +++ b/packages/patterns/package.json @@ -44,7 +44,8 @@ "@wordpress/icons": "file:../icons", "@wordpress/notices": "file:../notices", "@wordpress/private-apis": "file:../private-apis", - "@wordpress/url": "file:../url" + "@wordpress/url": "file:../url", + "nanoid": "^4.0.2" }, "peerDependencies": { "react": "^18.0.0", diff --git a/packages/patterns/src/components/partial-syncing-controls.js b/packages/patterns/src/components/partial-syncing-controls.js new file mode 100644 index 00000000000000..9ccfc519ecafb7 --- /dev/null +++ b/packages/patterns/src/components/partial-syncing-controls.js @@ -0,0 +1,74 @@ +/** + * External dependencies + */ +import { nanoid } from 'nanoid'; + +/** + * WordPress dependencies + */ +import { BaseControl, CheckboxControl } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; +import { InspectorControls } from '@wordpress/block-editor'; + +function PartialSyncingControls( { attributes, setAttributes } ) { + // Only the `content` attribute of the paragraph block is currently supported. + const attributeName = 'content'; + + const isPartiallySynced = + attributes.connections?.attributes?.[ attributeName ]?.source === + 'pattern_attributes'; + + function updateConnections( isChecked ) { + if ( ! isChecked ) { + setAttributes( { + connections: undefined, + } ); + return; + } + if ( typeof attributes.metadata?.id === 'string' ) { + setAttributes( { + connections: { + attributes: { + [ attributeName ]: { + source: 'pattern_attributes', + }, + }, + }, + } ); + return; + } + + const id = nanoid( 6 ); + setAttributes( { + connections: { + attributes: { + [ attributeName ]: { source: 'pattern_attributes' }, + }, + }, + metadata: { + ...attributes.metadata, + id, + }, + } ); + } + + return ( + + + + { __( 'Synced attributes' ) } + + { + updateConnections( isChecked ); + } } + /> + + + ); +} + +export default PartialSyncingControls; diff --git a/packages/patterns/src/private-apis.js b/packages/patterns/src/private-apis.js index 770a78fd4fa9de..c24fe2aea90c79 100644 --- a/packages/patterns/src/private-apis.js +++ b/packages/patterns/src/private-apis.js @@ -7,6 +7,7 @@ import DuplicatePatternModal from './components/duplicate-pattern-modal'; import RenamePatternModal from './components/rename-pattern-modal'; import PatternsMenuItems from './components'; import RenamePatternCategoryModal from './components/rename-pattern-category-modal'; +import PartialSyncingControls from './components/partial-syncing-controls'; import { PATTERN_TYPES, PATTERN_DEFAULT_CATEGORY, @@ -22,6 +23,7 @@ lock( privateApis, { RenamePatternModal, PatternsMenuItems, RenamePatternCategoryModal, + PartialSyncingControls, PATTERN_TYPES, PATTERN_DEFAULT_CATEGORY, PATTERN_USER_CATEGORY, From 6473224e96618bc687a1e02ed59a95e8d15c1280 Mon Sep 17 00:00:00 2001 From: Kai Hao Date: Mon, 27 Nov 2023 20:26:49 +0800 Subject: [PATCH 2/4] Downgrade package to fix tooling --- package-lock.json | 28 ++-------------------------- packages/patterns/package.json | 2 +- 2 files changed, 3 insertions(+), 27 deletions(-) diff --git a/package-lock.json b/package-lock.json index f37fe7a47d40bc..7c3539dd39fbe2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -56047,7 +56047,7 @@ "@wordpress/notices": "file:../notices", "@wordpress/private-apis": "file:../private-apis", "@wordpress/url": "file:../url", - "nanoid": "^4.0.2" + "nanoid": "^3.3.4" }, "engines": { "node": ">=16.0.0" @@ -56057,23 +56057,6 @@ "react-dom": "^18.0.0" } }, - "packages/patterns/node_modules/nanoid": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-4.0.2.tgz", - "integrity": "sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.js" - }, - "engines": { - "node": "^14 || ^16 || >=18" - } - }, "packages/plugins": { "name": "@wordpress/plugins", "version": "6.14.0", @@ -70935,14 +70918,7 @@ "@wordpress/notices": "file:../notices", "@wordpress/private-apis": "file:../private-apis", "@wordpress/url": "file:../url", - "nanoid": "^4.0.2" - }, - "dependencies": { - "nanoid": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-4.0.2.tgz", - "integrity": "sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw==" - } + "nanoid": "^3.3.4" } }, "@wordpress/plugins": { diff --git a/packages/patterns/package.json b/packages/patterns/package.json index 8172ad2a435547..783193ad2a6865 100644 --- a/packages/patterns/package.json +++ b/packages/patterns/package.json @@ -45,7 +45,7 @@ "@wordpress/notices": "file:../notices", "@wordpress/private-apis": "file:../private-apis", "@wordpress/url": "file:../url", - "nanoid": "^4.0.2" + "nanoid": "^3.3.4" }, "peerDependencies": { "react": "^18.0.0", From 7412aa8df5f73276d34d612a8840f4b92f12dc58 Mon Sep 17 00:00:00 2001 From: Kai Hao Date: Tue, 28 Nov 2023 14:47:29 +0800 Subject: [PATCH 3/4] Move to the editor package and allow core/button --- lib/experimental/blocks.php | 2 + lib/experimental/connection-sources/index.php | 3 +- packages/block-library/src/button/block.json | 3 +- packages/edit-site/src/hooks/index.js | 1 - packages/editor/src/hooks/index.js | 1 + .../src/hooks/pattern-partial-syncing.js | 18 ++-- .../components/partial-syncing-controls.js | 86 ++++++++++++------- packages/patterns/src/constants.js | 11 +++ packages/patterns/src/private-apis.js | 2 + 9 files changed, 86 insertions(+), 41 deletions(-) rename packages/{edit-site => editor}/src/hooks/pattern-partial-syncing.js (79%) diff --git a/lib/experimental/blocks.php b/lib/experimental/blocks.php index 31c5d314eecf28..323ae54d273386 100644 --- a/lib/experimental/blocks.php +++ b/lib/experimental/blocks.php @@ -97,9 +97,11 @@ function gutenberg_render_block_connections( $block_content, $block, $block_inst // Allowlist of blocks that support block connections. // Currently, we only allow the following blocks and attributes: // - Paragraph: content. + // - Button: text. // - Image: url. $blocks_attributes_allowlist = array( 'core/paragraph' => array( 'content' ), + 'core/button' => array( 'text' ), 'core/image' => array( 'url' ), ); diff --git a/lib/experimental/connection-sources/index.php b/lib/experimental/connection-sources/index.php index 043bb9d38bddda..435f97142f31ac 100644 --- a/lib/experimental/connection-sources/index.php +++ b/lib/experimental/connection-sources/index.php @@ -13,6 +13,7 @@ return get_post_meta( $block_instance->context['postId'], $meta_field, true ); }, 'pattern_attributes' => function ( $block_instance ) { - return _wp_array_get( $block_instance->context, array( 'overrides', $block_instance->attributes['metadata']['id'] ), false ); + $block_id = $block_instance->attributes['metadata']['id']; + return _wp_array_get( $block_instance->context, array( 'overrides', $block_id ), false ); }, ); diff --git a/packages/block-library/src/button/block.json b/packages/block-library/src/button/block.json index eec327b4ca48e4..4286f200fda4b9 100644 --- a/packages/block-library/src/button/block.json +++ b/packages/block-library/src/button/block.json @@ -118,7 +118,8 @@ "width": true } }, - "__experimentalSelector": ".wp-block-button .wp-block-button__link" + "__experimentalSelector": ".wp-block-button .wp-block-button__link", + "__experimentalConnections": true }, "styles": [ { "name": "fill", "label": "Fill", "isDefault": true }, diff --git a/packages/edit-site/src/hooks/index.js b/packages/edit-site/src/hooks/index.js index 9be1160f7f3672..4e871f4e3824eb 100644 --- a/packages/edit-site/src/hooks/index.js +++ b/packages/edit-site/src/hooks/index.js @@ -5,4 +5,3 @@ import './components'; import './push-changes-to-global-styles'; import './template-part-edit'; import './navigation-menu-edit'; -import './pattern-partial-syncing'; diff --git a/packages/editor/src/hooks/index.js b/packages/editor/src/hooks/index.js index 6e0934d63c0cfa..5a48ec1bf49566 100644 --- a/packages/editor/src/hooks/index.js +++ b/packages/editor/src/hooks/index.js @@ -3,3 +3,4 @@ */ import './custom-sources-backwards-compatibility'; import './default-autocompleters'; +import './pattern-partial-syncing'; diff --git a/packages/edit-site/src/hooks/pattern-partial-syncing.js b/packages/editor/src/hooks/pattern-partial-syncing.js similarity index 79% rename from packages/edit-site/src/hooks/pattern-partial-syncing.js rename to packages/editor/src/hooks/pattern-partial-syncing.js index c9927afba1d1a1..75a969435f8292 100644 --- a/packages/edit-site/src/hooks/pattern-partial-syncing.js +++ b/packages/editor/src/hooks/pattern-partial-syncing.js @@ -11,10 +11,14 @@ import { useSelect } from '@wordpress/data'; /** * Internal dependencies */ -import { store as editSiteStore } from '../store'; +import { store as editorStore } from '../store'; import { unlock } from '../lock-unlock'; -const { PartialSyncingControls, PATTERN_TYPES } = unlock( privateApis ); +const { + PartialSyncingControls, + PATTERN_TYPES, + PARTIAL_SYNCING_SUPPORTED_BLOCKS, +} = unlock( privateApis ); /** * Override the default edit UI to include a new block inspector control for @@ -35,19 +39,19 @@ const withPartialSyncingControls = createHigherOrderComponent( ); const isEditingPattern = useSelect( ( select ) => - select( editSiteStore ).getEditedPostType() === + select( editorStore ).getCurrentPostType() === PATTERN_TYPES.user, [] ); - // Check if editing a pattern and the current block is a paragraph block. - // Currently, only the paragraph block is supported. const shouldShowPartialSyncingControls = hasCustomFieldsSupport && props.isSelected && isEditingPattern && blockEditingMode === 'default' && - [ 'core/paragraph' ].includes( props.name ); + Object.keys( PARTIAL_SYNCING_SUPPORTED_BLOCKS ).includes( + props.name + ); return ( <> @@ -63,7 +67,7 @@ const withPartialSyncingControls = createHigherOrderComponent( if ( window.__experimentalConnections ) { addFilter( 'editor.BlockEdit', - 'core/edit-site/with-partial-syncing-controls', + 'core/editor/with-partial-syncing-controls', withPartialSyncingControls ); } diff --git a/packages/patterns/src/components/partial-syncing-controls.js b/packages/patterns/src/components/partial-syncing-controls.js index 9ccfc519ecafb7..42c39ce69e87bf 100644 --- a/packages/patterns/src/components/partial-syncing-controls.js +++ b/packages/patterns/src/components/partial-syncing-controls.js @@ -6,45 +6,60 @@ import { nanoid } from 'nanoid'; /** * WordPress dependencies */ +import { InspectorControls } from '@wordpress/block-editor'; import { BaseControl, CheckboxControl } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; -import { InspectorControls } from '@wordpress/block-editor'; -function PartialSyncingControls( { attributes, setAttributes } ) { - // Only the `content` attribute of the paragraph block is currently supported. - const attributeName = 'content'; +/** + * Internal dependencies + */ +import { PARTIAL_SYNCING_SUPPORTED_BLOCKS } from '../constants'; - const isPartiallySynced = - attributes.connections?.attributes?.[ attributeName ]?.source === - 'pattern_attributes'; +function PartialSyncingControls( { name, attributes, setAttributes } ) { + const syncedAttributes = PARTIAL_SYNCING_SUPPORTED_BLOCKS[ name ]; - function updateConnections( isChecked ) { + function updateConnections( attributeName, isChecked ) { if ( ! isChecked ) { + let updatedConnections = { + ...attributes.connections, + attributes: { + ...attributes.connections?.attributes, + [ attributeName ]: undefined, + }, + }; + if ( Object.keys( updatedConnections.attributes ).length === 1 ) { + updatedConnections.attributes = undefined; + } + if ( + Object.keys( updatedConnections ).length === 1 && + updateConnections.attributes === undefined + ) { + updatedConnections = undefined; + } setAttributes( { - connections: undefined, + connections: updatedConnections, } ); return; } - if ( typeof attributes.metadata?.id === 'string' ) { - setAttributes( { - connections: { - attributes: { - [ attributeName ]: { - source: 'pattern_attributes', - }, - }, + + const updatedConnections = { + ...attributes.connections, + attributes: { + ...attributes.connections?.attributes, + [ attributeName ]: { + source: 'pattern_attributes', }, - } ); + }, + }; + + if ( typeof attributes.metadata?.id === 'string' ) { + setAttributes( { connections: updatedConnections } ); return; } const id = nanoid( 6 ); setAttributes( { - connections: { - attributes: { - [ attributeName ]: { source: 'pattern_attributes' }, - }, - }, + connections: updatedConnections, metadata: { ...attributes.metadata, id, @@ -58,14 +73,23 @@ function PartialSyncingControls( { attributes, setAttributes } ) { { __( 'Synced attributes' ) } - { - updateConnections( isChecked ); - } } - /> + { Object.entries( syncedAttributes ).map( + ( [ attributeName, label ] ) => ( + { + updateConnections( attributeName, isChecked ); + } } + /> + ) + ) } ); diff --git a/packages/patterns/src/constants.js b/packages/patterns/src/constants.js index 465970b17b7aae..c61150874c5621 100644 --- a/packages/patterns/src/constants.js +++ b/packages/patterns/src/constants.js @@ -1,3 +1,8 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; + export const PATTERN_TYPES = { theme: 'pattern', user: 'wp_block', @@ -14,3 +19,9 @@ export const PATTERN_SYNC_TYPES = { full: 'fully', unsynced: 'unsynced', }; + +// TODO: This should not be hardcoded. Maybe there should be a config and/or an UI. +export const PARTIAL_SYNCING_SUPPORTED_BLOCKS = { + 'core/paragraph': { content: __( 'Content' ) }, + 'core/button': { text: __( 'Text' ) }, +}; diff --git a/packages/patterns/src/private-apis.js b/packages/patterns/src/private-apis.js index c24fe2aea90c79..b357efb1bc107a 100644 --- a/packages/patterns/src/private-apis.js +++ b/packages/patterns/src/private-apis.js @@ -14,6 +14,7 @@ import { PATTERN_USER_CATEGORY, EXCLUDED_PATTERN_SOURCES, PATTERN_SYNC_TYPES, + PARTIAL_SYNCING_SUPPORTED_BLOCKS, } from './constants'; export const privateApis = {}; @@ -29,4 +30,5 @@ lock( privateApis, { PATTERN_USER_CATEGORY, EXCLUDED_PATTERN_SOURCES, PATTERN_SYNC_TYPES, + PARTIAL_SYNCING_SUPPORTED_BLOCKS, } ); From e70b253c0be5ddb179442bb7c3cde0f7ce97bcc5 Mon Sep 17 00:00:00 2001 From: Kai Hao Date: Tue, 28 Nov 2023 15:53:29 +0800 Subject: [PATCH 4/4] Fix test resolver --- test/unit/scripts/resolver.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/unit/scripts/resolver.js b/test/unit/scripts/resolver.js index 2c359145f0b3e8..7672bb723e1248 100644 --- a/test/unit/scripts/resolver.js +++ b/test/unit/scripts/resolver.js @@ -24,7 +24,8 @@ module.exports = ( path, options ) => { pkg.name === 'uuid' || pkg.name === 'react-colorful' || pkg.name === '@eslint/eslintrc' || - pkg.name === 'expect' + pkg.name === 'expect' || + pkg.name === 'nanoid' ) { delete pkg.exports; delete pkg.module;