diff --git a/backport-changelog/6.7/7298.md b/backport-changelog/6.7/7298.md new file mode 100644 index 0000000000000..4c01ef5d4f46e --- /dev/null +++ b/backport-changelog/6.7/7298.md @@ -0,0 +1,3 @@ +https://github.com/WordPress/wordpress-develop/pull/7298 + +* https://github.com/WordPress/gutenberg/pull/65099 \ No newline at end of file diff --git a/lib/compat/wordpress-6.7/block-bindings.php b/lib/compat/wordpress-6.7/block-bindings.php index 9e82c1843f35a..a8f68c0f0f04e 100644 --- a/lib/compat/wordpress-6.7/block-bindings.php +++ b/lib/compat/wordpress-6.7/block-bindings.php @@ -53,3 +53,35 @@ function gutenberg_add_can_update_block_bindings_editor_setting( $editor_setting } add_filter( 'block_editor_settings_all', 'gutenberg_add_can_update_block_bindings_editor_setting', 10 ); + +/** + * Add `label` to `register_meta`. + * + * @param array $args Array of arguments for registering meta. + * @return array Modified arguments array including `label`. + */ +function gutenberg_update_meta_args_with_label( $args ) { + // Don't update schema when label isn't provided. + if ( ! isset( $args['label'] ) ) { + return $args; + } + + $schema = array( 'title' => $args['label'] ); + if ( ! is_array( $args['show_in_rest'] ) ) { + $args['show_in_rest'] = array( + 'schema' => $schema, + ); + return $args; + } + + if ( ! empty( $args['show_in_rest']['schema'] ) ) { + $args['show_in_rest']['schema'] = array_merge( $args['show_in_rest']['schema'], $schema ); + } else { + $args['show_in_rest']['schema'] = $schema; + } + + return $args; +} + +// Priority must be lower than 10 to ensure the label is not removed. +add_filter( 'register_meta_args', 'gutenberg_update_meta_args_with_label', 5, 1 ); diff --git a/packages/block-editor/src/components/rich-text/index.js b/packages/block-editor/src/components/rich-text/index.js index 732b8dbf2c089..387f388b8fdad 100644 --- a/packages/block-editor/src/components/rich-text/index.js +++ b/packages/block-editor/src/components/rich-text/index.js @@ -125,6 +125,7 @@ export function RichTextWrapper( const { clientId, isSelected: isBlockSelected, name: blockName } = context; const blockBindings = context[ blockBindingsKey ]; const blockContext = useContext( BlockContext ); + const registry = useRegistry(); const selector = ( select ) => { // Avoid subscribing to the block editor store if the block is not // selected. @@ -178,6 +179,10 @@ export function RichTextWrapper( const blockBindingsSource = getBlockBindingsSource( relatedBinding.source ); + const fieldsList = blockBindingsSource?.getFieldsList?.( { + registry, + context: blockContext, + } ); const _disableBoundBlock = ! blockBindingsSource?.canUserEditValue?.( { @@ -186,12 +191,16 @@ export function RichTextWrapper( args: relatedBinding.args, } ); + const bindingKey = + fieldsList?.[ relatedBinding?.args?.key ]?.label ?? + blockBindingsSource?.label; + const _bindingsPlaceholder = _disableBoundBlock - ? relatedBinding?.args?.key || blockBindingsSource?.label + ? bindingKey : sprintf( - /* translators: %s: source label or key */ + /* translators: %s: connected field label or source label */ __( 'Add %s' ), - relatedBinding?.args?.key || blockBindingsSource?.label + bindingKey ); return { @@ -201,7 +210,14 @@ export function RichTextWrapper( _bindingsPlaceholder, }; }, - [ blockBindings, identifier, blockName, blockContext, adjustedValue ] + [ + blockBindings, + identifier, + blockName, + blockContext, + registry, + adjustedValue, + ] ); const shouldDisableEditing = readOnly || disableBoundBlock; @@ -371,7 +387,6 @@ export function RichTextWrapper( element.focus(); } - const registry = useRegistry(); const TagName = tagName; return ( <> diff --git a/packages/block-editor/src/hooks/block-bindings.js b/packages/block-editor/src/hooks/block-bindings.js index a0bd8820d36c5..33284b4cd27fd 100644 --- a/packages/block-editor/src/hooks/block-bindings.js +++ b/packages/block-editor/src/hooks/block-bindings.js @@ -61,7 +61,7 @@ function BlockBindingsPanelDropdown( { fieldsList, attribute, binding } ) { { registeredSources[ name ].label } ) } - { Object.entries( fields ).map( ( [ key, value ] ) => ( + { Object.entries( fields ).map( ( [ key, args ] ) => ( @@ -77,10 +77,10 @@ function BlockBindingsPanelDropdown( { fieldsList, attribute, binding } ) { checked={ key === currentKey } > - { key } + { args?.label } - { value } + { args?.value } ) ) } @@ -94,7 +94,7 @@ function BlockBindingsPanelDropdown( { fieldsList, attribute, binding } ) { ); } -function BlockBindingsAttribute( { attribute, binding } ) { +function BlockBindingsAttribute( { attribute, binding, fieldsList } ) { const { source: sourceName, args } = binding || {}; const sourceProps = unlock( blocksPrivateApis ).getBlockBindingsSource( sourceName ); @@ -110,14 +110,16 @@ function BlockBindingsAttribute( { attribute, binding } ) { > { isSourceInvalid ? __( 'Invalid source' ) - : args?.key || sourceProps?.label || sourceName } + : fieldsList?.[ sourceName ]?.[ args?.key ]?.label || + sourceProps?.label || + sourceName } ) } ); } -function ReadOnlyBlockBindingsPanelItems( { bindings } ) { +function ReadOnlyBlockBindingsPanelItems( { bindings, fieldsList } ) { return ( <> { Object.entries( bindings ).map( ( [ attribute, binding ] ) => ( @@ -125,6 +127,7 @@ function ReadOnlyBlockBindingsPanelItems( { bindings } ) { ) ) } @@ -164,6 +167,7 @@ function EditableBlockBindingsPanelItems( { } @@ -276,6 +280,7 @@ export const BlockBindingsPanel = ( { name: blockName, metadata } ) => { { readOnly ? ( ) : ( { - metaFields[ key ] = props.default; - } ); + Object.entries( registeredFields || {} ).forEach( + ( [ key, props ] ) => { + if ( props.default ) { + metaFields[ key ] = props.default; + } + } + ); } else { metaFields = getEditedEntityRecord( 'postType', @@ -37,13 +37,20 @@ function getMetadata( registry, context ) { export default { name: 'core/post-meta', getValues( { registry, context, bindings } ) { - const metaFields = getMetadata( registry, context ); + const { getRegisteredPostMeta } = unlock( + registry.select( coreDataStore ) + ); + const registeredFields = getRegisteredPostMeta( context?.postType ); + const metaFields = getMetadata( registry, context, registeredFields ); const newValues = {}; for ( const [ attributeName, source ] of Object.entries( bindings ) ) { - // Use the key if the value is not set. + // Use the value, the field label, or the field key. + const metaKey = source.args.key; newValues[ attributeName ] = - metaFields?.[ source.args.key ] ?? source.args.key; + metaFields?.[ metaKey ] ?? + registeredFields?.[ metaKey ]?.title ?? + metaKey; } return newValues; }, @@ -103,18 +110,31 @@ export default { return true; }, getFieldsList( { registry, context } ) { - const metaFields = getMetadata( registry, context ); + const { getRegisteredPostMeta } = unlock( + registry.select( coreDataStore ) + ); + const registeredFields = getRegisteredPostMeta( context?.postType ); + const metaFields = getMetadata( registry, context, registeredFields ); if ( ! metaFields || ! Object.keys( metaFields ).length ) { return null; } - // Remove footnotes or private keys from the list of fields. - // TODO: Remove this once we retrieve the fields from 'types' endpoint in post or page editor. return Object.fromEntries( - Object.entries( metaFields ).filter( - ( [ key ] ) => key !== 'footnotes' && key.charAt( 0 ) !== '_' - ) + Object.entries( metaFields ) + // Remove footnotes or private keys from the list of fields. + .filter( + ( [ key ] ) => + key !== 'footnotes' && key.charAt( 0 ) !== '_' + ) + // Return object with label and value. + .map( ( [ key, value ] ) => [ + key, + { + label: registeredFields?.[ key ]?.title || key, + value, + }, + ] ) ); }, };