diff --git a/src/wp-includes/block-supports/layout.php b/src/wp-includes/block-supports/layout.php index bd1badc7a6cd6..3a1f5e7a0598d 100644 --- a/src/wp-includes/block-supports/layout.php +++ b/src/wp-includes/block-supports/layout.php @@ -6,6 +6,37 @@ * @since 5.8.0 */ +/** + * Gets the first style variation name from a className string that matches a registered style. + * + * @since 7.0.0 + * + * @param string $class_name CSS class string for a block. + * @param array> $registered_styles Currently registered block styles. + * @return string|null The name of the first registered variation, or null if none found. + */ +function wp_get_block_style_variation_name_from_registered_style( string $class_name, array $registered_styles = array() ): ?string { + if ( ! $class_name ) { + return null; + } + + $registered_names = array_filter( array_column( $registered_styles, 'name' ) ); + + $prefix = 'is-style-'; + $length = strlen( $prefix ); + + foreach ( explode( ' ', $class_name ) as $class ) { + if ( str_starts_with( $class, $prefix ) ) { + $variation = substr( $class, $length ); + if ( 'default' !== $variation && in_array( $variation, $registered_names, true ) ) { + return $variation; + } + } + } + + return null; +} + /** * Returns layout definitions, keyed by layout type. * @@ -854,12 +885,26 @@ function wp_render_layout_support_flag( $block_content, $block ) { $has_block_gap_support = isset( $block_gap ); // Get default blockGap value from global styles for use in layouts like grid. - // Check block-specific styles first, then fall back to root styles. + // Check style variation first, then block-specific styles, then fall back to root styles. $block_name = $block['blockName'] ?? ''; if ( null === $global_styles ) { $global_styles = wp_get_global_styles(); } - $global_block_gap_value = $global_styles['blocks'][ $block_name ]['spacing']['blockGap'] ?? ( $global_styles['spacing']['blockGap'] ?? null ); + + // Check if the block has an active style variation with a blockGap value. + // Only check the registry if the className contains a variation class to avoid unnecessary lookups. + $variation_block_gap_value = null; + $block_class_name = $block['attrs']['className'] ?? ''; + if ( $block_class_name && str_contains( $block_class_name, 'is-style-' ) && $block_name ) { + $styles_registry = WP_Block_Styles_Registry::get_instance(); + $registered_styles = $styles_registry->get_registered_styles_for_block( $block_name ); + $variation_name = wp_get_block_style_variation_name_from_registered_style( $block_class_name, $registered_styles ); + if ( $variation_name ) { + $variation_block_gap_value = $global_styles['blocks'][ $block_name ]['variations'][ $variation_name ]['spacing']['blockGap'] ?? null; + } + } + + $global_block_gap_value = $variation_block_gap_value ?? $global_styles['blocks'][ $block_name ]['spacing']['blockGap'] ?? $global_styles['spacing']['blockGap'] ?? null; if ( null !== $global_block_gap_value ) { $fallback_gap_value = $global_block_gap_value; diff --git a/tests/phpunit/tests/block-supports/layout.php b/tests/phpunit/tests/block-supports/layout.php index 8661077df8662..f47a7a3e35d2b 100644 --- a/tests/phpunit/tests/block-supports/layout.php +++ b/tests/phpunit/tests/block-supports/layout.php @@ -42,6 +42,22 @@ public function set_up() { // Clear caches. wp_clean_themes_cache(); unset( $GLOBALS['wp_themes'] ); + + /* + * Register a style variation with a custom blockGap value for testing. + */ + register_block_style( + 'core/group', + array( + 'name' => 'custom-gap', + 'label' => 'Custom Gap', + 'style_data' => array( + 'spacing' => array( + 'blockGap' => '99px', + ), + ), + ) + ); } public function tear_down() { @@ -54,6 +70,11 @@ public function tear_down() { wp_clean_themes_cache(); unset( $GLOBALS['wp_themes'] ); + + // Clean up variation test data. + unregister_block_style( 'core/group', 'custom-gap' ); + WP_Theme_JSON_Resolver::clean_cached_data(); + parent::tear_down(); } @@ -727,4 +748,131 @@ public function data_layout_classname_with_custom_blocks() { ), ); } + + /** + * Tests that block style variations with blockGap values are applied to layout styles. + * + * @ticket 64624 + * @covers ::wp_render_layout_support_flag + */ + public function test_layout_support_flag_uses_variation_block_gap_value() { + switch_theme( 'block-theme' ); + + $block_content = '
'; + $block = array( + 'blockName' => 'core/group', + 'attrs' => array( + 'className' => 'is-style-custom-gap', + 'layout' => array( + 'type' => 'grid', + 'columnCount' => 3, + 'minimumColumnWidth' => '12rem', + ), + ), + 'innerBlocks' => array(), + 'innerHTML' => '
', + 'innerContent' => array( + '
', + ), + ); + + wp_render_layout_support_flag( $block_content, $block ); + + // Get the generated CSS from the style engine. + $actual_stylesheet = wp_style_engine_get_stylesheet_from_context( 'block-supports', array( 'prettify' => false ) ); + + // The CSS grid declaration should contain the variation's blockGap value of 99px. + $this->assertStringContainsString( + 'grid-template-columns:repeat(auto-fill, minmax(max(min(12rem, 100%), (100% - (99px * (3 - 1))) /3), 1fr))', + $actual_stylesheet, + 'Generated CSS should contain the variation blockGap value of 99px.' + ); + } + + /** + * Tests that wp_get_block_style_variation_name_from_registered_style correctly extracts variation names from class strings. + * + * @ticket 64624 + * @covers ::wp_get_block_style_variation_name_from_registered_style + * + * @dataProvider data_get_block_style_variation_name_from_registered_style + * + * @param string $class_name CSS class string to test. + * @param array $registered_styles Registered block styles. + * @param string|null $expected_result Expected variation name or null. + */ + public function test_get_block_style_variation_name_from_registered_style( $class_name, $registered_styles, $expected_result ) { + $result = wp_get_block_style_variation_name_from_registered_style( $class_name, $registered_styles ); + $this->assertSame( $expected_result, $result ); + } + + /** + * Data provider for test_get_block_style_variation_name_from_registered_style. + * + * @return array + */ + public function data_get_block_style_variation_name_from_registered_style() { + return array( + 'empty class name' => array( + 'class_name' => '', + 'registered_styles' => array(), + 'expected_result' => null, + ), + 'no matching registered styles' => array( + 'class_name' => 'is-style-shadowed wp-block-button', + 'registered_styles' => array( + array( 'name' => 'rounded' ), + array( 'name' => 'outlined' ), + ), + 'expected_result' => null, + ), + 'single matching variation found' => array( + 'class_name' => 'wp-block-button is-style-rounded', + 'registered_styles' => array( + array( 'name' => 'rounded' ), + array( 'name' => 'outlined' ), + ), + 'expected_result' => 'rounded', + ), + 'ignores default style only' => array( + 'class_name' => 'is-style-default wp-block-button', + 'registered_styles' => array( + array( 'name' => 'default' ), + array( 'name' => 'rounded' ), + ), + 'expected_result' => null, + ), + 'ignores default and returns next variation' => array( + 'class_name' => 'is-style-default is-style-rounded wp-block-button', + 'registered_styles' => array( + array( 'name' => 'default' ), + array( 'name' => 'rounded' ), + array( 'name' => 'outlined' ), + ), + 'expected_result' => 'rounded', + ), + 'returns first matching variation when multiple present' => array( + 'class_name' => 'is-style-shadowed is-style-rounded', + 'registered_styles' => array( + array( 'name' => 'rounded' ), + array( 'name' => 'outlined' ), + array( 'name' => 'shadowed' ), + ), + 'expected_result' => 'shadowed', + ), + 'empty registered styles array' => array( + 'class_name' => 'is-style-rounded', + 'registered_styles' => array(), + 'expected_result' => null, + ), + 'registered styles with missing name property' => array( + 'class_name' => 'is-style-outlined wp-block-button', + 'registered_styles' => array( + array( 'label' => 'Rounded' ), + array( 'name' => 'outlined' ), + ), + 'expected_result' => 'outlined', + ), + ); + } }