diff --git a/backport-changelog/6.7/6836.md b/backport-changelog/6.7/6836.md new file mode 100644 index 00000000000000..feaa0f909f1f6f --- /dev/null +++ b/backport-changelog/6.7/6836.md @@ -0,0 +1,3 @@ +https://github.com/WordPress/wordpress-develop/pull/6836 + +* https://github.com/WordPress/gutenberg/pull/60100 diff --git a/docs/reference-guides/theme-json-reference/theme-json-living.md b/docs/reference-guides/theme-json-reference/theme-json-living.md index 59a820a16697c9..cf9ea9bc391136 100644 --- a/docs/reference-guides/theme-json-reference/theme-json-living.md +++ b/docs/reference-guides/theme-json-reference/theme-json-living.md @@ -206,6 +206,19 @@ Generate custom CSS custom properties of the form `--wp--custom--{key}--{nested- ## Styles +### background + +Background styles. + +| Property | Type | Props | +| --- | --- |--- | +| backgroundImage | string, object | | +| backgroundPosition | string, object | | +| backgroundRepeat | string, object | | +| backgroundSize | string, object | | + +--- + ### border Border styles. diff --git a/lib/block-supports/background.php b/lib/block-supports/background.php index 8e3c06159a1201..57b8d75f03d358 100644 --- a/lib/block-supports/background.php +++ b/lib/block-supports/background.php @@ -52,14 +52,14 @@ function gutenberg_render_background_support( $block_content, $block ) { return $block_content; } - $background_styles = array(); - $background_styles['backgroundImage'] = isset( $block_attributes['style']['background']['backgroundImage'] ) ? $block_attributes['style']['background']['backgroundImage'] : array(); + $background_styles = array(); + $background_styles['backgroundImage'] = $block_attributes['style']['background']['backgroundImage'] ?? null; + $background_styles['backgroundSize'] = $block_attributes['style']['background']['backgroundSize'] ?? null; + $background_styles['backgroundPosition'] = $block_attributes['style']['background']['backgroundPosition'] ?? null; + $background_styles['backgroundRepeat'] = $block_attributes['style']['background']['backgroundRepeat'] ?? null; if ( ! empty( $background_styles['backgroundImage'] ) ) { - $background_styles['backgroundSize'] = isset( $block_attributes['style']['background']['backgroundSize'] ) ? $block_attributes['style']['background']['backgroundSize'] : 'cover'; - $background_styles['backgroundPosition'] = isset( $block_attributes['style']['background']['backgroundPosition'] ) ? $block_attributes['style']['background']['backgroundPosition'] : null; - $background_styles['backgroundRepeat'] = isset( $block_attributes['style']['background']['backgroundRepeat'] ) ? $block_attributes['style']['background']['backgroundRepeat'] : null; - + $background_styles['backgroundSize'] = $background_styles['backgroundSize'] ?? 'cover'; // If the background size is set to `contain` and no position is set, set the position to `center`. if ( 'contain' === $background_styles['backgroundSize'] && ! $background_styles['backgroundPosition'] ) { $background_styles['backgroundPosition'] = 'center'; diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index e09dc899a24894..bd74852c52074a 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -503,10 +503,10 @@ class WP_Theme_JSON_Gutenberg { */ const VALID_STYLES = array( 'background' => array( - 'backgroundImage' => 'top', - 'backgroundPosition' => 'top', - 'backgroundRepeat' => 'top', - 'backgroundSize' => 'top', + 'backgroundImage' => null, + 'backgroundPosition' => null, + 'backgroundRepeat' => null, + 'backgroundSize' => null, ), 'border' => array( 'color' => null, diff --git a/lib/class-wp-theme-json-resolver-gutenberg.php b/lib/class-wp-theme-json-resolver-gutenberg.php index fba107b9814025..a2153e639db3ba 100644 --- a/lib/class-wp-theme-json-resolver-gutenberg.php +++ b/lib/class-wp-theme-json-resolver-gutenberg.php @@ -805,6 +805,7 @@ public static function get_style_variations( $scope = 'theme' ) { * as the value of `_link` object in REST API responses. * * @since 6.6.0 + * @since 6.7.0 Added support for resolving block styles. * * @param WP_Theme_JSON_Gutenberg $theme_json A theme json instance. * @return array An array of resolved paths. @@ -818,10 +819,11 @@ public static function get_resolved_theme_uris( $theme_json ) { $theme_json_data = $theme_json->get_raw_data(); - // Top level styles. - $background_image_url = $theme_json_data['styles']['background']['backgroundImage']['url'] ?? null; // Using the same file convention when registering web fonts. See: WP_Font_Face_Resolver:: to_theme_file_uri. $placeholder = 'file:./'; + + // Top level styles. + $background_image_url = $theme_json_data['styles']['background']['backgroundImage']['url'] ?? null; if ( isset( $background_image_url ) && is_string( $background_image_url ) && @@ -840,6 +842,33 @@ public static function get_resolved_theme_uris( $theme_json ) { $resolved_theme_uris[] = $resolved_theme_uri; } + // Block styles. + if ( ! empty( $theme_json_data['styles']['blocks'] ) ) { + foreach ( $theme_json_data['styles']['blocks'] as $block_name => $block_styles ) { + if ( ! isset( $block_styles['background']['backgroundImage']['url'] ) ) { + continue; + } + $background_image_url = $block_styles['background']['backgroundImage']['url'] ?? null; + if ( + isset( $background_image_url ) && + is_string( $background_image_url ) && + // Skip if the src doesn't start with the placeholder, as there's nothing to replace. + str_starts_with( $background_image_url, $placeholder ) ) { + $file_type = wp_check_filetype( $background_image_url ); + $src_url = str_replace( $placeholder, '', $background_image_url ); + $resolved_theme_uri = array( + 'name' => $background_image_url, + 'href' => sanitize_url( get_theme_file_uri( $src_url ) ), + 'target' => "styles.blocks.{$block_name}.background.backgroundImage.url", + ); + if ( isset( $file_type['type'] ) ) { + $resolved_theme_uri['type'] = $file_type['type']; + } + $resolved_theme_uris[] = $resolved_theme_uri; + } + } + } + return $resolved_theme_uris; } diff --git a/lib/global-styles-and-settings.php b/lib/global-styles-and-settings.php index 79c7028e32543e..e62d160d84aa78 100644 --- a/lib/global-styles-and-settings.php +++ b/lib/global-styles-and-settings.php @@ -258,6 +258,7 @@ function gutenberg_add_global_styles_block_custom_css() { function gutenberg_add_global_styles_for_blocks() { global $wp_styles; $tree = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data(); + $tree = WP_Theme_JSON_Resolver_Gutenberg::resolve_theme_file_uris( $tree ); $block_nodes = $tree->get_styles_block_nodes(); foreach ( $block_nodes as $metadata ) { $block_css = $tree->get_styles_for_block( $metadata ); 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 6baef04f39b6b3..92734b265341ac 100644 --- a/packages/block-editor/src/components/global-styles/background-panel.js +++ b/packages/block-editor/src/components/global-styles/background-panel.js @@ -269,6 +269,7 @@ function BackgroundImageControls( { inheritedValue, onRemoveImage = noop, displayInPanel, + themeFileURIs, } ) { const mediaUpload = useSelect( ( select ) => select( blockEditorStore ).getSettings().mediaUpload, @@ -392,7 +393,10 @@ function BackgroundImageControls( { name={ @@ -706,6 +710,7 @@ export default function BackgroundPanel( { onChange={ onChange } style={ value } inheritedValue={ inheritedValue } + themeFileURIs={ themeFileURIs } displayInPanel onRemoveImage={ () => { setIsDropDownOpen( false ); @@ -727,6 +732,7 @@ export default function BackgroundPanel( { onChange={ onChange } style={ value } inheritedValue={ inheritedValue } + themeFileURIs={ themeFileURIs } /> ) } diff --git a/packages/block-editor/src/components/global-styles/hooks.js b/packages/block-editor/src/components/global-styles/hooks.js index 50211db723e238..44a3d5e23e40cd 100644 --- a/packages/block-editor/src/components/global-styles/hooks.js +++ b/packages/block-editor/src/components/global-styles/hooks.js @@ -373,6 +373,15 @@ export function useSettingsForBlockElement( } } ); + [ 'backgroundImage', 'backgroundSize' ].forEach( ( key ) => { + if ( ! supportedStyles.includes( key ) ) { + updatedSettings.background = { + ...updatedSettings.background, + [ key ]: false, + }; + } + } ); + updatedSettings.shadow = supportedStyles.includes( 'shadow' ) ? updatedSettings.shadow : false; diff --git a/packages/block-editor/src/components/global-styles/use-global-styles-output.js b/packages/block-editor/src/components/global-styles/use-global-styles-output.js index c3eeb008964b36..d9fc247db17948 100644 --- a/packages/block-editor/src/components/global-styles/use-global-styles-output.js +++ b/packages/block-editor/src/components/global-styles/use-global-styles-output.js @@ -31,6 +31,7 @@ import { GlobalStylesContext } from './context'; import { useGlobalSetting } from './hooks'; import { getDuotoneFilter } from '../duotone/utils'; import { getGapCSSValue } from '../../hooks/gap'; +import { setBackgroundStyleDefaults } from '../../hooks/background'; import { store as blockEditorStore } from '../../store'; import { LAYOUT_DEFINITIONS } from '../../layouts/definitions'; import { getValueFromObjectPath, setImmutably } from '../../utils/object'; @@ -387,6 +388,20 @@ export function getStylesDeclarations( [] ); + /* + * Set background defaults. + * Applies to all background styles except the top-level site background. + */ + if ( ! isRoot && !! blockStyles.background ) { + blockStyles = { + ...blockStyles, + background: { + ...blockStyles.background, + ...setBackgroundStyleDefaults( blockStyles.background ), + }, + }; + } + // The goal is to move everything to server side generated engine styles // This is temporary as we absorb more and more styles into the engine. const extraRules = getCSSRules( blockStyles ); diff --git a/packages/block-editor/src/hooks/background.js b/packages/block-editor/src/hooks/background.js index c22e5cf20ff165..cd0b017831b795 100644 --- a/packages/block-editor/src/hooks/background.js +++ b/packages/block-editor/src/hooks/background.js @@ -16,6 +16,10 @@ import { useHasBackgroundPanel, hasBackgroundImageValue, } from '../components/global-styles/background-panel'; +import { + globalStylesDataKey, + globalStylesLinksDataKey, +} from '../store/private-keys'; export const BACKGROUND_SUPPORT_KEY = 'background'; @@ -134,10 +138,25 @@ export function BackgroundImagePanel( { setAttributes, settings, } ) { - const style = useSelect( - ( select ) => - select( blockEditorStore ).getBlockAttributes( clientId )?.style, - [ clientId ] + const { style, inheritedValue, _links } = useSelect( + ( select ) => { + const { getBlockAttributes, getSettings } = + select( blockEditorStore ); + const _settings = getSettings(); + return { + style: getBlockAttributes( clientId )?.style, + _links: _settings[ globalStylesLinksDataKey ], + /* + * @TODO 1. Pass inherited value down to all block style controls, + * See: packages/block-editor/src/hooks/style.js + * @TODO 2. Add support for block style variations, + * See implementation: packages/block-editor/src/hooks/block-style-variation.js + */ + inheritedValue: + _settings[ globalStylesDataKey ]?.blocks?.[ name ], + }; + }, + [ clientId, name ] ); if ( @@ -165,12 +184,14 @@ export function BackgroundImagePanel( { return ( ); } diff --git a/packages/block-editor/src/hooks/index.js b/packages/block-editor/src/hooks/index.js index bd1835571fdd4a..423e1f6f0bc322 100644 --- a/packages/block-editor/src/hooks/index.js +++ b/packages/block-editor/src/hooks/index.js @@ -87,5 +87,6 @@ export { getSpacingClassesAndStyles } from './use-spacing-props'; export { getTypographyClassesAndStyles } from './use-typography-props'; export { getGapCSSValue } from './gap'; export { useCachedTruthy } from './use-cached-truthy'; +export { setBackgroundStyleDefaults } from './background'; export { useZoomOut } from './use-zoom-out'; export { __unstableBlockStyleVariationOverridesWithConfig } from './block-style-variation'; diff --git a/packages/block-editor/src/private-apis.js b/packages/block-editor/src/private-apis.js index 44f68670556a30..c01d40d9d743b0 100644 --- a/packages/block-editor/src/private-apis.js +++ b/packages/block-editor/src/private-apis.js @@ -21,6 +21,7 @@ import BlockQuickNavigation from './components/block-quick-navigation'; import { LayoutStyle } from './components/block-list/layout'; import { BlockRemovalWarningModal } from './components/block-removal-warning-modal'; import { + setBackgroundStyleDefaults, useLayoutClasses, useLayoutStyles, __unstableBlockStyleVariationOverridesWithConfig, @@ -40,6 +41,7 @@ import { selectBlockPatternsKey, reusableBlocksSelectKey, globalStylesDataKey, + globalStylesLinksDataKey, } from './store/private-keys'; import { requiresWrapperOnCopy } from './components/writing-flow/utils'; import { PrivateRichText } from './components/rich-text/'; @@ -85,6 +87,7 @@ lock( privateApis, { usesContextKey, useFlashEditableBlocks, globalStylesDataKey, + globalStylesLinksDataKey, selectBlockPatternsKey, requiresWrapperOnCopy, PrivateRichText, @@ -95,4 +98,5 @@ lock( privateApis, { useSpacingSizes, useBlockDisplayTitle, __unstableBlockStyleVariationOverridesWithConfig, + setBackgroundStyleDefaults, } ); diff --git a/packages/block-editor/src/store/private-keys.js b/packages/block-editor/src/store/private-keys.js index 82264ebe191579..00fac5531b9c3f 100644 --- a/packages/block-editor/src/store/private-keys.js +++ b/packages/block-editor/src/store/private-keys.js @@ -1,3 +1,4 @@ export const globalStylesDataKey = Symbol( 'globalStylesDataKey' ); +export const globalStylesLinksDataKey = Symbol( 'globalStylesLinks' ); export const selectBlockPatternsKey = Symbol( 'selectBlockPatternsKey' ); export const reusableBlocksSelectKey = Symbol( 'reusableBlocksSelect' ); diff --git a/packages/edit-site/src/components/global-styles/screen-block.js b/packages/edit-site/src/components/global-styles/screen-block.js index 2368f7499acbf6..fd71cdfa630687 100644 --- a/packages/edit-site/src/components/global-styles/screen-block.js +++ b/packages/edit-site/src/components/global-styles/screen-block.js @@ -25,6 +25,11 @@ import { VariationsPanel, } from './variations/variations-panel'; +// Initial control values where no block style is set. +const BACKGROUND_BLOCK_DEFAULT_VALUES = { + backgroundSize: 'cover', +}; + function applyFallbackStyle( border ) { if ( ! border ) { return border; @@ -70,6 +75,8 @@ const { useHasFiltersPanel, useHasImageSettingsPanel, useGlobalStyle, + useHasBackgroundPanel, + BackgroundPanel: StylesBackgroundPanel, BorderPanel: StylesBorderPanel, ColorPanel: StylesColorPanel, TypographyPanel: StylesTypographyPanel, @@ -121,6 +128,7 @@ function ScreenBlock( { name, variation } ) { } const blockVariations = useBlockVariations( name ); + const hasBackgroundPanel = useHasBackgroundPanel( settings ); const hasTypographyPanel = useHasTypographyPanel( settings ); const hasColorPanel = useHasColorPanel( settings ); const hasBorderPanel = useHasBorderPanel( settings ); @@ -296,6 +304,20 @@ function ScreenBlock( { name, variation } ) { /> ) } + { hasBackgroundPanel && ( + + ) } + { canEditCSS && (

diff --git a/packages/edit-site/src/components/global-styles/screen-layout.js b/packages/edit-site/src/components/global-styles/screen-layout.js index 61c64b88f0e480..1e68309fe01866 100644 --- a/packages/edit-site/src/components/global-styles/screen-layout.js +++ b/packages/edit-site/src/components/global-styles/screen-layout.js @@ -23,7 +23,11 @@ function ScreenLayout() { const [ rawSettings ] = useGlobalSetting( '' ); const settings = useSettingsForBlockElement( rawSettings ); const hasDimensionsPanel = useHasDimensionsPanel( settings ); - const hasBackgroundPanel = useHasBackgroundPanel( settings ); + /* + * Use the raw settings to determine if the background panel should be displayed, + * as the background panel is not dependent on the block element settings. + */ + const hasBackgroundPanel = useHasBackgroundPanel( rawSettings ); return ( <> diff --git a/packages/editor/src/components/provider/use-block-editor-settings.js b/packages/editor/src/components/provider/use-block-editor-settings.js index 2917c6905e3f0d..823cf852d14814 100644 --- a/packages/editor/src/components/provider/use-block-editor-settings.js +++ b/packages/editor/src/components/provider/use-block-editor-settings.js @@ -27,7 +27,7 @@ import { lock, unlock } from '../../lock-unlock'; import { useGlobalStylesContext } from '../global-styles-provider'; const EMPTY_BLOCKS_LIST = []; -const DEFAULT_STYLES = {}; +const EMPTY_OBJECT = {}; function __experimentalReusableBlocksSelect( select ) { return ( @@ -88,8 +88,12 @@ const BLOCK_EDITOR_SETTINGS = [ '__experimentalArchiveTitleNameLabel', ]; -const { globalStylesDataKey, selectBlockPatternsKey, reusableBlocksSelectKey } = - unlock( privateApis ); +const { + globalStylesDataKey, + globalStylesLinksDataKey, + selectBlockPatternsKey, + reusableBlocksSelectKey, +} = unlock( privateApis ); /** * React hook used to compute the block editor settings to use for the post editor. @@ -179,7 +183,8 @@ function useBlockEditorSettings( settings, postType, postId, renderingMode ) { ); const { merged: mergedGlobalStyles } = useGlobalStylesContext(); - const globalStylesData = mergedGlobalStyles.styles ?? DEFAULT_STYLES; + const globalStylesData = mergedGlobalStyles.styles ?? EMPTY_OBJECT; + const globalStylesLinksData = mergedGlobalStyles._links ?? EMPTY_OBJECT; const settingsBlockPatterns = settings.__experimentalAdditionalBlockPatterns ?? // WP 6.0 @@ -268,6 +273,7 @@ function useBlockEditorSettings( settings, postType, postId, renderingMode ) { ) ), [ globalStylesDataKey ]: globalStylesData, + [ globalStylesLinksDataKey ]: globalStylesLinksData, allowedBlockTypes, allowRightClickOverrides, focusMode: focusMode && ! forceDisableFocusMode, diff --git a/phpunit/class-wp-theme-json-resolver-test.php b/phpunit/class-wp-theme-json-resolver-test.php index e7a1b44049393e..d2339f2496290c 100644 --- a/phpunit/class-wp-theme-json-resolver-test.php +++ b/phpunit/class-wp-theme-json-resolver-test.php @@ -1216,6 +1216,22 @@ public function test_resolve_theme_file_uris() { 'url' => 'file:./example/img/image.png', ), ), + 'blocks' => array( + 'core/quote' => array( + 'background' => array( + 'backgroundImage' => array( + 'url' => 'file:./example/img/quote.png', + ), + ), + ), + 'core/verse' => array( + 'background' => array( + 'backgroundImage' => array( + 'url' => 'file:./example/img/verse.png', + ), + ), + ), + ), ), ) ); @@ -1228,6 +1244,22 @@ public function test_resolve_theme_file_uris() { 'url' => 'https://example.org/wp-content/themes/example-theme/example/img/image.png', ), ), + 'blocks' => array( + 'core/quote' => array( + 'background' => array( + 'backgroundImage' => array( + 'url' => 'https://example.org/wp-content/themes/example-theme/example/img/quote.png', + ), + ), + ), + 'core/verse' => array( + 'background' => array( + 'backgroundImage' => array( + 'url' => 'https://example.org/wp-content/themes/example-theme/example/img/verse.png', + ), + ), + ), + ), ), ); @@ -1261,6 +1293,22 @@ public function test_get_resolved_theme_uris() { 'url' => 'file:./example/img/image.png', ), ), + 'blocks' => array( + 'core/quote' => array( + 'background' => array( + 'backgroundImage' => array( + 'url' => 'file:./example/img/quote.jpg', + ), + ), + ), + 'core/verse' => array( + 'background' => array( + 'backgroundImage' => array( + 'url' => 'file:./example/img/verse.gif', + ), + ), + ), + ), ), ) ); @@ -1272,6 +1320,18 @@ public function test_get_resolved_theme_uris() { 'target' => 'styles.background.backgroundImage.url', 'type' => 'image/png', ), + array( + 'name' => 'file:./example/img/quote.jpg', + 'href' => 'https://example.org/wp-content/themes/example-theme/example/img/quote.jpg', + 'target' => 'styles.blocks.core/quote.background.backgroundImage.url', + 'type' => 'image/jpeg', + ), + array( + 'name' => 'file:./example/img/verse.gif', + 'href' => 'https://example.org/wp-content/themes/example-theme/example/img/verse.gif', + 'target' => 'styles.blocks.core/verse.background.backgroundImage.url', + 'type' => 'image/gif', + ), ); /* diff --git a/phpunit/class-wp-theme-json-test.php b/phpunit/class-wp-theme-json-test.php index b188ce4fce6abc..2d6d4dbb668c94 100644 --- a/phpunit/class-wp-theme-json-test.php +++ b/phpunit/class-wp-theme-json-test.php @@ -4782,7 +4782,7 @@ public function test_get_top_level_background_image_styles() { ); $expected_styles = "html{min-height: calc(100% - var(--wp-admin--admin-bar--height, 0px));}:root :where(body){background-image: url('http://example.org/image.png');background-position: center center;background-repeat: no-repeat;background-size: contain;}"; - $this->assertSame( $expected_styles, $theme_json->get_styles_for_block( $body_node ), 'Styles returned from "::get_styles_for_block()" with top-level background styles type does not match expectations' ); + $this->assertSame( $expected_styles, $theme_json->get_styles_for_block( $body_node ), 'Styles returned from "::get_styles_for_block()" with top-level background styles do not match expectations' ); $theme_json = new WP_Theme_JSON_Gutenberg( array( @@ -4799,7 +4799,61 @@ public function test_get_top_level_background_image_styles() { ); $expected_styles = "html{min-height: calc(100% - var(--wp-admin--admin-bar--height, 0px));}:root :where(body){background-image: url('http://example.org/image.png');background-position: center center;background-repeat: no-repeat;background-size: contain;}"; - $this->assertSame( $expected_styles, $theme_json->get_styles_for_block( $body_node ), 'Styles returned from "::get_styles_for_block()" with top-level background image as string type does not match expectations' ); + $this->assertSame( $expected_styles, $theme_json->get_styles_for_block( $body_node ), 'Styles returned from "::get_styles_for_block()" with top-level background image as string type do not match expectations' ); + } + + public function test_get_block_background_image_styles() { + $theme_json = new WP_Theme_JSON_Gutenberg( + array( + 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, + 'styles' => array( + 'blocks' => array( + 'core/group' => array( + 'background' => array( + 'backgroundImage' => "url('http://example.org/group.png')", + 'backgroundSize' => 'cover', + 'backgroundRepeat' => 'no-repeat', + 'backgroundPosition' => 'center center', + ), + ), + 'core/quote' => array( + 'background' => array( + 'backgroundImage' => array( + 'url' => 'http://example.org/quote.png', + ), + 'backgroundSize' => 'cover', + 'backgroundRepeat' => 'no-repeat', + 'backgroundPosition' => 'center center', + ), + ), + ), + ), + ) + ); + + $quote_node = array( + 'name' => 'core/quote', + 'path' => array( 'styles', 'blocks', 'core/quote' ), + 'selector' => '.wp-block-quote', + 'selectors' => array( + 'root' => '.wp-block-quote', + ), + ); + + $quote_styles = ":root :where(.wp-block-quote){background-image: url('http://example.org/quote.png');background-position: center center;background-repeat: no-repeat;background-size: cover;}"; + $this->assertSame( $quote_styles, $theme_json->get_styles_for_block( $quote_node ), 'Styles returned from "::get_styles_for_block()" with block-level background styles do not match expectations' ); + + $group_node = array( + 'name' => 'core/group', + 'path' => array( 'styles', 'blocks', 'core/group' ), + 'selector' => '.wp-block-group', + 'selectors' => array( + 'root' => '.wp-block-group', + ), + ); + + $group_styles = ":root :where(.wp-block-group){background-image: url('http://example.org/group.png');background-position: center center;background-repeat: no-repeat;background-size: cover;}"; + $this->assertSame( $group_styles, $theme_json->get_styles_for_block( $group_node ), 'Styles returned from "::get_styles_for_block()" with block-level background styles as string type do not match expectations' ); } /** diff --git a/schemas/json/theme.json b/schemas/json/theme.json index ec42bd1d54f0b7..f1666253e3de3e 100644 --- a/schemas/json/theme.json +++ b/schemas/json/theme.json @@ -1162,6 +1162,68 @@ "stylesProperties": { "type": "object", "properties": { + "background": { + "description": "Background styles.", + "type": "object", + "properties": { + "backgroundImage": { + "description": "Sets the `background-image` CSS property.", + "oneOf": [ + { + "description": "A valid CSS value for the background-image CSS property.", + "type": "string" + }, + { + "type": "object", + "properties": { + "url": { + "description": "A URL to an image file, or a path to a file relative to the theme root directory, and prefixed with `file:`, e.g., 'file:./path/to/file.png'.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "$ref": "#/definitions/refComplete" + } + ] + }, + "backgroundPosition": { + "description": "Sets the `background-position` CSS property.", + "oneOf": [ + { + "type": "string" + }, + { + "$ref": "#/definitions/refComplete" + } + ] + }, + "backgroundRepeat": { + "description": "Sets the `background-repeat` CSS property.", + "oneOf": [ + { + "type": "string" + }, + { + "$ref": "#/definitions/refComplete" + } + ] + }, + "backgroundSize": { + "description": "Sets the `background-size` CSS property.", + "oneOf": [ + { + "type": "string" + }, + { + "$ref": "#/definitions/refComplete" + } + ] + } + }, + "additionalProperties": false + }, "border": { "description": "Border styles.", "type": "object", @@ -1814,6 +1876,7 @@ }, { "properties": { + "background": {}, "border": {}, "color": {}, "dimensions": {}, @@ -1840,6 +1903,7 @@ { "properties": { "border": {}, + "background": {}, "color": {}, "filter": {}, "shadow": {}, @@ -2248,6 +2312,7 @@ }, { "properties": { + "background": {}, "border": {}, "color": {}, "dimensions": {}, @@ -2743,6 +2808,7 @@ }, { "properties": { + "background": {}, "border": {}, "color": {}, "spacing": {}, @@ -2759,68 +2825,6 @@ "description": "Styles defined on a per-block basis using the block's selector.", "$ref": "#/definitions/stylesBlocksPropertiesComplete" }, - "background": { - "description": "Background styles.", - "type": "object", - "properties": { - "backgroundImage": { - "description": "Sets the `background-image` CSS property.", - "oneOf": [ - { - "description": "A valid CSS value for the background-image property, or a path to a file relative to the theme root directory, and prefixed with `file:`, e.g., 'file:./path/to/file.png'.", - "type": "string" - }, - { - "type": "object", - "properties": { - "url": { - "description": "A URL to an image file.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "$ref": "#/definitions/refComplete" - } - ] - }, - "backgroundPosition": { - "description": "Sets the `background-position` CSS property.", - "oneOf": [ - { - "type": "string" - }, - { - "$ref": "#/definitions/refComplete" - } - ] - }, - "backgroundRepeat": { - "description": "Sets the `background-repeat` CSS property.", - "oneOf": [ - { - "type": "string" - }, - { - "$ref": "#/definitions/refComplete" - } - ] - }, - "backgroundSize": { - "description": "Sets the `background-size` CSS property.", - "oneOf": [ - { - "type": "string" - }, - { - "$ref": "#/definitions/refComplete" - } - ] - } - }, - "additionalProperties": false - }, "variations": { "$ref": "#/definitions/stylesVariationsProperties" }