From 8d21b431994dc27e0bec0a4b74276b7e9af5d209 Mon Sep 17 00:00:00 2001 From: ramon Date: Fri, 14 Jun 2024 12:08:20 +1000 Subject: [PATCH] This commit checks for theme block styles in the block supports hook so a background block control can determine whether it has an inherited value. If there is an inherited value, the block control will provide an option to "remove" the theme style. This commit also sends resolved theme asset URI via the _links property so that these images can be used in block control previews. Added resolver tests for blocks Use `get_styles_for_block` in unit tests when testing block nodes Use `get_styles_for_block` in unit tests when testing block nodes Add since annotation backport changelog --- backport-changelog/6.7/6836.md | 3 + ...class-wp-theme-json-resolver-gutenberg.php | 1 + packages/block-editor/src/hooks/background.js | 29 ++++++- packages/block-editor/src/private-apis.js | 2 + .../block-editor/src/store/private-keys.js | 1 + .../provider/use-block-editor-settings.js | 14 +++- phpunit/class-wp-theme-json-resolver-test.php | 60 +++++++++++++ phpunit/class-wp-theme-json-test.php | 84 +++++++++++++------ 8 files changed, 159 insertions(+), 35 deletions(-) create mode 100644 backport-changelog/6.7/6836.md 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/lib/class-wp-theme-json-resolver-gutenberg.php b/lib/class-wp-theme-json-resolver-gutenberg.php index e1c64f2bb7914e..644fdb0562dd34 100644 --- a/lib/class-wp-theme-json-resolver-gutenberg.php +++ b/lib/class-wp-theme-json-resolver-gutenberg.php @@ -781,6 +781,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. diff --git a/packages/block-editor/src/hooks/background.js b/packages/block-editor/src/hooks/background.js index 3e23efcbf5fc96..dbdbe921bb10c0 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 ( @@ -170,6 +189,7 @@ export function BackgroundImagePanel( { return ( ); } diff --git a/packages/block-editor/src/private-apis.js b/packages/block-editor/src/private-apis.js index 32b75ef811eb1d..e121b032bf175f 100644 --- a/packages/block-editor/src/private-apis.js +++ b/packages/block-editor/src/private-apis.js @@ -40,6 +40,7 @@ import { selectBlockPatternsKey, reusableBlocksSelectKey, globalStylesDataKey, + globalStylesLinksDataKey, } from './store/private-keys'; import { requiresWrapperOnCopy } from './components/writing-flow/utils'; import { PrivateRichText } from './components/rich-text/'; @@ -83,6 +84,7 @@ lock( privateApis, { usesContextKey, useFlashEditableBlocks, globalStylesDataKey, + globalStylesLinksDataKey, selectBlockPatternsKey, requiresWrapperOnCopy, PrivateRichText, 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/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 50e1d9d846899e..aa225bb136295e 100644 --- a/phpunit/class-wp-theme-json-resolver-test.php +++ b/phpunit/class-wp-theme-json-resolver-test.php @@ -1204,6 +1204,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', + ), + ), + ), + ), ), ) ); @@ -1216,6 +1232,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', + ), + ), + ), + ), ), ); @@ -1249,6 +1281,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', + ), + ), + ), + ), ), ) ); @@ -1260,6 +1308,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 38b4467a4cf922..355c178f1fe5e8 100644 --- a/phpunit/class-wp-theme-json-test.php +++ b/phpunit/class-wp-theme-json-test.php @@ -4788,36 +4788,62 @@ public function test_get_shadow_styles_for_blocks() { $this->assertSame( $expected_styles, $theme_json->get_stylesheet() ); } - public function test_get_top_level_and_block_background_image_styles() { + public function test_get_top_level_background_image_styles() { $theme_json = new WP_Theme_JSON_Gutenberg( array( 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, 'styles' => array( 'background' => array( 'backgroundImage' => array( - 'url' => 'http://example.org/body.png', + 'url' => 'http://example.org/image.png', ), 'backgroundSize' => 'contain', 'backgroundRepeat' => 'no-repeat', 'backgroundPosition' => 'center center', ), - 'blocks' => array( - 'core/paragraph' => array( + ), + ) + ); + + $expected_styles = ":where(body) { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }:where(.is-layout-flex){gap: 0.5em;}:where(.is-layout-grid){gap: 0.5em;}.is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}.is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}.is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}.is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}.is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){margin-left: auto !important;margin-right: auto !important;}body .is-layout-flex{display: flex;}.is-layout-flex{flex-wrap: wrap;align-items: center;}.is-layout-flex > :is(*, div){margin: 0;}body .is-layout-grid{display: grid;}.is-layout-grid > :is(*, div){margin: 0;}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_stylesheet(), 'Styles returned from "::get_stylesheet()" with top-level background styles does not match expectations' ); + + $theme_json = new WP_Theme_JSON_Gutenberg( + array( + 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, + 'styles' => array( + 'background' => array( + 'backgroundImage' => "url('http://example.org/image.png')", + 'backgroundSize' => 'contain', + 'backgroundRepeat' => 'no-repeat', + 'backgroundPosition' => 'center center', + ), + ), + ) + ); + + $expected_styles = ":where(body) { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }:where(.is-layout-flex){gap: 0.5em;}:where(.is-layout-grid){gap: 0.5em;}.is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}.is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}.is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}.is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}.is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){margin-left: auto !important;margin-right: auto !important;}body .is-layout-flex{display: flex;}.is-layout-flex{flex-wrap: wrap;align-items: center;}.is-layout-flex > :is(*, div){margin: 0;}body .is-layout-grid{display: grid;}.is-layout-grid > :is(*, div){margin: 0;}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_stylesheet(), 'Styles returned from "::get_stylesheet()" with top-level background image as string type does 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' => array( - 'url' => 'http://example.org/paragraph.png', - ), + 'backgroundImage' => "url('http://example.org/group.png')", 'backgroundSize' => 'cover', 'backgroundRepeat' => 'no-repeat', 'backgroundPosition' => 'center center', ), ), - ), - 'elements' => array( - 'button' => array( + 'core/quote' => array( 'background' => array( 'backgroundImage' => array( - 'url' => 'http://example.org/button.png', + 'url' => 'http://example.org/quote.png', ), 'backgroundSize' => 'cover', 'backgroundRepeat' => 'no-repeat', @@ -4829,25 +4855,29 @@ public function test_get_top_level_and_block_background_image_styles() { ) ); - $expected_styles = ":where(body) { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }:where(.is-layout-flex){gap: 0.5em;}:where(.is-layout-grid){gap: 0.5em;}.is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}.is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}.is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}.is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}.is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){margin-left: auto !important;margin-right: auto !important;}body .is-layout-flex{display: flex;}.is-layout-flex{flex-wrap: wrap;align-items: center;}.is-layout-flex > :is(*, div){margin: 0;}body .is-layout-grid{display: grid;}.is-layout-grid > :is(*, div){margin: 0;}html{min-height: calc(100% - var(--wp-admin--admin-bar--height, 0px));}:root :where(body){background-image: url('http://example.org/body.png');background-position: center center;background-repeat: no-repeat;background-size: contain;}:root :where(.wp-element-button, .wp-block-button__link){background-image: url('http://example.org/button.png');background-position: center center;background-repeat: no-repeat;background-size: cover;}:root :where(p){background-image: url('http://example.org/paragraph.png');background-position: center center;background-repeat: no-repeat;background-size: cover;}"; - $this->assertSame( $expected_styles, $theme_json->get_stylesheet(), 'Styles returned from "::get_stylesheet()" with top-level background styles type does not match expectations' ); + $quote_node = array( + 'name' => 'core/quote', + 'path' => array( 'styles', 'blocks', 'core/quote' ), + 'selector' => '.wp-block-quote', + 'selectors' => array( + 'root' => '.wp-block-quote', + ), + ); - $theme_json = new WP_Theme_JSON_Gutenberg( - array( - 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, - 'styles' => array( - 'background' => array( - 'backgroundImage' => "url('http://example.org/image.png')", - 'backgroundSize' => 'contain', - 'backgroundRepeat' => 'no-repeat', - 'backgroundPosition' => 'center center', - ), - ), - ) + $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_stylesheet()" with block-level background styles does 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', + ), ); - $expected_styles = ":where(body) { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }:where(.is-layout-flex){gap: 0.5em;}:where(.is-layout-grid){gap: 0.5em;}.is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}.is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}.is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}.is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}.is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){margin-left: auto !important;margin-right: auto !important;}body .is-layout-flex{display: flex;}.is-layout-flex{flex-wrap: wrap;align-items: center;}.is-layout-flex > :is(*, div){margin: 0;}body .is-layout-grid{display: grid;}.is-layout-grid > :is(*, div){margin: 0;}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_stylesheet(), 'Styles returned from "::get_stylesheet()" with top-level background image as string type does not match expectations' ); + $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_stylesheet()" with block-level background styles as string type does not match expectations' ); } /**