diff --git a/src/wp-includes/block-supports/background.php b/src/wp-includes/block-supports/background.php index 8d464cc789ee4..47b361295e3bc 100644 --- a/src/wp-includes/block-supports/background.php +++ b/src/wp-includes/block-supports/background.php @@ -34,6 +34,60 @@ function wp_register_background_support( $block_type ) { } } +/** + * Returns background classes and inline styles for block attributes, like the JS + * `getBackgroundClassesAndStyles()`. Does not check block support or skipped + * serialization. + * + * @since 7.2.0 + * + * @param array $block_attributes Block attributes. + * @return array Array with `class` and `style` keys, each present only when non-empty. + */ +function wp_get_background_classes_and_styles( $block_attributes ) { + $background = $block_attributes['style']['background'] ?? null; + + if ( ! is_array( $background ) ) { + return array(); + } + + $background_styles = array( + 'backgroundImage' => $background['backgroundImage'] ?? null, + 'backgroundSize' => $background['backgroundSize'] ?? null, + 'backgroundPosition' => $background['backgroundPosition'] ?? null, + 'backgroundRepeat' => $background['backgroundRepeat'] ?? null, + 'backgroundAttachment' => $background['backgroundAttachment'] ?? null, + 'gradient' => $background['gradient'] ?? null, + 'backgroundClip' => $background['backgroundClip'] ?? null, + ); + + if ( ! empty( $background_styles['backgroundImage'] ) ) { + $background_styles['backgroundSize'] ??= 'cover'; + if ( 'contain' === $background_styles['backgroundSize'] && ! $background_styles['backgroundPosition'] ) { + $background_styles['backgroundPosition'] = '50% 50%'; + } + } + + $styles = wp_style_engine_get_styles( array( 'background' => $background_styles ) ); + + if ( empty( $styles['css'] ) ) { + return array(); + } + + $classes_and_styles = array(); + + $has_background = ! empty( $background_styles['backgroundImage'] ) || ! empty( $background_styles['gradient'] ); + + // A text clip paints the glyphs, not the box, so there is no block background. + if ( $has_background && 'text' !== $background_styles['backgroundClip'] ) { + $classes_and_styles['class'] = 'has-background'; + } + + $classes_and_styles['style'] = $styles['css']; + + return $classes_and_styles; +} + /** * Renders the background styles to the block wrapper. * This block support uses the `render_block` hook to ensure that @@ -91,36 +145,31 @@ function wp_render_background_support( $block_content, $block ) { return $block_content; } - $background_styles = array(); - - if ( ! $skip_background_image ) { - $background_styles['backgroundImage'] = $block_attributes['style']['background']['backgroundImage'] ?? null; - $background_styles['backgroundSize'] = $block_attributes['style']['background']['backgroundSize'] ?? null; - $background_styles['backgroundPosition'] = $block_attributes['style']['background']['backgroundPosition'] ?? null; - $background_styles['backgroundRepeat'] = $block_attributes['style']['background']['backgroundRepeat'] ?? null; - $background_styles['backgroundAttachment'] = $block_attributes['style']['background']['backgroundAttachment'] ?? null; - - if ( ! empty( $background_styles['backgroundImage'] ) ) { - $background_styles['backgroundSize'] ??= 'cover'; - - // If the background size is set to `contain` and no position is set, set the position to `center`. - if ( 'contain' === $background_styles['backgroundSize'] && ! $background_styles['backgroundPosition'] ) { - $background_styles['backgroundPosition'] = '50% 50%'; - } + // The helper ignores a non-array background, so only an array needs filtering. + if ( is_array( $block_attributes['style']['background'] ) ) { + if ( $skip_background_image ) { + unset( + $block_attributes['style']['background']['backgroundImage'], + $block_attributes['style']['background']['backgroundSize'], + $block_attributes['style']['background']['backgroundPosition'], + $block_attributes['style']['background']['backgroundRepeat'], + $block_attributes['style']['background']['backgroundAttachment'] + ); } - } - if ( ! $skip_background_gradient ) { - $background_styles['gradient'] = $block_attributes['style']['background']['gradient'] ?? null; - } + if ( $skip_background_gradient ) { + unset( $block_attributes['style']['background']['gradient'] ); + } - if ( ! $skip_background_clip ) { - $background_styles['backgroundClip'] = $block_attributes['style']['background']['backgroundClip'] ?? null; + // Serialized values only, so a skipped clip cannot drop `has-background`. + if ( $skip_background_clip ) { + unset( $block_attributes['style']['background']['backgroundClip'] ); + } } - $styles = wp_style_engine_get_styles( array( 'background' => $background_styles ) ); + $styles = wp_get_background_classes_and_styles( $block_attributes ); - if ( ! empty( $styles['css'] ) ) { + if ( ! empty( $styles['style'] ) ) { // Inject background styles to the first element, presuming it's the wrapper, if it exists. $tags = new WP_HTML_Tag_Processor( $block_content ); @@ -128,24 +177,15 @@ function wp_render_background_support( $block_content, $block ) { $existing_style = $tags->get_attribute( 'style' ); if ( is_string( $existing_style ) && '' !== $existing_style ) { $separator = str_ends_with( $existing_style, ';' ) ? '' : ';'; - $updated_style = "{$existing_style}{$separator}{$styles['css']}"; + $updated_style = "{$existing_style}{$separator}{$styles['style']}"; } else { - $updated_style = $styles['css']; + $updated_style = $styles['style']; } $tags->set_attribute( 'style', $updated_style ); - /* - * A background clipped to the text paints the glyphs rather than the - * block's box, so there is no block background to announce. Read the - * clip from the serialized styles so an unsupported or skipped value - * left in the block's attributes cannot suppress the class. - */ - $background_clip = $background_styles['backgroundClip'] ?? null; - $has_background = ! empty( $background_styles['backgroundImage'] ) || ! empty( $background_styles['gradient'] ); - - if ( $has_background && 'text' !== $background_clip ) { - $tags->add_class( 'has-background' ); + if ( ! empty( $styles['class'] ) ) { + $tags->add_class( $styles['class'] ); } } diff --git a/src/wp-includes/block-supports/border.php b/src/wp-includes/block-supports/border.php index 89a3ef175e234..e5afd5518998a 100644 --- a/src/wp-includes/block-supports/border.php +++ b/src/wp-includes/block-supports/border.php @@ -52,76 +52,110 @@ function wp_apply_border_support( $block_type, $block_attributes ) { return array(); } - $border_block_styles = array(); - $has_border_color_support = wp_has_border_feature_support( $block_type, 'color' ); - $has_border_width_support = wp_has_border_feature_support( $block_type, 'width' ); - - // Border radius. - if ( - wp_has_border_feature_support( $block_type, 'radius' ) && - isset( $block_attributes['style']['border']['radius'] ) && - ! wp_should_skip_block_supports_serialization( $block_type, '__experimentalBorder', 'radius' ) - ) { - $border_radius = $block_attributes['style']['border']['radius']; - - if ( is_numeric( $border_radius ) ) { - $border_radius .= 'px'; - } + $has_border_color_support = wp_has_border_feature_support( $block_type, 'color' ); + $has_border_radius_support = wp_has_border_feature_support( $block_type, 'radius' ); + $has_border_style_support = wp_has_border_feature_support( $block_type, 'style' ); + $has_border_width_support = wp_has_border_feature_support( $block_type, 'width' ); + $skip_color = wp_should_skip_block_supports_serialization( $block_type, '__experimentalBorder', 'color' ); + $skip_radius = wp_should_skip_block_supports_serialization( $block_type, '__experimentalBorder', 'radius' ); + $skip_style = wp_should_skip_block_supports_serialization( $block_type, '__experimentalBorder', 'style' ); + $skip_width = wp_should_skip_block_supports_serialization( $block_type, '__experimentalBorder', 'width' ); + + // The helper reads only `style.border`, so a non-array value counts as no border styles. + if ( ! isset( $block_attributes['style']['border'] ) || ! is_array( $block_attributes['style']['border'] ) ) { + unset( $block_attributes['style'] ); + } + + if ( ! $has_border_radius_support || $skip_radius ) { + unset( $block_attributes['style']['border']['radius'] ); + } - $border_block_styles['radius'] = $border_radius; + if ( ! $has_border_style_support || $skip_style ) { + unset( $block_attributes['style']['border']['style'] ); } - // Border style. - if ( - wp_has_border_feature_support( $block_type, 'style' ) && - isset( $block_attributes['style']['border']['style'] ) && - ! wp_should_skip_block_supports_serialization( $block_type, '__experimentalBorder', 'style' ) - ) { - $border_block_styles['style'] = $block_attributes['style']['border']['style']; + if ( ! $has_border_width_support || $skip_width ) { + unset( $block_attributes['style']['border']['width'] ); } - // Border width. - if ( - $has_border_width_support && - isset( $block_attributes['style']['border']['width'] ) && - ! wp_should_skip_block_supports_serialization( $block_type, '__experimentalBorder', 'width' ) - ) { - $border_width = $block_attributes['style']['border']['width']; + if ( ! $has_border_color_support || $skip_color ) { + unset( $block_attributes['style']['border']['color'], $block_attributes['borderColor'] ); + } + + // Sides are filtered by skipped serialization only, not by feature support. + foreach ( array( 'top', 'right', 'bottom', 'left' ) as $side ) { + $has_side = isset( $block_attributes['style']['border'][ $side ] ) && is_array( $block_attributes['style']['border'][ $side ] ); + + if ( ! $has_side || ( ! $has_border_color_support && ! $has_border_width_support ) ) { + unset( $block_attributes['style']['border'][ $side ] ); + continue; + } + + if ( $skip_width ) { + unset( $block_attributes['style']['border'][ $side ]['width'] ); + } - // This check handles original unitless implementation. - if ( is_numeric( $border_width ) ) { - $border_width .= 'px'; + if ( $skip_color ) { + unset( $block_attributes['style']['border'][ $side ]['color'] ); } - $border_block_styles['width'] = $border_width; - } - - // Border color. - if ( - $has_border_color_support && - ! wp_should_skip_block_supports_serialization( $block_type, '__experimentalBorder', 'color' ) - ) { - $preset_border_color = array_key_exists( 'borderColor', $block_attributes ) ? "var:preset|color|{$block_attributes['borderColor']}" : null; - $custom_border_color = $block_attributes['style']['border']['color'] ?? null; - $border_block_styles['color'] = $preset_border_color ? $preset_border_color : $custom_border_color; - } - - // Generates styles for individual border sides. - if ( $has_border_color_support || $has_border_width_support ) { - foreach ( array( 'top', 'right', 'bottom', 'left' ) as $side ) { - $border = $block_attributes['style']['border'][ $side ] ?? null; - $border_side_values = array( - 'width' => isset( $border['width'] ) && ! wp_should_skip_block_supports_serialization( $block_type, '__experimentalBorder', 'width' ) ? $border['width'] : null, - 'color' => isset( $border['color'] ) && ! wp_should_skip_block_supports_serialization( $block_type, '__experimentalBorder', 'color' ) ? $border['color'] : null, - 'style' => isset( $border['style'] ) && ! wp_should_skip_block_supports_serialization( $block_type, '__experimentalBorder', 'style' ) ? $border['style'] : null, - ); - $border_block_styles[ $side ] = $border_side_values; + if ( $skip_style ) { + unset( $block_attributes['style']['border'][ $side ]['style'] ); } } - // Collect classes and styles. + return wp_get_border_classes_and_styles( $block_attributes ); +} + +/** + * Returns border classes and inline styles for block attributes, like the JS + * `getBorderClassesAndStyles()`. Does not check block support or skipped + * serialization. + * + * @since 7.2.0 + * + * @param array $block_attributes Block attributes. + * @return array Array with `class` and `style` keys, each present only when non-empty. + */ +function wp_get_border_classes_and_styles( $block_attributes ) { + if ( ! is_array( $block_attributes ) ) { + return array(); + } + + $border = isset( $block_attributes['style']['border'] ) && is_array( $block_attributes['style']['border'] ) + ? $block_attributes['style']['border'] + : array(); + $border_styles = array(); + + // Unitless radius and width are from the original implementation. + if ( isset( $border['radius'] ) ) { + $border_styles['radius'] = is_numeric( $border['radius'] ) ? "{$border['radius']}px" : $border['radius']; + } + + if ( isset( $border['style'] ) ) { + $border_styles['style'] = $border['style']; + } + + if ( isset( $border['width'] ) ) { + $border_styles['width'] = is_numeric( $border['width'] ) ? "{$border['width']}px" : $border['width']; + } + + $preset_border_color = array_key_exists( 'borderColor', $block_attributes ) ? "var:preset|color|{$block_attributes['borderColor']}" : null; + $border_styles['color'] = $preset_border_color ? $preset_border_color : ( $border['color'] ?? null ); + + foreach ( array( 'top', 'right', 'bottom', 'left' ) as $side ) { + if ( ! isset( $border[ $side ] ) || ! is_array( $border[ $side ] ) ) { + continue; + } + $border_styles[ $side ] = array( + 'width' => $border[ $side ]['width'] ?? null, + 'color' => $border[ $side ]['color'] ?? null, + 'style' => $border[ $side ]['style'] ?? null, + ); + } + $attributes = array(); - $styles = wp_style_engine_get_styles( array( 'border' => $border_block_styles ) ); + $styles = wp_style_engine_get_styles( array( 'border' => $border_styles ) ); if ( ! empty( $styles['classnames'] ) ) { $attributes['class'] = $styles['classnames']; diff --git a/src/wp-includes/block-supports/colors.php b/src/wp-includes/block-supports/colors.php index 3c3396c587439..01b8b4c80b52b 100644 --- a/src/wp-includes/block-supports/colors.php +++ b/src/wp-includes/block-supports/colors.php @@ -98,54 +98,87 @@ function wp_apply_colors_support( WP_Block_Type $block_type, array $block_attrib ( isset( $color_support['background'] ) && $color_support['background'] ) || ( is_array( $color_support ) && ! isset( $color_support['background'] ) ); $has_gradients_support = $color_support['gradients'] ?? false; - $color_block_styles = array(); + $skip_text = wp_should_skip_block_supports_serialization( $block_type, 'color', 'text' ); + $skip_background = wp_should_skip_block_supports_serialization( $block_type, 'color', 'background' ); + $skip_gradients = wp_should_skip_block_supports_serialization( $block_type, 'color', 'gradients' ); - // Text colors. - if ( $has_text_colors_support && ! wp_should_skip_block_supports_serialization( $block_type, 'color', 'text' ) ) { - $preset_text_color = array_key_exists( 'textColor', $block_attributes ) ? "var:preset|color|{$block_attributes['textColor']}" : null; - $custom_text_color = $block_attributes['style']['color']['text'] ?? null; - $color_block_styles['text'] = $preset_text_color ? $preset_text_color : $custom_text_color; - } - - // Background colors. - if ( $has_background_colors_support && ! wp_should_skip_block_supports_serialization( $block_type, 'color', 'background' ) ) { - $preset_background_color = array_key_exists( 'backgroundColor', $block_attributes ) ? "var:preset|color|{$block_attributes['backgroundColor']}" : null; - $custom_background_color = $block_attributes['style']['color']['background'] ?? null; - $color_block_styles['background'] = $preset_background_color ? $preset_background_color : $custom_background_color; - } - - // Gradients. - // Suppress color.gradient CSS when background.gradient is supported and - // explicitly set. background.php owns CSS generation in that case, and - // emitting the background shorthand here would conflict with it. + // background.php owns the CSS when a background gradient is set, so skip color.gradient. $has_background_gradient_support = block_has_support( $block_type, array( 'background', 'gradient' ), false ); $has_background_gradient_value = ! empty( $block_attributes['style']['background']['gradient'] ); - - if ( - $has_gradients_support && - ! wp_should_skip_block_supports_serialization( $block_type, 'color', 'gradients' ) && - ! ( $has_background_gradient_support && $has_background_gradient_value ) - ) { - $preset_gradient_color = array_key_exists( 'gradient', $block_attributes ) ? "var:preset|gradient|{$block_attributes['gradient']}" : null; - $custom_gradient_color = $block_attributes['style']['color']['gradient'] ?? null; - $color_block_styles['gradient'] = $preset_gradient_color ? $preset_gradient_color : $custom_gradient_color; - } - - $attributes = array(); - $styles = wp_style_engine_get_styles( array( 'color' => $color_block_styles ), array( 'convert_vars_to_classnames' => true ) ); + $has_background_gradient = $has_background_gradient_support && $has_background_gradient_value; /* * Mirror of the background support: a color clipped to the text paints the * glyphs, not the box, so only a gradient still adds `has-background`. + * Read before the attributes are filtered for the helper. */ $clips_to_text = 'text' === ( $block_attributes['style']['background']['backgroundClip'] ?? null ) && block_has_support( $block_type, array( 'background', 'backgroundClip' ), false ) && ! wp_should_skip_block_supports_serialization( $block_type, 'background', 'backgroundClip' ); - if ( $clips_to_text && ! empty( $styles['classnames'] ) && empty( $color_block_styles['gradient'] ) ) { - $styles['classnames'] = implode( ' ', array_diff( explode( ' ', $styles['classnames'] ), array( 'has-background' ) ) ); + // The helper reads only `style.color`, so a non-array value counts as no color styles. + if ( ! isset( $block_attributes['style']['color'] ) || ! is_array( $block_attributes['style']['color'] ) ) { + unset( $block_attributes['style'] ); + } + + if ( ! $has_text_colors_support || $skip_text ) { + unset( $block_attributes['textColor'], $block_attributes['style']['color']['text'] ); + } + + if ( ! $has_background_colors_support || $skip_background ) { + unset( $block_attributes['backgroundColor'], $block_attributes['style']['color']['background'] ); + } + + if ( ! $has_gradients_support || $skip_gradients || $has_background_gradient ) { + unset( $block_attributes['gradient'], $block_attributes['style']['color']['gradient'] ); } + $classes_and_styles = wp_get_color_classes_and_styles( $block_attributes ); + $has_gradient = ! empty( $block_attributes['gradient'] ) || ! empty( $block_attributes['style']['color']['gradient'] ); + + if ( $clips_to_text && ! $has_gradient && ! empty( $classes_and_styles['class'] ) ) { + $classes = array_diff( explode( ' ', $classes_and_styles['class'] ), array( 'has-background' ) ); + if ( empty( $classes ) ) { + unset( $classes_and_styles['class'] ); + } else { + $classes_and_styles['class'] = implode( ' ', $classes ); + } + } + + return $classes_and_styles; +} + +/** + * Returns color classes and inline styles for block attributes, like the JS + * `getColorClassesAndStyles()`. Does not check block support or skipped + * serialization. + * + * @since 7.2.0 + * + * @param array $block_attributes Block attributes. + * @return array Array with `class` and `style` keys, each present only when non-empty. + */ +function wp_get_color_classes_and_styles( $block_attributes ) { + if ( ! is_array( $block_attributes ) ) { + return array(); + } + + $color_block_styles = array(); + $preset_attributes = array( + 'text' => array( 'textColor', 'color' ), + 'background' => array( 'backgroundColor', 'color' ), + 'gradient' => array( 'gradient', 'gradient' ), + ); + + foreach ( $preset_attributes as $feature => list( $preset_attribute, $preset_type ) ) { + $preset_value = array_key_exists( $preset_attribute, $block_attributes ) ? "var:preset|{$preset_type}|{$block_attributes[ $preset_attribute ]}" : null; + $custom_value = $block_attributes['style']['color'][ $feature ] ?? null; + $color_block_styles[ $feature ] = $preset_value ?? $custom_value; + } + + $attributes = array(); + $styles = wp_style_engine_get_styles( array( 'color' => $color_block_styles ), array( 'convert_vars_to_classnames' => true ) ); + if ( ! empty( $styles['classnames'] ) ) { $attributes['class'] = $styles['classnames']; } diff --git a/src/wp-includes/block-supports/shadow.php b/src/wp-includes/block-supports/shadow.php index 4c09ce9d08f0f..9469b68f55307 100644 --- a/src/wp-includes/block-supports/shadow.php +++ b/src/wp-includes/block-supports/shadow.php @@ -60,13 +60,28 @@ function wp_apply_shadow_support( $block_type, $block_attributes ) { return array(); } - $shadow_block_styles = array(); + return wp_get_shadow_classes_and_styles( $block_attributes ); +} - $custom_shadow = $block_attributes['style']['shadow'] ?? null; - $shadow_block_styles['shadow'] = $custom_shadow; +/** + * Returns shadow classes and inline styles for block attributes, like the JS + * `getShadowClassesAndStyles()`. Does not check block support or skipped + * serialization. + * + * @since 7.2.0 + * + * @param array $block_attributes Block attributes. + * @return array Array with `class` and `style` keys, each present only when non-empty. + */ +function wp_get_shadow_classes_and_styles( $block_attributes ) { + $shadow = $block_attributes['style']['shadow'] ?? null; $attributes = array(); - $styles = wp_style_engine_get_styles( $shadow_block_styles ); + $styles = wp_style_engine_get_styles( array( 'shadow' => $shadow ) ); + + if ( ! empty( $styles['classnames'] ) ) { + $attributes['class'] = $styles['classnames']; + } if ( ! empty( $styles['css'] ) ) { $attributes['style'] = $styles['css']; diff --git a/src/wp-includes/block-supports/spacing.php b/src/wp-includes/block-supports/spacing.php index a778eebeb475a..e7064007360bc 100644 --- a/src/wp-includes/block-supports/spacing.php +++ b/src/wp-includes/block-supports/spacing.php @@ -49,28 +49,55 @@ function wp_apply_spacing_support( $block_type, $block_attributes ) { return array(); } - $attributes = array(); + if ( ! isset( $block_attributes['style']['spacing'] ) || ! is_array( $block_attributes['style']['spacing'] ) ) { + return array(); + } + $has_padding_support = block_has_support( $block_type, array( 'spacing', 'padding' ), false ); $has_margin_support = block_has_support( $block_type, array( 'spacing', 'margin' ), false ); - $block_styles = $block_attributes['style'] ?? null; + $skip_padding = wp_should_skip_block_supports_serialization( $block_type, 'spacing', 'padding' ); + $skip_margin = wp_should_skip_block_supports_serialization( $block_type, 'spacing', 'margin' ); - if ( ! $block_styles ) { - return $attributes; + if ( ! $has_padding_support || $skip_padding ) { + unset( $block_attributes['style']['spacing']['padding'] ); } - $skip_padding = wp_should_skip_block_supports_serialization( $block_type, 'spacing', 'padding' ); - $skip_margin = wp_should_skip_block_supports_serialization( $block_type, 'spacing', 'margin' ); - $spacing_block_styles = array( - 'padding' => null, - 'margin' => null, - ); - if ( $has_padding_support && ! $skip_padding ) { - $spacing_block_styles['padding'] = $block_styles['spacing']['padding'] ?? null; + if ( ! $has_margin_support || $skip_margin ) { + unset( $block_attributes['style']['spacing']['margin'] ); + } + + return wp_get_spacing_classes_and_styles( $block_attributes ); +} + +/** + * Returns spacing classes and inline styles for block attributes, like the JS + * `getSpacingClassesAndStyles()`. Does not check block support or skipped + * serialization. + * + * @since 7.2.0 + * + * @param array $block_attributes Block attributes. + * @return array Array with `class` and `style` keys, each present only when non-empty. + */ +function wp_get_spacing_classes_and_styles( $block_attributes ) { + $spacing = $block_attributes['style']['spacing'] ?? null; + if ( ! is_array( $spacing ) ) { + return array(); } - if ( $has_margin_support && ! $skip_margin ) { - $spacing_block_styles['margin'] = $block_styles['spacing']['margin'] ?? null; + + $styles = wp_style_engine_get_styles( + array( + 'spacing' => array( + 'padding' => $spacing['padding'] ?? null, + 'margin' => $spacing['margin'] ?? null, + ), + ) + ); + $attributes = array(); + + if ( ! empty( $styles['classnames'] ) ) { + $attributes['class'] = $styles['classnames']; } - $styles = wp_style_engine_get_styles( array( 'spacing' => $spacing_block_styles ) ); if ( ! empty( $styles['css'] ) ) { $attributes['style'] = $styles['css']; diff --git a/tests/phpunit/tests/block-supports/border.php b/tests/phpunit/tests/block-supports/border.php index cd1c1def70e26..2bab64e9741c6 100644 --- a/tests/phpunit/tests/block-supports/border.php +++ b/tests/phpunit/tests/block-supports/border.php @@ -21,6 +21,35 @@ public function tear_down() { parent::tear_down(); } + /** + * Registers a new block for testing border support. + * + * @param string $block_name Name for the test block. + * @param array $supports Array defining block support configuration. + * @return WP_Block_Type The block type for the newly registered test block. + */ + private function register_bordered_block_with_support( $block_name, $supports = array() ) { + $this->test_block_name = $block_name; + register_block_type( + $this->test_block_name, + array( + 'api_version' => 3, + 'attributes' => array( + 'borderColor' => array( + 'type' => 'string', + ), + 'style' => array( + 'type' => 'object', + ), + ), + 'supports' => $supports, + ) + ); + $registry = WP_Block_Type_Registry::get_instance(); + + return $registry->get_registered( $this->test_block_name ); + } + /** * @ticket 55505 */ @@ -159,4 +188,152 @@ public function test_radius_with_individual_skipped_serialization_block_supports $this->assertSame( $expected, $actual ); } + + /** + * @ticket 66269 + */ + public function test_border_color_preset_with_skipped_color_serialization() { + $block_type = $this->register_bordered_block_with_support( + 'test/border-color-preset-with-skipped-color-serialization', + array( + '__experimentalBorder' => array( + 'color' => true, + 'width' => true, + '__experimentalSkipSerialization' => array( 'color' ), + ), + ) + ); + $block_attrs = array( + 'borderColor' => 'red', + 'style' => array( 'border' => array( 'width' => '1px' ) ), + ); + + $this->assertSame( + array( 'style' => 'border-width:1px;' ), + wp_apply_border_support( $block_type, $block_attrs ) + ); + } + + /** + * @ticket 66269 + */ + public function test_split_borders_with_color_support_only_keeps_side_style() { + $block_type = $this->register_bordered_block_with_support( + 'test/split-borders-with-color-support-only', + array( + '__experimentalBorder' => array( + 'color' => true, + ), + ) + ); + $block_attrs = array( + 'style' => array( + 'border' => array( + 'style' => 'solid', + 'top' => array( + 'color' => '#72aee6', + 'style' => 'dashed', + ), + ), + ), + ); + $actual = wp_apply_border_support( $block_type, $block_attrs ); + $expected = array( + 'style' => 'border-top-color:#72aee6;border-top-style:dashed;', + ); + + $this->assertSame( $expected, $actual ); + } + + /** + * @ticket 66269 + */ + public function test_split_borders_with_width_support_only_keeps_side_color() { + $block_type = $this->register_bordered_block_with_support( + 'test/split-borders-with-width-support-only', + array( + '__experimentalBorder' => array( + 'width' => true, + ), + ) + ); + $block_attrs = array( + 'borderColor' => 'red', + 'style' => array( + 'border' => array( + 'top' => array( + 'color' => '#72aee6', + 'width' => '2px', + ), + ), + ), + ); + $actual = wp_apply_border_support( $block_type, $block_attrs ); + $expected = array( + 'style' => 'border-top-width:2px;border-top-color:#72aee6;', + ); + + $this->assertSame( $expected, $actual ); + } + + /** + * @ticket 66269 + */ + public function test_split_borders_without_color_or_width_support_are_dropped() { + $block_type = $this->register_bordered_block_with_support( + 'test/split-borders-without-color-or-width-support', + array( + '__experimentalBorder' => array( + 'radius' => true, + 'style' => true, + ), + ) + ); + $block_attrs = array( + 'style' => array( + 'border' => array( + 'radius' => '5px', + 'top' => array( + 'style' => 'dashed', + 'width' => '2px', + ), + ), + ), + ); + $actual = wp_apply_border_support( $block_type, $block_attrs ); + $expected = array( + 'style' => 'border-radius:5px;', + ); + + $this->assertSame( $expected, $actual ); + } + + /** + * @ticket 66269 + */ + public function test_non_array_border_and_sides_are_ignored() { + $block_type = $this->register_bordered_block_with_support( + 'test/non-array-border-and-sides', + array( + '__experimentalBorder' => true, + ) + ); + + $this->assertSame( array(), wp_apply_border_support( $block_type, array( 'style' => 'invalid' ) ) ); + $this->assertSame( array(), wp_apply_border_support( $block_type, array( 'style' => array( 'border' => 'invalid' ) ) ) ); + $this->assertSame( + array( 'style' => 'border-bottom-width:1px;' ), + wp_apply_border_support( + $block_type, + array( + 'style' => array( + 'border' => array( + 'top' => 'invalid', + 'bottom' => array( 'width' => '1px' ), + ), + ), + ) + ) + ); + } } diff --git a/tests/phpunit/tests/block-supports/colors.php b/tests/phpunit/tests/block-supports/colors.php index 9ddb2a475e832..f26acd479b373 100644 --- a/tests/phpunit/tests/block-supports/colors.php +++ b/tests/phpunit/tests/block-supports/colors.php @@ -407,4 +407,78 @@ public function data_background_color_has_background_with_a_clip() { ), ); } + + /** + * @ticket 66269 + */ + public function test_color_support_true_applies_text_and_background_colors() { + $this->test_block_name = 'test/color-support-true'; + register_block_type( + $this->test_block_name, + array( + 'api_version' => 3, + 'attributes' => array( + 'textColor' => array( + 'type' => 'string', + ), + 'backgroundColor' => array( + 'type' => 'string', + ), + ), + 'supports' => array( + 'color' => true, + ), + ) + ); + + $registry = WP_Block_Type_Registry::get_instance(); + $block_type = $registry->get_registered( $this->test_block_name ); + + $block_atts = array( + 'textColor' => 'contrast', + 'backgroundColor' => 'base', + ); + + $actual = wp_apply_colors_support( $block_type, $block_atts ); + $expected = array( + 'class' => 'has-text-color has-contrast-color has-background has-base-background-color', + ); + + $this->assertSame( $expected, $actual ); + } + + /** + * @ticket 66269 + */ + public function test_color_text_support_disabled_skips_text_color() { + $this->test_block_name = 'test/color-text-support-disabled'; + register_block_type( + $this->test_block_name, + array( + 'api_version' => 3, + 'attributes' => array( + 'textColor' => array( + 'type' => 'string', + ), + ), + 'supports' => array( + 'color' => array( + 'text' => false, + ), + ), + ) + ); + + $registry = WP_Block_Type_Registry::get_instance(); + $block_type = $registry->get_registered( $this->test_block_name ); + + $block_atts = array( + 'textColor' => 'contrast', + ); + + $actual = wp_apply_colors_support( $block_type, $block_atts ); + $expected = array(); + + $this->assertSame( $expected, $actual ); + } } diff --git a/tests/phpunit/tests/block-supports/spacing.php b/tests/phpunit/tests/block-supports/spacing.php index 2cfcd76d99af8..ac50818491655 100644 --- a/tests/phpunit/tests/block-supports/spacing.php +++ b/tests/phpunit/tests/block-supports/spacing.php @@ -164,4 +164,67 @@ public function test_margin_with_individual_skipped_serialization_block_supports $this->assertSame( $expected, $actual ); } + + /** + * @ticket 66269 + */ + public function test_unsupported_padding_is_not_applied() { + $this->test_block_name = 'test/unsupported-padding-is-not-applied'; + register_block_type( + $this->test_block_name, + array( + 'api_version' => 3, + 'attributes' => array( + 'style' => array( + 'type' => 'object', + ), + ), + 'supports' => array( + 'spacing' => array( + 'margin' => true, + ), + ), + ) + ); + $registry = WP_Block_Type_Registry::get_instance(); + $block_type = $registry->get_registered( $this->test_block_name ); + $block_atts = array( + 'style' => array( + 'spacing' => array( + 'margin' => '1px', + 'padding' => '2px', + ), + ), + ); + + $actual = wp_apply_spacing_support( $block_type, $block_atts ); + $expected = array( + 'style' => 'margin:1px;', + ); + + $this->assertSame( $expected, $actual ); + } + + /** + * @ticket 66269 + */ + public function test_spacing_without_style_attribute() { + $this->test_block_name = 'test/spacing-without-style-attribute'; + register_block_type( + $this->test_block_name, + array( + 'api_version' => 3, + 'supports' => array( + 'spacing' => array( + 'margin' => true, + 'padding' => true, + ), + ), + ) + ); + $registry = WP_Block_Type_Registry::get_instance(); + $block_type = $registry->get_registered( $this->test_block_name ); + + $this->assertSame( array(), wp_apply_spacing_support( $block_type, array() ) ); + } } diff --git a/tests/phpunit/tests/block-supports/wpGetBackgroundClassesAndStyles.php b/tests/phpunit/tests/block-supports/wpGetBackgroundClassesAndStyles.php new file mode 100644 index 0000000000000..45ee6e62df673 --- /dev/null +++ b/tests/phpunit/tests/block-supports/wpGetBackgroundClassesAndStyles.php @@ -0,0 +1,172 @@ +assertSame( $expected, wp_get_background_classes_and_styles( $block_attributes ) ); + } + + /** + * Data provider. + * + * @return array[] + */ + public function data_get_background_classes_and_styles() { + return array( + 'custom image gets default size' => array( + 'block_attributes' => array( + 'style' => array( + 'background' => array( + 'backgroundImage' => array( 'url' => 'https://example.com/image.jpg' ), + ), + ), + ), + 'expected' => array( + 'class' => 'has-background', + 'style' => "background-image:url('https://example.com/image.jpg');background-size:cover;", + ), + ), + 'contain without position is centered' => array( + 'block_attributes' => array( + 'style' => array( + 'background' => array( + 'backgroundImage' => array( 'url' => 'https://example.com/image.jpg' ), + 'backgroundSize' => 'contain', + 'backgroundRepeat' => 'no-repeat', + 'backgroundAttachment' => 'fixed', + ), + ), + ), + 'expected' => array( + 'class' => 'has-background', + 'style' => "background-image:url('https://example.com/image.jpg');background-position:50% 50%;background-repeat:no-repeat;background-size:contain;background-attachment:fixed;", + ), + ), + 'preset gradient' => array( + 'block_attributes' => array( + 'style' => array( + 'background' => array( + 'gradient' => 'var:preset|gradient|vivid-cyan-blue-to-vivid-purple', + ), + ), + ), + 'expected' => array( + 'class' => 'has-background', + 'style' => 'background-image:var(--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple);', + ), + ), + 'gradient clipped to text has no class' => array( + 'block_attributes' => array( + 'style' => array( + 'background' => array( + 'gradient' => 'linear-gradient(red, blue)', + 'backgroundClip' => 'text', + ), + ), + ), + 'expected' => array( + 'style' => 'background-image:linear-gradient(red, blue);background-clip:text;-webkit-background-clip:text;-webkit-text-fill-color:transparent;', + ), + ), + 'clip alone has no class' => array( + 'block_attributes' => array( + 'style' => array( + 'background' => array( + 'backgroundClip' => 'padding-box', + ), + ), + ), + 'expected' => array( + 'style' => 'background-clip:padding-box;-webkit-text-fill-color:currentColor;', + ), + ), + 'image clipped to text has no class' => array( + 'block_attributes' => array( + 'style' => array( + 'background' => array( + 'backgroundImage' => array( 'url' => 'https://example.com/image.jpg' ), + 'backgroundClip' => 'text', + ), + ), + ), + 'expected' => array( + 'style' => "background-image:url('https://example.com/image.jpg');background-size:cover;background-clip:text;-webkit-background-clip:text;-webkit-text-fill-color:transparent;", + ), + ), + 'image without url gets default size' => array( + 'block_attributes' => array( + 'style' => array( + 'background' => array( + 'backgroundImage' => array( 'id' => 1 ), + ), + ), + ), + 'expected' => array( + 'class' => 'has-background', + 'style' => 'background-size:cover;', + ), + ), + 'numeric position is not output' => array( + 'block_attributes' => array( + 'style' => array( + 'background' => array( + 'backgroundImage' => array( 'url' => 'https://example.com/image.jpg' ), + 'backgroundPosition' => 50, + ), + ), + ), + 'expected' => array( + 'class' => 'has-background', + 'style' => "background-image:url('https://example.com/image.jpg');background-size:cover;", + ), + ), + 'zero gradient outputs nothing' => array( + 'block_attributes' => array( + 'style' => array( + 'background' => array( + 'gradient' => 0, + ), + ), + ), + 'expected' => array(), + ), + 'empty background' => array( + 'block_attributes' => array( + 'style' => array( + 'background' => array(), + ), + ), + 'expected' => array(), + ), + 'no style attribute' => array( + 'block_attributes' => array(), + 'expected' => array(), + ), + 'background is not an array' => array( + 'block_attributes' => array( + 'style' => array( + 'background' => 'linear-gradient(red, blue)', + ), + ), + 'expected' => array(), + ), + 'attributes are not an array' => array( + 'block_attributes' => 'style', + 'expected' => array(), + ), + ); + } +} diff --git a/tests/phpunit/tests/block-supports/wpGetBorderClassesAndStyles.php b/tests/phpunit/tests/block-supports/wpGetBorderClassesAndStyles.php new file mode 100644 index 0000000000000..1707f2d71d27e --- /dev/null +++ b/tests/phpunit/tests/block-supports/wpGetBorderClassesAndStyles.php @@ -0,0 +1,131 @@ +assertSame( $expected, wp_get_border_classes_and_styles( $block_attributes ) ); + } + + /** + * Data provider. + * + * @return array[] + */ + public function data_get_border_classes_and_styles() { + return array( + 'custom' => array( + 'block_attributes' => array( + 'style' => array( + 'border' => array( + 'radius' => '10px', + 'style' => 'solid', + 'width' => '2px', + 'color' => '#ff0000', + 'top' => array( + 'style' => 'dashed', + 'color' => '#00ff00', + 'width' => '3px', + ), + ), + ), + ), + 'expected' => array( + 'class' => 'has-border-color', + 'style' => 'border-color:#ff0000;border-radius:10px;border-style:solid;border-width:2px;border-top-width:3px;border-top-color:#00ff00;border-top-style:dashed;', + ), + ), + 'preset wins over custom' => array( + 'block_attributes' => array( + 'borderColor' => 'red', + 'style' => array( + 'border' => array( + 'color' => '#ff0000', + ), + ), + ), + 'expected' => array( + 'class' => 'has-border-color has-red-border-color', + ), + ), + 'preset only' => array( + 'block_attributes' => array( + 'borderColor' => 'red', + ), + 'expected' => array( + 'class' => 'has-border-color has-red-border-color', + ), + ), + 'numeric' => array( + 'block_attributes' => array( + 'style' => array( + 'border' => array( + 'radius' => 5, + 'width' => 2, + ), + ), + ), + 'expected' => array( + 'style' => 'border-radius:5px;border-width:2px;', + ), + ), + 'zero' => array( + 'block_attributes' => array( + 'style' => array( + 'border' => array( + 'radius' => 0, + 'width' => 0, + ), + ), + ), + 'expected' => array( + 'style' => 'border-radius:0px;border-width:0px;', + ), + ), + 'empty' => array( + 'block_attributes' => array(), + 'expected' => array(), + ), + 'malformed border' => array( + 'block_attributes' => array( + 'style' => array( + 'border' => 'solid', + ), + ), + 'expected' => array(), + ), + 'malformed side' => array( + 'block_attributes' => array( + 'style' => array( + 'border' => array( + 'top' => '1px solid', + ), + ), + ), + 'expected' => array(), + ), + 'malformed style' => array( + 'block_attributes' => array( + 'style' => 'border:1px solid', + ), + 'expected' => array(), + ), + 'malformed non-array' => array( + 'block_attributes' => 'border', + 'expected' => array(), + ), + ); + } +} diff --git a/tests/phpunit/tests/block-supports/wpGetColorClassesAndStyles.php b/tests/phpunit/tests/block-supports/wpGetColorClassesAndStyles.php new file mode 100644 index 0000000000000..df998dac26409 --- /dev/null +++ b/tests/phpunit/tests/block-supports/wpGetColorClassesAndStyles.php @@ -0,0 +1,118 @@ +assertSame( $expected, wp_get_color_classes_and_styles( $block_attributes ) ); + } + + /** + * Data provider. + * + * @return array[] + */ + public function data_get_color_classes_and_styles() { + return array( + 'custom' => array( + 'block_attributes' => array( + 'style' => array( + 'color' => array( + 'text' => '#d92828', + 'background' => '#ffffff', + 'gradient' => 'linear-gradient(135deg,rgb(6,147,227) 0%,rgb(155,81,224) 100%)', + ), + ), + ), + 'expected' => array( + 'class' => 'has-text-color has-background', + 'style' => 'color:#d92828;background-color:#ffffff;background:linear-gradient(135deg,rgb(6,147,227) 0%,rgb(155,81,224) 100%);', + ), + ), + 'preset' => array( + 'block_attributes' => array( + 'textColor' => 'contrast', + 'backgroundColor' => 'base', + 'gradient' => 'vivid-cyan-blue-to-vivid-purple', + ), + 'expected' => array( + 'class' => 'has-text-color has-contrast-color has-background has-base-background-color has-vivid-cyan-blue-to-vivid-purple-gradient-background', + ), + ), + 'preset wins over custom' => array( + 'block_attributes' => array( + 'textColor' => 'contrast', + 'style' => array( + 'color' => array( + 'text' => '#d92828', + ), + ), + ), + 'expected' => array( + 'class' => 'has-text-color has-contrast-color', + ), + ), + 'null preset wins over custom' => array( + 'block_attributes' => array( + 'textColor' => null, + 'style' => array( + 'color' => array( + 'text' => '#d92828', + ), + ), + ), + 'expected' => array( + 'class' => 'has-text-color', + ), + ), + 'numeric' => array( + 'block_attributes' => array( + 'textColor' => 1, + ), + 'expected' => array( + 'class' => 'has-text-color has-1-color', + ), + ), + 'zero' => array( + 'block_attributes' => array( + 'style' => array( + 'color' => array( + 'text' => '0', + ), + ), + ), + 'expected' => array( + 'style' => 'color:0;', + ), + ), + 'empty' => array( + 'block_attributes' => array(), + 'expected' => array(), + ), + 'malformed color' => array( + 'block_attributes' => array( + 'style' => array( + 'color' => '#d92828', + ), + ), + 'expected' => array(), + ), + 'malformed attributes' => array( + 'block_attributes' => 'color', + 'expected' => array(), + ), + ); + } +} diff --git a/tests/phpunit/tests/block-supports/wpGetShadowClassesAndStyles.php b/tests/phpunit/tests/block-supports/wpGetShadowClassesAndStyles.php new file mode 100644 index 0000000000000..9034c53b71570 --- /dev/null +++ b/tests/phpunit/tests/block-supports/wpGetShadowClassesAndStyles.php @@ -0,0 +1,67 @@ +assertSame( $expected, wp_get_shadow_classes_and_styles( $block_attributes ) ); + } + + /** + * Data provider. + * + * @return array[] + */ + public function data_get_shadow_classes_and_styles() { + return array( + 'custom shadow' => array( + 'block_attributes' => array( 'style' => array( 'shadow' => '1px 1px 1px #000' ) ), + 'expected' => array( 'style' => 'box-shadow:1px 1px 1px #000;' ), + ), + 'preset shadow' => array( + 'block_attributes' => array( 'style' => array( 'shadow' => 'var:preset|shadow|natural' ) ), + 'expected' => array( 'style' => 'box-shadow:var(--wp--preset--shadow--natural);' ), + ), + 'numeric shadow' => array( + 'block_attributes' => array( 'style' => array( 'shadow' => 5 ) ), + 'expected' => array(), + ), + 'zero shadow' => array( + 'block_attributes' => array( 'style' => array( 'shadow' => 0 ) ), + 'expected' => array(), + ), + 'empty shadow' => array( + 'block_attributes' => array( 'style' => array( 'shadow' => '' ) ), + 'expected' => array(), + ), + 'no style attribute' => array( + 'block_attributes' => array(), + 'expected' => array(), + ), + 'non-array style' => array( + 'block_attributes' => array( 'style' => 'box-shadow:1px 1px 1px #000' ), + 'expected' => array(), + ), + 'array shadow' => array( + 'block_attributes' => array( 'style' => array( 'shadow' => array( '1px 1px 1px #000' ) ) ), + 'expected' => array(), + ), + 'non-array attributes' => array( + 'block_attributes' => null, + 'expected' => array(), + ), + ); + } +} diff --git a/tests/phpunit/tests/block-supports/wpGetSpacingClassesAndStyles.php b/tests/phpunit/tests/block-supports/wpGetSpacingClassesAndStyles.php new file mode 100644 index 0000000000000..cc10e9b24e908 --- /dev/null +++ b/tests/phpunit/tests/block-supports/wpGetSpacingClassesAndStyles.php @@ -0,0 +1,112 @@ +assertSame( $expected, wp_get_spacing_classes_and_styles( $block_attributes ) ); + } + + /** + * Data provider. + * + * @return array[] + */ + public function data_get_spacing_classes_and_styles() { + return array( + 'custom values' => array( + 'block_attributes' => array( + 'style' => array( + 'spacing' => array( + 'padding' => array( + 'top' => '1px', + 'left' => '2em', + ), + 'margin' => '3px', + ), + ), + ), + 'expected' => array( + 'style' => 'padding-top:1px;padding-left:2em;margin:3px;', + ), + ), + 'presets' => array( + 'block_attributes' => array( + 'style' => array( + 'spacing' => array( + 'padding' => 'var:preset|spacing|40', + 'margin' => array( 'bottom' => 'var:preset|spacing|20' ), + ), + ), + ), + 'expected' => array( + 'style' => 'padding:var(--wp--preset--spacing--40);margin-bottom:var(--wp--preset--spacing--20);', + ), + ), + 'block gap is ignored' => array( + 'block_attributes' => array( + 'style' => array( + 'spacing' => array( 'blockGap' => '2em' ), + ), + ), + 'expected' => array(), + ), + 'numeric values' => array( + 'block_attributes' => array( + 'style' => array( + 'spacing' => array( 'padding' => array( 'top' => 10 ) ), + ), + ), + 'expected' => array(), + ), + 'zero' => array( + 'block_attributes' => array( + 'style' => array( + 'spacing' => array( 'margin' => '0' ), + ), + ), + 'expected' => array( + 'style' => 'margin:0;', + ), + ), + 'empty spacing' => array( + 'block_attributes' => array( + 'style' => array( + 'spacing' => array(), + ), + ), + 'expected' => array(), + ), + 'no style' => array( + 'block_attributes' => array(), + 'expected' => array(), + ), + 'malformed spacing' => array( + 'block_attributes' => array( + 'style' => array( + 'spacing' => '10px', + ), + ), + 'expected' => array(), + ), + 'malformed style' => array( + 'block_attributes' => array( + 'style' => 'padding:10px', + ), + 'expected' => array(), + ), + ); + } +} diff --git a/tests/phpunit/tests/block-supports/wpRenderBackgroundSupport.php b/tests/phpunit/tests/block-supports/wpRenderBackgroundSupport.php index 894022e6d7bf2..bb9d9e474e5ab 100644 --- a/tests/phpunit/tests/block-supports/wpRenderBackgroundSupport.php +++ b/tests/phpunit/tests/block-supports/wpRenderBackgroundSupport.php @@ -71,6 +71,7 @@ public function filter_set_theme_root() { * @ticket 61858 * @ticket 64974 * @ticket 66094 + * @ticket 66269 * * @covers ::wp_render_background_support * @@ -269,6 +270,23 @@ public static function data_background_block_support() { 'expected_wrapper' => '