From 6b105847bd7ee352a88d8a3af60d2bf747d4ccef Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Fri, 21 Jun 2024 11:12:19 +1000 Subject: [PATCH 01/35] Remove variation style registration from theme json and theme style variations --- lib/class-wp-rest-global-styles-controller-gutenberg.php | 7 +------ lib/class-wp-theme-json-resolver-gutenberg.php | 8 -------- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/lib/class-wp-rest-global-styles-controller-gutenberg.php b/lib/class-wp-rest-global-styles-controller-gutenberg.php index c933a18a4fa59a..19c348bacec30e 100644 --- a/lib/class-wp-rest-global-styles-controller-gutenberg.php +++ b/lib/class-wp-rest-global-styles-controller-gutenberg.php @@ -331,14 +331,9 @@ protected function prepare_item_for_database( $request ) { $config['styles'] = $existing_config['styles']; } - // Register theme-defined variations. + // Register theme-defined variations e.g. from block style variation partials under `/styles`. WP_Theme_JSON_Resolver_Gutenberg::get_theme_data(); - // Register user-defined variations. - if ( isset( $request['styles']['blocks']['variations'] ) && ! empty( $config['styles']['blocks']['variations'] ) ) { - gutenberg_register_block_style_variations_from_theme_json_data( $config['styles']['blocks']['variations'] ); - } - if ( isset( $request['settings'] ) ) { $config['settings'] = $request['settings']; } elseif ( isset( $existing_config['settings'] ) ) { diff --git a/lib/class-wp-theme-json-resolver-gutenberg.php b/lib/class-wp-theme-json-resolver-gutenberg.php index 6a8d2558d1c85a..8e296775534e8e 100644 --- a/lib/class-wp-theme-json-resolver-gutenberg.php +++ b/lib/class-wp-theme-json-resolver-gutenberg.php @@ -247,10 +247,6 @@ public static function get_theme_data( $deprecated = array(), $options = array() $theme_json_data = array( 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA ); } - // Register variations defined by the theme. - $variations = $theme_json_data['styles']['blocks']['variations'] ?? array(); - gutenberg_register_block_style_variations_from_theme_json_data( $variations ); - // Register variations defined by theme partials (theme.json files in the styles directory). $variations = static::get_style_variations( 'block' ); gutenberg_register_block_style_variations_from_theme_json_data( $variations ); @@ -547,10 +543,6 @@ public static function get_user_data() { } } - // Register variations defined by the user. - $variations = $config['styles']['blocks']['variations'] ?? array(); - gutenberg_register_block_style_variations_from_theme_json_data( $variations ); - /** This filter is documented in wp-includes/class-wp-theme-json-resolver.php */ $theme_json = apply_filters( 'wp_theme_json_data_user', new WP_Theme_JSON_Data_Gutenberg( $config, 'custom' ) ); static::$user = $theme_json->get_theme_json(); From fbb6775443e6320e4cffc45d81a2499c52df71ae Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Fri, 21 Jun 2024 11:30:51 +1000 Subject: [PATCH 02/35] Make variation style registration specific to partials --- lib/block-supports/block-style-variations.php | 30 +++++-------------- ...class-wp-theme-json-resolver-gutenberg.php | 2 +- 2 files changed, 8 insertions(+), 24 deletions(-) diff --git a/lib/block-supports/block-style-variations.php b/lib/block-supports/block-style-variations.php index 4adefa0f4e440e..b16e70b3e15d45 100644 --- a/lib/block-supports/block-style-variations.php +++ b/lib/block-supports/block-style-variations.php @@ -422,44 +422,28 @@ function gutenberg_enqueue_block_style_variation_styles() { /** - * Registers any block style variations contained within the provided - * theme.json data. + * Registers block style variations read in from theme.json partials. * * @access private * * @param array $variations Shared block style variations. */ -function gutenberg_register_block_style_variations_from_theme_json_data( $variations ) { +function gutenberg_register_block_style_variations_from_theme_json_partials( $variations ) { if ( empty( $variations ) ) { return; } - $registry = WP_Block_Styles_Registry::get_instance(); - $have_named_variations = ! wp_is_numeric_array( $variations ); + $registry = WP_Block_Styles_Registry::get_instance(); - foreach ( $variations as $key => $variation ) { - $supported_blocks = $variation['blockTypes'] ?? array(); - - /* - * Standalone theme.json partial files for block style variations - * will have their styles under a top-level property by the same name. - * Variations defined within an existing theme.json or theme style - * variation will themselves already be the required styles data. - */ - $variation_data = $variation['styles'] ?? $variation; - - if ( empty( $variation_data ) ) { + foreach ( $variations as $variation ) { + if ( empty( $variation['blockTypes'] ) || empty( $variation['styles'] ) ) { continue; } - /* - * Block style variations read in via standalone theme.json partials - * need to have their name set to the kebab case version of their title. - */ - $variation_name = $have_named_variations ? $key : ( $variation['slug'] ?? _wp_to_kebab_case( $variation['title'] ) ); + $variation_name = $variation['slug'] ?? _wp_to_kebab_case( $variation['title'] ); $variation_label = $variation['title'] ?? $variation_name; - foreach ( $supported_blocks as $block_type ) { + foreach ( $variation['blockTypes'] as $block_type ) { $registered_styles = $registry->get_registered_styles_for_block( $block_type ); // Register block style variation if it hasn't already been registered. diff --git a/lib/class-wp-theme-json-resolver-gutenberg.php b/lib/class-wp-theme-json-resolver-gutenberg.php index 8e296775534e8e..50af78eb106727 100644 --- a/lib/class-wp-theme-json-resolver-gutenberg.php +++ b/lib/class-wp-theme-json-resolver-gutenberg.php @@ -249,7 +249,7 @@ public static function get_theme_data( $deprecated = array(), $options = array() // Register variations defined by theme partials (theme.json files in the styles directory). $variations = static::get_style_variations( 'block' ); - gutenberg_register_block_style_variations_from_theme_json_data( $variations ); + gutenberg_register_block_style_variations_from_theme_json_partials( $variations ); /** * Filters the data provided by the theme for global styles and settings. From e00fbfd2343465901049f8dca559f2fe5b58e6fc Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Fri, 21 Jun 2024 12:16:55 +1000 Subject: [PATCH 03/35] Inject partial theme.json variations before filters --- ...class-wp-theme-json-resolver-gutenberg.php | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/lib/class-wp-theme-json-resolver-gutenberg.php b/lib/class-wp-theme-json-resolver-gutenberg.php index 50af78eb106727..6dfc9643b82297 100644 --- a/lib/class-wp-theme-json-resolver-gutenberg.php +++ b/lib/class-wp-theme-json-resolver-gutenberg.php @@ -251,6 +251,15 @@ public static function get_theme_data( $deprecated = array(), $options = array() $variations = static::get_style_variations( 'block' ); gutenberg_register_block_style_variations_from_theme_json_partials( $variations ); + /* + * Merge theme.json defined variations with partial definitions. + * + * When the resulting data is passed to `WP_Theme_JSON_Data_Gutenberg` + * it will create a `WP_Theme_JSON_Gutenberg` instance which in turn + * unwraps shared variations into their respective block types. + */ + $theme_json_data = static::inject_shared_variations_from_theme_json_partials( $theme_json_data, $variations ); + /** * Filters the data provided by the theme for global styles and settings. * @@ -851,4 +860,44 @@ public static function resolve_theme_file_uris( $theme_json ) { return $theme_json; } + + /** + * Adds shared block style variation definitions sourced from theme.json partials + * to the supplied theme.json data. + * + * @param array $data Array following the theme.json specification. + * @param array $variations Shared block style variations. + * @return array Theme json data including shared block style variation definitions. + */ + private static function inject_shared_variations_from_theme_json_partials( $data, $variations ) { + $new_variations = array(); + + if ( empty( $variations ) ) { + return $data; + } + + foreach ( $variations as $variation ) { + if ( empty( $variation['styles'] ) || empty( $variation['blockTypes'] ) ) { + continue; + } + + $variation_name = $variation['slug'] ?? _wp_to_kebab_case( $variation['title'] ); + unset( $variation['slug'] ); + unset( $variation['title'] ); + + $new_variations[ $variation_name ] = $variation; + } + + if ( empty( $new_variations ) ) { + return $data; + } + + // Merge shared variation definitions with theme.json file taking precedence + // over those sourced from partial theme.json files. + $current_variations = $theme_json_data['styles']['variations'] ?? array(); + $merged_variations = array_replace_recursive( $new_variations, $current_variations ); + _wp_array_set( $data, array( 'styles', 'variations' ), $merged_variations ); + + return $data; + } } From 87b5be297d8b7e5f3018e426a72592bcf1d35e07 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Fri, 21 Jun 2024 12:17:22 +1000 Subject: [PATCH 04/35] Remove existing filters that resolve shared definitions --- lib/block-supports/block-style-variations.php | 185 ------------------ 1 file changed, 185 deletions(-) diff --git a/lib/block-supports/block-style-variations.php b/lib/block-supports/block-style-variations.php index b16e70b3e15d45..1c049f4a0fee58 100644 --- a/lib/block-supports/block-style-variations.php +++ b/lib/block-supports/block-style-variations.php @@ -208,170 +208,6 @@ function gutenberg_render_block_style_variation_class_name( $block_content, $blo return $tags->get_updated_html(); } -/** - * Collects block style variation data for merging with theme.json data. - * - * @since 6.6.0 - * - * @param array $variations Shared block style variations. - * - * @return array Block variations data to be merged under `styles.blocks`. - */ -function gutenberg_resolve_block_style_variations( $variations ) { - $variations_data = array(); - - if ( empty( $variations ) ) { - return $variations_data; - } - - $have_named_variations = ! wp_is_numeric_array( $variations ); - - foreach ( $variations as $key => $variation ) { - $supported_blocks = $variation['blockTypes'] ?? array(); - - /* - * Standalone theme.json partial files for block style variations - * will have their styles under a top-level property by the same name. - * Variations defined within an existing theme.json or theme style - * variation will themselves already be the required styles data. - */ - $variation_data = $variation['styles'] ?? $variation; - - if ( empty( $variation_data ) ) { - continue; - } - - /* - * Block style variations read in via standalone theme.json partials - * need to have their name set to the kebab case version of their title. - */ - $variation_name = $have_named_variations ? $key : ( $variation['slug'] ?? _wp_to_kebab_case( $variation['title'] ) ); - - foreach ( $supported_blocks as $block_type ) { - // Add block style variation data under current block type. - $path = array( $block_type, 'variations', $variation_name ); - _wp_array_set( $variations_data, $path, $variation_data ); - } - } - - return $variations_data; -} - -/** - * Merges variations data with existing theme.json data ensuring that the - * current theme.json data values take precedence. - * - * @since 6.6.0 - * - * @param array $variations_data Block style variations data keyed by block type. - * @param WP_Theme_JSON_Data_Gutenberg $theme_json Current theme.json data. - * @param string $origin Origin for the theme.json data. - * - * @return WP_Theme_JSON_Gutenberg The merged theme.json data. - */ -function gutenberg_merge_block_style_variations_data( $variations_data, $theme_json, $origin = 'theme' ) { - if ( empty( $variations_data ) ) { - return $theme_json; - } - - $variations_theme_json_data = array( - 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, - 'styles' => array( 'blocks' => $variations_data ), - ); - - $variations_theme_json = new WP_Theme_JSON_Data_Gutenberg( $variations_theme_json_data, $origin ); - - /* - * Merge the current theme.json data over shared variation data so that - * any explicit per block variation values take precedence. - */ - return $variations_theme_json->update_with( $theme_json->get_data() ); -} - -/** - * Merges any shared block style variation definitions from a theme style - * variation into their appropriate block type within theme json styles. Any - * custom user selections already made will take precedence over the shared - * style variation value. - * - * @since 6.6.0 - * - * @param WP_Theme_JSON_Data_Gutenberg $theme_json Current theme.json data. - * - * @return WP_Theme_JSON_Data_Gutenberg - */ -function gutenberg_resolve_block_style_variations_from_theme_style_variation( $theme_json ) { - $theme_json_data = $theme_json->get_data(); - $shared_variations = $theme_json_data['styles']['blocks']['variations'] ?? array(); - $variations_data = gutenberg_resolve_block_style_variations( $shared_variations ); - - return gutenberg_merge_block_style_variations_data( $variations_data, $theme_json, 'user' ); -} - -/** - * Merges block style variation data sourced from standalone partial - * theme.json files. - * - * @since 6.6.0 - * - * @param WP_Theme_JSON_Data_Gutenberg $theme_json Current theme.json data. - * - * @return WP_Theme_JSON_Data_Gutenberg - */ -function gutenberg_resolve_block_style_variations_from_theme_json_partials( $theme_json ) { - $block_style_variations = WP_Theme_JSON_Resolver_Gutenberg::get_style_variations( 'block' ); - $variations_data = gutenberg_resolve_block_style_variations( $block_style_variations ); - - return gutenberg_merge_block_style_variations_data( $variations_data, $theme_json ); -} - -/** - * Merges shared block style variations registered within the - * `styles.blocks.variations` property of the primary theme.json file. - * - * @since 6.6.0 - * - * @param WP_Theme_JSON_Data_Gutenberg $theme_json Current theme.json data. - * - * @return WP_Theme_JSON_Data_Gutenberg - */ -function gutenberg_resolve_block_style_variations_from_primary_theme_json( $theme_json ) { - $theme_json_data = $theme_json->get_data(); - $block_style_variations = $theme_json_data['styles']['blocks']['variations'] ?? array(); - $variations_data = gutenberg_resolve_block_style_variations( $block_style_variations ); - - return gutenberg_merge_block_style_variations_data( $variations_data, $theme_json ); -} - -/** - * Merges block style variations registered via the block styles registry with a - * style object, under their appropriate block types within theme.json styles. - * Any variation values defined within the theme.json specific to a block type - * will take precedence over these shared definitions. - * - * @since 6.6.0 - * - * @param WP_Theme_JSON_Data_Gutenberg $theme_json Current theme.json data. - * - * @return WP_Theme_JSON_Data_Gutenberg - */ -function gutenberg_resolve_block_style_variations_from_styles_registry( $theme_json ) { - $registry = WP_Block_Styles_Registry::get_instance(); - $styles = $registry->get_all_registered(); - $variations_data = array(); - - foreach ( $styles as $block_type => $variations ) { - foreach ( $variations as $variation_name => $variation ) { - if ( ! empty( $variation['style_data'] ) ) { - $path = array( $block_type, 'variations', $variation_name ); - _wp_array_set( $variations_data, $path, $variation['style_data'] ); - } - } - } - - return gutenberg_merge_block_style_variations_data( $variations_data, $theme_json ); -} - /** * Enqueues styles for block style variations. * @@ -395,32 +231,11 @@ function gutenberg_enqueue_block_style_variation_styles() { remove_action( 'wp_enqueue_scripts', 'wp_enqueue_block_style_variation_styles' ); } -if ( function_exists( 'wp_resolve_block_style_variations_from_primary_theme_json' ) ) { - remove_filter( 'wp_theme_json_data_theme', 'wp_resolve_block_style_variations_from_primary_theme_json' ); -} -if ( function_exists( 'wp_resolve_block_style_variations_from_theme_json_partials' ) ) { - remove_filter( 'wp_theme_json_data_theme', 'wp_resolve_block_style_variations_from_theme_json_partials' ); -} -if ( function_exists( 'wp_resolve_block_style_variations_from_styles_registry' ) ) { - remove_filter( 'wp_theme_json_data_theme', 'wp_resolve_block_style_variations_from_styles_registry' ); -} -if ( function_exists( 'wp_resolve_block_style_variations_from_theme_style_variation' ) ) { - remove_filter( 'wp_theme_json_data_user', 'wp_resolve_block_style_variations_from_theme_style_variation' ); -} - // Add Gutenberg filters and action. add_filter( 'render_block_data', 'gutenberg_render_block_style_variation_support_styles', 10, 2 ); add_filter( 'render_block', 'gutenberg_render_block_style_variation_class_name', 10, 2 ); add_action( 'wp_enqueue_scripts', 'gutenberg_enqueue_block_style_variation_styles', 1 ); -// Resolve block style variations from all their potential sources. The order here is deliberate. -add_filter( 'wp_theme_json_data_theme', 'gutenberg_resolve_block_style_variations_from_primary_theme_json', 10, 1 ); -add_filter( 'wp_theme_json_data_theme', 'gutenberg_resolve_block_style_variations_from_theme_json_partials', 10, 1 ); -add_filter( 'wp_theme_json_data_theme', 'gutenberg_resolve_block_style_variations_from_styles_registry', 10, 1 ); - -add_filter( 'wp_theme_json_data_user', 'gutenberg_resolve_block_style_variations_from_theme_style_variation', 10, 1 ); - - /** * Registers block style variations read in from theme.json partials. * From 6001757d222b120fe580af7ab40394b3840e1b5b Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Fri, 21 Jun 2024 12:32:02 +1000 Subject: [PATCH 05/35] Unwrap shared variation definitions within theme json constructor --- lib/class-wp-theme-json-gutenberg.php | 64 +++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index 6bcc1728e7f9e4..86b72defafcbf9 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -743,6 +743,7 @@ public function __construct( $theme_json = array( 'version' => WP_Theme_JSON_Gut } $this->theme_json = WP_Theme_JSON_Schema_Gutenberg::migrate( $theme_json, $origin ); + $this->theme_json = static::unwrap_shared_block_style_variations( $this->theme_json ); $registry = WP_Block_Type_Registry::get_instance(); $valid_block_names = array_keys( $registry->get_all_registered() ); $valid_element_names = array_keys( static::ELEMENTS ); @@ -790,6 +791,69 @@ public function __construct( $theme_json = array( 'version' => WP_Theme_JSON_Gut } } + /** + * Unwraps shared block style variations. + * + * @since 6.6.0 + * + * { + * "styles": { + * "blocks": { + * "variations": { + * "blockTypes": [ "core/paragraph", "core/group" ], + * "color": { "background": "backgroundColor" } + * } + * } + * } + * } + * + * It returns the following output: + * + * { + * "styles": { + * "blocks": { + * "variations": { + * "core/paragraph": { "color": { "background": "backgroundColor" } }, + * "core/group": { "color": { "background": "backgroundColor" } } + * } + * } + * } + * } + * + * @param array $theme_json A structure that follows the theme.json schema. + * @return array Theme json data with shared variation definitions unwrapped under appropriate block types. + */ + private static function unwrap_shared_block_style_variations( $theme_json ) { + if ( ! isset( $theme_json['styles']['variations'] ) ) { + return $theme_json; + } + + $new_theme_json = $theme_json; + $variations = $new_theme_json['styles']['variations']; + + foreach ( $variations as $variation_name => $data ) { + if ( ! isset( $data['blockTypes'] ) ) { + // Skip shared variations that do not declare blockTypes. + continue; + } + + $block_names = $data['blockTypes']; + unset( $data['blockTypes'] ); + unset( $data['title'] ); + + foreach ( $block_names as $block_name ) { + // Existing per-block-type variation styles should take precedence over shared definition values. + $block_type_variation_data = $new_theme_json['styles']['blocks'][ $block_name ]['variations'][ $variation_name ] ?? array(); + $merged_variation_data = array_replace_recursive( $data, $block_type_variation_data ); + _wp_array_set( $new_theme_json, array( 'styles', 'blocks', $block_name, 'variations', $variation_name ), $merged_variation_data ); + } + } + + unset( $new_theme_json['styles']['variations'] ); + + return $new_theme_json; + } + /** * Enables some opt-in settings if theme declared support. * From 82efc1753754ec0e6e8a7b44872dafb6f9e8bb62 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Fri, 21 Jun 2024 13:20:05 +1000 Subject: [PATCH 06/35] Remove retrieval of user origin data in block style variations test --- phpunit/block-supports/block-style-variations-test.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/phpunit/block-supports/block-style-variations-test.php b/phpunit/block-supports/block-style-variations-test.php index ca84d3a93ec4e0..aa77efaf2498d0 100644 --- a/phpunit/block-supports/block-style-variations-test.php +++ b/phpunit/block-supports/block-style-variations-test.php @@ -101,9 +101,6 @@ public function test_add_registered_block_styles_to_theme_data() { // Register theme-defined variations. WP_Theme_JSON_Resolver_Gutenberg::get_theme_data(); - // Register user-defined variations. - WP_Theme_JSON_Resolver_Gutenberg::get_user_data(); - $variation_styles_data = array( 'color' => array( 'background' => 'darkslateblue', From 18c0636416aa5f09d1bc5c92db70aa36466c13b6 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Fri, 21 Jun 2024 14:10:49 +1000 Subject: [PATCH 07/35] Remove styles.blocks.variations from internal schema --- lib/class-wp-theme-json-gutenberg.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index 86b72defafcbf9..28edaadfdd404b 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -1016,12 +1016,6 @@ protected static function sanitize( $input, $valid_block_names, $valid_element_n $schema['settings']['blocks'] = $schema_settings_blocks; $schema['settings']['typography']['fontFamilies'] = static::schema_in_root_and_per_origin( static::FONT_FAMILY_SCHEMA ); - /* - * Shared block style variations can be registered from the theme.json data so we can't - * validate them against pre-registered block style variations. - */ - $schema['styles']['blocks']['variations'] = null; - // Remove anything that's not present in the schema. foreach ( array( 'styles', 'settings' ) as $subtree ) { if ( ! isset( $input[ $subtree ] ) ) { From b73836041fe468974d53e409f0b9851aa8432313 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Fri, 21 Jun 2024 14:13:36 +1000 Subject: [PATCH 08/35] Add unit test for unwrapping shared definitions --- phpunit/class-wp-theme-json-test.php | 47 ++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/phpunit/class-wp-theme-json-test.php b/phpunit/class-wp-theme-json-test.php index 30653c8f1c6278..31194f8912ca46 100644 --- a/phpunit/class-wp-theme-json-test.php +++ b/phpunit/class-wp-theme-json-test.php @@ -3739,6 +3739,53 @@ public function test_sanitize_for_unregistered_style_variations() { $this->assertSameSetsWithIndex( $expected, $sanitized_theme_json, 'Sanitized theme.json styles does not match' ); } + public function test_unwraps_block_style_variations() { + gutenberg_register_block_style( + array( 'core/paragraph', 'core/group' ), + array( + 'name' => 'myVariation', + 'label' => 'My variation', + ) + ); + + $input = new WP_Theme_JSON_Gutenberg( + array( + 'version' => 3, + 'styles' => array( + 'variations' => array( + 'myVariation' => array( + 'title' => 'My variation', + 'blockTypes' => array( 'core/paragraph', 'core/group' ), + 'color' => array( 'background' => 'backgroundColor' ), + ), + ), + ), + ) + ); + $expected = array( + 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, + 'styles' => array( + 'blocks' => array( + 'core/paragraph' => array( + 'variations' => array( + 'myVariation' => array( + 'color' => array( 'background' => 'backgroundColor' ), + ), + ), + ), + 'core/group' => array( + 'variations' => array( + 'myVariation' => array( + 'color' => array( 'background' => 'backgroundColor' ), + ), + ), + ), + ), + ), + ); + $this->assertSameSetsWithIndex( $expected, $input->get_raw_data(), 'Unwrapped block style variations do not match' ); + } + /** * @dataProvider data_sanitize_for_block_with_style_variations * From 28ce75e41b4412555a2245b6046f425499d1a854 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Fri, 21 Jun 2024 16:37:18 +1000 Subject: [PATCH 09/35] Fix injection of variation partials data --- lib/class-wp-theme-json-resolver-gutenberg.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/class-wp-theme-json-resolver-gutenberg.php b/lib/class-wp-theme-json-resolver-gutenberg.php index 6dfc9643b82297..b4033c6998071e 100644 --- a/lib/class-wp-theme-json-resolver-gutenberg.php +++ b/lib/class-wp-theme-json-resolver-gutenberg.php @@ -881,11 +881,10 @@ private static function inject_shared_variations_from_theme_json_partials( $data continue; } - $variation_name = $variation['slug'] ?? _wp_to_kebab_case( $variation['title'] ); - unset( $variation['slug'] ); - unset( $variation['title'] ); - - $new_variations[ $variation_name ] = $variation; + $variation_name = $variation['slug'] ?? _wp_to_kebab_case( $variation['title'] ); + $variation_data = $variation['styles']; + $variation_data['blockTypes'] = $variation['blockTypes']; + $new_variations[ $variation_name ] = $variation_data; } if ( empty( $new_variations ) ) { From 9b41371ca670cfcd8b814e36ef5f8872050da3e6 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Fri, 21 Jun 2024 16:48:34 +1000 Subject: [PATCH 10/35] Fix block style registry variations --- ...class-wp-theme-json-resolver-gutenberg.php | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/lib/class-wp-theme-json-resolver-gutenberg.php b/lib/class-wp-theme-json-resolver-gutenberg.php index b4033c6998071e..daa05a43d52e0f 100644 --- a/lib/class-wp-theme-json-resolver-gutenberg.php +++ b/lib/class-wp-theme-json-resolver-gutenberg.php @@ -260,6 +260,12 @@ public static function get_theme_data( $deprecated = array(), $options = array() */ $theme_json_data = static::inject_shared_variations_from_theme_json_partials( $theme_json_data, $variations ); + /* + * Merge any block style variations registered through the WP_Block_Styles_Registry + * with a style object. + */ + $theme_json_data = static::inject_shared_variations_from_style_registry( $theme_json_data ); + /** * Filters the data provided by the theme for global styles and settings. * @@ -865,6 +871,8 @@ public static function resolve_theme_file_uris( $theme_json ) { * Adds shared block style variation definitions sourced from theme.json partials * to the supplied theme.json data. * + * @since 6.6.0 + * * @param array $data Array following the theme.json specification. * @param array $variations Shared block style variations. * @return array Theme json data including shared block style variation definitions. @@ -899,4 +907,35 @@ private static function inject_shared_variations_from_theme_json_partials( $data return $data; } + + /** + * Adds shared block style variation definitions sourced from the WP_Block_Styles_Registry. + * + * @since 6.6.0 + * + * @param array $data Array following the theme.json specification. + * @return array Theme json data including shared block style variation definitions. + */ + private static function inject_shared_variations_from_style_registry( $data ) { + $registry = WP_Block_Styles_Registry::get_instance(); + $styles = $registry->get_all_registered(); + $variations_data = array(); + + /* + * As the block style registry stores the styles per block type we don't have + * a shared variation to inject. It will go directly into the block type's variations. + */ + foreach ( $styles as $block_type => $variations ) { + foreach ( $variations as $variation_name => $variation ) { + if ( ! empty( $variation['style_data'] ) ) { + $current_variation = $theme_json_data['styles']['blocks'][ $block_type ]['variations'][ $variation_name ] ?? array(); + $merged_variation = array_replace_recursive( $variation['style_data'], $current_variation ); + $path = array( 'styles', 'blocks', $block_type, 'variations', $variation_name ); + _wp_array_set( $data, $path, $merged_variation ); + } + } + } + + return $data; + } } From ffe3db82bedfd18bd5e41a6369022f49eebf2926 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Fri, 21 Jun 2024 17:39:54 +1000 Subject: [PATCH 11/35] Make registry variations be overridden by partials again --- lib/block-supports/block-style-variations.php | 66 +++++++++++++++++++ ...class-wp-theme-json-resolver-gutenberg.php | 40 +---------- .../block-style-variations-test.php | 5 +- 3 files changed, 70 insertions(+), 41 deletions(-) diff --git a/lib/block-supports/block-style-variations.php b/lib/block-supports/block-style-variations.php index 1c049f4a0fee58..4ca0fe5cec9c57 100644 --- a/lib/block-supports/block-style-variations.php +++ b/lib/block-supports/block-style-variations.php @@ -274,3 +274,69 @@ function gutenberg_register_block_style_variations_from_theme_json_partials( $va } } } + +/** + * Merges variations data with existing theme.json data ensuring that the + * current theme.json data values take precedence. + * + * @since 6.6.0 + * + * @param array $variations_data Block style variations data keyed by block type. + * @param WP_Theme_JSON_Data_Gutenberg $theme_json Current theme.json data. + * @param string $origin Origin for the theme.json data. + * + * @return WP_Theme_JSON_Gutenberg The merged theme.json data. + */ +function gutenberg_merge_block_style_variations_data( $variations_data, $theme_json, $origin = 'theme' ) { + if ( empty( $variations_data ) ) { + return $theme_json; + } + + $variations_theme_json_data = array( + 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, + 'styles' => array( 'blocks' => $variations_data ), + ); + + $variations_theme_json = new WP_Theme_JSON_Data_Gutenberg( $variations_theme_json_data, $origin ); + + /* + * Merge the current theme.json data over shared variation data so that + * any explicit per block variation values take precedence. + */ + return $variations_theme_json->update_with( $theme_json->get_data() ); +} + +/** + * Merges block style variations registered via the block styles registry with a + * style object, under their appropriate block types within theme.json styles. + * Any variation values defined within the theme.json specific to a block type + * will take precedence over these shared definitions. + * + * @since 6.6.0 + * + * @param WP_Theme_JSON_Data_Gutenberg $theme_json Current theme.json data. + * + * @return WP_Theme_JSON_Data_Gutenberg + */ +function gutenberg_resolve_block_style_variations_from_styles_registry( $theme_json ) { + $registry = WP_Block_Styles_Registry::get_instance(); + $styles = $registry->get_all_registered(); + $variations_data = array(); + + foreach ( $styles as $block_type => $variations ) { + foreach ( $variations as $variation_name => $variation ) { + if ( ! empty( $variation['style_data'] ) ) { + $path = array( $block_type, 'variations', $variation_name ); + _wp_array_set( $variations_data, $path, $variation['style_data'] ); + } + } + } + + return gutenberg_merge_block_style_variations_data( $variations_data, $theme_json ); +} + +if ( function_exists( 'wp_resolve_block_style_variations_from_styles_registry' ) ) { + remove_filter( 'wp_theme_json_data_theme', 'wp_resolve_block_style_variations_from_styles_registry' ); +} + +add_filter( 'wp_theme_json_data_theme', 'gutenberg_resolve_block_style_variations_from_styles_registry', 10, 1 ); diff --git a/lib/class-wp-theme-json-resolver-gutenberg.php b/lib/class-wp-theme-json-resolver-gutenberg.php index daa05a43d52e0f..fa1045130836de 100644 --- a/lib/class-wp-theme-json-resolver-gutenberg.php +++ b/lib/class-wp-theme-json-resolver-gutenberg.php @@ -257,15 +257,12 @@ public static function get_theme_data( $deprecated = array(), $options = array() * When the resulting data is passed to `WP_Theme_JSON_Data_Gutenberg` * it will create a `WP_Theme_JSON_Gutenberg` instance which in turn * unwraps shared variations into their respective block types. + * + * Note: WP_Block_Styles_Registry defined are still merged via + * wp_theme_json_data_theme filter so partials etc can take precedence. */ $theme_json_data = static::inject_shared_variations_from_theme_json_partials( $theme_json_data, $variations ); - /* - * Merge any block style variations registered through the WP_Block_Styles_Registry - * with a style object. - */ - $theme_json_data = static::inject_shared_variations_from_style_registry( $theme_json_data ); - /** * Filters the data provided by the theme for global styles and settings. * @@ -907,35 +904,4 @@ private static function inject_shared_variations_from_theme_json_partials( $data return $data; } - - /** - * Adds shared block style variation definitions sourced from the WP_Block_Styles_Registry. - * - * @since 6.6.0 - * - * @param array $data Array following the theme.json specification. - * @return array Theme json data including shared block style variation definitions. - */ - private static function inject_shared_variations_from_style_registry( $data ) { - $registry = WP_Block_Styles_Registry::get_instance(); - $styles = $registry->get_all_registered(); - $variations_data = array(); - - /* - * As the block style registry stores the styles per block type we don't have - * a shared variation to inject. It will go directly into the block type's variations. - */ - foreach ( $styles as $block_type => $variations ) { - foreach ( $variations as $variation_name => $variation ) { - if ( ! empty( $variation['style_data'] ) ) { - $current_variation = $theme_json_data['styles']['blocks'][ $block_type ]['variations'][ $variation_name ] ?? array(); - $merged_variation = array_replace_recursive( $variation['style_data'], $current_variation ); - $path = array( 'styles', 'blocks', $block_type, 'variations', $variation_name ); - _wp_array_set( $data, $path, $merged_variation ); - } - } - } - - return $data; - } } diff --git a/phpunit/block-supports/block-style-variations-test.php b/phpunit/block-supports/block-style-variations-test.php index aa77efaf2498d0..ac81be0f6a826d 100644 --- a/phpunit/block-supports/block-style-variations-test.php +++ b/phpunit/block-supports/block-style-variations-test.php @@ -98,9 +98,6 @@ public function filter_set_theme_root() { public function test_add_registered_block_styles_to_theme_data() { switch_theme( 'block-theme' ); - // Register theme-defined variations. - WP_Theme_JSON_Resolver_Gutenberg::get_theme_data(); - $variation_styles_data = array( 'color' => array( 'background' => 'darkslateblue', @@ -186,6 +183,6 @@ public function test_add_registered_block_styles_to_theme_data() { unregister_block_style( 'core/group', 'my-variation' ); unregister_block_style( 'core/group', 'WithSlug' ); - $this->assertSameSetsWithIndex( $group_styles, $expected ); + $this->assertSameSetsWithIndex( $expected, $group_styles, 'Variation data does not match' ); } } From 024288327d88b22f988e42688b98272b738a3aaa Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Fri, 21 Jun 2024 17:59:11 +1000 Subject: [PATCH 12/35] Fix global styles API test --- ...lobal-styles-controller-gutenberg-test.php | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/phpunit/class-wp-rest-global-styles-controller-gutenberg-test.php b/phpunit/class-wp-rest-global-styles-controller-gutenberg-test.php index 66a236c1c40a9e..3fd0bb21ce562b 100644 --- a/phpunit/class-wp-rest-global-styles-controller-gutenberg-test.php +++ b/phpunit/class-wp-rest-global-styles-controller-gutenberg-test.php @@ -528,6 +528,18 @@ public function test_update_item_with_custom_block_style_variations() { grant_super_admin( self::$admin_id ); } + /* + * For variations to be resolved they have to have been registered + * via either a theme.json partial or through the WP_Block_Styles_Registry. + */ + register_block_style( + 'core/group', + array( + 'name' => 'fromThemeStyleVariation', + 'label' => 'From Theme Style Variation', + ) + ); + $group_variations = array( 'fromThemeStyleVariation' => array( 'color' => array( @@ -541,16 +553,16 @@ public function test_update_item_with_custom_block_style_variations() { $request->set_body_params( array( 'styles' => array( - 'blocks' => array( - 'variations' => array( - 'fromThemeStyleVariation' => array( - 'blockTypes' => array( 'core/group', 'core/columns' ), - 'color' => array( - 'background' => '#000000', - 'text' => '#ffffff', - ), + 'variations' => array( + 'fromThemeStyleVariation' => array( + 'blockTypes' => array( 'core/group', 'core/columns' ), + 'color' => array( + 'background' => '#000000', + 'text' => '#ffffff', ), ), + ), + 'blocks' => array( 'core/group' => array( 'variations' => $group_variations, ), From e98075283d6c422c6f0a5450fe79ef9591c0d4d5 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Fri, 21 Jun 2024 18:08:56 +1000 Subject: [PATCH 13/35] Fix copy paste docblock --- lib/class-wp-theme-json-gutenberg.php | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index 28edaadfdd404b..671a09d907a5d9 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -798,8 +798,8 @@ public function __construct( $theme_json = array( 'version' => WP_Theme_JSON_Gut * * { * "styles": { - * "blocks": { - * "variations": { + * "variations": { + * "section-a": { * "blockTypes": [ "core/paragraph", "core/group" ], * "color": { "background": "backgroundColor" } * } @@ -813,8 +813,16 @@ public function __construct( $theme_json = array( 'version' => WP_Theme_JSON_Gut * "styles": { * "blocks": { * "variations": { - * "core/paragraph": { "color": { "background": "backgroundColor" } }, - * "core/group": { "color": { "background": "backgroundColor" } } + * "core/paragraph": { + * "variations": { + * "section-a": { "color": { "background": "backgroundColor" } } + * } + * }, + * "core/group": { + * "variations": { + * "section-a": { "color": { "background": "backgroundColor" } } + * } + * } * } * } * } From 0d86f4b2c6b9cb8cd83dfe23b5f84d997ffd7c20 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Fri, 21 Jun 2024 18:32:25 +1000 Subject: [PATCH 14/35] Switch to directly registering variations from partials --- lib/class-wp-rest-global-styles-controller-gutenberg.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/class-wp-rest-global-styles-controller-gutenberg.php b/lib/class-wp-rest-global-styles-controller-gutenberg.php index 19c348bacec30e..aa2e7bf88e420a 100644 --- a/lib/class-wp-rest-global-styles-controller-gutenberg.php +++ b/lib/class-wp-rest-global-styles-controller-gutenberg.php @@ -332,7 +332,8 @@ protected function prepare_item_for_database( $request ) { } // Register theme-defined variations e.g. from block style variation partials under `/styles`. - WP_Theme_JSON_Resolver_Gutenberg::get_theme_data(); + $variations = WP_Theme_JSON_Resolver_Gutenberg::get_style_variations( 'block' ); + gutenberg_register_block_style_variations_from_theme_json_partials( $variations ); if ( isset( $request['settings'] ) ) { $config['settings'] = $request['settings']; From 77e9f50e71f6112da2f97c41f3c23bba00936de9 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Fri, 21 Jun 2024 18:35:23 +1000 Subject: [PATCH 15/35] Fix typo --- lib/class-wp-theme-json-resolver-gutenberg.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/class-wp-theme-json-resolver-gutenberg.php b/lib/class-wp-theme-json-resolver-gutenberg.php index fa1045130836de..033a6345bffddd 100644 --- a/lib/class-wp-theme-json-resolver-gutenberg.php +++ b/lib/class-wp-theme-json-resolver-gutenberg.php @@ -898,7 +898,7 @@ private static function inject_shared_variations_from_theme_json_partials( $data // Merge shared variation definitions with theme.json file taking precedence // over those sourced from partial theme.json files. - $current_variations = $theme_json_data['styles']['variations'] ?? array(); + $current_variations = $data['styles']['variations'] ?? array(); $merged_variations = array_replace_recursive( $new_variations, $current_variations ); _wp_array_set( $data, array( 'styles', 'variations' ), $merged_variations ); From eb78e911fcd803565a7166f0affdcf1d64364b22 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Fri, 21 Jun 2024 20:43:49 +1000 Subject: [PATCH 16/35] Try removing blockTypes from shared variation definitions --- lib/block-supports/block-style-variations.php | 66 ------------------- lib/class-wp-theme-json-gutenberg.php | 33 +++++----- ...class-wp-theme-json-resolver-gutenberg.php | 4 +- 3 files changed, 17 insertions(+), 86 deletions(-) diff --git a/lib/block-supports/block-style-variations.php b/lib/block-supports/block-style-variations.php index 4ca0fe5cec9c57..1c049f4a0fee58 100644 --- a/lib/block-supports/block-style-variations.php +++ b/lib/block-supports/block-style-variations.php @@ -274,69 +274,3 @@ function gutenberg_register_block_style_variations_from_theme_json_partials( $va } } } - -/** - * Merges variations data with existing theme.json data ensuring that the - * current theme.json data values take precedence. - * - * @since 6.6.0 - * - * @param array $variations_data Block style variations data keyed by block type. - * @param WP_Theme_JSON_Data_Gutenberg $theme_json Current theme.json data. - * @param string $origin Origin for the theme.json data. - * - * @return WP_Theme_JSON_Gutenberg The merged theme.json data. - */ -function gutenberg_merge_block_style_variations_data( $variations_data, $theme_json, $origin = 'theme' ) { - if ( empty( $variations_data ) ) { - return $theme_json; - } - - $variations_theme_json_data = array( - 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, - 'styles' => array( 'blocks' => $variations_data ), - ); - - $variations_theme_json = new WP_Theme_JSON_Data_Gutenberg( $variations_theme_json_data, $origin ); - - /* - * Merge the current theme.json data over shared variation data so that - * any explicit per block variation values take precedence. - */ - return $variations_theme_json->update_with( $theme_json->get_data() ); -} - -/** - * Merges block style variations registered via the block styles registry with a - * style object, under their appropriate block types within theme.json styles. - * Any variation values defined within the theme.json specific to a block type - * will take precedence over these shared definitions. - * - * @since 6.6.0 - * - * @param WP_Theme_JSON_Data_Gutenberg $theme_json Current theme.json data. - * - * @return WP_Theme_JSON_Data_Gutenberg - */ -function gutenberg_resolve_block_style_variations_from_styles_registry( $theme_json ) { - $registry = WP_Block_Styles_Registry::get_instance(); - $styles = $registry->get_all_registered(); - $variations_data = array(); - - foreach ( $styles as $block_type => $variations ) { - foreach ( $variations as $variation_name => $variation ) { - if ( ! empty( $variation['style_data'] ) ) { - $path = array( $block_type, 'variations', $variation_name ); - _wp_array_set( $variations_data, $path, $variation['style_data'] ); - } - } - } - - return gutenberg_merge_block_style_variations_data( $variations_data, $theme_json ); -} - -if ( function_exists( 'wp_resolve_block_style_variations_from_styles_registry' ) ) { - remove_filter( 'wp_theme_json_data_theme', 'wp_resolve_block_style_variations_from_styles_registry' ); -} - -add_filter( 'wp_theme_json_data_theme', 'gutenberg_resolve_block_style_variations_from_styles_registry', 10, 1 ); diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index 671a09d907a5d9..3e4601133a080b 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -737,7 +737,7 @@ public static function get_element_class_name( $element ) { * @param string $origin Optional. What source of data this object represents. * One of 'blocks', 'default', 'theme', or 'custom'. Default 'theme'. */ - public function __construct( $theme_json = array( 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA ), $origin = 'theme' ) { + public function __construct( $theme_json = array( 'version' => self::LATEST_SCHEMA ), $origin = 'theme' ) { if ( ! in_array( $origin, static::VALID_ORIGINS, true ) ) { $origin = 'theme'; } @@ -836,24 +836,23 @@ private static function unwrap_shared_block_style_variations( $theme_json ) { return $theme_json; } - $new_theme_json = $theme_json; - $variations = $new_theme_json['styles']['variations']; + $registered_styles = WP_Block_Styles_Registry::get_instance()->get_all_registered(); - foreach ( $variations as $variation_name => $data ) { - if ( ! isset( $data['blockTypes'] ) ) { - // Skip shared variations that do not declare blockTypes. - continue; - } + if ( empty( $registered_styles ) ) { + return $theme_json; + } - $block_names = $data['blockTypes']; - unset( $data['blockTypes'] ); - unset( $data['title'] ); + $new_theme_json = $theme_json; + $variations = $new_theme_json['styles']['variations']; - foreach ( $block_names as $block_name ) { - // Existing per-block-type variation styles should take precedence over shared definition values. - $block_type_variation_data = $new_theme_json['styles']['blocks'][ $block_name ]['variations'][ $variation_name ] ?? array(); - $merged_variation_data = array_replace_recursive( $data, $block_type_variation_data ); - _wp_array_set( $new_theme_json, array( 'styles', 'blocks', $block_name, 'variations', $variation_name ), $merged_variation_data ); + foreach ( $registered_styles as $block_type => $registered_variations ) { + foreach ( $registered_variations as $variation_name => $variation_data ) { + $registered_data = $variation_data['style_data'] ?? array(); + $shared_variation_data = $variations[ $variation_name ] ?? array(); + $merged_variation_data = array_replace_recursive( $registered_data, $shared_variation_data ); + if ( ! empty( $merged_variation_data ) ) { + _wp_array_set( $new_theme_json, array( 'styles', 'blocks', $block_type, 'variations', $variation_name ), $merged_variation_data ); + } } } @@ -3376,7 +3375,7 @@ protected static function filter_slugs( $node, $slugs ) { * @since 5.9.0 * @since 6.6.0 Added support for block style variation element styles and $origin parameter. * - * @param array $theme_json Structure to sanitize. + * @param array $theme_json Structure to sanitize. * @param string $origin Optional. What source of data this object represents. * One of 'blocks', 'default', 'theme', or 'custom'. Default 'theme'. * @return array Sanitized structure. diff --git a/lib/class-wp-theme-json-resolver-gutenberg.php b/lib/class-wp-theme-json-resolver-gutenberg.php index 033a6345bffddd..adc9333a6e2b6b 100644 --- a/lib/class-wp-theme-json-resolver-gutenberg.php +++ b/lib/class-wp-theme-json-resolver-gutenberg.php @@ -887,9 +887,7 @@ private static function inject_shared_variations_from_theme_json_partials( $data } $variation_name = $variation['slug'] ?? _wp_to_kebab_case( $variation['title'] ); - $variation_data = $variation['styles']; - $variation_data['blockTypes'] = $variation['blockTypes']; - $new_variations[ $variation_name ] = $variation_data; + $new_variations[ $variation_name ] = $variation['styles']; } if ( empty( $new_variations ) ) { From 39920cc4dacdebe567e6d1f8460876358905babf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Maneiro?= <583546+oandregal@users.noreply.github.com> Date: Fri, 21 Jun 2024 13:55:29 +0200 Subject: [PATCH 17/35] Update testing grounds --- phpunit/class-wp-theme-json-test.php | 47 ++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/phpunit/class-wp-theme-json-test.php b/phpunit/class-wp-theme-json-test.php index 31194f8912ca46..4f89d036714c1f 100644 --- a/phpunit/class-wp-theme-json-test.php +++ b/phpunit/class-wp-theme-json-test.php @@ -3750,13 +3750,32 @@ public function test_unwraps_block_style_variations() { $input = new WP_Theme_JSON_Gutenberg( array( - 'version' => 3, + 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, 'styles' => array( 'variations' => array( 'myVariation' => array( - 'title' => 'My variation', - 'blockTypes' => array( 'core/paragraph', 'core/group' ), - 'color' => array( 'background' => 'backgroundColor' ), + 'color' => array( + 'background' => 'topLevel', + 'gradient' => 'topLevel' + ), + 'typography' => array( + 'fontFamily' => 'topLevel', + ), + ), + ), + 'blocks' => array( + 'core/paragraph' => array( + 'variations' => array( + 'myVariation' => array( + 'color' => array( + 'background' => 'blockLevel', + 'text' => 'blockLevel', + ), + 'outline' => array( + 'offset' => 'blockLevel', + ), + ), + ), ), ), ), @@ -3769,14 +3788,30 @@ public function test_unwraps_block_style_variations() { 'core/paragraph' => array( 'variations' => array( 'myVariation' => array( - 'color' => array( 'background' => 'backgroundColor' ), + 'color' => array( + 'background' => 'blockLevel', + 'gradient' => 'topLevel', + 'text' => 'blockLevel', + ), + 'typography' => array( + 'fontFamily' => 'topLevel' + ), + 'outline' => array( + 'offset' => 'blockLevel', + ), ), ), ), 'core/group' => array( 'variations' => array( 'myVariation' => array( - 'color' => array( 'background' => 'backgroundColor' ), + 'color' => array( + 'background' => 'topLevel', + 'gradient' => 'topLevel', + ), + 'typography' => array( + 'fontFamily' => 'topLevel', + ), ), ), ), From 5f146c8d7ba96d6709ac4ffefd84624886ab2746 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Maneiro?= <583546+oandregal@users.noreply.github.com> Date: Fri, 21 Jun 2024 14:00:57 +0200 Subject: [PATCH 18/35] Merge top & block level data instead of block registry data --- lib/class-wp-theme-json-gutenberg.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index 3e4601133a080b..63feef6919badf 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -847,11 +847,11 @@ private static function unwrap_shared_block_style_variations( $theme_json ) { foreach ( $registered_styles as $block_type => $registered_variations ) { foreach ( $registered_variations as $variation_name => $variation_data ) { - $registered_data = $variation_data['style_data'] ?? array(); - $shared_variation_data = $variations[ $variation_name ] ?? array(); - $merged_variation_data = array_replace_recursive( $registered_data, $shared_variation_data ); - if ( ! empty( $merged_variation_data ) ) { - _wp_array_set( $new_theme_json, array( 'styles', 'blocks', $block_type, 'variations', $variation_name ), $merged_variation_data ); + $block_level_data = $new_theme_json['styles']['blocks'][ $block_type]['variations'][ $variation_name ] ?? array(); + $top_level_data = $variations[ $variation_name ] ?? array(); + $merged_data = array_replace_recursive( $top_level_data, $block_level_data ); + if ( ! empty( $merged_data ) ) { + _wp_array_set( $new_theme_json, array( 'styles', 'blocks', $block_type, 'variations', $variation_name ), $merged_data ); } } } From d8b0335f1549a80617bda6192e4ccd68e0c9f400 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Maneiro?= <583546+oandregal@users.noreply.github.com> Date: Fri, 21 Jun 2024 14:39:35 +0200 Subject: [PATCH 19/35] Make linter happy --- lib/class-wp-theme-json-gutenberg.php | 2 +- phpunit/class-wp-theme-json-test.php | 17 +++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index 63feef6919badf..4d63eb6b057aa6 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -847,7 +847,7 @@ private static function unwrap_shared_block_style_variations( $theme_json ) { foreach ( $registered_styles as $block_type => $registered_variations ) { foreach ( $registered_variations as $variation_name => $variation_data ) { - $block_level_data = $new_theme_json['styles']['blocks'][ $block_type]['variations'][ $variation_name ] ?? array(); + $block_level_data = $new_theme_json['styles']['blocks'][ $block_type ]['variations'][ $variation_name ] ?? array(); $top_level_data = $variations[ $variation_name ] ?? array(); $merged_data = array_replace_recursive( $top_level_data, $block_level_data ); if ( ! empty( $merged_data ) ) { diff --git a/phpunit/class-wp-theme-json-test.php b/phpunit/class-wp-theme-json-test.php index 4f89d036714c1f..b188ce4fce6abc 100644 --- a/phpunit/class-wp-theme-json-test.php +++ b/phpunit/class-wp-theme-json-test.php @@ -3748,7 +3748,7 @@ public function test_unwraps_block_style_variations() { ) ); - $input = new WP_Theme_JSON_Gutenberg( + $input = new WP_Theme_JSON_Gutenberg( array( 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, 'styles' => array( @@ -3756,18 +3756,18 @@ public function test_unwraps_block_style_variations() { 'myVariation' => array( 'color' => array( 'background' => 'topLevel', - 'gradient' => 'topLevel' + 'gradient' => 'topLevel', ), 'typography' => array( 'fontFamily' => 'topLevel', ), ), ), - 'blocks' => array( + 'blocks' => array( 'core/paragraph' => array( 'variations' => array( 'myVariation' => array( - 'color' => array( + 'color' => array( 'background' => 'blockLevel', 'text' => 'blockLevel', ), @@ -3781,6 +3781,7 @@ public function test_unwraps_block_style_variations() { ), ) ); + $expected = array( 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, 'styles' => array( @@ -3788,15 +3789,15 @@ public function test_unwraps_block_style_variations() { 'core/paragraph' => array( 'variations' => array( 'myVariation' => array( - 'color' => array( + 'color' => array( 'background' => 'blockLevel', 'gradient' => 'topLevel', 'text' => 'blockLevel', ), 'typography' => array( - 'fontFamily' => 'topLevel' + 'fontFamily' => 'topLevel', ), - 'outline' => array( + 'outline' => array( 'offset' => 'blockLevel', ), ), @@ -3805,7 +3806,7 @@ public function test_unwraps_block_style_variations() { 'core/group' => array( 'variations' => array( 'myVariation' => array( - 'color' => array( + 'color' => array( 'background' => 'topLevel', 'gradient' => 'topLevel', ), From 20f2754e3dc8ed5669c4434b30e6e50419437e7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Maneiro?= <583546+oandregal@users.noreply.github.com> Date: Fri, 21 Jun 2024 16:24:27 +0200 Subject: [PATCH 20/35] Update PHP docblock --- lib/class-wp-theme-json-gutenberg.php | 29 +++++++++++++++------------ 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index 4d63eb6b057aa6..eb6836f97a029b 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -794,15 +794,18 @@ public function __construct( $theme_json = array( 'version' => self::LATEST_SCHE /** * Unwraps shared block style variations. * - * @since 6.6.0 + * It takes the shared variations (styles.variations.variationName) + * and applies them to all the blocks that have the given variation registered + * (styles.blocks.blockType.variations.variationName). + * + * For example, given the core/paragraph and core/group blocks have register the section-a + * style variation, and given the following input: + * * * { * "styles": { * "variations": { - * "section-a": { - * "blockTypes": [ "core/paragraph", "core/group" ], - * "color": { "background": "backgroundColor" } - * } + * "section-a": { "color": { "background": "backgroundColor" } } * } * } * } @@ -812,22 +815,22 @@ public function __construct( $theme_json = array( 'version' => self::LATEST_SCHE * { * "styles": { * "blocks": { - * "variations": { - * "core/paragraph": { - * "variations": { + * "core/paragraph": { + * "variations": { * "section-a": { "color": { "background": "backgroundColor" } } - * } * }, - * "core/group": { - * "variations": { - * "section-a": { "color": { "background": "backgroundColor" } } - * } + * }, + * "core/group": { + * "variations": { + * "section-a": { "color": { "background": "backgroundColor" } } * } * } * } * } * } * + * @since 6.6.0 + * * @param array $theme_json A structure that follows the theme.json schema. * @return array Theme json data with shared variation definitions unwrapped under appropriate block types. */ From 016f7ba156e73dfce65a17e117ac812c6e94cb9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Maneiro?= <583546+oandregal@users.noreply.github.com> Date: Fri, 21 Jun 2024 16:41:43 +0200 Subject: [PATCH 21/35] Process variations from block styles registry. --- lib/block-supports/block-style-variations.php | 16 +++++ ...class-wp-theme-json-resolver-gutenberg.php | 61 ++++++++++++++++--- .../block-style-variations-test.php | 2 + 3 files changed, 69 insertions(+), 10 deletions(-) diff --git a/lib/block-supports/block-style-variations.php b/lib/block-supports/block-style-variations.php index 1c049f4a0fee58..12c2453681b419 100644 --- a/lib/block-supports/block-style-variations.php +++ b/lib/block-supports/block-style-variations.php @@ -274,3 +274,19 @@ function gutenberg_register_block_style_variations_from_theme_json_partials( $va } } } + +// DO NOT BACKPORT TO CORE. +// To be removed when core has backported this PR. +if ( function_exists( 'wp_resolve_block_style_variations_from_styles_registry' ) ) { + remove_filter( 'wp_theme_json_data_theme', 'wp_resolve_block_style_variations_from_styles_registry' ); +} +if ( function_exists( 'wp_resolve_block_style_variations_from_primary_theme_json' ) ) { + remove_filter( 'wp_theme_json_data_theme', 'wp_resolve_block_style_variations_from_primary_theme_json' ); +} +if ( function_exists( 'wp_resolve_block_style_variations_from_theme_json_partials' ) ) { + remove_filter( 'wp_theme_json_data_theme', 'wp_resolve_block_style_variations_from_theme_json_partials' ); +} +if ( function_exists( 'wp_resolve_block_style_variations_from_theme_style_variation' ) ) { + remove_filter( 'wp_theme_json_data_user', 'wp_resolve_block_style_variations_from_theme_style_variation' ); +} +// END OF DO NOT BACKPORT TO CORE. diff --git a/lib/class-wp-theme-json-resolver-gutenberg.php b/lib/class-wp-theme-json-resolver-gutenberg.php index adc9333a6e2b6b..2147b9e6e15d99 100644 --- a/lib/class-wp-theme-json-resolver-gutenberg.php +++ b/lib/class-wp-theme-json-resolver-gutenberg.php @@ -252,16 +252,21 @@ public static function get_theme_data( $deprecated = array(), $options = array() gutenberg_register_block_style_variations_from_theme_json_partials( $variations ); /* - * Merge theme.json defined variations with partial definitions. + * Source variations from the block registry and block style variation files. Then, merge them into the existing theme.json data. * - * When the resulting data is passed to `WP_Theme_JSON_Data_Gutenberg` - * it will create a `WP_Theme_JSON_Gutenberg` instance which in turn - * unwraps shared variations into their respective block types. + * In case the same style properties are defined in several sources, this is how we should resolve the values, + * from higher to lower priority: + * + * - styles.blocks.blockType.variations from theme.json + * - styles.variations from theme.json + * - variations from block style variation files + * - variations from block styles registry + * + * See test_add_registered_block_styles_to_theme_data and test_unwraps_block_style_variations. * - * Note: WP_Block_Styles_Registry defined are still merged via - * wp_theme_json_data_theme filter so partials etc can take precedence. */ - $theme_json_data = static::inject_shared_variations_from_theme_json_partials( $theme_json_data, $variations ); + $theme_json_data = static::inject_variations_from_block_style_variation_files( $theme_json_data, $variations ); + $theme_json_data = static::inject_variations_from_block_styles_registry( $theme_json_data ); /** * Filters the data provided by the theme for global styles and settings. @@ -865,8 +870,7 @@ public static function resolve_theme_file_uris( $theme_json ) { } /** - * Adds shared block style variation definitions sourced from theme.json partials - * to the supplied theme.json data. + * Adds variations sourced from block style variations files to the supplied theme.json data. * * @since 6.6.0 * @@ -874,7 +878,7 @@ public static function resolve_theme_file_uris( $theme_json ) { * @param array $variations Shared block style variations. * @return array Theme json data including shared block style variation definitions. */ - private static function inject_shared_variations_from_theme_json_partials( $data, $variations ) { + private static function inject_variations_from_block_style_variation_files( $data, $variations ) { $new_variations = array(); if ( empty( $variations ) ) { @@ -902,4 +906,41 @@ private static function inject_shared_variations_from_theme_json_partials( $data return $data; } + + /** + * Adds variations sourced from the block styles registry to the supplied theme.json data. + * + * @since 6.6.0 + * + * @param array $data Array following the theme.json specification. + * @param array $variations Shared block style variations. + * @return array Theme json data including shared block style variation definitions. + */ + private static function inject_variations_from_block_styles_registry( $data ) { + $registry = WP_Block_Styles_Registry::get_instance(); + $styles = $registry->get_all_registered(); + + foreach ( $styles as $block_type => $variations ) { + foreach ( $variations as $variation_name => $variation ) { + if ( empty( $variation['style_data'] ) ) { + continue; + } + + // First, override registry styles with any top-level styles. + if ( ! empty( $data['styles']['variations'][ $variation_name ] ) ) { + $variation['style_data'] = array_replace_recursive( $variation['style_data'], $data['styles']['variations'][ $variation_name ] ?? array() ); + } + + // Then, override registry styles with any block-level styles. + if ( ! empty( $data['styles']['blocks'][ $block_type ]['variations'][ $variation_name ] ) ) { + $variation['style_data'] = array_replace_recursive( $variation['style_data'], $data['styles']['blocks'][ $block_type ]['variations'][ $variation_name ] ); + } + + $path = array( 'styles', 'blocks', $block_type, 'variations', $variation_name ); + _wp_array_set( $data, $path, $variation['style_data'] ); + } + } + + return $data; + } } diff --git a/phpunit/block-supports/block-style-variations-test.php b/phpunit/block-supports/block-style-variations-test.php index ac81be0f6a826d..f75d89135c6e86 100644 --- a/phpunit/block-supports/block-style-variations-test.php +++ b/phpunit/block-supports/block-style-variations-test.php @@ -158,6 +158,7 @@ public function test_add_registered_block_styles_to_theme_data() { 'text' => 'midnightblue', ), ), + 'my-variation' => $variation_styles_data, /* @@ -177,6 +178,7 @@ public function test_add_registered_block_styles_to_theme_data() { 'text' => 'lightblue', ), ), + ), ); From 7b9a2fe327639df511fd9f8652a5130371de8509 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Maneiro?= <583546+oandregal@users.noreply.github.com> Date: Fri, 21 Jun 2024 18:15:32 +0200 Subject: [PATCH 22/35] Make linter happy --- lib/class-wp-theme-json-resolver-gutenberg.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/class-wp-theme-json-resolver-gutenberg.php b/lib/class-wp-theme-json-resolver-gutenberg.php index 2147b9e6e15d99..07adb3506fefa5 100644 --- a/lib/class-wp-theme-json-resolver-gutenberg.php +++ b/lib/class-wp-theme-json-resolver-gutenberg.php @@ -917,8 +917,8 @@ private static function inject_variations_from_block_style_variation_files( $dat * @return array Theme json data including shared block style variation definitions. */ private static function inject_variations_from_block_styles_registry( $data ) { - $registry = WP_Block_Styles_Registry::get_instance(); - $styles = $registry->get_all_registered(); + $registry = WP_Block_Styles_Registry::get_instance(); + $styles = $registry->get_all_registered(); foreach ( $styles as $block_type => $variations ) { foreach ( $variations as $variation_name => $variation ) { From 4d560296754d9a18bd61d3cc146e2aec0abe96cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Maneiro?= <583546+oandregal@users.noreply.github.com> Date: Fri, 21 Jun 2024 18:34:35 +0200 Subject: [PATCH 23/35] Add backport --- backport-changelog/6.6/6873.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 backport-changelog/6.6/6873.md diff --git a/backport-changelog/6.6/6873.md b/backport-changelog/6.6/6873.md new file mode 100644 index 00000000000000..745966d0dc0407 --- /dev/null +++ b/backport-changelog/6.6/6873.md @@ -0,0 +1,3 @@ +https://github.com/WordPress/wordpress-develop/pull/6873 + +* https://github.com/WordPress/gutenberg/pull/62712 From 2e04467fc3a0113877c4584ffeb7640cd9690de4 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Sat, 22 Jun 2024 14:22:49 +1000 Subject: [PATCH 24/35] Fix minor nits --- lib/class-wp-theme-json-gutenberg.php | 11 +++++------ lib/class-wp-theme-json-resolver-gutenberg.php | 14 ++++++++------ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index eb6836f97a029b..5878251c2a39f5 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -737,7 +737,7 @@ public static function get_element_class_name( $element ) { * @param string $origin Optional. What source of data this object represents. * One of 'blocks', 'default', 'theme', or 'custom'. Default 'theme'. */ - public function __construct( $theme_json = array( 'version' => self::LATEST_SCHEMA ), $origin = 'theme' ) { + public function __construct( $theme_json = array( 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA ), $origin = 'theme' ) { if ( ! in_array( $origin, static::VALID_ORIGINS, true ) ) { $origin = 'theme'; } @@ -794,13 +794,12 @@ public function __construct( $theme_json = array( 'version' => self::LATEST_SCHE /** * Unwraps shared block style variations. * - * It takes the shared variations (styles.variations.variationName) - * and applies them to all the blocks that have the given variation registered + * It takes the shared variations (styles.variations.variationName) and + * applies them to all the blocks that have the given variation registered * (styles.blocks.blockType.variations.variationName). * - * For example, given the core/paragraph and core/group blocks have register the section-a - * style variation, and given the following input: - * + * For example, given the `core/paragraph` and `core/group` blocks have + * registered the `section-a` style variation, and given the following input: * * { * "styles": { diff --git a/lib/class-wp-theme-json-resolver-gutenberg.php b/lib/class-wp-theme-json-resolver-gutenberg.php index 07adb3506fefa5..499176d22a35d4 100644 --- a/lib/class-wp-theme-json-resolver-gutenberg.php +++ b/lib/class-wp-theme-json-resolver-gutenberg.php @@ -220,7 +220,8 @@ protected static function has_same_registered_blocks( $origin ) { * @since 5.8.0 * @since 5.9.0 Theme supports have been inlined and the `$theme_support_data` argument removed. * @since 6.0.0 Added an `$options` parameter to allow the theme data to be returned without theme supports. - * @since 6.6.0 Add support for 'default-font-sizes' and 'default-spacing-sizes' theme supports. + * @since 6.6.0 Added support for 'default-font-sizes' and 'default-spacing-sizes' theme supports. + * Added registration and merging of block style variations from partial theme.json files and the block styles registry. * * @param array $deprecated Deprecated. Not used. * @param array $options { @@ -247,12 +248,15 @@ public static function get_theme_data( $deprecated = array(), $options = array() $theme_json_data = array( 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA ); } - // Register variations defined by theme partials (theme.json files in the styles directory). + /* + * Register variations defined by theme partials (theme.json files in the styles directory). + * This is required so the variations pass sanitization of theme.json data. + */ $variations = static::get_style_variations( 'block' ); gutenberg_register_block_style_variations_from_theme_json_partials( $variations ); /* - * Source variations from the block registry and block style variation files. Then, merge them into the existing theme.json data. + * Source variations from the block styles registry and block style variation files. Then, merge them into the existing theme.json data. * * In case the same style properties are defined in several sources, this is how we should resolve the values, * from higher to lower priority: @@ -263,7 +267,6 @@ public static function get_theme_data( $deprecated = array(), $options = array() * - variations from block styles registry * * See test_add_registered_block_styles_to_theme_data and test_unwraps_block_style_variations. - * */ $theme_json_data = static::inject_variations_from_block_style_variation_files( $theme_json_data, $variations ); $theme_json_data = static::inject_variations_from_block_styles_registry( $theme_json_data ); @@ -913,8 +916,7 @@ private static function inject_variations_from_block_style_variation_files( $dat * @since 6.6.0 * * @param array $data Array following the theme.json specification. - * @param array $variations Shared block style variations. - * @return array Theme json data including shared block style variation definitions. + * @return array Theme json data including variations from the block styles registry. */ private static function inject_variations_from_block_styles_registry( $data ) { $registry = WP_Block_Styles_Registry::get_instance(); From 681741fbe1bb4135ceca4e60c01f4062b7d6b07e Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Sat, 22 Jun 2024 14:24:38 +1000 Subject: [PATCH 25/35] Remove extraneous whitespace --- phpunit/block-supports/block-style-variations-test.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/phpunit/block-supports/block-style-variations-test.php b/phpunit/block-supports/block-style-variations-test.php index f75d89135c6e86..ac81be0f6a826d 100644 --- a/phpunit/block-supports/block-style-variations-test.php +++ b/phpunit/block-supports/block-style-variations-test.php @@ -158,7 +158,6 @@ public function test_add_registered_block_styles_to_theme_data() { 'text' => 'midnightblue', ), ), - 'my-variation' => $variation_styles_data, /* @@ -178,7 +177,6 @@ public function test_add_registered_block_styles_to_theme_data() { 'text' => 'lightblue', ), ), - ), ); From 76264330c17cc068bbb9def8702583619a038966 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Sat, 22 Jun 2024 16:42:16 +1000 Subject: [PATCH 26/35] Add test for variation styles merge order --- phpunit/class-wp-theme-json-resolver-test.php | 89 +++++++++++++++++++ .../theme.json | 53 ++++++++++- 2 files changed, 141 insertions(+), 1 deletion(-) diff --git a/phpunit/class-wp-theme-json-resolver-test.php b/phpunit/class-wp-theme-json-resolver-test.php index 6333c3d1dd7760..e7a1b44049393e 100644 --- a/phpunit/class-wp-theme-json-resolver-test.php +++ b/phpunit/class-wp-theme-json-resolver-test.php @@ -1289,4 +1289,93 @@ public function test_get_resolved_theme_uris() { $this->assertSame( $expected_data, $actual ); } + + /** + * Tests that block style variations data gets merged in the following + * priority order, from highest priority to lowest. + * + * - `styles.blocks.blockType.variations` from theme.json + * - `styles.variations` from theme.json + * - variations from block style variation files under `/styles` + * - variations from `WP_Block_Styles_Registry` + */ + public function test_block_style_variation_merge_order() { + switch_theme( 'block-theme-child-with-block-style-variations' ); + + /* + * Register style for a block that isn't included in the block style variation's partial + * theme.json's blockTypes. The name must match though so we can ensure the partial's + * styles do not get applied to this block. + */ + register_block_style( + 'core/heading', + array( + 'name' => 'block-style-variation-b', + 'label' => 'Heading only variation', + ) + ); + + // Register variation for a block that will be partially overridden at all levels. + register_block_style( + 'core/media-text', + array( + 'name' => 'block-style-variation-a', + 'label' => 'Block Style Variation A', + 'style_data' => array( + 'color' => array( + 'background' => 'pink', + 'gradient' => 'var(--custom)', + ), + ), + ) + ); + + $data = WP_Theme_JSON_Resolver_Gutenberg::get_theme_data()->get_raw_data(); + $block_styles = $data['styles']['blocks'] ?? array(); + $actual = array_intersect_key( + $block_styles, + array_flip( array( 'core/button', 'core/media-text', 'core/heading' ) ) + ); + $expected = array( + 'core/button' => array( + 'variations' => array( + 'outline' => array( + 'color' => array( + 'background' => 'red', + 'text' => 'white', + ), + ), + ), + ), + 'core/media-text' => array( + 'variations' => array( + 'block-style-variation-a' => array( + 'color' => array( + 'background' => 'blue', + 'gradient' => 'var(--custom)', + 'text' => 'aliceblue', + ), + 'typography' => array( + 'fontSize' => '1.5em', + 'lineHeight' => '1.4em', + ), + ), + ), + ), + 'core/heading' => array( + 'variations' => array( + 'block-style-variation-b' => array( + 'typography' => array( + 'fontSize' => '3em', + ), + ), + ), + ), + ); + + unregister_block_style( 'core/heading', 'block-style-variation-b' ); + unregister_block_style( 'core/media-text', 'block-style-variation-a' ); + + $this->assertSameSetsWithIndex( $expected, $actual, 'Merged variation styles do not match.' ); + } } diff --git a/phpunit/data/themedir1/block-theme-child-with-block-style-variations/theme.json b/phpunit/data/themedir1/block-theme-child-with-block-style-variations/theme.json index a471d8f326a4ab..fee9382dce1472 100644 --- a/phpunit/data/themedir1/block-theme-child-with-block-style-variations/theme.json +++ b/phpunit/data/themedir1/block-theme-child-with-block-style-variations/theme.json @@ -1,4 +1,55 @@ { "$schema": "https://schemas.wp.org/trunk/theme.json", - "version": 3 + "version": 3, + "styles": { + "variations": { + "outline": { + "color": { + "background": "green", + "text": "white" + } + }, + "block-style-variation-a": { + "color": { + "background": "darkseagreen" + }, + "typography": { + "fontSize": "2em", + "lineHeight": "1.4em" + } + } + }, + "blocks": { + "core/button": { + "variations": { + "outline": { + "color": { + "background": "red" + } + } + } + }, + "core/media-text": { + "variations": { + "block-style-variation-a": { + "color": { + "background": "blue" + }, + "typography": { + "fontSize": "1.5em" + } + } + } + }, + "core/heading": { + "variations": { + "block-style-variation-b": { + "typography": { + "fontSize": "3em" + } + } + } + } + } + } } From d77a6b0e09f8a57e50fa840004da8166740f25b0 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Sat, 22 Jun 2024 17:08:44 +1000 Subject: [PATCH 27/35] Prevent partial styles overriding registered variation for different block --- ...class-wp-theme-json-resolver-gutenberg.php | 42 +++++++++++-------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/lib/class-wp-theme-json-resolver-gutenberg.php b/lib/class-wp-theme-json-resolver-gutenberg.php index 499176d22a35d4..ff2011fc4d542e 100644 --- a/lib/class-wp-theme-json-resolver-gutenberg.php +++ b/lib/class-wp-theme-json-resolver-gutenberg.php @@ -882,8 +882,6 @@ public static function resolve_theme_file_uris( $theme_json ) { * @return array Theme json data including shared block style variation definitions. */ private static function inject_variations_from_block_style_variation_files( $data, $variations ) { - $new_variations = array(); - if ( empty( $variations ) ) { return $data; } @@ -893,19 +891,25 @@ private static function inject_variations_from_block_style_variation_files( $dat continue; } - $variation_name = $variation['slug'] ?? _wp_to_kebab_case( $variation['title'] ); - $new_variations[ $variation_name ] = $variation['styles']; - } + $variation_name = $variation['slug'] ?? _wp_to_kebab_case( $variation['title'] ); - if ( empty( $new_variations ) ) { - return $data; - } + foreach ( $variation['blockTypes'] as $block_type ) { + // First, override partial styles with any top-level styles. + $top_level_data = $data['styles']['variations'][ $variation_name ] ?? array(); + if ( ! empty( $top_level_data ) ) { + $variation['styles'] = array_replace_recursive( $variation['styles'], $top_level_data ); + } - // Merge shared variation definitions with theme.json file taking precedence - // over those sourced from partial theme.json files. - $current_variations = $data['styles']['variations'] ?? array(); - $merged_variations = array_replace_recursive( $new_variations, $current_variations ); - _wp_array_set( $data, array( 'styles', 'variations' ), $merged_variations ); + // The, override styles so far with any block-level styles. + $block_level_data = $data['styles']['blocks'][ $block_type ]['variations'][ $variation_name ] ?? array(); + if ( ! empty( $block_level_data ) ) { + $variation['styles'] = array_replace_recursive( $variation['styles'], $block_level_data ); + } + + $path = array( 'styles', 'blocks', $block_type, 'variations', $variation_name ); + _wp_array_set( $data, $path, $variation['styles'] ); + } + } return $data; } @@ -929,13 +933,15 @@ private static function inject_variations_from_block_styles_registry( $data ) { } // First, override registry styles with any top-level styles. - if ( ! empty( $data['styles']['variations'][ $variation_name ] ) ) { - $variation['style_data'] = array_replace_recursive( $variation['style_data'], $data['styles']['variations'][ $variation_name ] ?? array() ); + $top_level_data = $data['styles']['variations'][ $variation_name ] ?? array(); + if ( ! empty( $top_level_data ) ) { + $variation['style_data'] = array_replace_recursive( $variation['style_data'], $top_level_data ); } - // Then, override registry styles with any block-level styles. - if ( ! empty( $data['styles']['blocks'][ $block_type ]['variations'][ $variation_name ] ) ) { - $variation['style_data'] = array_replace_recursive( $variation['style_data'], $data['styles']['blocks'][ $block_type ]['variations'][ $variation_name ] ); + // Then, override styles so far with any block-level styles. + $block_level_data = $data['styles']['blocks'][ $block_type ]['variations'][ $variation_name ] ?? array(); + if ( ! empty( $block_level_data ) ) { + $variation['style_data'] = array_replace_recursive( $variation['style_data'], $block_level_data ); } $path = array( 'styles', 'blocks', $block_type, 'variations', $variation_name ); From d781aed53e6856efa4e51f1569f68c0bdd5285e1 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Sat, 22 Jun 2024 17:47:43 +1000 Subject: [PATCH 28/35] Fix test_add_registered_block_styles_to_theme_data --- .../block-style-variations-test.php | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/phpunit/block-supports/block-style-variations-test.php b/phpunit/block-supports/block-style-variations-test.php index ac81be0f6a826d..0236beff468ee4 100644 --- a/phpunit/block-supports/block-style-variations-test.php +++ b/phpunit/block-supports/block-style-variations-test.php @@ -152,13 +152,6 @@ public function test_add_registered_block_styles_to_theme_data() { $group_styles = $theme_json['styles']['blocks']['core/group'] ?? array(); $expected = array( 'variations' => array( - 'WithSlug' => array( - 'color' => array( - 'background' => 'aliceblue', - 'text' => 'midnightblue', - ), - ), - 'my-variation' => $variation_styles_data, /* * The following block style variations are registered @@ -177,6 +170,17 @@ public function test_add_registered_block_styles_to_theme_data() { 'text' => 'lightblue', ), ), + + /* + * Manually registered variations. + */ + 'WithSlug' => array( + 'color' => array( + 'background' => 'aliceblue', + 'text' => 'midnightblue', + ), + ), + 'my-variation' => $variation_styles_data, ), ); From e33638ff976b5c2cb147f544495e8c8f4804ee84 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Sat, 22 Jun 2024 17:48:15 +1000 Subject: [PATCH 29/35] Update unwrapping to include variations for core blocks not in registry --- lib/class-wp-theme-json-gutenberg.php | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index 5878251c2a39f5..918ee29093a8e7 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -743,11 +743,11 @@ public function __construct( $theme_json = array( 'version' => WP_Theme_JSON_Gut } $this->theme_json = WP_Theme_JSON_Schema_Gutenberg::migrate( $theme_json, $origin ); - $this->theme_json = static::unwrap_shared_block_style_variations( $this->theme_json ); $registry = WP_Block_Type_Registry::get_instance(); $valid_block_names = array_keys( $registry->get_all_registered() ); $valid_element_names = array_keys( static::ELEMENTS ); $valid_variations = static::get_valid_block_style_variations(); + $this->theme_json = static::unwrap_shared_block_style_variations( $this->theme_json, $valid_variations ); $this->theme_json = static::sanitize( $this->theme_json, $valid_block_names, $valid_element_names, $valid_variations ); $this->theme_json = static::maybe_opt_in_into_settings( $this->theme_json ); @@ -830,25 +830,21 @@ public function __construct( $theme_json = array( 'version' => WP_Theme_JSON_Gut * * @since 6.6.0 * - * @param array $theme_json A structure that follows the theme.json schema. + * @param array $theme_json A structure that follows the theme.json schema. + * @param array $valid_variations Valid block style variations. + * * @return array Theme json data with shared variation definitions unwrapped under appropriate block types. */ - private static function unwrap_shared_block_style_variations( $theme_json ) { - if ( ! isset( $theme_json['styles']['variations'] ) ) { - return $theme_json; - } - - $registered_styles = WP_Block_Styles_Registry::get_instance()->get_all_registered(); - - if ( empty( $registered_styles ) ) { + private static function unwrap_shared_block_style_variations( $theme_json, $valid_variations ) { + if ( empty( $theme_json['styles']['variations'] ) || empty( $valid_variations ) ) { return $theme_json; } $new_theme_json = $theme_json; $variations = $new_theme_json['styles']['variations']; - foreach ( $registered_styles as $block_type => $registered_variations ) { - foreach ( $registered_variations as $variation_name => $variation_data ) { + foreach ( $valid_variations as $block_type => $registered_variations ) { + foreach ( $registered_variations as $variation_name ) { $block_level_data = $new_theme_json['styles']['blocks'][ $block_type ]['variations'][ $variation_name ] ?? array(); $top_level_data = $variations[ $variation_name ] ?? array(); $merged_data = array_replace_recursive( $top_level_data, $block_level_data ); From ba38fa3bb83ce630cb5e98f77ff88b87a9afc17b Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Sun, 23 Jun 2024 21:01:41 +1000 Subject: [PATCH 30/35] Fix typo --- lib/class-wp-theme-json-resolver-gutenberg.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/class-wp-theme-json-resolver-gutenberg.php b/lib/class-wp-theme-json-resolver-gutenberg.php index ff2011fc4d542e..fba107b9814025 100644 --- a/lib/class-wp-theme-json-resolver-gutenberg.php +++ b/lib/class-wp-theme-json-resolver-gutenberg.php @@ -900,7 +900,7 @@ private static function inject_variations_from_block_style_variation_files( $dat $variation['styles'] = array_replace_recursive( $variation['styles'], $top_level_data ); } - // The, override styles so far with any block-level styles. + // Then, override styles so far with any block-level styles. $block_level_data = $data['styles']['blocks'][ $block_type ]['variations'][ $variation_name ] ?? array(); if ( ! empty( $block_level_data ) ) { $variation['styles'] = array_replace_recursive( $variation['styles'], $block_level_data ); From 37f68650f504f57b90c9fa6234d7dea175eb62a8 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Sun, 23 Jun 2024 21:22:28 +1000 Subject: [PATCH 31/35] Add since comment for change to theme json constructor --- lib/class-wp-theme-json-gutenberg.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index 918ee29093a8e7..9762bb610f77b9 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -730,8 +730,8 @@ public static function get_element_class_name( $element ) { * Constructor. * * @since 5.8.0 - * @since 6.6.0 Key spacingScale by origin, and pre-generate the - * spacingSizes from spacingScale. + * @since 6.6.0 Key spacingScale by origin, and pre-generate the spacingSizes from spacingScale. + * Added unwrapping of shared block style variations into block type variations if registered. * * @param array $theme_json A structure that follows the theme.json schema. * @param string $origin Optional. What source of data this object represents. From e90b542e70f1d4eece51de36e4e34931a9aa027c Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Sun, 23 Jun 2024 22:25:27 +1000 Subject: [PATCH 32/35] Try relocating variations in theme.json schema This might end up being removed to deemphasize its availability. --- schemas/json/theme.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/schemas/json/theme.json b/schemas/json/theme.json index 5b9e37ef7378a6..9fbd2f2fabd165 100644 --- a/schemas/json/theme.json +++ b/schemas/json/theme.json @@ -1938,9 +1938,6 @@ "stylesBlocksPropertiesComplete": { "type": "object", "properties": { - "variations": { - "$ref": "#/definitions/stylesBlocksSharedVariationProperties" - }, "core/archives": { "$ref": "#/definitions/stylesPropertiesAndElementsComplete" }, @@ -2271,15 +2268,15 @@ } ] }, - "stylesBlocksSharedVariationProperties": { + "stylesVariationsProperties": { "type": "object", "patternProperties": { "^[a-z][a-z0-9-]*$": { - "$ref": "#/definitions/stylesSharedVariationProperties" + "$ref": "#/definitions/stylesVariationProperties" } } }, - "stylesSharedVariationProperties": { + "stylesVariationProperties": { "type": "object", "allOf": [ { @@ -2833,6 +2830,9 @@ } }, "additionalProperties": false + }, + "variations": { + "$ref": "#/definitions/stylesVariationsProperties" } }, "additionalProperties": false From af5b1804863d368d08351ce192f06f25531b4f01 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Mon, 24 Jun 2024 13:51:59 +1000 Subject: [PATCH 33/35] Remove title and block types from shared definitions --- schemas/json/theme.json | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/schemas/json/theme.json b/schemas/json/theme.json index 9fbd2f2fabd165..ec42bd1d54f0b7 100644 --- a/schemas/json/theme.json +++ b/schemas/json/theme.json @@ -2284,16 +2284,6 @@ }, { "properties": { - "title": { - "type": "string", - "description": "Style variation name." - }, - "blockTypes": { - "type": "array", - "items": { - "type": "string" - } - }, "border": {}, "color": {}, "dimensions": {}, From b7013f83c7244d06c7c8c744c076032f2b622dba Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Mon, 24 Jun 2024 14:21:19 +1000 Subject: [PATCH 34/35] Remove title from translatable theme.json schema --- lib/theme-i18n.json | 9 --------- 1 file changed, 9 deletions(-) diff --git a/lib/theme-i18n.json b/lib/theme-i18n.json index fe541e65c676ba..e4d14502132cbe 100644 --- a/lib/theme-i18n.json +++ b/lib/theme-i18n.json @@ -81,15 +81,6 @@ } } }, - "styles": { - "blocks": { - "variations": { - "*": { - "title": "Style variation name" - } - } - } - }, "customTemplates": [ { "title": "Custom template name" From 1e0065ebe50788f69237e93752a779314e9c9c31 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Mon, 24 Jun 2024 17:57:29 +1000 Subject: [PATCH 35/35] Ensure block style variations are registered when retrieving theme style variations --- lib/class-wp-rest-global-styles-controller-gutenberg.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/class-wp-rest-global-styles-controller-gutenberg.php b/lib/class-wp-rest-global-styles-controller-gutenberg.php index aa2e7bf88e420a..421408e6f20b4a 100644 --- a/lib/class-wp-rest-global-styles-controller-gutenberg.php +++ b/lib/class-wp-rest-global-styles-controller-gutenberg.php @@ -706,7 +706,12 @@ public function get_theme_items( $request ) { ); } - $response = array(); + $response = array(); + + // Register theme-defined variations e.g. from block style variation partials under `/styles`. + $partials = WP_Theme_JSON_Resolver_Gutenberg::get_style_variations( 'block' ); + gutenberg_register_block_style_variations_from_theme_json_partials( $partials ); + $variations = WP_Theme_JSON_Resolver_Gutenberg::get_style_variations(); // Add resolved theme asset links.