diff --git a/backport-changelog/6.7/7137.md b/backport-changelog/6.7/7137.md
index 1eba52ebaf508..00771b8bc6c21 100644
--- a/backport-changelog/6.7/7137.md
+++ b/backport-changelog/6.7/7137.md
@@ -1,4 +1,5 @@
https://github.com/WordPress/wordpress-develop/pull/7137
+* https://github.com/WordPress/gutenberg/pull/64128
* https://github.com/WordPress/gutenberg/pull/64192
* https://github.com/WordPress/gutenberg/pull/64328
diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php
index 20ea31090407b..756ef06c80aa8 100644
--- a/lib/class-wp-theme-json-gutenberg.php
+++ b/lib/class-wp-theme-json-gutenberg.php
@@ -2329,7 +2329,7 @@ protected static function flatten_tree( $tree, $prefix = '', $token = '--' ) {
* ```php
* array(
* 'name' => 'property_name',
- * 'value' => 'property_value,
+ * 'value' => 'property_value',
* )
* ```
*
@@ -2338,6 +2338,7 @@ protected static function flatten_tree( $tree, $prefix = '', $token = '--' ) {
* @since 6.1.0 Added `$theme_json`, `$selector`, and `$use_root_padding` parameters.
* @since 6.5.0 Output a `min-height: unset` rule when `aspect-ratio` is set.
* @since 6.6.0 Passing current theme JSON settings to wp_get_typography_font_size_value(). Using style engine to correctly fetch background CSS values.
+ * @since 6.7.0 Allow ref resolution of background properties.
*
* @param array $styles Styles to process.
* @param array $settings Theme settings.
@@ -2381,21 +2382,28 @@ protected static function compute_style_properties( $styles, $settings = array()
$root_variable_duplicates[] = substr( $css_property, $root_style_length );
}
- // Processes background styles.
- if ( 'background' === $value_path[0] && isset( $styles['background'] ) ) {
- /*
- * For user-uploaded images at the block level, assign defaults.
- * Matches defaults applied in the editor and in block supports: background.php.
- */
- if ( static::ROOT_BLOCK_SELECTOR !== $selector && ! empty( $styles['background']['backgroundImage']['id'] ) ) {
- $styles['background']['backgroundSize'] = $styles['background']['backgroundSize'] ?? 'cover';
- // If the background size is set to `contain` and no position is set, set the position to `center`.
- if ( 'contain' === $styles['background']['backgroundSize'] && empty( $styles['background']['backgroundPosition'] ) ) {
- $styles['background']['backgroundPosition'] = '50% 50%';
- }
+ /*
+ * Processes background image styles.
+ * If the value is a URL, it will be converted to a CSS `url()` value.
+ * For an uploaded image (images with a database ID), apply size and position
+ * defaults equal to those applied in block supports in lib/background.php.
+ */
+ if ( 'background-image' === $css_property && ! empty( $value ) ) {
+ $background_styles = gutenberg_style_engine_get_styles(
+ array( 'background' => array( 'backgroundImage' => $value ) )
+ );
+
+ $value = $background_styles['declarations'][ $css_property ];
+ }
+ if ( empty( $value ) && static::ROOT_BLOCK_SELECTOR !== $selector && ! empty( $styles['background']['backgroundImage']['id'] ) ) {
+ if ( 'background-size' === $css_property ) {
+ $value = 'cover';
+ }
+ // If the background size is set to `contain` and no position is set, set the position to `center`.
+ if ( 'background-position' === $css_property ) {
+ $background_size = $styles['background']['backgroundSize'] ?? null;
+ $value = 'contain' === $background_size ? '50% 50%' : null;
}
- $background_styles = gutenberg_style_engine_get_styles( array( 'background' => $styles['background'] ) );
- $value = $background_styles['declarations'][ $css_property ] ?? $value;
}
// Skip if empty and not "0" or value represents array of longhand values.
@@ -2463,6 +2471,7 @@ protected static function compute_style_properties( $styles, $settings = array()
* @since 5.8.0
* @since 5.9.0 Added support for values of array type, which are returned as is.
* @since 6.1.0 Added the `$theme_json` parameter.
+ * @since 6.7.0 Added support for background image refs
*
* @param array $styles Styles subtree.
* @param array $path Which property to process.
@@ -2479,15 +2488,17 @@ protected static function get_property_value( $styles, $path, $theme_json = null
}
/*
- * This converts references to a path to the value at that path
- * where the values is an array with a "ref" key, pointing to a path.
+ * Where the current value is an array with a 'ref' key pointing
+ * to a path, this converts that path into the value at that path.
* For example: { "ref": "style.color.background" } => "#fff".
*/
if ( is_array( $value ) && isset( $value['ref'] ) ) {
$value_path = explode( '.', $value['ref'] );
- $ref_value = _wp_array_get( $theme_json, $value_path );
+ $ref_value = _wp_array_get( $theme_json, $value_path, null );
+ // Background Image refs can refer to a string or an array containing a URL string.
+ $ref_value_url = $ref_value['url'] ?? null;
// Only use the ref value if we find anything.
- if ( ! empty( $ref_value ) && is_string( $ref_value ) ) {
+ if ( ! empty( $ref_value ) && ( is_string( $ref_value ) || is_string( $ref_value_url ) ) ) {
$value = $ref_value;
}
@@ -3247,6 +3258,25 @@ public function merge( $incoming ) {
}
}
}
+
+ /*
+ * Style values are merged at the leaf level, however
+ * some values provide exceptions, namely style values that are
+ * objects and represent unique definitions for the style.
+ */
+ $style_nodes = static::get_styles_block_nodes();
+ foreach ( $style_nodes as $style_node ) {
+ $path = $style_node['path'];
+ /*
+ * Background image styles should be replaced, not merged,
+ * as they themselves are specific object definitions for the style.
+ */
+ $background_image_path = array_merge( $path, static::PROPERTIES_METADATA['background-image'] );
+ $content = _wp_array_get( $incoming_data, $background_image_path, null );
+ if ( isset( $content ) ) {
+ _wp_array_set( $this->theme_json, $background_image_path, $content );
+ }
+ }
}
/**
diff --git a/packages/block-editor/src/components/global-styles/background-panel.js b/packages/block-editor/src/components/global-styles/background-panel.js
index 14b37bb300765..1373c54764d15 100644
--- a/packages/block-editor/src/components/global-styles/background-panel.js
+++ b/packages/block-editor/src/components/global-styles/background-panel.js
@@ -34,6 +34,7 @@ import {
useRef,
useState,
useEffect,
+ useMemo,
} from '@wordpress/element';
import { useDispatch, useSelect } from '@wordpress/data';
import { focus } from '@wordpress/dom';
@@ -42,11 +43,15 @@ import { isBlobURL } from '@wordpress/blob';
/**
* Internal dependencies
*/
-import { useToolsPanelDropdownMenuProps } from './utils';
+import { useToolsPanelDropdownMenuProps, getResolvedValue } from './utils';
import { setImmutably } from '../../utils/object';
import MediaReplaceFlow from '../media-replace-flow';
import { store as blockEditorStore } from '../../store';
-import { getResolvedThemeFilePath } from './theme-file-uri-utils';
+
+import {
+ globalStylesDataKey,
+ globalStylesLinksDataKey,
+} from '../../store/private-keys';
const IMAGE_BACKGROUND_TYPE = 'image';
const DEFAULT_CONTROLS = {
@@ -270,7 +275,6 @@ function BackgroundImageControls( {
onRemoveImage = noop,
onResetImage = noop,
displayInPanel,
- themeFileURIs,
defaultValues,
} ) {
const mediaUpload = useSelect(
@@ -404,10 +408,7 @@ function BackgroundImageControls( {
name={
@@ -449,7 +450,6 @@ function BackgroundSizeControls( {
style,
inheritedValue,
defaultValues,
- themeFileURIs,
} ) {
const sizeValue =
style?.background?.backgroundSize ||
@@ -587,7 +587,7 @@ function BackgroundSizeControls( {
@@ -697,8 +697,44 @@ export default function BackgroundPanel( {
defaultControls = DEFAULT_CONTROLS,
defaultValues = {},
headerLabel = __( 'Background image' ),
- themeFileURIs,
} ) {
+ /*
+ * Resolve any inherited "ref" pointers.
+ * Should the block editor need resolved, inherited values
+ * across all controls, this could be abstracted into a hook,
+ * e.g., useResolveGlobalStyle
+ */
+ const { globalStyles, _links } = useSelect( ( select ) => {
+ const { getSettings } = select( blockEditorStore );
+ const _settings = getSettings();
+ return {
+ globalStyles: _settings[ globalStylesDataKey ],
+ _links: _settings[ globalStylesLinksDataKey ],
+ };
+ }, [] );
+ const resolvedInheritedValue = useMemo( () => {
+ const resolvedValues = {
+ background: {},
+ };
+
+ if ( ! inheritedValue?.background ) {
+ return inheritedValue;
+ }
+
+ Object.entries( inheritedValue?.background ).forEach(
+ ( [ key, backgroundValue ] ) => {
+ resolvedValues.background[ key ] = getResolvedValue(
+ backgroundValue,
+ {
+ styles: globalStyles,
+ _links,
+ }
+ );
+ }
+ );
+ return resolvedValues;
+ }, [ globalStyles, _links, inheritedValue ] );
+
const resetAllFilter = useCallback( ( previousValue ) => {
return {
...previousValue,
@@ -710,11 +746,11 @@ export default function BackgroundPanel( {
onChange( setImmutably( value, [ 'background' ], {} ) );
const { title, url } = value?.background?.backgroundImage || {
- ...inheritedValue?.background?.backgroundImage,
+ ...resolvedInheritedValue?.background?.backgroundImage,
};
const hasImageValue =
hasBackgroundImageValue( value ) ||
- hasBackgroundImageValue( inheritedValue );
+ hasBackgroundImageValue( resolvedInheritedValue );
const imageValue =
value?.background?.backgroundImage ||
@@ -756,10 +792,7 @@ export default function BackgroundPanel( {
@@ -767,8 +800,7 @@ export default function BackgroundPanel( {
{
setIsDropDownOpen( false );
@@ -784,8 +816,7 @@ export default function BackgroundPanel( {
panelId={ panelId }
style={ value }
defaultValues={ defaultValues }
- inheritedValue={ inheritedValue }
- themeFileURIs={ themeFileURIs }
+ inheritedValue={ resolvedInheritedValue }
/>
@@ -793,8 +824,7 @@ export default function BackgroundPanel( {
{
setIsDropDownOpen( false );
diff --git a/packages/block-editor/src/components/global-styles/hooks.js b/packages/block-editor/src/components/global-styles/hooks.js
index a1a4fc1a0a6ae..2be77aec18a2c 100644
--- a/packages/block-editor/src/components/global-styles/hooks.js
+++ b/packages/block-editor/src/components/global-styles/hooks.js
@@ -209,11 +209,6 @@ export function useGlobalStyle(
return [ result, setStyle ];
}
-export function useGlobalStyleLinks() {
- const { merged: mergedConfig } = useContext( GlobalStylesContext );
- return mergedConfig?._links;
-}
-
/**
* React hook that overrides a global settings object with block and element specific settings.
*
diff --git a/packages/block-editor/src/components/global-styles/index.js b/packages/block-editor/src/components/global-styles/index.js
index 062df0a5606e9..8096a48569f19 100644
--- a/packages/block-editor/src/components/global-styles/index.js
+++ b/packages/block-editor/src/components/global-styles/index.js
@@ -3,7 +3,6 @@ export {
useGlobalSetting,
useGlobalStyle,
useSettingsForBlockElement,
- useGlobalStyleLinks,
} from './hooks';
export { getBlockCSSSelector } from './get-block-css-selector';
export {
diff --git a/packages/block-editor/src/components/global-styles/test/theme-file-uri-utils.js b/packages/block-editor/src/components/global-styles/test/theme-file-uri-utils.js
deleted file mode 100644
index e239bb0941ea9..0000000000000
--- a/packages/block-editor/src/components/global-styles/test/theme-file-uri-utils.js
+++ /dev/null
@@ -1,41 +0,0 @@
-/**
- * Internal dependencies
- */
-import { getResolvedThemeFilePath } from '../theme-file-uri-utils';
-
-const themeFileURIs = [
- {
- name: 'file:./assets/image.jpg',
- href: 'https://wordpress.org/assets/image.jpg',
- target: 'styles.background.backgroundImage.url',
- },
- {
- name: 'file:./assets/other/image.jpg',
- href: 'https://wordpress.org/assets/other/image.jpg',
- target: "styles.blocks.['core/group].background.backgroundImage.url",
- },
-];
-
-describe( 'getResolvedThemeFilePath()', () => {
- it.each( [
- [
- 'file:./assets/image.jpg',
- 'https://wordpress.org/assets/image.jpg',
- 'Should return absolute URL if found in themeFileURIs',
- ],
- [
- 'file:./misc/image.jpg',
- 'file:./misc/image.jpg',
- 'Should return value if not found in themeFileURIs',
- ],
- [
- 'https://wordpress.org/assets/image.jpg',
- 'https://wordpress.org/assets/image.jpg',
- 'Should not match absolute URLs',
- ],
- ] )( 'Given file %s and return value %s: %s', ( file, returnedValue ) => {
- expect(
- getResolvedThemeFilePath( file, themeFileURIs ) === returnedValue
- ).toBe( true );
- } );
-} );
diff --git a/packages/block-editor/src/components/global-styles/test/use-global-styles-output.js b/packages/block-editor/src/components/global-styles/test/use-global-styles-output.js
index 1b061f6921f2c..5022e8ba591db 100644
--- a/packages/block-editor/src/components/global-styles/test/use-global-styles-output.js
+++ b/packages/block-editor/src/components/global-styles/test/use-global-styles-output.js
@@ -1008,9 +1008,23 @@ describe( 'global styles renderer', () => {
ref: 'styles.elements.h1.typography.letterSpacing',
},
},
+ background: {
+ backgroundImage: {
+ ref: 'styles.background.backgroundImage',
+ },
+ backgroundSize: {
+ ref: 'styles.background.backgroundSize',
+ },
+ },
};
const tree = {
styles: {
+ background: {
+ backgroundImage: {
+ url: 'http://my-image.org/image.gif',
+ },
+ backgroundSize: 'cover',
+ },
elements: {
h1: {
typography: {
@@ -1026,6 +1040,8 @@ describe( 'global styles renderer', () => {
).toEqual( [
'font-size: var(--wp--preset--font-size--xx-large)',
'letter-spacing: 2px',
+ "background-image: url( 'http://my-image.org/image.gif' )",
+ 'background-size: cover',
] );
} );
it( 'should set default values for block background styles', () => {
diff --git a/packages/block-editor/src/components/global-styles/theme-file-uri-utils.js b/packages/block-editor/src/components/global-styles/theme-file-uri-utils.js
deleted file mode 100644
index 96b3e2e4cb68b..0000000000000
--- a/packages/block-editor/src/components/global-styles/theme-file-uri-utils.js
+++ /dev/null
@@ -1,18 +0,0 @@
-/**
- * Looks up a theme file URI based on a relative path.
- *
- * @param {string} file A relative path.
- * @param {Array