From 9e5f8e6fad56574dd25ca91a7c2ae08324b570da Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Thu, 5 Dec 2019 18:44:49 -0500 Subject: [PATCH 01/10] E2E Tests: Enqueue meta attribute test block at correct action --- .../plugins/meta-attribute-block.php | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/packages/e2e-tests/plugins/meta-attribute-block.php b/packages/e2e-tests/plugins/meta-attribute-block.php index 45fbcc661e108..58eec7c04244a 100644 --- a/packages/e2e-tests/plugins/meta-attribute-block.php +++ b/packages/e2e-tests/plugins/meta-attribute-block.php @@ -8,20 +8,9 @@ */ /** - * Registers a custom script and a custom meta for the plugin. + * Registers a custom meta for use by the test block. */ function init_test_meta_attribute_block_plugin() { - wp_enqueue_script( - 'gutenberg-test-meta-attribute-block', - plugins_url( 'meta-attribute-block/index.js', __FILE__ ), - array( - 'wp-blocks', - 'wp-element', - ), - filemtime( plugin_dir_path( __FILE__ ) . 'meta-attribute-block/index.js' ), - true - ); - register_meta( 'post', 'my_meta', @@ -34,3 +23,20 @@ function init_test_meta_attribute_block_plugin() { } add_action( 'init', 'init_test_meta_attribute_block_plugin' ); + +/** + * Enqueues block assets for the custom meta test block. + */ +function enqueue_test_meta_attribute_block() { + wp_enqueue_script( + 'gutenberg-test-meta-attribute-block', + plugins_url( 'meta-attribute-block/index.js', __FILE__ ), + array( + 'wp-blocks', + 'wp-element', + ), + filemtime( plugin_dir_path( __FILE__ ) . 'meta-attribute-block/index.js' ), + true + ); +} +add_action( 'enqueue_block_assets', 'enqueue_test_meta_attribute_block' ); From 75a7ff9c4446a479604f9a287afafbee7c394134 Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Thu, 5 Dec 2019 18:44:55 -0500 Subject: [PATCH 02/10] Editor: Shim meta attribute source on registered `edit` component --- .../custom-sources-backwards-compatibility.js | 120 ++++++++++-------- 1 file changed, 70 insertions(+), 50 deletions(-) diff --git a/packages/editor/src/hooks/custom-sources-backwards-compatibility.js b/packages/editor/src/hooks/custom-sources-backwards-compatibility.js index 6b56281feade7..67b4674ae2179 100644 --- a/packages/editor/src/hooks/custom-sources-backwards-compatibility.js +++ b/packages/editor/src/hooks/custom-sources-backwards-compatibility.js @@ -1,66 +1,68 @@ +/** + * External dependencies + */ +import { pickBy, mapValues, isEmpty, mapKeys } from 'lodash'; + /** * WordPress dependencies */ -import { getBlockType } from '@wordpress/blocks'; import { useSelect } from '@wordpress/data'; import { useEntityProp } from '@wordpress/core-data'; -import { useMemo, useCallback } from '@wordpress/element'; +import { useMemo } from '@wordpress/element'; import { createHigherOrderComponent } from '@wordpress/compose'; import { addFilter } from '@wordpress/hooks'; -const EMPTY_OBJECT = {}; -function useMetaAttributeSource( name, _attributes, _setAttributes ) { - const { attributes: attributeTypes = EMPTY_OBJECT } = - getBlockType( name ) || EMPTY_OBJECT; - let [ attributes, setAttributes ] = [ _attributes, _setAttributes ]; +/** @typedef {import('@wordpress/compose').WPHigherOrderComponent} WPHigherOrderComponent */ + +/** @typedef {import('@wordpress/blocks').WPBlockSettings} WPBlockSettings */ + +/** + * Object mapping an attribute key to its corresponding meta property. + * + * @typedef {Object} WPMetaAttributeMapping + */ - if ( Object.values( attributeTypes ).some( ( type ) => type.source === 'meta' ) ) { - // eslint-disable-next-line react-hooks/rules-of-hooks - const type = useSelect( ( select ) => select( 'core/editor' ).getCurrentPostType(), [] ); - // eslint-disable-next-line react-hooks/rules-of-hooks - const [ meta, setMeta ] = useEntityProp( 'postType', type, 'meta' ); +/** + * Given a meta attribute mapping, returns a new higher-order component which + * manages attributes merging and updates to consume from and mirror to the + * associated entity record. + * + * @param {WPMetaAttributeMapping} metaKeys Meta attribute mapping. + * + * @return {WPHigherOrderComponent} Higher-order component. + */ +const createWithMetaAttributeSource = ( metaKeys ) => createHigherOrderComponent( + ( BlockEdit ) => ( { attributes, setAttributes, name, ...props } ) => { + const postType = useSelect( ( select ) => select( 'core/editor' ).getCurrentPostType() ); + const [ meta, setMeta ] = useEntityProp( 'postType', postType, 'meta' ); - // eslint-disable-next-line react-hooks/rules-of-hooks - attributes = useMemo( + const mergedAttributes = useMemo( () => ( { - ..._attributes, - ...Object.keys( attributeTypes ).reduce( ( acc, key ) => { - if ( attributeTypes[ key ].source === 'meta' ) { - acc[ key ] = meta[ attributeTypes[ key ].meta ]; - } - return acc; - }, {} ), + ...attributes, + ...mapValues( metaKeys, ( metaKey ) => meta[ metaKey ] ), } ), - [ attributeTypes, meta, _attributes ] + [ attributes, meta ] ); - // eslint-disable-next-line react-hooks/rules-of-hooks - setAttributes = useCallback( - ( ...args ) => { - Object.keys( args[ 0 ] ).forEach( ( key ) => { - if ( attributeTypes[ key ].source === 'meta' ) { - setMeta( { [ attributeTypes[ key ].meta ]: args[ 0 ][ key ] } ); + return ( + { + const nextMeta = mapKeys( + // Filter to intersection of keys between the updated + // attributes and those with an associated meta key. + pickBy( nextAttributes, ( value, key ) => metaKeys[ key ] ), + + // Rename the keys to the expected meta key name. + ( value, attributeKey ) => metaKeys[ attributeKey ], + ); + + if ( ! isEmpty( nextMeta ) ) { + setMeta( nextMeta ); } - } ); - return _setAttributes( ...args ); - }, - [ attributeTypes, setMeta, _setAttributes ] - ); - } - return [ attributes, setAttributes ]; -} -const withMetaAttributeSource = createHigherOrderComponent( - ( BlockListBlock ) => ( { attributes, setAttributes, name, ...props } ) => { - [ attributes, setAttributes ] = useMetaAttributeSource( - name, - attributes, - setAttributes - ); - return ( - @@ -69,8 +71,26 @@ const withMetaAttributeSource = createHigherOrderComponent( 'withMetaAttributeSource' ); +/** + * Filters a registered block settings to enhance a block's `edit` component to + * upgrade meta-sourced attributes to use the post's meta entity property. + * + * @param {WPBlockSettings} settings Registered block settings. + * + * @return {WPBlockSettings} Filtered block settings. + */ +function shimAttributeSource( settings ) { + /** @type {WPMetaAttributeMapping} */ + const metaAttributes = mapValues( pickBy( settings.attributes, { source: 'meta' } ), 'meta' ); + if ( ! isEmpty( metaAttributes ) ) { + settings.edit = createWithMetaAttributeSource( metaAttributes )( settings.edit ); + } + + return settings; +} + addFilter( - 'editor.BlockListBlock', - 'core/editor/custom-sources-backwards-compatibility/with-meta-attribute-source', - withMetaAttributeSource + 'blocks.registerBlockType', + 'core/editor/custom-sources-backwards-compatibility/shim-attribute-source', + shimAttributeSource ); From 417cc8a603d4cbc9a76c570b0d337d5e2250b745 Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Thu, 5 Dec 2019 19:16:25 -0500 Subject: [PATCH 03/10] Editor: Remove unused name explicit destructuring --- .../editor/src/hooks/custom-sources-backwards-compatibility.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/editor/src/hooks/custom-sources-backwards-compatibility.js b/packages/editor/src/hooks/custom-sources-backwards-compatibility.js index 67b4674ae2179..d755cbc1190ba 100644 --- a/packages/editor/src/hooks/custom-sources-backwards-compatibility.js +++ b/packages/editor/src/hooks/custom-sources-backwards-compatibility.js @@ -32,7 +32,7 @@ import { addFilter } from '@wordpress/hooks'; * @return {WPHigherOrderComponent} Higher-order component. */ const createWithMetaAttributeSource = ( metaKeys ) => createHigherOrderComponent( - ( BlockEdit ) => ( { attributes, setAttributes, name, ...props } ) => { + ( BlockEdit ) => ( { attributes, setAttributes, ...props } ) => { const postType = useSelect( ( select ) => select( 'core/editor' ).getCurrentPostType() ); const [ meta, setMeta ] = useEntityProp( 'postType', postType, 'meta' ); @@ -63,7 +63,6 @@ const createWithMetaAttributeSource = ( metaKeys ) => createHigherOrderComponent setAttributes( nextAttributes ); } } - name={ name } { ...props } /> ); From 140000dbaeec563113a6e4b30c3e7d99942adc0f Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Fri, 6 Dec 2019 12:56:27 -0500 Subject: [PATCH 04/10] Editor: Improve wording around attribute merging, updating Co-Authored-By: Enrique Piqueras --- .../editor/src/hooks/custom-sources-backwards-compatibility.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/editor/src/hooks/custom-sources-backwards-compatibility.js b/packages/editor/src/hooks/custom-sources-backwards-compatibility.js index d755cbc1190ba..5596c76866038 100644 --- a/packages/editor/src/hooks/custom-sources-backwards-compatibility.js +++ b/packages/editor/src/hooks/custom-sources-backwards-compatibility.js @@ -24,7 +24,7 @@ import { addFilter } from '@wordpress/hooks'; /** * Given a meta attribute mapping, returns a new higher-order component which - * manages attributes merging and updates to consume from and mirror to the + * manages attribute merging and updating to consume from and mirror to the * associated entity record. * * @param {WPMetaAttributeMapping} metaKeys Meta attribute mapping. From 5a1294b4da7f56d6253b2e8d4f54d350a5bbd417 Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Fri, 6 Dec 2019 12:59:46 -0500 Subject: [PATCH 05/10] Editor: Clarify attribute meta mapping description --- .../src/hooks/custom-sources-backwards-compatibility.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/editor/src/hooks/custom-sources-backwards-compatibility.js b/packages/editor/src/hooks/custom-sources-backwards-compatibility.js index 5596c76866038..ecbef4ef7cacb 100644 --- a/packages/editor/src/hooks/custom-sources-backwards-compatibility.js +++ b/packages/editor/src/hooks/custom-sources-backwards-compatibility.js @@ -17,7 +17,10 @@ import { addFilter } from '@wordpress/hooks'; /** @typedef {import('@wordpress/blocks').WPBlockSettings} WPBlockSettings */ /** - * Object mapping an attribute key to its corresponding meta property. + * Object whose keys are the names of block attributes, where each value + * represents the meta key to which the block attribute is intended to save. + * + * @see https://developer.wordpress.org/reference/functions/register_meta/ * * @typedef {Object} WPMetaAttributeMapping */ From 8bbe63603eb397c6949f6c810488784bbd878aa3 Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Fri, 6 Dec 2019 13:02:37 -0500 Subject: [PATCH 06/10] Editor: Improve meta override behavior description Co-Authored-By: Enrique Piqueras --- .../src/hooks/custom-sources-backwards-compatibility.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/editor/src/hooks/custom-sources-backwards-compatibility.js b/packages/editor/src/hooks/custom-sources-backwards-compatibility.js index ecbef4ef7cacb..cde4a77603f7a 100644 --- a/packages/editor/src/hooks/custom-sources-backwards-compatibility.js +++ b/packages/editor/src/hooks/custom-sources-backwards-compatibility.js @@ -26,9 +26,10 @@ import { addFilter } from '@wordpress/hooks'; */ /** - * Given a meta attribute mapping, returns a new higher-order component which - * manages attribute merging and updating to consume from and mirror to the - * associated entity record. + * Given a mapping of attribute names (meta source attributes) to their + * associated meta key, returns a higher order component that overrides its + * `attributes` and `setAttributes` props to sync any changes with the edited + * post's meta keys. * * @param {WPMetaAttributeMapping} metaKeys Meta attribute mapping. * From 842d061d3dc93b5c33ce4eac64765d7ee4cd06d7 Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Fri, 6 Dec 2019 13:09:18 -0500 Subject: [PATCH 07/10] Editor: Pass dependencies array to useSelect Co-Authored-By: Enrique Piqueras --- .../editor/src/hooks/custom-sources-backwards-compatibility.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/editor/src/hooks/custom-sources-backwards-compatibility.js b/packages/editor/src/hooks/custom-sources-backwards-compatibility.js index cde4a77603f7a..7a4339520b3c2 100644 --- a/packages/editor/src/hooks/custom-sources-backwards-compatibility.js +++ b/packages/editor/src/hooks/custom-sources-backwards-compatibility.js @@ -37,7 +37,7 @@ import { addFilter } from '@wordpress/hooks'; */ const createWithMetaAttributeSource = ( metaKeys ) => createHigherOrderComponent( ( BlockEdit ) => ( { attributes, setAttributes, ...props } ) => { - const postType = useSelect( ( select ) => select( 'core/editor' ).getCurrentPostType() ); + const postType = useSelect( ( select ) => select( 'core/editor' ).getCurrentPostType(), [] ); const [ meta, setMeta ] = useEntityProp( 'postType', postType, 'meta' ); const mergedAttributes = useMemo( From 94f541627004ac2cb3d35ed6f29b476032ed481a Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Fri, 6 Dec 2019 13:11:01 -0500 Subject: [PATCH 08/10] Editor: Rename metaKeys to metaAttributes Co-Authored-By: Enrique Piqueras --- .../hooks/custom-sources-backwards-compatibility.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/editor/src/hooks/custom-sources-backwards-compatibility.js b/packages/editor/src/hooks/custom-sources-backwards-compatibility.js index 7a4339520b3c2..2058ecc6e2ea5 100644 --- a/packages/editor/src/hooks/custom-sources-backwards-compatibility.js +++ b/packages/editor/src/hooks/custom-sources-backwards-compatibility.js @@ -31,11 +31,11 @@ import { addFilter } from '@wordpress/hooks'; * `attributes` and `setAttributes` props to sync any changes with the edited * post's meta keys. * - * @param {WPMetaAttributeMapping} metaKeys Meta attribute mapping. + * @param {WPMetaAttributeMapping} metaAttributes Meta attribute mapping. * * @return {WPHigherOrderComponent} Higher-order component. */ -const createWithMetaAttributeSource = ( metaKeys ) => createHigherOrderComponent( +const createWithMetaAttributeSource = ( metaAttributes ) => createHigherOrderComponent( ( BlockEdit ) => ( { attributes, setAttributes, ...props } ) => { const postType = useSelect( ( select ) => select( 'core/editor' ).getCurrentPostType(), [] ); const [ meta, setMeta ] = useEntityProp( 'postType', postType, 'meta' ); @@ -43,7 +43,7 @@ const createWithMetaAttributeSource = ( metaKeys ) => createHigherOrderComponent const mergedAttributes = useMemo( () => ( { ...attributes, - ...mapValues( metaKeys, ( metaKey ) => meta[ metaKey ] ), + ...mapValues( metaAttributes, ( metaKey ) => meta[ metaKey ] ), } ), [ attributes, meta ] ); @@ -55,10 +55,10 @@ const createWithMetaAttributeSource = ( metaKeys ) => createHigherOrderComponent const nextMeta = mapKeys( // Filter to intersection of keys between the updated // attributes and those with an associated meta key. - pickBy( nextAttributes, ( value, key ) => metaKeys[ key ] ), + pickBy( nextAttributes, ( value, key ) => metaAttributes[ key ] ), // Rename the keys to the expected meta key name. - ( value, attributeKey ) => metaKeys[ attributeKey ], + ( value, attributeKey ) => metaAttributes[ attributeKey ], ); if ( ! isEmpty( nextMeta ) ) { From b4aecddcd5740884f00d3ca33bc515fb596f5762 Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Fri, 6 Dec 2019 13:12:17 -0500 Subject: [PATCH 09/10] Editor: Update filter function docs to possessive "block's" Co-Authored-By: Enrique Piqueras --- .../src/hooks/custom-sources-backwards-compatibility.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/editor/src/hooks/custom-sources-backwards-compatibility.js b/packages/editor/src/hooks/custom-sources-backwards-compatibility.js index 2058ecc6e2ea5..1ec1cbadbf2a6 100644 --- a/packages/editor/src/hooks/custom-sources-backwards-compatibility.js +++ b/packages/editor/src/hooks/custom-sources-backwards-compatibility.js @@ -75,8 +75,8 @@ const createWithMetaAttributeSource = ( metaAttributes ) => createHigherOrderCom ); /** - * Filters a registered block settings to enhance a block's `edit` component to - * upgrade meta-sourced attributes to use the post's meta entity property. + * Filters a registered block's settings to enhance a block's `edit` component + * to upgrade meta-sourced attributes to use the post's meta entity property. * * @param {WPBlockSettings} settings Registered block settings. * From 35e2c8cbf0540cff0cfe29e1bbebfe564f2c9545 Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Fri, 6 Dec 2019 14:06:07 -0500 Subject: [PATCH 10/10] Editor: Remove newline between imported typedefs --- .../editor/src/hooks/custom-sources-backwards-compatibility.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/editor/src/hooks/custom-sources-backwards-compatibility.js b/packages/editor/src/hooks/custom-sources-backwards-compatibility.js index 1ec1cbadbf2a6..ff52b478f105f 100644 --- a/packages/editor/src/hooks/custom-sources-backwards-compatibility.js +++ b/packages/editor/src/hooks/custom-sources-backwards-compatibility.js @@ -13,7 +13,6 @@ import { createHigherOrderComponent } from '@wordpress/compose'; import { addFilter } from '@wordpress/hooks'; /** @typedef {import('@wordpress/compose').WPHigherOrderComponent} WPHigherOrderComponent */ - /** @typedef {import('@wordpress/blocks').WPBlockSettings} WPBlockSettings */ /**