From e18b9ba1787a37d9d2e86fad346eb073aab94927 Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Wed, 7 Sep 2022 17:22:21 +1000 Subject: [PATCH 01/16] Backport foundation for Layout block support refactor --- src/wp-includes/block-editor.php | 27 + .../class-wp-theme-json-resolver.php | 50 ++ src/wp-includes/class-wp-theme-json.php | 566 +++++++++++++++--- .../global-styles-and-settings.php | 2 +- src/wp-includes/theme.json | 189 +++++- 5 files changed, 744 insertions(+), 90 deletions(-) diff --git a/src/wp-includes/block-editor.php b/src/wp-includes/block-editor.php index 11df4c9a100c6..85fb129ebcc7d 100644 --- a/src/wp-includes/block-editor.php +++ b/src/wp-includes/block-editor.php @@ -418,6 +418,18 @@ function get_block_editor_settings( array $custom_settings, $block_editor_contex $block_classes['css'] = $actual_css; $global_styles[] = $block_classes; } + } else { + // If there is no `theme.json` file, ensure base layout styles are still available. + $block_classes = array( + 'css' => 'base-layout-styles', + '__unstableType' => 'base-layout', + 'isGlobalStyles' => true, + ); + $actual_css = wp_get_global_stylesheet( array( $block_classes['css'] ) ); + if ( '' !== $actual_css ) { + $block_classes['css'] = $actual_css; + $global_styles[] = $block_classes; + } } $editor_settings['styles'] = array_merge( $global_styles, get_block_editor_theme_styles() ); @@ -475,9 +487,24 @@ function get_block_editor_settings( array $custom_settings, $block_editor_contex $editor_settings['enableCustomSpacing'] = $editor_settings['__experimentalFeatures']['spacing']['padding']; unset( $editor_settings['__experimentalFeatures']['spacing']['padding'] ); } + if ( isset( $editor_settings['__experimentalFeatures']['spacing']['customSpacingSize'] ) ) { + $editor_settings['disableCustomSpacingSizes'] = ! $editor_ettings['__experimentalFeatures']['spacing']['customSpacingSize']; + unset( $editor_settings['__experimentalFeatures']['spacing']['customSpacingSize'] ); + } + + if ( isset( $editor_settings['__experimentalFeatures']['spacing']['spacingSizes'] ) ) { + $spacing_sizes_by_origin = $editor_settings['__experimentalFeatures']['spacing']['spacingSizes']; + $editor_settings['spacingSizes'] = isset( $spacing_sizes_by_origin['custom'] ) ? + $spacing_sizes_by_origin['custom'] : ( + isset( $spacing_sizes_by_origin['theme'] ) ? + $spacing_sizes_by_origin['theme'] : + $spacing_sizes_by_origin['default'] + ); + } $editor_settings['__unstableResolvedAssets'] = _wp_get_iframed_editor_assets(); $editor_settings['localAutosaveInterval'] = 15; + $editor_settings['disableLayoutStyles'] = current_theme_supports( 'disable-layout-styles' ); $editor_settings['__experimentalDiscussionSettings'] = array( 'commentOrder' => get_option( 'comment_order' ), 'commentsPerPage' => get_option( 'comments_per_page' ), diff --git a/src/wp-includes/class-wp-theme-json-resolver.php b/src/wp-includes/class-wp-theme-json-resolver.php index c9bb42cac5b2f..5bc181594f509 100644 --- a/src/wp-includes/class-wp-theme-json-resolver.php +++ b/src/wp-includes/class-wp-theme-json-resolver.php @@ -231,6 +231,54 @@ public static function get_theme_data( $deprecated = array(), $options = array() return $with_theme_supports; } + /** + * Gets the styles for blocks from the block.json file. + * + * @since 6.1.0 + * + * @return WP_Theme_JSON + */ + public static function get_block_data() { + $registry = WP_Block_Type_Registry::get_instance(); + $blocks = $registry->get_all_registered(); + $config = array( 'version' => 1 ); + foreach ( $blocks as $block_name => $block_type ) { + if ( isset( $block_type->supports['__experimentalStyle'] ) ) { + $config['styles']['blocks'][ $block_name ] = static::remove_JSON_comments( $block_type->supports['__experimentalStyle'] ); + } + + if ( + isset( $block_type->supports['spacing']['blockGap']['__experimentalDefault'] ) && + null === _wp_array_get( $config, array( 'styles', 'blocks', $block_name, 'spacing', 'blockGap' ), null ) + ) { + // Ensure an empty placeholder value exists for the block, if it provides a default blockGap value. + // The real blockGap value to be used will be determined when the styles are rendered for output. + $config['styles']['blocks'][ $block_name ]['spacing']['blockGap'] = null; + } + } + + // Core here means it's the lower level part of the styles chain. + // It can be a core or a third-party block. + return new WP_Theme_JSON( $config, 'core' ); + } + + /** + * When given an array, this will remove any keys with the name `//`. + * + * @param array $array The array to filter. + * @return array The filtered array. + */ + private static function remove_JSON_comments( $array ) { + unset( $array['//'] ); + foreach ( $array as $k => $v ) { + if ( is_array( $v ) ) { + $array[ $k ] = static::remove_JSON_comments( $v ); + } + } + + return $array; + } + /** * Returns the custom post type that contains the user's origin config * for the active theme or a void array if none are found. @@ -369,6 +417,7 @@ public static function get_user_data() { * @since 5.8.0 * @since 5.9.0 Added user data, removed the `$settings` parameter, * added the `$origin` parameter. + * @since 6.1.0 Added block data. * * @param string $origin Optional. To what level should we merge data. * Valid values are 'theme' or 'custom'. Default 'custom'. @@ -381,6 +430,7 @@ public static function get_merged_data( $origin = 'custom' ) { $result = new WP_Theme_JSON(); $result->merge( static::get_core_data() ); + $result->merge( static::get_block_data() ); $result->merge( static::get_theme_data() ); if ( 'custom' === $origin ) { diff --git a/src/wp-includes/class-wp-theme-json.php b/src/wp-includes/class-wp-theme-json.php index 69a349f970cc7..766743f547c3f 100644 --- a/src/wp-includes/class-wp-theme-json.php +++ b/src/wp-includes/class-wp-theme-json.php @@ -176,40 +176,60 @@ class WP_Theme_JSON { * `letter-spacing`, `margin-*`, `padding-*`, `--wp--style--block-gap`, * `text-decoration`, `text-transform`, and `filter` properties, * simplified the metadata structure. + * @since 6.1.0 Added the `border-*-color`, `border-*-width`, `border-*-style`, + * `--wp--style--root--padding-*`, and `box-shadow` properties, + * removed the `--wp--style--block-gap` property. * @var array */ const PROPERTIES_METADATA = array( - 'background' => array( 'color', 'gradient' ), - 'background-color' => array( 'color', 'background' ), - 'border-radius' => array( 'border', 'radius' ), - 'border-top-left-radius' => array( 'border', 'radius', 'topLeft' ), - 'border-top-right-radius' => array( 'border', 'radius', 'topRight' ), - 'border-bottom-left-radius' => array( 'border', 'radius', 'bottomLeft' ), - 'border-bottom-right-radius' => array( 'border', 'radius', 'bottomRight' ), - 'border-color' => array( 'border', 'color' ), - 'border-width' => array( 'border', 'width' ), - 'border-style' => array( 'border', 'style' ), - 'color' => array( 'color', 'text' ), - 'font-family' => array( 'typography', 'fontFamily' ), - 'font-size' => array( 'typography', 'fontSize' ), - 'font-style' => array( 'typography', 'fontStyle' ), - 'font-weight' => array( 'typography', 'fontWeight' ), - 'letter-spacing' => array( 'typography', 'letterSpacing' ), - 'line-height' => array( 'typography', 'lineHeight' ), - 'margin' => array( 'spacing', 'margin' ), - 'margin-top' => array( 'spacing', 'margin', 'top' ), - 'margin-right' => array( 'spacing', 'margin', 'right' ), - 'margin-bottom' => array( 'spacing', 'margin', 'bottom' ), - 'margin-left' => array( 'spacing', 'margin', 'left' ), - 'padding' => array( 'spacing', 'padding' ), - 'padding-top' => array( 'spacing', 'padding', 'top' ), - 'padding-right' => array( 'spacing', 'padding', 'right' ), - 'padding-bottom' => array( 'spacing', 'padding', 'bottom' ), - 'padding-left' => array( 'spacing', 'padding', 'left' ), - '--wp--style--block-gap' => array( 'spacing', 'blockGap' ), - 'text-decoration' => array( 'typography', 'textDecoration' ), - 'text-transform' => array( 'typography', 'textTransform' ), - 'filter' => array( 'filter', 'duotone' ), + 'background' => array( 'color', 'gradient' ), + 'background-color' => array( 'color', 'background' ), + 'border-radius' => array( 'border', 'radius' ), + 'border-top-left-radius' => array( 'border', 'radius', 'topLeft' ), + 'border-top-right-radius' => array( 'border', 'radius', 'topRight' ), + 'border-bottom-left-radius' => array( 'border', 'radius', 'bottomLeft' ), + 'border-bottom-right-radius' => array( 'border', 'radius', 'bottomRight' ), + 'border-color' => array( 'border', 'color' ), + 'border-width' => array( 'border', 'width' ), + 'border-style' => array( 'border', 'style' ), + 'border-top-color' => array( 'border', 'top', 'color' ), + 'border-top-width' => array( 'border', 'top', 'width' ), + 'border-top-style' => array( 'border', 'top', 'style' ), + 'border-right-color' => array( 'border', 'right', 'color' ), + 'border-right-width' => array( 'border', 'right', 'width' ), + 'border-right-style' => array( 'border', 'right', 'style' ), + 'border-bottom-color' => array( 'border', 'bottom', 'color' ), + 'border-bottom-width' => array( 'border', 'bottom', 'width' ), + 'border-bottom-style' => array( 'border', 'bottom', 'style' ), + 'border-left-color' => array( 'border', 'left', 'color' ), + 'border-left-width' => array( 'border', 'left', 'width' ), + 'border-left-style' => array( 'border', 'left', 'style' ), + 'color' => array( 'color', 'text' ), + 'font-family' => array( 'typography', 'fontFamily' ), + 'font-size' => array( 'typography', 'fontSize' ), + 'font-style' => array( 'typography', 'fontStyle' ), + 'font-weight' => array( 'typography', 'fontWeight' ), + 'letter-spacing' => array( 'typography', 'letterSpacing' ), + 'line-height' => array( 'typography', 'lineHeight' ), + 'margin' => array( 'spacing', 'margin' ), + 'margin-top' => array( 'spacing', 'margin', 'top' ), + 'margin-right' => array( 'spacing', 'margin', 'right' ), + 'margin-bottom' => array( 'spacing', 'margin', 'bottom' ), + 'margin-left' => array( 'spacing', 'margin', 'left' ), + 'padding' => array( 'spacing', 'padding' ), + 'padding-top' => array( 'spacing', 'padding', 'top' ), + 'padding-right' => array( 'spacing', 'padding', 'right' ), + 'padding-bottom' => array( 'spacing', 'padding', 'bottom' ), + 'padding-left' => array( 'spacing', 'padding', 'left' ), + '--wp--style--root--padding' => array( 'spacing', 'padding' ), + '--wp--style--root--padding-top' => array( 'spacing', 'padding', 'top' ), + '--wp--style--root--padding-right' => array( 'spacing', 'padding', 'right' ), + '--wp--style--root--padding-bottom' => array( 'spacing', 'padding', 'bottom' ), + '--wp--style--root--padding-left' => array( 'spacing', 'padding', 'left' ), + 'text-decoration' => array( 'typography', 'textDecoration' ), + 'text-transform' => array( 'typography', 'textTransform' ), + 'filter' => array( 'filter', 'duotone' ), + 'box-shadow' => array( 'shadow' ), ); /** @@ -253,6 +273,7 @@ class WP_Theme_JSON { * added new properties for `border`, `color`, `spacing`, * and `typography`, and renamed others according to the new schema. * @since 6.0.0 Added `color.defaultDuotone`. + * @since 6.1.0 Added `layout.definitions`. * @var array */ const VALID_SETTINGS = array( @@ -280,6 +301,7 @@ class WP_Theme_JSON { 'custom' => null, 'layout' => array( 'contentSize' => null, + 'definitions' => null, 'wideSize' => null, ), 'spacing' => array( @@ -309,6 +331,9 @@ class WP_Theme_JSON { * @since 5.9.0 Renamed from `ALLOWED_STYLES` to `VALID_STYLES`, * added new properties for `border`, `filter`, `spacing`, * and `typography`. + * @since 6.1.0 Added new property for `shadow`, + * new side properties for `border`, + * updated `blockGap` to be allowed at any level. * @var array */ const VALID_STYLES = array( @@ -317,19 +342,24 @@ class WP_Theme_JSON { 'radius' => null, 'style' => null, 'width' => null, + 'top' => null, + 'right' => null, + 'bottom' => null, + 'left' => null, ), 'color' => array( 'background' => null, 'gradient' => null, 'text' => null, ), + 'shadow' => null, 'filter' => array( 'duotone' => null, ), 'spacing' => array( 'margin' => null, 'padding' => null, - 'blockGap' => 'top', + 'blockGap' => null, ), 'typography' => array( 'fontFamily' => null, @@ -540,9 +570,11 @@ protected static function sanitize( $input, $valid_block_names, $valid_element_n */ $styles_non_top_level = static::VALID_STYLES; foreach ( array_keys( $styles_non_top_level ) as $section ) { - foreach ( array_keys( $styles_non_top_level[ $section ] ) as $prop ) { - if ( 'top' === $styles_non_top_level[ $section ][ $prop ] ) { - unset( $styles_non_top_level[ $section ][ $prop ] ); + if ( array_key_exists( $section, $styles_non_top_level ) && is_array( $styles_non_top_level[ $section ] ) ) { + foreach ( array_keys( $styles_non_top_level[ $section ] ) as $prop ) { + if ( 'top' === $styles_non_top_level[ $section ][ $prop ] ) { + unset( $styles_non_top_level[ $section ][ $prop ] ); + } } } } @@ -810,6 +842,24 @@ public function get_stylesheet( $types = array( 'variables', 'styles', 'presets' if ( in_array( 'styles', $types, true ) ) { $stylesheet .= $this->get_block_classes( $style_nodes ); + } elseif ( in_array( 'base-layout-styles', $types, true ) ) { + // Base layout styles are provided as part of `styles`, so only output separately if explicitly requested. + // For backwards compatibility, the Columns block is explicitly included, to support a different default gap value. + $base_styles_nodes = array( + array( + 'path' => array( 'styles' ), + 'selector' => static::ROOT_BLOCK_SELECTOR, + ), + array( + 'path' => array( 'styles', 'blocks', 'core/columns' ), + 'selector' => '.wp-block-columns', + 'name' => 'core/columns', + ), + ); + + foreach ( $base_styles_nodes as $base_style_node ) { + $stylesheet .= $this->get_layout_styles( $base_style_node ); + } } if ( in_array( 'presets', $types, true ) ) { @@ -883,6 +933,7 @@ public function get_template_parts() { * @since 5.9.0 Renamed from `get_block_styles()` to `get_block_classes()` * and no longer returns preset classes. * Removed the `$setting_nodes` parameter. + * @since 6.1.0 Moved most internal logic to `get_styles_for_block()`. * * @param array $style_nodes Nodes with styles. * @return string The new stylesheet. @@ -900,6 +951,293 @@ protected function get_block_classes( $style_nodes ) { return $block_rules; } + /** + * Transform spacing scale values into an array of spacing scale presets. + * + * @since 6.1.0 + */ + public function set_spacing_sizes() { + $spacing_scale = _wp_array_get( $this->theme_json, array( 'settings', 'spacing', 'spacingScale' ), array() ); + + if ( ! is_numeric( $spacing_scale['steps'] ) + || ! isset( $spacing_scale['mediumStep'] ) + || ! isset( $spacing_scale['unit'] ) + || ! isset( $spacing_scale['operator'] ) + || ! isset( $spacing_scale['increment'] ) + || ! isset( $spacing_scale['steps'] ) + || ! is_numeric( $spacing_scale['increment'] ) + || ! is_numeric( $spacing_scale['mediumStep'] ) + || ( '+' !== $spacing_scale['operator'] && '*' !== $spacing_scale['operator'] ) ) { + if ( ! empty( $spacing_scale ) ) { + trigger_error( __( 'Some of the theme.json settings.spacing.spacingScale values are invalid' ), E_USER_NOTICE ); + } + return null; + } + + // If theme authors want to prevent the generation of the core spacing scale they can set their theme.json spacingScale.steps to 0. + if ( 0 === $spacing_scale['steps'] ) { + return null; + } + + $unit = '%' === $spacing_scale['unit'] ? '%' : sanitize_title( $spacing_scale['unit'] ); + $current_step = $spacing_scale['mediumStep']; + $steps_mid_point = round( ( ( $spacing_scale['steps'] ) / 2 ), 0 ); + $x_small_count = null; + $below_sizes = array(); + $slug = 40; + $remainder = 0; + + for ( $x = $steps_mid_point - 1; $spacing_scale['steps'] > 1 && $slug > 0 && $x > 0; $x-- ) { + $current_step = '+' === $spacing_scale['operator'] + ? $current_step - $spacing_scale['increment'] + : ( $spacing_scale['increment'] > 1 ? $current_step / $spacing_scale['increment'] : $current_step * $spacing_scale['increment'] ); + + if ( $current_step <= 0 ) { + $remainder = $x; + break; + } + + $below_sizes[] = array( + /* translators: %s: Multiple of t-shirt sizing, eg. 2X-Small */ + 'name' => $x === $steps_mid_point - 1 ? __( 'Small' ) : sprintf( __( '%sX-Small', ), strval( $x_small_count ) ), + 'slug' => (string) $slug, + 'size' => round( $current_step, 2 ) . $unit, + ); + + if ( $x === $steps_mid_point - 2 ) { + $x_small_count = 2; + } + + if ( $x < $steps_mid_point - 2 ) { + $x_small_count++; + } + + $slug = $slug - 10; + } + + $below_sizes = array_reverse( $below_sizes ); + + $below_sizes[] = array( + 'name' => __( 'Medium' ), + 'slug' => '50', + 'size' => $spacing_scale['mediumStep'] . $unit, + ); + + $current_step = $spacing_scale['mediumStep']; + $x_large_count = null; + $above_sizes = array(); + $slug = 60; + $steps_above = ( $spacing_scale['steps'] - $steps_mid_point ) + $remainder; + + for ( $x = 0; $x < $steps_above; $x++ ) { + $current_step = '+' === $spacing_scale['operator'] + ? $current_step + $spacing_scale['increment'] + : ( $spacing_scale['increment'] >= 1 ? $current_step * $spacing_scale['increment'] : $current_step / $spacing_scale['increment'] ); + + $above_sizes[] = array( + /* translators: %s: Multiple of t-shirt sizing, eg. 2X-Large */ + 'name' => 0 === $x ? __( 'Large' ) : sprintf( __( '%sX-Large' ), strval( $x_large_count ) ), + 'slug' => (string) $slug, + 'size' => round( $current_step, 2 ) . $unit, + ); + + if ( 1 === $x ) { + $x_large_count = 2; + } + + if ( $x > 1 ) { + $x_large_count++; + } + + $slug = $slug + 10; + } + + _wp_array_set( $this->theme_json, array( 'settings', 'spacing', 'spacingSizes', 'default' ), array_merge( $below_sizes, $above_sizes ) ); + } + + /** + * Get the CSS layout rules for a particular block from theme.json layout definitions. + * + * @since 6.1.0 + * + * @param array $block_metadata Metadata about the block to get styles for. + * + * @return string Layout styles for the block. + */ + protected function get_layout_styles( $block_metadata ) { + $block_rules = ''; + $block_type = null; + + // Skip outputting layout styles if explicitly disabled. + if ( current_theme_supports( 'disable-layout-styles' ) ) { + return $block_rules; + } + + if ( isset( $block_metadata['name'] ) ) { + $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block_metadata['name'] ); + if ( ! block_has_support( $block_type, array( '__experimentalLayout' ), false ) ) { + return $block_rules; + } + } + + $selector = isset( $block_metadata['selector'] ) ? $block_metadata['selector'] : ''; + $has_block_gap_support = _wp_array_get( $this->theme_json, array( 'settings', 'spacing', 'blockGap' ) ) !== null; + $has_fallback_gap_support = ! $has_block_gap_support; // This setting isn't useful yet: it exists as a placeholder for a future explicit fallback gap styles support. + $node = _wp_array_get( $this->theme_json, $block_metadata['path'], array() ); + $layout_definitions = _wp_array_get( $this->theme_json, array( 'settings', 'layout', 'definitions' ), array() ); + $layout_selector_pattern = '/^[a-zA-Z0-9\-\.\ *+>:\(\)]*$/'; // Allow alphanumeric classnames, spaces, wildcard, sibling, child combinator and pseudo class selectors. + + // Gap styles will only be output if the theme has block gap support, or supports a fallback gap. + // Default layout gap styles will be skipped for themes that do not explicitly opt-in to blockGap with a `true` or `false` value. + if ( $has_block_gap_support || $has_fallback_gap_support ) { + $block_gap_value = null; + // Use a fallback gap value if block gap support is not available. + if ( ! $has_block_gap_support ) { + $block_gap_value = static::ROOT_BLOCK_SELECTOR === $selector ? '0.5em' : null; + if ( ! empty( $block_type ) ) { + $block_gap_value = _wp_array_get( $block_type->supports, array( 'spacing', 'blockGap', '__experimentalDefault' ), null ); + } + } else { + $block_gap_value = static::get_property_value( $node, array( 'spacing', 'blockGap' ) ); + } + + // Support split row / column values and concatenate to a shorthand value. + if ( is_array( $block_gap_value ) ) { + if ( isset( $block_gap_value['top'] ) && isset( $block_gap_value['left'] ) ) { + $gap_row = static::get_property_value( $node, array( 'spacing', 'blockGap', 'top' ) ); + $gap_column = static::get_property_value( $node, array( 'spacing', 'blockGap', 'left' ) ); + $block_gap_value = $gap_row === $gap_column ? $gap_row : $gap_row . ' ' . $gap_column; + } else { + // Skip outputting gap value if not all sides are provided. + $block_gap_value = null; + } + } + + if ( null !== $block_gap_value ) { + foreach ( $layout_definitions as $layout_definition_key => $layout_definition ) { + // Allow skipping default layout for themes that opt-in to block styles, but opt-out of blockGap. + if ( ! $has_block_gap_support && 'default' === $layout_definition_key ) { + continue; + } + + $class_name = sanitize_title( _wp_array_get( $layout_definition, array( 'className' ), false ) ); + $spacing_rules = _wp_array_get( $layout_definition, array( 'spacingStyles' ), array() ); + + if ( + ! empty( $class_name ) && + ! empty( $spacing_rules ) + ) { + foreach ( $spacing_rules as $spacing_rule ) { + $declarations = array(); + if ( + isset( $spacing_rule['selector'] ) && + preg_match( $layout_selector_pattern, $spacing_rule['selector'] ) && + ! empty( $spacing_rule['rules'] ) + ) { + // Iterate over each of the styling rules and substitute non-string values such as `null` with the real `blockGap` value. + foreach ( $spacing_rule['rules'] as $css_property => $css_value ) { + $current_css_value = is_string( $css_value ) ? $css_value : $block_gap_value; + if ( static::is_safe_css_declaration( $css_property, $current_css_value ) ) { + $declarations[] = array( + 'name' => $css_property, + 'value' => $current_css_value, + ); + } + } + + if ( ! $has_block_gap_support ) { + // For fallback gap styles, use lower specificity, to ensure styles do not unintentionally override theme styles. + $format = static::ROOT_BLOCK_SELECTOR === $selector ? ':where(.%2$s%3$s)' : ':where(%1$s.%2$s%3$s)'; + $layout_selector = sprintf( + $format, + $selector, + $class_name, + $spacing_rule['selector'] + ); + } else { + $format = static::ROOT_BLOCK_SELECTOR === $selector ? '%s .%s%s' : '%s.%s%s'; + $layout_selector = sprintf( + $format, + $selector, + $class_name, + $spacing_rule['selector'] + ); + } + $block_rules .= static::to_ruleset( $layout_selector, $declarations ); + } + } + } + } + } + } + + // Output base styles. + if ( + static::ROOT_BLOCK_SELECTOR === $selector + ) { + $valid_display_modes = array( 'block', 'flex', 'grid' ); + foreach ( $layout_definitions as $layout_definition ) { + $class_name = sanitize_title( _wp_array_get( $layout_definition, array( 'className' ), false ) ); + $base_style_rules = _wp_array_get( $layout_definition, array( 'baseStyles' ), array() ); + + if ( + ! empty( $class_name ) && + ! empty( $base_style_rules ) + ) { + // Output display mode. This requires special handling as `display` is not exposed in `safe_style_css_filter`. + if ( + ! empty( $layout_definition['displayMode'] ) && + is_string( $layout_definition['displayMode'] ) && + in_array( $layout_definition['displayMode'], $valid_display_modes, true ) + ) { + $layout_selector = sprintf( + '%s .%s', + $selector, + $class_name + ); + $block_rules .= static::to_ruleset( + $layout_selector, + array( + array( + 'name' => 'display', + 'value' => $layout_definition['displayMode'], + ), + ) + ); + } + + foreach ( $base_style_rules as $base_style_rule ) { + $declarations = array(); + + if ( + isset( $base_style_rule['selector'] ) && + preg_match( $layout_selector_pattern, $base_style_rule['selector'] ) && + ! empty( $base_style_rule['rules'] ) + ) { + foreach ( $base_style_rule['rules'] as $css_property => $css_value ) { + if ( static::is_safe_css_declaration( $css_property, $css_value ) ) { + $declarations[] = array( + 'name' => $css_property, + 'value' => $css_value, + ); + } + } + + $layout_selector = sprintf( + '%s .%s%s', + $selector, + $class_name, + $base_style_rule['selector'] + ); + $block_rules .= static::to_ruleset( $layout_selector, $declarations ); + } + } + } + } + } + return $block_rules; + } + /** * Creates new rulesets as classes for each preset value such as: * @@ -1658,36 +1996,59 @@ private static function get_block_nodes( $theme_json ) { * * @since 6.1.0 * - * @param array $block_metadata Meta data about the block to get styles for. - * @return array Styles for the block. + * @param array $block_metadata Metadata about the block to get styles for. + * + * @return string Styles for the block. */ public function get_styles_for_block( $block_metadata ) { + $node = _wp_array_get( $this->theme_json, $block_metadata['path'], array() ); + $use_root_padding = isset( $this->theme_json['settings']['useRootPaddingAwareAlignments'] ) && true === $this->theme_json['settings']['useRootPaddingAwareAlignments']; + $selector = $block_metadata['selector']; + $settings = _wp_array_get( $this->theme_json, array( 'settings' ) ); + + // Process style declarations for block support features the current + // block contains selectors for. Values for a feature with a custom + // selector are filtered from the theme.json node before it is + // processed as normal. + $feature_declarations = array(); + + if ( ! empty( $block_metadata['features'] ) ) { + foreach ( $block_metadata['features'] as $feature_name => $feature_selector ) { + if ( ! empty( $node[ $feature_name ] ) ) { + // Create temporary node containing only the feature data + // to leverage existing `compute_style_properties` function. + $feature = array( $feature_name => $node[ $feature_name ] ); + // Generate the feature's declarations only. + $new_feature_declarations = static::compute_style_properties( $feature, $settings, null, $this->theme_json ); + + // Merge new declarations with any that already exist for + // the feature selector. This may occur when multiple block + // support features use the same custom selector. + if ( isset( $feature_declarations[ $feature_selector ] ) ) { + $feature_declarations[ $feature_selector ] = array_merge( $feature_declarations[ $feature_selector ], $new_feature_declarations ); + } else { + $feature_declarations[ $feature_selector ] = $new_feature_declarations; + } - $node = _wp_array_get( $this->theme_json, $block_metadata['path'], array() ); - - $selector = $block_metadata['selector']; - $settings = _wp_array_get( $this->theme_json, array( 'settings' ) ); + // Remove the feature from the block's node now the + // styles will be included under the feature level selector. + unset( $node[ $feature_name ] ); + } + } + } - /* - * Get a reference to element name from path. - * $block_metadata['path'] = array( 'styles','elements','link' ); - * Make sure that $block_metadata['path'] describes an element node, like [ 'styles', 'element', 'link' ]. - * Skip non-element paths like just ['styles']. - */ + // Get a reference to element name from path. + // $block_metadata['path'] = array('styles','elements','link'); + // Make sure that $block_metadata['path'] describes an element node, like ['styles', 'element', 'link']. + // Skip non-element paths like just ['styles']. $is_processing_element = in_array( 'elements', $block_metadata['path'], true ); $current_element = $is_processing_element ? $block_metadata['path'][ count( $block_metadata['path'] ) - 1 ] : null; - $element_pseudo_allowed = array(); + $element_pseudo_allowed = array_key_exists( $current_element, static::VALID_ELEMENT_PSEUDO_SELECTORS ) ? static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ] : array(); - if ( array_key_exists( $current_element, static::VALID_ELEMENT_PSEUDO_SELECTORS ) ) { - $element_pseudo_allowed = static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ]; - } - - /* - * Check for allowed pseudo classes (e.g. ":hover") from the $selector ("a:hover"). - * This also resets the array keys. - */ + // Check for allowed pseudo classes (e.g. ":hover") from the $selector ("a:hover"). + // This also resets the array keys. $pseudo_matches = array_values( array_filter( $element_pseudo_allowed, @@ -1699,26 +2060,19 @@ function( $pseudo_selector ) use ( $selector ) { $pseudo_selector = isset( $pseudo_matches[0] ) ? $pseudo_matches[0] : null; - /* - * If the current selector is a pseudo selector that's defined in the allow list for the current - * element then compute the style properties for it. - * Otherwise just compute the styles for the default selector as normal. - */ - if ( $pseudo_selector && isset( $node[ $pseudo_selector ] ) && - array_key_exists( $current_element, static::VALID_ELEMENT_PSEUDO_SELECTORS ) - && in_array( $pseudo_selector, static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ], true ) - ) { - $declarations = static::compute_style_properties( $node[ $pseudo_selector ], $settings, null, $this->theme_json ); + // If the current selector is a pseudo selector that's defined in the allow list for the current + // element then compute the style properties for it. + // Otherwise just compute the styles for the default selector as normal. + if ( $pseudo_selector && isset( $node[ $pseudo_selector ] ) && array_key_exists( $current_element, static::VALID_ELEMENT_PSEUDO_SELECTORS ) && in_array( $pseudo_selector, static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ], true ) ) { + $declarations = static::compute_style_properties( $node[ $pseudo_selector ], $settings, null, $this->theme_json, $selector, $use_root_padding ); } else { - $declarations = static::compute_style_properties( $node, $settings, null, $this->theme_json ); + $declarations = static::compute_style_properties( $node, $settings, null, $this->theme_json, $selector, $use_root_padding ); } $block_rules = ''; - /* - * 1. Separate the declarations that use the general selector - * from the ones using the duotone selector. - */ + // 1. Separate the ones who use the general selector + // and the ones who use the duotone selector. $declarations_duotone = array(); foreach ( $declarations as $index => $declaration ) { if ( 'filter' === $declaration['name'] ) { @@ -1727,18 +2081,6 @@ function( $pseudo_selector ) use ( $selector ) { } } - /* - * Reset default browser margin on the root body element. - * This is set on the root selector **before** generating the ruleset - * from the `theme.json`. This is to ensure that if the `theme.json` declares - * `margin` in its `spacing` declaration for the `body` element then these - * user-generated values take precedence in the CSS cascade. - * @link https://github.com/WordPress/gutenberg/issues/36147. - */ - if ( static::ROOT_BLOCK_SELECTOR === $selector ) { - $block_rules .= 'body { margin: 0; }'; - } - // 2. Generate and append the rules that use the general selector. $block_rules .= static::to_ruleset( $selector, $declarations ); @@ -1748,16 +2090,70 @@ function( $pseudo_selector ) use ( $selector ) { $block_rules .= static::to_ruleset( $selector_duotone, $declarations_duotone ); } + // 4. Generate Layout block gap styles. + if ( + static::ROOT_BLOCK_SELECTOR !== $selector && + ! empty( $block_metadata['name'] ) + ) { + $block_rules .= $this->get_layout_styles( $block_metadata ); + } + + // 5. Generate and append the feature level rulesets. + foreach ( $feature_declarations as $feature_selector => $individual_feature_declarations ) { + $block_rules .= static::to_ruleset( $feature_selector, $individual_feature_declarations ); + } + + if ( static::ROOT_BLOCK_SELECTOR === $selector ) { + /* + * Reset default browser margin on the root body element. + * This is set on the root selector **before** generating the ruleset + * from the `theme.json`. This is to ensure that if the `theme.json` declares + * `margin` in its `spacing` declaration for the `body` element then these + * user-generated values take precedence in the CSS cascade. + * @link https://github.com/WordPress/gutenberg/issues/36147. + */ + $block_rules .= 'body { margin: 0;'; + + /* + * If there are content and wide widths in theme.json, output them + * as custom properties on the body element so all blocks can use them. + */ + if ( isset( $settings['layout']['contentSize'] ) || isset( $settings['layout']['wideSize'] ) ) { + $content_size = isset( $settings['layout']['contentSize'] ) ? $settings['layout']['contentSize'] : $settings['layout']['wideSize']; + $content_size = static::is_safe_css_declaration( 'max-width', $content_size ) ? $content_size : 'initial'; + $wide_size = isset( $settings['layout']['wideSize'] ) ? $settings['layout']['wideSize'] : $settings['layout']['contentSize']; + $wide_size = static::is_safe_css_declaration( 'max-width', $wide_size ) ? $wide_size : 'initial'; + $block_rules .= '--wp--style--global--content-size: ' . $content_size . ';'; + $block_rules .= '--wp--style--global--wide-size: ' . $wide_size . ';'; + } + + $block_rules .= '}'; + } + if ( static::ROOT_BLOCK_SELECTOR === $selector ) { + + if ( $use_root_padding ) { + $block_rules .= '.wp-site-blocks { padding-top: var(--wp--style--root--padding-top); padding-bottom: var(--wp--style--root--padding-bottom); }'; + $block_rules .= '.has-global-padding { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }'; + $block_rules .= '.has-global-padding > .alignfull { margin-right: calc(var(--wp--style--root--padding-right) * -1); margin-left: calc(var(--wp--style--root--padding-left) * -1); }'; + $block_rules .= '.has-global-padding > .alignfull > :where([class*="wp-block-"]:not(.alignfull):not(.alignfull):not([class*="__"]),p,h1,h2,h3,h4,h5,h6,ul,ol) { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }'; + } + $block_rules .= '.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }'; $block_rules .= '.wp-site-blocks > .alignright { float: right; margin-left: 2em; }'; $block_rules .= '.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }'; + $block_gap_value = _wp_array_get( $this->theme_json, array( 'styles', 'spacing', 'blockGap' ), '0.5em' ); $has_block_gap_support = _wp_array_get( $this->theme_json, array( 'settings', 'spacing', 'blockGap' ) ) !== null; if ( $has_block_gap_support ) { - $block_rules .= '.wp-site-blocks > * { margin-block-start: 0; margin-block-end: 0; }'; - $block_rules .= '.wp-site-blocks > * + * { margin-block-start: var( --wp--style--block-gap ); }'; + $block_gap_value = static::get_property_value( $this->theme_json, array( 'styles', 'spacing', 'blockGap' ) ); + $block_rules .= '.wp-site-blocks > * { margin-block-start: 0; margin-block-end: 0; }'; + $block_rules .= ".wp-site-blocks > * + * { margin-block-start: $block_gap_value; }"; + + // For backwards compatibility, ensure the legacy block gap CSS variable is still available. + $block_rules .= "$selector { --wp--style--block-gap: $block_gap_value; }"; } + $block_rules .= $this->get_layout_styles( $block_metadata ); } return $block_rules; diff --git a/src/wp-includes/global-styles-and-settings.php b/src/wp-includes/global-styles-and-settings.php index 2e642cc2b1e05..1187837ed651f 100644 --- a/src/wp-includes/global-styles-and-settings.php +++ b/src/wp-includes/global-styles-and-settings.php @@ -107,7 +107,7 @@ function wp_get_global_stylesheet( $types = array() ) { $supports_theme_json = WP_Theme_JSON_Resolver::theme_has_support(); if ( empty( $types ) && ! $supports_theme_json ) { - $types = array( 'variables', 'presets' ); + $types = array( 'variables', 'presets', 'base-layout-styles' ); } elseif ( empty( $types ) ) { $types = array( 'variables', 'styles', 'presets' ); } diff --git a/src/wp-includes/theme.json b/src/wp-includes/theme.json index 1ef000559b30b..40432cedf3877 100644 --- a/src/wp-includes/theme.json +++ b/src/wp-includes/theme.json @@ -2,6 +2,7 @@ "version": 2, "settings": { "appearanceTools": false, + "useRootPaddingAwareAlignments": false, "border": { "color": false, "radius": false, @@ -12,8 +13,8 @@ "background": true, "custom": true, "customDuotone": true, - "customGradient": true, - "defaultDuotone": true, + "customGradient": true, + "defaultDuotone": true, "defaultGradients": true, "defaultPalette": true, "duotone": [ @@ -185,11 +186,158 @@ ], "text": true }, + "layout": { + "definitions": { + "default": { + "name": "default", + "slug": "flow", + "className": "is-layout-flow", + "baseStyles": [ + { + "selector": " > .alignleft", + "rules": { + "float": "left", + "margin-inline-start": "0", + "margin-inline-end": "2em" + } + }, + { + "selector": " > .alignright", + "rules": { + "float": "right", + "margin-inline-start": "2em", + "margin-inline-end": "0" + } + }, + { + "selector": " > .aligncenter", + "rules": { + "margin-left": "auto !important", + "margin-right": "auto !important" + } + } + ], + "spacingStyles": [ + { + "selector": " > *", + "rules": { + "margin-block-start": "0", + "margin-block-end": "0" + } + }, + { + "selector": " > * + *", + "rules": { + "margin-block-start": null, + "margin-block-end": "0" + } + } + ] + }, + "constrained": { + "name": "constrained", + "slug": "constrained", + "className": "is-layout-constrained", + "baseStyles": [ + { + "selector": " > .alignleft", + "rules": { + "float": "left", + "margin-inline-start": "0", + "margin-inline-end": "2em" + } + }, + { + "selector": " > .alignright", + "rules": { + "float": "right", + "margin-inline-start": "2em", + "margin-inline-end": "0" + } + }, + { + "selector": " > .aligncenter", + "rules": { + "margin-left": "auto !important", + "margin-right": "auto !important" + } + }, + { + "selector": " > :where(:not(.alignleft):not(.alignright):not(.alignfull))", + "rules": { + "max-width": "var(--wp--style--global--content-size)", + "margin-left": "auto !important", + "margin-right": "auto !important" + } + }, + { + "selector": " > .alignwide", + "rules": { + "max-width": "var(--wp--style--global--wide-size)" + } + } + ], + "spacingStyles": [ + { + "selector": " > *", + "rules": { + "margin-block-start": "0", + "margin-block-end": "0" + } + }, + { + "selector": " > * + *", + "rules": { + "margin-block-start": null, + "margin-block-end": "0" + } + } + ] + }, + "flex": { + "name": "flex", + "slug": "flex", + "className": "is-layout-flex", + "displayMode": "flex", + "baseStyles": [ + { + "selector": "", + "rules": { + "flex-wrap": "wrap", + "align-items": "center" + } + }, + { + "selector": " > *", + "rules": { + "margin": "0" + } + } + ], + "spacingStyles": [ + { + "selector": "", + "rules": { + "gap": null + } + } + ] + } + } + }, "spacing": { "blockGap": null, "margin": false, "padding": false, - "units": [ "px", "em", "rem", "vh", "vw", "%" ] + "customSpacingSize": true, + "units": [ "px", "em", "rem", "vh", "vw", "%" ], + "spacingScale": { + "operator": "*", + "increment": 1.5, + "steps": 7, + "mediumStep": 1.5, + "unit": "rem" + } }, "typography": { "customFontSize": true, @@ -240,6 +388,39 @@ } }, "styles": { - "spacing": { "blockGap": "24px" } + "elements": { + "button": { + "color": { + "text": "#fff", + "background": "#32373c" + }, + "spacing": { + "padding": "calc(0.667em + 2px) calc(1.333em + 2px)" + }, + "typography": { + "fontSize": "inherit", + "fontFamily": "inherit", + "lineHeight": "inherit", + "textDecoration": "none" + }, + "border": { + "width": "0" + } + }, + "link": { + "typography": { + "textDecoration": "underline" + } + } + }, + "spacing": { + "blockGap": "24px", + "padding": { + "top": "0px", + "right": "0px", + "bottom": "0px", + "left": "0px" + } + } } } From a91fbb1bdd0d0fb9d7acf456113d4317577e364a Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Thu, 8 Sep 2022 15:45:09 +1000 Subject: [PATCH 02/16] Fix a couple of linting issues --- src/wp-includes/class-wp-theme-json-resolver.php | 6 +++--- src/wp-includes/class-wp-theme-json.php | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/wp-includes/class-wp-theme-json-resolver.php b/src/wp-includes/class-wp-theme-json-resolver.php index 5bc181594f509..05e35ab17e47e 100644 --- a/src/wp-includes/class-wp-theme-json-resolver.php +++ b/src/wp-includes/class-wp-theme-json-resolver.php @@ -244,7 +244,7 @@ public static function get_block_data() { $config = array( 'version' => 1 ); foreach ( $blocks as $block_name => $block_type ) { if ( isset( $block_type->supports['__experimentalStyle'] ) ) { - $config['styles']['blocks'][ $block_name ] = static::remove_JSON_comments( $block_type->supports['__experimentalStyle'] ); + $config['styles']['blocks'][ $block_name ] = static::remove_json_comments( $block_type->supports['__experimentalStyle'] ); } if ( @@ -268,11 +268,11 @@ public static function get_block_data() { * @param array $array The array to filter. * @return array The filtered array. */ - private static function remove_JSON_comments( $array ) { + private static function remove_json_comments( $array ) { unset( $array['//'] ); foreach ( $array as $k => $v ) { if ( is_array( $v ) ) { - $array[ $k ] = static::remove_JSON_comments( $v ); + $array[ $k ] = static::remove_json_comments( $v ); } } diff --git a/src/wp-includes/class-wp-theme-json.php b/src/wp-includes/class-wp-theme-json.php index 766743f547c3f..050c991ed185a 100644 --- a/src/wp-includes/class-wp-theme-json.php +++ b/src/wp-includes/class-wp-theme-json.php @@ -999,7 +999,7 @@ public function set_spacing_sizes() { $below_sizes[] = array( /* translators: %s: Multiple of t-shirt sizing, eg. 2X-Small */ - 'name' => $x === $steps_mid_point - 1 ? __( 'Small' ) : sprintf( __( '%sX-Small', ), strval( $x_small_count ) ), + 'name' => $x === $steps_mid_point - 1 ? __( 'Small' ) : sprintf( __( '%sX-Small' ), strval( $x_small_count ) ), 'slug' => (string) $slug, 'size' => round( $current_step, 2 ) . $unit, ); From b636388c6552beeff7ed6b0eba7b4dfe4a87425b Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Thu, 8 Sep 2022 15:58:23 +1000 Subject: [PATCH 03/16] Add missing lines related to disable-layout-styles --- src/wp-includes/block-editor.php | 1 + src/wp-includes/theme.php | 7 +++++++ tests/phpunit/tests/blocks/editor.php | 1 + 3 files changed, 9 insertions(+) diff --git a/src/wp-includes/block-editor.php b/src/wp-includes/block-editor.php index 85fb129ebcc7d..2ae468cae73ea 100644 --- a/src/wp-includes/block-editor.php +++ b/src/wp-includes/block-editor.php @@ -209,6 +209,7 @@ function get_default_block_editor_settings() { 'disableCustomColors' => get_theme_support( 'disable-custom-colors' ), 'disableCustomFontSizes' => get_theme_support( 'disable-custom-font-sizes' ), 'disableCustomGradients' => get_theme_support( 'disable-custom-gradients' ), + 'disableLayoutStyles' => get_theme_support( 'disable-layout-styles' ), 'enableCustomLineHeight' => get_theme_support( 'custom-line-height' ), 'enableCustomSpacing' => get_theme_support( 'custom-spacing' ), 'enableCustomUnits' => get_theme_support( 'custom-units' ), diff --git a/src/wp-includes/theme.php b/src/wp-includes/theme.php index 3c7b297b2a802..18afee3abd54d 100644 --- a/src/wp-includes/theme.php +++ b/src/wp-includes/theme.php @@ -4026,6 +4026,13 @@ function create_initial_theme_features() { 'show_in_rest' => true, ) ); + register_theme_feature( + 'disable-layout-styles', + array( + 'description' => __( 'Whether the theme disables generated layout styles.' ), + 'show_in_rest' => true, + ) + ); register_theme_feature( 'editor-color-palette', array( diff --git a/tests/phpunit/tests/blocks/editor.php b/tests/phpunit/tests/blocks/editor.php index f08c350168ff1..1efa8ebb09fc0 100644 --- a/tests/phpunit/tests/blocks/editor.php +++ b/tests/phpunit/tests/blocks/editor.php @@ -249,6 +249,7 @@ public function test_get_default_block_editor_settings() { $this->assertFalse( $settings['disableCustomColors'] ); $this->assertFalse( $settings['disableCustomFontSizes'] ); $this->assertFalse( $settings['disableCustomGradients'] ); + $this->assertFalse( $settings['disableLayoutStyles'] ); $this->assertFalse( $settings['enableCustomLineHeight'] ); $this->assertFalse( $settings['enableCustomSpacing'] ); $this->assertFalse( $settings['enableCustomUnits'] ); From 8f611b93dd29dc1e7c800f0c2be6b488e1e9e8b1 Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Thu, 8 Sep 2022 16:04:19 +1000 Subject: [PATCH 04/16] Add whitespace to please some existing tests --- src/wp-includes/class-wp-theme-json.php | 434 +++++++++--------------- 1 file changed, 165 insertions(+), 269 deletions(-) diff --git a/src/wp-includes/class-wp-theme-json.php b/src/wp-includes/class-wp-theme-json.php index 050c991ed185a..61bafd8f286b0 100644 --- a/src/wp-includes/class-wp-theme-json.php +++ b/src/wp-includes/class-wp-theme-json.php @@ -951,110 +951,6 @@ protected function get_block_classes( $style_nodes ) { return $block_rules; } - /** - * Transform spacing scale values into an array of spacing scale presets. - * - * @since 6.1.0 - */ - public function set_spacing_sizes() { - $spacing_scale = _wp_array_get( $this->theme_json, array( 'settings', 'spacing', 'spacingScale' ), array() ); - - if ( ! is_numeric( $spacing_scale['steps'] ) - || ! isset( $spacing_scale['mediumStep'] ) - || ! isset( $spacing_scale['unit'] ) - || ! isset( $spacing_scale['operator'] ) - || ! isset( $spacing_scale['increment'] ) - || ! isset( $spacing_scale['steps'] ) - || ! is_numeric( $spacing_scale['increment'] ) - || ! is_numeric( $spacing_scale['mediumStep'] ) - || ( '+' !== $spacing_scale['operator'] && '*' !== $spacing_scale['operator'] ) ) { - if ( ! empty( $spacing_scale ) ) { - trigger_error( __( 'Some of the theme.json settings.spacing.spacingScale values are invalid' ), E_USER_NOTICE ); - } - return null; - } - - // If theme authors want to prevent the generation of the core spacing scale they can set their theme.json spacingScale.steps to 0. - if ( 0 === $spacing_scale['steps'] ) { - return null; - } - - $unit = '%' === $spacing_scale['unit'] ? '%' : sanitize_title( $spacing_scale['unit'] ); - $current_step = $spacing_scale['mediumStep']; - $steps_mid_point = round( ( ( $spacing_scale['steps'] ) / 2 ), 0 ); - $x_small_count = null; - $below_sizes = array(); - $slug = 40; - $remainder = 0; - - for ( $x = $steps_mid_point - 1; $spacing_scale['steps'] > 1 && $slug > 0 && $x > 0; $x-- ) { - $current_step = '+' === $spacing_scale['operator'] - ? $current_step - $spacing_scale['increment'] - : ( $spacing_scale['increment'] > 1 ? $current_step / $spacing_scale['increment'] : $current_step * $spacing_scale['increment'] ); - - if ( $current_step <= 0 ) { - $remainder = $x; - break; - } - - $below_sizes[] = array( - /* translators: %s: Multiple of t-shirt sizing, eg. 2X-Small */ - 'name' => $x === $steps_mid_point - 1 ? __( 'Small' ) : sprintf( __( '%sX-Small' ), strval( $x_small_count ) ), - 'slug' => (string) $slug, - 'size' => round( $current_step, 2 ) . $unit, - ); - - if ( $x === $steps_mid_point - 2 ) { - $x_small_count = 2; - } - - if ( $x < $steps_mid_point - 2 ) { - $x_small_count++; - } - - $slug = $slug - 10; - } - - $below_sizes = array_reverse( $below_sizes ); - - $below_sizes[] = array( - 'name' => __( 'Medium' ), - 'slug' => '50', - 'size' => $spacing_scale['mediumStep'] . $unit, - ); - - $current_step = $spacing_scale['mediumStep']; - $x_large_count = null; - $above_sizes = array(); - $slug = 60; - $steps_above = ( $spacing_scale['steps'] - $steps_mid_point ) + $remainder; - - for ( $x = 0; $x < $steps_above; $x++ ) { - $current_step = '+' === $spacing_scale['operator'] - ? $current_step + $spacing_scale['increment'] - : ( $spacing_scale['increment'] >= 1 ? $current_step * $spacing_scale['increment'] : $current_step / $spacing_scale['increment'] ); - - $above_sizes[] = array( - /* translators: %s: Multiple of t-shirt sizing, eg. 2X-Large */ - 'name' => 0 === $x ? __( 'Large' ) : sprintf( __( '%sX-Large' ), strval( $x_large_count ) ), - 'slug' => (string) $slug, - 'size' => round( $current_step, 2 ) . $unit, - ); - - if ( 1 === $x ) { - $x_large_count = 2; - } - - if ( $x > 1 ) { - $x_large_count++; - } - - $slug = $slug + 10; - } - - _wp_array_set( $this->theme_json, array( 'settings', 'spacing', 'spacingSizes', 'default' ), array_merge( $below_sizes, $above_sizes ) ); - } - /** * Get the CSS layout rules for a particular block from theme.json layout definitions. * @@ -1992,171 +1888,171 @@ private static function get_block_nodes( $theme_json ) { } /** - * Gets the CSS rules for a particular block from theme.json. - * - * @since 6.1.0 - * - * @param array $block_metadata Metadata about the block to get styles for. - * - * @return string Styles for the block. - */ - public function get_styles_for_block( $block_metadata ) { - $node = _wp_array_get( $this->theme_json, $block_metadata['path'], array() ); - $use_root_padding = isset( $this->theme_json['settings']['useRootPaddingAwareAlignments'] ) && true === $this->theme_json['settings']['useRootPaddingAwareAlignments']; - $selector = $block_metadata['selector']; - $settings = _wp_array_get( $this->theme_json, array( 'settings' ) ); - - // Process style declarations for block support features the current - // block contains selectors for. Values for a feature with a custom - // selector are filtered from the theme.json node before it is - // processed as normal. - $feature_declarations = array(); - - if ( ! empty( $block_metadata['features'] ) ) { - foreach ( $block_metadata['features'] as $feature_name => $feature_selector ) { - if ( ! empty( $node[ $feature_name ] ) ) { - // Create temporary node containing only the feature data - // to leverage existing `compute_style_properties` function. - $feature = array( $feature_name => $node[ $feature_name ] ); - // Generate the feature's declarations only. - $new_feature_declarations = static::compute_style_properties( $feature, $settings, null, $this->theme_json ); - - // Merge new declarations with any that already exist for - // the feature selector. This may occur when multiple block - // support features use the same custom selector. - if ( isset( $feature_declarations[ $feature_selector ] ) ) { - $feature_declarations[ $feature_selector ] = array_merge( $feature_declarations[ $feature_selector ], $new_feature_declarations ); - } else { - $feature_declarations[ $feature_selector ] = $new_feature_declarations; - } - - // Remove the feature from the block's node now the - // styles will be included under the feature level selector. - unset( $node[ $feature_name ] ); - } - } - } - - // Get a reference to element name from path. - // $block_metadata['path'] = array('styles','elements','link'); - // Make sure that $block_metadata['path'] describes an element node, like ['styles', 'element', 'link']. - // Skip non-element paths like just ['styles']. - $is_processing_element = in_array( 'elements', $block_metadata['path'], true ); - - $current_element = $is_processing_element ? $block_metadata['path'][ count( $block_metadata['path'] ) - 1 ] : null; - - $element_pseudo_allowed = array_key_exists( $current_element, static::VALID_ELEMENT_PSEUDO_SELECTORS ) ? static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ] : array(); - - // Check for allowed pseudo classes (e.g. ":hover") from the $selector ("a:hover"). - // This also resets the array keys. - $pseudo_matches = array_values( - array_filter( - $element_pseudo_allowed, - function( $pseudo_selector ) use ( $selector ) { - return str_contains( $selector, $pseudo_selector ); - } - ) - ); - - $pseudo_selector = isset( $pseudo_matches[0] ) ? $pseudo_matches[0] : null; - - // If the current selector is a pseudo selector that's defined in the allow list for the current - // element then compute the style properties for it. - // Otherwise just compute the styles for the default selector as normal. - if ( $pseudo_selector && isset( $node[ $pseudo_selector ] ) && array_key_exists( $current_element, static::VALID_ELEMENT_PSEUDO_SELECTORS ) && in_array( $pseudo_selector, static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ], true ) ) { - $declarations = static::compute_style_properties( $node[ $pseudo_selector ], $settings, null, $this->theme_json, $selector, $use_root_padding ); - } else { - $declarations = static::compute_style_properties( $node, $settings, null, $this->theme_json, $selector, $use_root_padding ); - } - - $block_rules = ''; - - // 1. Separate the ones who use the general selector - // and the ones who use the duotone selector. - $declarations_duotone = array(); - foreach ( $declarations as $index => $declaration ) { - if ( 'filter' === $declaration['name'] ) { - unset( $declarations[ $index ] ); - $declarations_duotone[] = $declaration; - } - } - - // 2. Generate and append the rules that use the general selector. - $block_rules .= static::to_ruleset( $selector, $declarations ); - - // 3. Generate and append the rules that use the duotone selector. - if ( isset( $block_metadata['duotone'] ) && ! empty( $declarations_duotone ) ) { - $selector_duotone = static::scope_selector( $block_metadata['selector'], $block_metadata['duotone'] ); - $block_rules .= static::to_ruleset( $selector_duotone, $declarations_duotone ); - } - - // 4. Generate Layout block gap styles. - if ( - static::ROOT_BLOCK_SELECTOR !== $selector && - ! empty( $block_metadata['name'] ) - ) { - $block_rules .= $this->get_layout_styles( $block_metadata ); - } - - // 5. Generate and append the feature level rulesets. - foreach ( $feature_declarations as $feature_selector => $individual_feature_declarations ) { - $block_rules .= static::to_ruleset( $feature_selector, $individual_feature_declarations ); - } - - if ( static::ROOT_BLOCK_SELECTOR === $selector ) { - /* - * Reset default browser margin on the root body element. - * This is set on the root selector **before** generating the ruleset - * from the `theme.json`. This is to ensure that if the `theme.json` declares - * `margin` in its `spacing` declaration for the `body` element then these - * user-generated values take precedence in the CSS cascade. - * @link https://github.com/WordPress/gutenberg/issues/36147. - */ - $block_rules .= 'body { margin: 0;'; - - /* - * If there are content and wide widths in theme.json, output them - * as custom properties on the body element so all blocks can use them. - */ - if ( isset( $settings['layout']['contentSize'] ) || isset( $settings['layout']['wideSize'] ) ) { - $content_size = isset( $settings['layout']['contentSize'] ) ? $settings['layout']['contentSize'] : $settings['layout']['wideSize']; - $content_size = static::is_safe_css_declaration( 'max-width', $content_size ) ? $content_size : 'initial'; - $wide_size = isset( $settings['layout']['wideSize'] ) ? $settings['layout']['wideSize'] : $settings['layout']['contentSize']; - $wide_size = static::is_safe_css_declaration( 'max-width', $wide_size ) ? $wide_size : 'initial'; - $block_rules .= '--wp--style--global--content-size: ' . $content_size . ';'; - $block_rules .= '--wp--style--global--wide-size: ' . $wide_size . ';'; - } - - $block_rules .= '}'; - } - - if ( static::ROOT_BLOCK_SELECTOR === $selector ) { - - if ( $use_root_padding ) { - $block_rules .= '.wp-site-blocks { padding-top: var(--wp--style--root--padding-top); padding-bottom: var(--wp--style--root--padding-bottom); }'; - $block_rules .= '.has-global-padding { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }'; - $block_rules .= '.has-global-padding > .alignfull { margin-right: calc(var(--wp--style--root--padding-right) * -1); margin-left: calc(var(--wp--style--root--padding-left) * -1); }'; - $block_rules .= '.has-global-padding > .alignfull > :where([class*="wp-block-"]:not(.alignfull):not(.alignfull):not([class*="__"]),p,h1,h2,h3,h4,h5,h6,ul,ol) { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }'; - } - - $block_rules .= '.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }'; - $block_rules .= '.wp-site-blocks > .alignright { float: right; margin-left: 2em; }'; - $block_rules .= '.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }'; - - $block_gap_value = _wp_array_get( $this->theme_json, array( 'styles', 'spacing', 'blockGap' ), '0.5em' ); - $has_block_gap_support = _wp_array_get( $this->theme_json, array( 'settings', 'spacing', 'blockGap' ) ) !== null; - if ( $has_block_gap_support ) { - $block_gap_value = static::get_property_value( $this->theme_json, array( 'styles', 'spacing', 'blockGap' ) ); - $block_rules .= '.wp-site-blocks > * { margin-block-start: 0; margin-block-end: 0; }'; - $block_rules .= ".wp-site-blocks > * + * { margin-block-start: $block_gap_value; }"; - - // For backwards compatibility, ensure the legacy block gap CSS variable is still available. - $block_rules .= "$selector { --wp--style--block-gap: $block_gap_value; }"; - } - $block_rules .= $this->get_layout_styles( $block_metadata ); - } - - return $block_rules; + * Gets the CSS rules for a particular block from theme.json. + * + * @since 6.1.0 + * + * @param array $block_metadata Metadata about the block to get styles for. + * + * @return string Styles for the block. + */ + public function get_styles_for_block( $block_metadata ) { + $node = _wp_array_get( $this->theme_json, $block_metadata['path'], array() ); + $use_root_padding = isset( $this->theme_json['settings']['useRootPaddingAwareAlignments'] ) && true === $this->theme_json['settings']['useRootPaddingAwareAlignments']; + $selector = $block_metadata['selector']; + $settings = _wp_array_get( $this->theme_json, array( 'settings' ) ); + + // Process style declarations for block support features the current + // block contains selectors for. Values for a feature with a custom + // selector are filtered from the theme.json node before it is + // processed as normal. + $feature_declarations = array(); + + if ( ! empty( $block_metadata['features'] ) ) { + foreach ( $block_metadata['features'] as $feature_name => $feature_selector ) { + if ( ! empty( $node[ $feature_name ] ) ) { + // Create temporary node containing only the feature data + // to leverage existing `compute_style_properties` function. + $feature = array( $feature_name => $node[ $feature_name ] ); + // Generate the feature's declarations only. + $new_feature_declarations = static::compute_style_properties( $feature, $settings, null, $this->theme_json ); + + // Merge new declarations with any that already exist for + // the feature selector. This may occur when multiple block + // support features use the same custom selector. + if ( isset( $feature_declarations[ $feature_selector ] ) ) { + $feature_declarations[ $feature_selector ] = array_merge( $feature_declarations[ $feature_selector ], $new_feature_declarations ); + } else { + $feature_declarations[ $feature_selector ] = $new_feature_declarations; + } + + // Remove the feature from the block's node now the + // styles will be included under the feature level selector. + unset( $node[ $feature_name ] ); + } + } + } + + // Get a reference to element name from path. + // $block_metadata['path'] = array('styles','elements','link'); + // Make sure that $block_metadata['path'] describes an element node, like ['styles', 'element', 'link']. + // Skip non-element paths like just ['styles']. + $is_processing_element = in_array( 'elements', $block_metadata['path'], true ); + + $current_element = $is_processing_element ? $block_metadata['path'][ count( $block_metadata['path'] ) - 1 ] : null; + + $element_pseudo_allowed = array_key_exists( $current_element, static::VALID_ELEMENT_PSEUDO_SELECTORS ) ? static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ] : array(); + + // Check for allowed pseudo classes (e.g. ":hover") from the $selector ("a:hover"). + // This also resets the array keys. + $pseudo_matches = array_values( + array_filter( + $element_pseudo_allowed, + function( $pseudo_selector ) use ( $selector ) { + return str_contains( $selector, $pseudo_selector ); + } + ) + ); + + $pseudo_selector = isset( $pseudo_matches[0] ) ? $pseudo_matches[0] : null; + + // If the current selector is a pseudo selector that's defined in the allow list for the current + // element then compute the style properties for it. + // Otherwise just compute the styles for the default selector as normal. + if ( $pseudo_selector && isset( $node[ $pseudo_selector ] ) && array_key_exists( $current_element, static::VALID_ELEMENT_PSEUDO_SELECTORS ) && in_array( $pseudo_selector, static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ], true ) ) { + $declarations = static::compute_style_properties( $node[ $pseudo_selector ], $settings, null, $this->theme_json, $selector, $use_root_padding ); + } else { + $declarations = static::compute_style_properties( $node, $settings, null, $this->theme_json, $selector, $use_root_padding ); + } + + $block_rules = ''; + + // 1. Separate the ones who use the general selector + // and the ones who use the duotone selector. + $declarations_duotone = array(); + foreach ( $declarations as $index => $declaration ) { + if ( 'filter' === $declaration['name'] ) { + unset( $declarations[ $index ] ); + $declarations_duotone[] = $declaration; + } + } + + // 2. Generate and append the rules that use the general selector. + $block_rules .= static::to_ruleset( $selector, $declarations ); + + // 3. Generate and append the rules that use the duotone selector. + if ( isset( $block_metadata['duotone'] ) && ! empty( $declarations_duotone ) ) { + $selector_duotone = static::scope_selector( $block_metadata['selector'], $block_metadata['duotone'] ); + $block_rules .= static::to_ruleset( $selector_duotone, $declarations_duotone ); + } + + // 4. Generate Layout block gap styles. + if ( + static::ROOT_BLOCK_SELECTOR !== $selector && + ! empty( $block_metadata['name'] ) + ) { + $block_rules .= $this->get_layout_styles( $block_metadata ); + } + + // 5. Generate and append the feature level rulesets. + foreach ( $feature_declarations as $feature_selector => $individual_feature_declarations ) { + $block_rules .= static::to_ruleset( $feature_selector, $individual_feature_declarations ); + } + + if ( static::ROOT_BLOCK_SELECTOR === $selector ) { + /* + * Reset default browser margin on the root body element. + * This is set on the root selector **before** generating the ruleset + * from the `theme.json`. This is to ensure that if the `theme.json` declares + * `margin` in its `spacing` declaration for the `body` element then these + * user-generated values take precedence in the CSS cascade. + * @link https://github.com/WordPress/gutenberg/issues/36147. + */ + $block_rules .= 'body { margin: 0;'; + + /* + * If there are content and wide widths in theme.json, output them + * as custom properties on the body element so all blocks can use them. + */ + if ( isset( $settings['layout']['contentSize'] ) || isset( $settings['layout']['wideSize'] ) ) { + $content_size = isset( $settings['layout']['contentSize'] ) ? $settings['layout']['contentSize'] : $settings['layout']['wideSize']; + $content_size = static::is_safe_css_declaration( 'max-width', $content_size ) ? $content_size : 'initial'; + $wide_size = isset( $settings['layout']['wideSize'] ) ? $settings['layout']['wideSize'] : $settings['layout']['contentSize']; + $wide_size = static::is_safe_css_declaration( 'max-width', $wide_size ) ? $wide_size : 'initial'; + $block_rules .= '--wp--style--global--content-size: ' . $content_size . ';'; + $block_rules .= '--wp--style--global--wide-size: ' . $wide_size . ';'; + } + + $block_rules .= ' }'; + } + + if ( static::ROOT_BLOCK_SELECTOR === $selector ) { + + if ( $use_root_padding ) { + $block_rules .= '.wp-site-blocks { padding-top: var(--wp--style--root--padding-top); padding-bottom: var(--wp--style--root--padding-bottom); }'; + $block_rules .= '.has-global-padding { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }'; + $block_rules .= '.has-global-padding > .alignfull { margin-right: calc(var(--wp--style--root--padding-right) * -1); margin-left: calc(var(--wp--style--root--padding-left) * -1); }'; + $block_rules .= '.has-global-padding > .alignfull > :where([class*="wp-block-"]:not(.alignfull):not(.alignfull):not([class*="__"]),p,h1,h2,h3,h4,h5,h6,ul,ol) { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }'; + } + + $block_rules .= '.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }'; + $block_rules .= '.wp-site-blocks > .alignright { float: right; margin-left: 2em; }'; + $block_rules .= '.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }'; + + $block_gap_value = _wp_array_get( $this->theme_json, array( 'styles', 'spacing', 'blockGap' ), '0.5em' ); + $has_block_gap_support = _wp_array_get( $this->theme_json, array( 'settings', 'spacing', 'blockGap' ) ) !== null; + if ( $has_block_gap_support ) { + $block_gap_value = static::get_property_value( $this->theme_json, array( 'styles', 'spacing', 'blockGap' ) ); + $block_rules .= '.wp-site-blocks > * { margin-block-start: 0; margin-block-end: 0; }'; + $block_rules .= ".wp-site-blocks > * + * { margin-block-start: $block_gap_value; }"; + + // For backwards compatibility, ensure the legacy block gap CSS variable is still available. + $block_rules .= "$selector { --wp--style--block-gap: $block_gap_value; }"; + } + $block_rules .= $this->get_layout_styles( $block_metadata ); + } + + return $block_rules; } /** From 174fb974db062408a298032b9fab7d9d617c4ddb Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Thu, 8 Sep 2022 16:27:49 +1000 Subject: [PATCH 05/16] Merge in required changes for root padding --- src/wp-includes/class-wp-theme-json.php | 41 ++++++++++++++++++++----- tests/phpunit/tests/blocks/editor.php | 2 +- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/wp-includes/class-wp-theme-json.php b/src/wp-includes/class-wp-theme-json.php index 61bafd8f286b0..2067cdad576b8 100644 --- a/src/wp-includes/class-wp-theme-json.php +++ b/src/wp-includes/class-wp-theme-json.php @@ -1560,15 +1560,17 @@ protected static function flatten_tree( $tree, $prefix = '', $token = '--' ) { * * @since 5.8.0 * @since 5.9.0 Added the `$settings` and `$properties` parameters. - * @since 6.1.0 Added the `$theme_json` parameter. - * - * @param array $styles Styles to process. - * @param array $settings Theme settings. - * @param array $properties Properties metadata. - * @param array $theme_json Theme JSON array. - * @return array Returns the modified $declarations. + * @since 6.1.0 Added `$theme_json`, `$selector`, and `$use_root_padding` parameters. + * + * @param array $styles Styles to process. + * @param array $settings Theme settings. + * @param array $properties Properties metadata. + * @param array $theme_json Theme JSON array. + * @param string $selector The style block selector. + * @param boolean $use_root_padding Whether to add custom properties at root level. + * @return array Returns the modified $declarations. */ - protected static function compute_style_properties( $styles, $settings = array(), $properties = null, $theme_json = null ) { + protected static function compute_style_properties( $styles, $settings = array(), $properties = null, $theme_json = null, $selector = null, $use_root_padding = null ) { if ( null === $properties ) { $properties = static::PROPERTIES_METADATA; } @@ -1578,9 +1580,24 @@ protected static function compute_style_properties( $styles, $settings = array() return $declarations; } + $root_variable_duplicates = array(); + foreach ( $properties as $css_property => $value_path ) { $value = static::get_property_value( $styles, $value_path, $theme_json ); + if ( str_starts_with( $css_property, '--wp--style--root--' ) && ( static::ROOT_BLOCK_SELECTOR !== $selector || ! $use_root_padding ) ) { + continue; + } + // Root-level padding styles don't currently support strings with CSS shorthand values. + // This may change: https://github.com/WordPress/gutenberg/issues/40132. + if ( '--wp--style--root--padding' === $css_property && is_string( $value ) ) { + continue; + } + + if ( str_starts_with( $css_property, '--wp--style--root--' ) && $use_root_padding ) { + $root_variable_duplicates[] = substr( $css_property, strlen( '--wp--style--root--' ) ); + } + // Look up protected properties, keyed by value path. // Skip protected properties that are explicitly set to `null`. if ( is_array( $value_path ) ) { @@ -1605,6 +1622,14 @@ protected static function compute_style_properties( $styles, $settings = array() ); } + // If a variable value is added to the root, the corresponding property should be removed. + foreach ( $root_variable_duplicates as $duplicate ) { + $discard = array_search( $duplicate, array_column( $declarations, 'name' ), true ); + if ( is_numeric( $discard ) ) { + array_splice( $declarations, $discard, 1 ); + } + } + return $declarations; } diff --git a/tests/phpunit/tests/blocks/editor.php b/tests/phpunit/tests/blocks/editor.php index 1efa8ebb09fc0..53b8d96956379 100644 --- a/tests/phpunit/tests/blocks/editor.php +++ b/tests/phpunit/tests/blocks/editor.php @@ -202,7 +202,7 @@ public function test_get_allowed_block_types_deprecated_filter_post_editor() { public function test_get_default_block_editor_settings() { $settings = get_default_block_editor_settings(); - $this->assertCount( 18, $settings ); + $this->assertCount( 19, $settings ); $this->assertFalse( $settings['alignWide'] ); $this->assertIsArray( $settings['allowedMimeTypes'] ); $this->assertTrue( $settings['allowedBlockTypes'] ); From e225be6b5cea0fe73612cadf0ef024e7213ba684 Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Thu, 8 Sep 2022 16:59:40 +1000 Subject: [PATCH 06/16] Try to fix failing tests --- .../tests/rest-api/rest-themes-controller.php | 3 ++- tests/phpunit/tests/theme/wpThemeJson.php | 13 ++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/tests/phpunit/tests/rest-api/rest-themes-controller.php b/tests/phpunit/tests/rest-api/rest-themes-controller.php index da03319286136..8055e95116067 100644 --- a/tests/phpunit/tests/rest-api/rest-themes-controller.php +++ b/tests/phpunit/tests/rest-api/rest-themes-controller.php @@ -396,6 +396,7 @@ public function test_get_item_schema() { $this->assertArrayHasKey( 'dark-editor-style', $theme_supports ); $this->assertArrayHasKey( 'disable-custom-font-sizes', $theme_supports ); $this->assertArrayHasKey( 'disable-custom-gradients', $theme_supports ); + $this->assertArrayHasKey( 'disable-layout-styles', $theme_supports ); $this->assertArrayHasKey( 'editor-color-palette', $theme_supports ); $this->assertArrayHasKey( 'editor-font-sizes', $theme_supports ); $this->assertArrayHasKey( 'editor-gradient-presets', $theme_supports ); @@ -406,7 +407,7 @@ public function test_get_item_schema() { $this->assertArrayHasKey( 'responsive-embeds', $theme_supports ); $this->assertArrayHasKey( 'title-tag', $theme_supports ); $this->assertArrayHasKey( 'wp-block-styles', $theme_supports ); - $this->assertCount( 21, $theme_supports ); + $this->assertCount( 22, $theme_supports ); } /** diff --git a/tests/phpunit/tests/theme/wpThemeJson.php b/tests/phpunit/tests/theme/wpThemeJson.php index 740ea4a6d7b9c..6748a8a7a9793 100644 --- a/tests/phpunit/tests/theme/wpThemeJson.php +++ b/tests/phpunit/tests/theme/wpThemeJson.php @@ -423,9 +423,9 @@ public function test_get_stylesheet_renders_enabled_protected_properties() { ) ); - $expected = 'body { margin: 0; }body{--wp--style--block-gap: 1em;}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }.wp-site-blocks > * { margin-block-start: 0; margin-block-end: 0; }.wp-site-blocks > * + * { margin-block-start: var( --wp--style--block-gap ); }'; - $this->assertSame( $expected, $theme_json->get_stylesheet() ); - $this->assertSame( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) ); + $expected = 'body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }.wp-site-blocks > * { margin-block-start: 0; margin-block-end: 0; }.wp-site-blocks > * + * { margin-block-start: 1em; }body { --wp--style--block-gap: 1em; }'; + $this->assertEquals( $expected, $theme_json->get_stylesheet() ); + $this->assertEquals( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) ); } /** @@ -544,13 +544,16 @@ public function test_get_stylesheet() { ), ), ), + 'spacing' => array( + 'blockGap' => '24px', + ), ), 'misc' => 'value', ) ); $variables = 'body{--wp--preset--color--grey: grey;--wp--preset--font-family--small: 14px;--wp--preset--font-family--big: 41px;}.wp-block-group{--wp--custom--base-font: 16;--wp--custom--line-height--small: 1.2;--wp--custom--line-height--medium: 1.4;--wp--custom--line-height--large: 1.8;}'; - $styles = 'body { margin: 0; }body{color: var(--wp--preset--color--grey);}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }a:where(:not(.wp-element-button)){background-color: #333;color: #111;}.wp-block-group{border-radius: 10px;padding: 24px;}.wp-block-group a:where(:not(.wp-element-button)){color: #111;}h1,h2,h3,h4,h5,h6{color: #123456;}h1 a:where(:not(.wp-element-button)),h2 a:where(:not(.wp-element-button)),h3 a:where(:not(.wp-element-button)),h4 a:where(:not(.wp-element-button)),h5 a:where(:not(.wp-element-button)),h6 a:where(:not(.wp-element-button)){background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a:where(:not(.wp-element-button)){background-color: #777;color: #555;}.wp-block-image{border-top-left-radius: 10px;border-bottom-right-radius: 1em;margin-bottom: 30px;}'; + $styles = 'body { margin: 0; }body{color: var(--wp--preset--color--grey);}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }.wp-site-blocks > * { margin-block-start: 0; margin-block-end: 0; }.wp-site-blocks > * + * { margin-block-start: 24px; }body { --wp--style--block-gap: 24px; }a{background-color: #333;color: #111;}.wp-block-group{border-radius: 10px;padding: 24px;}.wp-block-group a{color: #111;}h1,h2,h3,h4,h5,h6{color: #123456;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a{background-color: #777;color: #555;}.wp-block-image{border-top-left-radius: 10px;border-bottom-right-radius: 1em;margin-bottom: 30px;}'; $presets = '.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}.has-small-font-family{font-family: var(--wp--preset--font-family--small) !important;}.has-big-font-family{font-family: var(--wp--preset--font-family--big) !important;}'; $all = $variables . $styles . $presets; $this->assertSame( $all, $theme_json->get_stylesheet() ); @@ -2676,7 +2679,7 @@ public function test_sanitization() { 'core/group' => array( 'spacing' => array( 'margin' => 'valid value', - 'blockGap' => 'invalid value', + 'blockGap' => 'url("https://wordpress.org/?invalid-value")', ), ), ), From cfafdea2305c5e16e2ce0863d1a698d36877d9c5 Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Thu, 8 Sep 2022 17:05:45 +1000 Subject: [PATCH 07/16] Try adding in tests from Gutenberg --- tests/phpunit/tests/theme/wpThemeJson.php | 349 +++++++++++++++++++++- 1 file changed, 348 insertions(+), 1 deletion(-) diff --git a/tests/phpunit/tests/theme/wpThemeJson.php b/tests/phpunit/tests/theme/wpThemeJson.php index 6748a8a7a9793..8fef9c29b861c 100644 --- a/tests/phpunit/tests/theme/wpThemeJson.php +++ b/tests/phpunit/tests/theme/wpThemeJson.php @@ -2988,7 +2988,7 @@ public function test_get_property_value_valid() { ), ), ), - ), + ) ) ); @@ -3084,4 +3084,351 @@ public function test_get_property_value_self() { $this->assertSame( $expected, $theme_json->get_stylesheet() ); } + + /** + * @dataProvider data_get_layout_definitions + * + * @ticket 56467 + * + * @param array $layout_definitions Layout definitions as stored in core theme.json. + */ + public function test_get_stylesheet_generates_layout_styles( $layout_definitions ) { + $theme_json = new WP_Theme_JSON_Gutenberg( + array( + 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, + 'settings' => array( + 'layout' => array( + 'definitions' => $layout_definitions, + ), + 'spacing' => array( + 'blockGap' => true, + ), + ), + 'styles' => array( + 'spacing' => array( + 'blockGap' => '1em', + ), + ), + ), + 'default' + ); + + // Results also include root site blocks styles. + $this->assertEquals( + 'body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }.wp-site-blocks > * { margin-block-start: 0; margin-block-end: 0; }.wp-site-blocks > * + * { margin-block-start: 1em; }body { --wp--style--block-gap: 1em; }body .is-layout-flow > *{margin-block-start: 0;margin-block-end: 0;}body .is-layout-flow > * + *{margin-block-start: 1em;margin-block-end: 0;}body .is-layout-flex{gap: 1em;}body .is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-flex{display: flex;}body .is-layout-flex{flex-wrap: wrap;align-items: center;}', + $theme_json->get_stylesheet( array( 'styles' ) ) + ); + } + + /** + * @dataProvider data_get_layout_definitions + * + * @ticket 56467 + * + * @param array $layout_definitions Layout definitions as stored in core theme.json. + */ + public function test_get_stylesheet_generates_layout_styles_with_spacing_presets( $layout_definitions ) { + $theme_json = new WP_Theme_JSON_Gutenberg( + array( + 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, + 'settings' => array( + 'layout' => array( + 'definitions' => $layout_definitions, + ), + 'spacing' => array( + 'blockGap' => true, + ), + ), + 'styles' => array( + 'spacing' => array( + 'blockGap' => 'var:preset|spacing|60', + ), + ), + ), + 'default' + ); + + // Results also include root site blocks styles. + $this->assertEquals( + 'body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }.wp-site-blocks > * { margin-block-start: 0; margin-block-end: 0; }.wp-site-blocks > * + * { margin-block-start: var(--wp--preset--spacing--60); }body { --wp--style--block-gap: var(--wp--preset--spacing--60); }body .is-layout-flow > *{margin-block-start: 0;margin-block-end: 0;}body .is-layout-flow > * + *{margin-block-start: var(--wp--preset--spacing--60);margin-block-end: 0;}body .is-layout-flex{gap: var(--wp--preset--spacing--60);}body .is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-flex{display: flex;}body .is-layout-flex{flex-wrap: wrap;align-items: center;}', + $theme_json->get_stylesheet( array( 'styles' ) ) + ); + } + + /** + * @dataProvider data_get_layout_definitions + * + * @ticket 56467 + * + * @param array $layout_definitions Layout definitions as stored in core theme.json. + */ + public function test_get_stylesheet_generates_fallback_gap_layout_styles( $layout_definitions ) { + $theme_json = new WP_Theme_JSON_Gutenberg( + array( + 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, + 'settings' => array( + 'layout' => array( + 'definitions' => $layout_definitions, + ), + 'spacing' => array( + 'blockGap' => null, + ), + ), + 'styles' => array( + 'spacing' => array( + 'blockGap' => '1em', + ), + ), + ), + 'default' + ); + $stylesheet = $theme_json->get_stylesheet( array( 'styles' ) ); + + // Results also include root site blocks styles. + $this->assertEquals( + 'body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }:where(.is-layout-flex){gap: 0.5em;}body .is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-flex{display: flex;}body .is-layout-flex{flex-wrap: wrap;align-items: center;}', + $stylesheet + ); + } + + /** + * @dataProvider data_get_layout_definitions + * + * @ticket 56467 + * + * @param array $layout_definitions Layout definitions as stored in core theme.json. + */ + public function test_get_stylesheet_generates_base_fallback_gap_layout_styles( $layout_definitions ) { + $theme_json = new WP_Theme_JSON_Gutenberg( + array( + 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, + 'settings' => array( + 'layout' => array( + 'definitions' => $layout_definitions, + ), + 'spacing' => array( + 'blockGap' => null, + ), + ), + ), + 'default' + ); + $stylesheet = $theme_json->get_stylesheet( array( 'base-layout-styles' ) ); + + // Note the `base-layout-styles` includes a fallback gap for the Columns block for backwards compatibility. + $this->assertEquals( + ':where(.is-layout-flex){gap: 0.5em;}body .is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-flex{display: flex;}body .is-layout-flex{flex-wrap: wrap;align-items: center;}:where(.wp-block-columns.is-layout-flex){gap: 2em;}', + $stylesheet + ); + } + + /** + * @dataProvider data_get_layout_definitions + * + * @ticket 56467 + * + * @param array $layout_definitions Layout definitions as stored in core theme.json. + */ + public function test_get_stylesheet_skips_layout_styles( $layout_definitions ) { + add_theme_support( 'disable-layout-styles' ); + $theme_json = new WP_Theme_JSON_Gutenberg( + array( + 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, + 'settings' => array( + 'layout' => array( + 'definitions' => $layout_definitions, + ), + 'spacing' => array( + 'blockGap' => null, + ), + ), + ), + 'default' + ); + $stylesheet = $theme_json->get_stylesheet( array( 'base-layout-styles' ) ); + remove_theme_support( 'disable-layout-styles' ); + + // All Layout styles should be skipped. + $this->assertEquals( + '', + $stylesheet + ); + } + + /** + * Data provider for layout tests. + * + * @ticket 56467 + * + * @return array + */ + public function data_get_layout_definitions() { + return array( + 'layout definitions' => array( + array( + 'default' => array( + 'name' => 'default', + 'slug' => 'flow', + 'className' => 'is-layout-flow', + 'baseStyles' => array( + array( + 'selector' => ' > .alignleft', + 'rules' => array( + 'float' => 'left', + 'margin-inline-start' => '0', + 'margin-inline-end' => '2em', + ), + ), + array( + 'selector' => ' > .alignright', + 'rules' => array( + 'float' => 'right', + 'margin-inline-start' => '2em', + 'margin-inline-end' => '0', + ), + ), + array( + 'selector' => ' > .aligncenter', + 'rules' => array( + 'margin-left' => 'auto !important', + 'margin-right' => 'auto !important', + ), + ), + ), + 'spacingStyles' => array( + array( + 'selector' => ' > *', + 'rules' => array( + 'margin-block-start' => '0', + 'margin-block-end' => '0', + ), + ), + array( + 'selector' => ' > * + *', + 'rules' => array( + 'margin-block-start' => null, + 'margin-block-end' => '0', + ), + ), + ), + ), + 'flex' => array( + 'name' => 'flex', + 'slug' => 'flex', + 'className' => 'is-layout-flex', + 'displayMode' => 'flex', + 'baseStyles' => array( + array( + 'selector' => '', + 'rules' => array( + 'flex-wrap' => 'wrap', + 'align-items' => 'center', + ), + ), + ), + 'spacingStyles' => array( + array( + 'selector' => '', + 'rules' => array( + 'gap' => null, + ), + ), + ), + ), + ), + ), + ); + } + + /** + * @ticket 56467 + */ + function test_get_styles_for_block_with_padding_aware_alignments() { + $theme_json = new WP_Theme_JSON_Gutenberg( + array( + 'version' => 2, + 'styles' => array( + 'spacing' => array( + 'padding' => array( + 'top' => '10px', + 'right' => '12px', + 'bottom' => '10px', + 'left' => '12px', + ), + ), + ), + 'settings' => array( + 'useRootPaddingAwareAlignments' => true, + ), + ) + ); + + $metadata = array( + 'path' => array( + '0' => 'styles', + ), + 'selector' => 'body', + ); + + $expected = 'body{--wp--style--root--padding-top: 10px;--wp--style--root--padding-right: 12px;--wp--style--root--padding-bottom: 10px;--wp--style--root--padding-left: 12px;}body { margin: 0; }.wp-site-blocks { padding-top: var(--wp--style--root--padding-top); padding-bottom: var(--wp--style--root--padding-bottom); }.has-global-padding { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }.has-global-padding > .alignfull { margin-right: calc(var(--wp--style--root--padding-right) * -1); margin-left: calc(var(--wp--style--root--padding-left) * -1); }.has-global-padding > .alignfull > :where([class*="wp-block-"]:not(.alignfull):not(.alignfull):not([class*="__"]),p,h1,h2,h3,h4,h5,h6,ul,ol) { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }'; + $this->assertEquals( $expected, $theme_json->get_styles_for_block( $metadata ) ); + } + + /** + * @ticket 56467 + */ + function test_get_styles_for_block_without_padding_aware_alignments() { + $theme_json = new WP_Theme_JSON_Gutenberg( + array( + 'version' => 2, + 'styles' => array( + 'spacing' => array( + 'padding' => array( + 'top' => '10px', + 'right' => '12px', + 'bottom' => '10px', + 'left' => '12px', + ), + ), + ), + ) + ); + + $metadata = array( + 'path' => array( + '0' => 'styles', + ), + 'selector' => 'body', + ); + + $expected = 'body{padding-top: 10px;padding-right: 12px;padding-bottom: 10px;padding-left: 12px;}body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }'; + $this->assertEquals( $expected, $theme_json->get_styles_for_block( $metadata ) ); + } + + /** + * @ticket 56467 + */ + function test_get_styles_for_block_with_content_width() { + $theme_json = new WP_Theme_JSON_Gutenberg( + array( + 'version' => 2, + 'settings' => array( + 'layout' => array( + 'contentSize' => '800px', + 'wideSize' => '1000px', + ), + ), + ) + ); + + $metadata = array( + 'path' => array( + '0' => 'settings', + ), + 'selector' => 'body', + ); + + $expected = 'body { margin: 0;--wp--style--global--content-size: 800px;--wp--style--global--wide-size: 1000px; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }'; + $this->assertEquals( $expected, $theme_json->get_styles_for_block( $metadata ) ); + } } From 97611521041fae6237dc362c47aa1a8d59145e8f Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Thu, 8 Sep 2022 17:13:55 +1000 Subject: [PATCH 08/16] Remove Gutenberg suffix --- tests/phpunit/tests/theme/wpThemeJson.php | 361 +++++++++++++++++++++- 1 file changed, 348 insertions(+), 13 deletions(-) diff --git a/tests/phpunit/tests/theme/wpThemeJson.php b/tests/phpunit/tests/theme/wpThemeJson.php index 8fef9c29b861c..0b54d99cbaa72 100644 --- a/tests/phpunit/tests/theme/wpThemeJson.php +++ b/tests/phpunit/tests/theme/wpThemeJson.php @@ -3093,9 +3093,9 @@ public function test_get_property_value_self() { * @param array $layout_definitions Layout definitions as stored in core theme.json. */ public function test_get_stylesheet_generates_layout_styles( $layout_definitions ) { - $theme_json = new WP_Theme_JSON_Gutenberg( + $theme_json = new WP_Theme_JSON( array( - 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, + 'version' => WP_Theme_JSON::LATEST_SCHEMA, 'settings' => array( 'layout' => array( 'definitions' => $layout_definitions, @@ -3128,9 +3128,9 @@ public function test_get_stylesheet_generates_layout_styles( $layout_definitions * @param array $layout_definitions Layout definitions as stored in core theme.json. */ public function test_get_stylesheet_generates_layout_styles_with_spacing_presets( $layout_definitions ) { - $theme_json = new WP_Theme_JSON_Gutenberg( + $theme_json = new WP_Theme_JSON( array( - 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, + 'version' => WP_Theme_JSON::LATEST_SCHEMA, 'settings' => array( 'layout' => array( 'definitions' => $layout_definitions, @@ -3163,9 +3163,9 @@ public function test_get_stylesheet_generates_layout_styles_with_spacing_presets * @param array $layout_definitions Layout definitions as stored in core theme.json. */ public function test_get_stylesheet_generates_fallback_gap_layout_styles( $layout_definitions ) { - $theme_json = new WP_Theme_JSON_Gutenberg( + $theme_json = new WP_Theme_JSON( array( - 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, + 'version' => WP_Theme_JSON::LATEST_SCHEMA, 'settings' => array( 'layout' => array( 'definitions' => $layout_definitions, @@ -3199,9 +3199,9 @@ public function test_get_stylesheet_generates_fallback_gap_layout_styles( $layou * @param array $layout_definitions Layout definitions as stored in core theme.json. */ public function test_get_stylesheet_generates_base_fallback_gap_layout_styles( $layout_definitions ) { - $theme_json = new WP_Theme_JSON_Gutenberg( + $theme_json = new WP_Theme_JSON( array( - 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, + 'version' => WP_Theme_JSON::LATEST_SCHEMA, 'settings' => array( 'layout' => array( 'definitions' => $layout_definitions, @@ -3231,9 +3231,9 @@ public function test_get_stylesheet_generates_base_fallback_gap_layout_styles( $ */ public function test_get_stylesheet_skips_layout_styles( $layout_definitions ) { add_theme_support( 'disable-layout-styles' ); - $theme_json = new WP_Theme_JSON_Gutenberg( + $theme_json = new WP_Theme_JSON( array( - 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, + 'version' => WP_Theme_JSON::LATEST_SCHEMA, 'settings' => array( 'layout' => array( 'definitions' => $layout_definitions, @@ -3340,11 +3340,346 @@ public function data_get_layout_definitions() { ); } + /** + * Tests generating the spacing presets array based on the spacing scale provided. + * + * @ticket 56467 + * + * @dataProvider data_generate_spacing_scale_fixtures + */ + function test_set_spacing_sizes( $spacing_scale, $expected_output ) { + $theme_json = new WP_Theme_JSON( + array( + 'version' => 2, + 'settings' => array( + 'spacing' => array( + 'spacingScale' => $spacing_scale, + ), + ), + ) + ); + + $theme_json->set_spacing_sizes(); + $this->assertSame( $expected_output, _wp_array_get( $theme_json->get_raw_data(), array( 'settings', 'spacing', 'spacingSizes', 'default' ) ) ); + } + + /** + * Data provider for spacing scale tests. + * + * @ticket 56467 + * + * @return array + */ + function data_generate_spacing_scale_fixtures() { + return array( + 'one_step_spacing_scale' => array( + 'spacingScale' => array( + 'operator' => '+', + 'increment' => 1.5, + 'steps' => 1, + 'mediumStep' => 4, + 'unit' => 'rem', + ), + 'expected_output' => array( + array( + 'name' => 'Medium', + 'slug' => '50', + 'size' => '4rem', + ), + ), + ), + + 'two_step_spacing_scale_should_add_step_above_medium' => array( + 'spacingScale' => array( + 'operator' => '+', + 'increment' => 1.5, + 'steps' => 2, + 'mediumStep' => 4, + 'unit' => 'rem', + ), + 'expected_output' => array( + array( + 'name' => 'Medium', + 'slug' => '50', + 'size' => '4rem', + ), + array( + 'name' => 'Large', + 'slug' => '60', + 'size' => '5.5rem', + ), + ), + ), + + 'three_step_spacing_scale_should_add_step_above_and_below_medium' => array( + 'spacingScale' => array( + 'operator' => '+', + 'increment' => 1.5, + 'steps' => 3, + 'mediumStep' => 4, + 'unit' => 'rem', + ), + 'expected_output' => array( + array( + 'name' => 'Small', + 'slug' => '40', + 'size' => '2.5rem', + ), + array( + 'name' => 'Medium', + 'slug' => '50', + 'size' => '4rem', + ), + array( + 'name' => 'Large', + 'slug' => '60', + 'size' => '5.5rem', + ), + ), + ), + + 'even_step_spacing_scale_steps_should_add_extra_step_above_medium' => array( + 'spacingScale' => array( + 'operator' => '+', + 'increment' => 1.5, + 'steps' => 4, + 'mediumStep' => 4, + 'unit' => 'rem', + ), + 'expected_output' => array( + array( + 'name' => 'Small', + 'slug' => '40', + 'size' => '2.5rem', + ), + array( + 'name' => 'Medium', + 'slug' => '50', + 'size' => '4rem', + ), + array( + 'name' => 'Large', + 'slug' => '60', + 'size' => '5.5rem', + ), + array( + 'name' => 'X-Large', + 'slug' => '70', + 'size' => '7rem', + ), + ), + ), + + 'if_bottom_end_will_go_below_zero_should_add_extra_steps_above_medium_instead' => array( + 'spacingScale' => array( + 'operator' => '+', + 'increment' => 2.5, + 'steps' => 5, + 'mediumStep' => 5, + 'unit' => 'rem', + ), + 'expected_output' => array( + array( + 'name' => 'Small', + 'slug' => '40', + 'size' => '2.5rem', + ), + array( + 'name' => 'Medium', + 'slug' => '50', + 'size' => '5rem', + ), + array( + 'name' => 'Large', + 'slug' => '60', + 'size' => '7.5rem', + ), + array( + 'name' => 'X-Large', + 'slug' => '70', + 'size' => '10rem', + ), + array( + 'name' => '2X-Large', + 'slug' => '80', + 'size' => '12.5rem', + ), + ), + ), + + 'multiplier_should_correctly_calculate_above_and_below_medium' => array( + 'spacingScale' => array( + 'operator' => '*', + 'increment' => 1.5, + 'steps' => 5, + 'mediumStep' => 1.5, + 'unit' => 'rem', + ), + 'expected_output' => array( + array( + 'name' => 'X-Small', + 'slug' => '30', + 'size' => '0.67rem', + ), + array( + 'name' => 'Small', + 'slug' => '40', + 'size' => '1rem', + ), + array( + 'name' => 'Medium', + 'slug' => '50', + 'size' => '1.5rem', + ), + array( + 'name' => 'Large', + 'slug' => '60', + 'size' => '2.25rem', + ), + array( + 'name' => 'X-Large', + 'slug' => '70', + 'size' => '3.38rem', + ), + ), + ), + + 'increment_<_1_combined_with_*_operator_should_act_as_divisor_to_calculate_above_and_below_medium' => array( + 'spacingScale' => array( + 'operator' => '*', + 'increment' => 0.25, + 'steps' => 5, + 'mediumStep' => 1.5, + 'unit' => 'rem', + ), + 'expected_output' => array( + array( + 'name' => 'X-Small', + 'slug' => '30', + 'size' => '0.09rem', + ), + array( + 'name' => 'Small', + 'slug' => '40', + 'size' => '0.38rem', + ), + array( + 'name' => 'Medium', + 'slug' => '50', + 'size' => '1.5rem', + ), + array( + 'name' => 'Large', + 'slug' => '60', + 'size' => '6rem', + ), + array( + 'name' => 'X-Large', + 'slug' => '70', + 'size' => '24rem', + ), + ), + ), + ); + } + + /** + * Tests generating the spacing presets array based on the spacing scale provided. + * + * @ticket 56467 + * + * @dataProvider data_set_spacing_sizes_when_invalid + */ + public function test_set_spacing_sizes_when_invalid( $spacing_scale, $expected_output ) { + $this->expectNotice(); + $this->expectNoticeMessage( 'Some of the theme.json settings.spacing.spacingScale values are invalid' ); + + $theme_json = new WP_Theme_JSON( + array( + 'version' => 2, + 'settings' => array( + 'spacing' => array( + 'spacingScale' => $spacing_scale, + ), + ), + ) + ); + + $theme_json->set_spacing_sizes(); + $this->assertSame( $expected_output, _wp_array_get( $theme_json->get_raw_data(), array( 'settings', 'spacing', 'spacingSizes', 'default' ) ) ); + } + + /** + * Data provider for spacing scale tests. + * + * @ticket 56467 + * + * @return array + */ + function data_set_spacing_sizes_when_invalid() { + return array( + + 'invalid_spacing_scale_values_missing_operator' => array( + 'spacingScale' => array( + 'operator' => '', + 'increment' => 1.5, + 'steps' => 1, + 'mediumStep' => 4, + 'unit' => 'rem', + ), + 'expected_output' => null, + ), + + 'invalid_spacing_scale_values_non_numeric_increment' => array( + 'spacingScale' => array( + 'operator' => '+', + 'increment' => 'add two to previous value', + 'steps' => 1, + 'mediumStep' => 4, + 'unit' => 'rem', + ), + 'expected_output' => null, + ), + + 'invalid_spacing_scale_values_non_numeric_steps' => array( + 'spacingScale' => array( + 'operator' => '+', + 'increment' => 1.5, + 'steps' => 'spiral staircase preferred', + 'mediumStep' => 4, + 'unit' => 'rem', + ), + 'expected_output' => null, + ), + + 'invalid_spacing_scale_values_non_numeric_medium_step' => array( + 'spacingScale' => array( + 'operator' => '+', + 'increment' => 1.5, + 'steps' => 5, + 'mediumStep' => 'That which is just right', + 'unit' => 'rem', + ), + 'expected_output' => null, + ), + + 'invalid_spacing_scale_values_missing_unit' => array( + 'spacingScale' => array( + 'operator' => '+', + 'increment' => 1.5, + 'steps' => 5, + 'mediumStep' => 4, + ), + 'expected_output' => null, + ), + ); + } + /** * @ticket 56467 */ function test_get_styles_for_block_with_padding_aware_alignments() { - $theme_json = new WP_Theme_JSON_Gutenberg( + $theme_json = new WP_Theme_JSON( array( 'version' => 2, 'styles' => array( @@ -3378,7 +3713,7 @@ function test_get_styles_for_block_with_padding_aware_alignments() { * @ticket 56467 */ function test_get_styles_for_block_without_padding_aware_alignments() { - $theme_json = new WP_Theme_JSON_Gutenberg( + $theme_json = new WP_Theme_JSON( array( 'version' => 2, 'styles' => array( @@ -3409,7 +3744,7 @@ function test_get_styles_for_block_without_padding_aware_alignments() { * @ticket 56467 */ function test_get_styles_for_block_with_content_width() { - $theme_json = new WP_Theme_JSON_Gutenberg( + $theme_json = new WP_Theme_JSON( array( 'version' => 2, 'settings' => array( From 754c7edcb8c02089c9505ab827c4c65a0ada8ead Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Mon, 12 Sep 2022 15:29:27 +1000 Subject: [PATCH 09/16] Remove spacing tests --- tests/phpunit/tests/theme/wpThemeJson.php | 335 ---------------------- 1 file changed, 335 deletions(-) diff --git a/tests/phpunit/tests/theme/wpThemeJson.php b/tests/phpunit/tests/theme/wpThemeJson.php index 0b54d99cbaa72..2fba75e2ae40a 100644 --- a/tests/phpunit/tests/theme/wpThemeJson.php +++ b/tests/phpunit/tests/theme/wpThemeJson.php @@ -3340,341 +3340,6 @@ public function data_get_layout_definitions() { ); } - /** - * Tests generating the spacing presets array based on the spacing scale provided. - * - * @ticket 56467 - * - * @dataProvider data_generate_spacing_scale_fixtures - */ - function test_set_spacing_sizes( $spacing_scale, $expected_output ) { - $theme_json = new WP_Theme_JSON( - array( - 'version' => 2, - 'settings' => array( - 'spacing' => array( - 'spacingScale' => $spacing_scale, - ), - ), - ) - ); - - $theme_json->set_spacing_sizes(); - $this->assertSame( $expected_output, _wp_array_get( $theme_json->get_raw_data(), array( 'settings', 'spacing', 'spacingSizes', 'default' ) ) ); - } - - /** - * Data provider for spacing scale tests. - * - * @ticket 56467 - * - * @return array - */ - function data_generate_spacing_scale_fixtures() { - return array( - 'one_step_spacing_scale' => array( - 'spacingScale' => array( - 'operator' => '+', - 'increment' => 1.5, - 'steps' => 1, - 'mediumStep' => 4, - 'unit' => 'rem', - ), - 'expected_output' => array( - array( - 'name' => 'Medium', - 'slug' => '50', - 'size' => '4rem', - ), - ), - ), - - 'two_step_spacing_scale_should_add_step_above_medium' => array( - 'spacingScale' => array( - 'operator' => '+', - 'increment' => 1.5, - 'steps' => 2, - 'mediumStep' => 4, - 'unit' => 'rem', - ), - 'expected_output' => array( - array( - 'name' => 'Medium', - 'slug' => '50', - 'size' => '4rem', - ), - array( - 'name' => 'Large', - 'slug' => '60', - 'size' => '5.5rem', - ), - ), - ), - - 'three_step_spacing_scale_should_add_step_above_and_below_medium' => array( - 'spacingScale' => array( - 'operator' => '+', - 'increment' => 1.5, - 'steps' => 3, - 'mediumStep' => 4, - 'unit' => 'rem', - ), - 'expected_output' => array( - array( - 'name' => 'Small', - 'slug' => '40', - 'size' => '2.5rem', - ), - array( - 'name' => 'Medium', - 'slug' => '50', - 'size' => '4rem', - ), - array( - 'name' => 'Large', - 'slug' => '60', - 'size' => '5.5rem', - ), - ), - ), - - 'even_step_spacing_scale_steps_should_add_extra_step_above_medium' => array( - 'spacingScale' => array( - 'operator' => '+', - 'increment' => 1.5, - 'steps' => 4, - 'mediumStep' => 4, - 'unit' => 'rem', - ), - 'expected_output' => array( - array( - 'name' => 'Small', - 'slug' => '40', - 'size' => '2.5rem', - ), - array( - 'name' => 'Medium', - 'slug' => '50', - 'size' => '4rem', - ), - array( - 'name' => 'Large', - 'slug' => '60', - 'size' => '5.5rem', - ), - array( - 'name' => 'X-Large', - 'slug' => '70', - 'size' => '7rem', - ), - ), - ), - - 'if_bottom_end_will_go_below_zero_should_add_extra_steps_above_medium_instead' => array( - 'spacingScale' => array( - 'operator' => '+', - 'increment' => 2.5, - 'steps' => 5, - 'mediumStep' => 5, - 'unit' => 'rem', - ), - 'expected_output' => array( - array( - 'name' => 'Small', - 'slug' => '40', - 'size' => '2.5rem', - ), - array( - 'name' => 'Medium', - 'slug' => '50', - 'size' => '5rem', - ), - array( - 'name' => 'Large', - 'slug' => '60', - 'size' => '7.5rem', - ), - array( - 'name' => 'X-Large', - 'slug' => '70', - 'size' => '10rem', - ), - array( - 'name' => '2X-Large', - 'slug' => '80', - 'size' => '12.5rem', - ), - ), - ), - - 'multiplier_should_correctly_calculate_above_and_below_medium' => array( - 'spacingScale' => array( - 'operator' => '*', - 'increment' => 1.5, - 'steps' => 5, - 'mediumStep' => 1.5, - 'unit' => 'rem', - ), - 'expected_output' => array( - array( - 'name' => 'X-Small', - 'slug' => '30', - 'size' => '0.67rem', - ), - array( - 'name' => 'Small', - 'slug' => '40', - 'size' => '1rem', - ), - array( - 'name' => 'Medium', - 'slug' => '50', - 'size' => '1.5rem', - ), - array( - 'name' => 'Large', - 'slug' => '60', - 'size' => '2.25rem', - ), - array( - 'name' => 'X-Large', - 'slug' => '70', - 'size' => '3.38rem', - ), - ), - ), - - 'increment_<_1_combined_with_*_operator_should_act_as_divisor_to_calculate_above_and_below_medium' => array( - 'spacingScale' => array( - 'operator' => '*', - 'increment' => 0.25, - 'steps' => 5, - 'mediumStep' => 1.5, - 'unit' => 'rem', - ), - 'expected_output' => array( - array( - 'name' => 'X-Small', - 'slug' => '30', - 'size' => '0.09rem', - ), - array( - 'name' => 'Small', - 'slug' => '40', - 'size' => '0.38rem', - ), - array( - 'name' => 'Medium', - 'slug' => '50', - 'size' => '1.5rem', - ), - array( - 'name' => 'Large', - 'slug' => '60', - 'size' => '6rem', - ), - array( - 'name' => 'X-Large', - 'slug' => '70', - 'size' => '24rem', - ), - ), - ), - ); - } - - /** - * Tests generating the spacing presets array based on the spacing scale provided. - * - * @ticket 56467 - * - * @dataProvider data_set_spacing_sizes_when_invalid - */ - public function test_set_spacing_sizes_when_invalid( $spacing_scale, $expected_output ) { - $this->expectNotice(); - $this->expectNoticeMessage( 'Some of the theme.json settings.spacing.spacingScale values are invalid' ); - - $theme_json = new WP_Theme_JSON( - array( - 'version' => 2, - 'settings' => array( - 'spacing' => array( - 'spacingScale' => $spacing_scale, - ), - ), - ) - ); - - $theme_json->set_spacing_sizes(); - $this->assertSame( $expected_output, _wp_array_get( $theme_json->get_raw_data(), array( 'settings', 'spacing', 'spacingSizes', 'default' ) ) ); - } - - /** - * Data provider for spacing scale tests. - * - * @ticket 56467 - * - * @return array - */ - function data_set_spacing_sizes_when_invalid() { - return array( - - 'invalid_spacing_scale_values_missing_operator' => array( - 'spacingScale' => array( - 'operator' => '', - 'increment' => 1.5, - 'steps' => 1, - 'mediumStep' => 4, - 'unit' => 'rem', - ), - 'expected_output' => null, - ), - - 'invalid_spacing_scale_values_non_numeric_increment' => array( - 'spacingScale' => array( - 'operator' => '+', - 'increment' => 'add two to previous value', - 'steps' => 1, - 'mediumStep' => 4, - 'unit' => 'rem', - ), - 'expected_output' => null, - ), - - 'invalid_spacing_scale_values_non_numeric_steps' => array( - 'spacingScale' => array( - 'operator' => '+', - 'increment' => 1.5, - 'steps' => 'spiral staircase preferred', - 'mediumStep' => 4, - 'unit' => 'rem', - ), - 'expected_output' => null, - ), - - 'invalid_spacing_scale_values_non_numeric_medium_step' => array( - 'spacingScale' => array( - 'operator' => '+', - 'increment' => 1.5, - 'steps' => 5, - 'mediumStep' => 'That which is just right', - 'unit' => 'rem', - ), - 'expected_output' => null, - ), - - 'invalid_spacing_scale_values_missing_unit' => array( - 'spacingScale' => array( - 'operator' => '+', - 'increment' => 1.5, - 'steps' => 5, - 'mediumStep' => 4, - ), - 'expected_output' => null, - ), - ); - } - /** * @ticket 56467 */ From e06b75554e0bb53b7e056f3b9c8fcbf064a0b8fd Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Mon, 12 Sep 2022 15:46:20 +1000 Subject: [PATCH 10/16] Adjust spacing --- src/wp-includes/class-wp-theme-json.php | 330 +++++++++++----------- tests/phpunit/tests/theme/wpThemeJson.php | 22 +- 2 files changed, 176 insertions(+), 176 deletions(-) diff --git a/src/wp-includes/class-wp-theme-json.php b/src/wp-includes/class-wp-theme-json.php index 2067cdad576b8..e71c38d455480 100644 --- a/src/wp-includes/class-wp-theme-json.php +++ b/src/wp-includes/class-wp-theme-json.php @@ -1913,171 +1913,171 @@ private static function get_block_nodes( $theme_json ) { } /** - * Gets the CSS rules for a particular block from theme.json. - * - * @since 6.1.0 - * - * @param array $block_metadata Metadata about the block to get styles for. - * - * @return string Styles for the block. - */ - public function get_styles_for_block( $block_metadata ) { - $node = _wp_array_get( $this->theme_json, $block_metadata['path'], array() ); - $use_root_padding = isset( $this->theme_json['settings']['useRootPaddingAwareAlignments'] ) && true === $this->theme_json['settings']['useRootPaddingAwareAlignments']; - $selector = $block_metadata['selector']; - $settings = _wp_array_get( $this->theme_json, array( 'settings' ) ); - - // Process style declarations for block support features the current - // block contains selectors for. Values for a feature with a custom - // selector are filtered from the theme.json node before it is - // processed as normal. - $feature_declarations = array(); - - if ( ! empty( $block_metadata['features'] ) ) { - foreach ( $block_metadata['features'] as $feature_name => $feature_selector ) { - if ( ! empty( $node[ $feature_name ] ) ) { - // Create temporary node containing only the feature data - // to leverage existing `compute_style_properties` function. - $feature = array( $feature_name => $node[ $feature_name ] ); - // Generate the feature's declarations only. - $new_feature_declarations = static::compute_style_properties( $feature, $settings, null, $this->theme_json ); - - // Merge new declarations with any that already exist for - // the feature selector. This may occur when multiple block - // support features use the same custom selector. - if ( isset( $feature_declarations[ $feature_selector ] ) ) { - $feature_declarations[ $feature_selector ] = array_merge( $feature_declarations[ $feature_selector ], $new_feature_declarations ); - } else { - $feature_declarations[ $feature_selector ] = $new_feature_declarations; - } - - // Remove the feature from the block's node now the - // styles will be included under the feature level selector. - unset( $node[ $feature_name ] ); - } - } - } - - // Get a reference to element name from path. - // $block_metadata['path'] = array('styles','elements','link'); - // Make sure that $block_metadata['path'] describes an element node, like ['styles', 'element', 'link']. - // Skip non-element paths like just ['styles']. - $is_processing_element = in_array( 'elements', $block_metadata['path'], true ); - - $current_element = $is_processing_element ? $block_metadata['path'][ count( $block_metadata['path'] ) - 1 ] : null; - - $element_pseudo_allowed = array_key_exists( $current_element, static::VALID_ELEMENT_PSEUDO_SELECTORS ) ? static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ] : array(); - - // Check for allowed pseudo classes (e.g. ":hover") from the $selector ("a:hover"). - // This also resets the array keys. - $pseudo_matches = array_values( - array_filter( - $element_pseudo_allowed, - function( $pseudo_selector ) use ( $selector ) { - return str_contains( $selector, $pseudo_selector ); - } - ) - ); - - $pseudo_selector = isset( $pseudo_matches[0] ) ? $pseudo_matches[0] : null; - - // If the current selector is a pseudo selector that's defined in the allow list for the current - // element then compute the style properties for it. - // Otherwise just compute the styles for the default selector as normal. - if ( $pseudo_selector && isset( $node[ $pseudo_selector ] ) && array_key_exists( $current_element, static::VALID_ELEMENT_PSEUDO_SELECTORS ) && in_array( $pseudo_selector, static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ], true ) ) { - $declarations = static::compute_style_properties( $node[ $pseudo_selector ], $settings, null, $this->theme_json, $selector, $use_root_padding ); - } else { - $declarations = static::compute_style_properties( $node, $settings, null, $this->theme_json, $selector, $use_root_padding ); - } - - $block_rules = ''; - - // 1. Separate the ones who use the general selector - // and the ones who use the duotone selector. - $declarations_duotone = array(); - foreach ( $declarations as $index => $declaration ) { - if ( 'filter' === $declaration['name'] ) { - unset( $declarations[ $index ] ); - $declarations_duotone[] = $declaration; - } - } - - // 2. Generate and append the rules that use the general selector. - $block_rules .= static::to_ruleset( $selector, $declarations ); - - // 3. Generate and append the rules that use the duotone selector. - if ( isset( $block_metadata['duotone'] ) && ! empty( $declarations_duotone ) ) { - $selector_duotone = static::scope_selector( $block_metadata['selector'], $block_metadata['duotone'] ); - $block_rules .= static::to_ruleset( $selector_duotone, $declarations_duotone ); - } - - // 4. Generate Layout block gap styles. - if ( - static::ROOT_BLOCK_SELECTOR !== $selector && - ! empty( $block_metadata['name'] ) - ) { - $block_rules .= $this->get_layout_styles( $block_metadata ); - } - - // 5. Generate and append the feature level rulesets. - foreach ( $feature_declarations as $feature_selector => $individual_feature_declarations ) { - $block_rules .= static::to_ruleset( $feature_selector, $individual_feature_declarations ); - } - - if ( static::ROOT_BLOCK_SELECTOR === $selector ) { - /* - * Reset default browser margin on the root body element. - * This is set on the root selector **before** generating the ruleset - * from the `theme.json`. This is to ensure that if the `theme.json` declares - * `margin` in its `spacing` declaration for the `body` element then these - * user-generated values take precedence in the CSS cascade. - * @link https://github.com/WordPress/gutenberg/issues/36147. - */ - $block_rules .= 'body { margin: 0;'; - - /* - * If there are content and wide widths in theme.json, output them - * as custom properties on the body element so all blocks can use them. - */ - if ( isset( $settings['layout']['contentSize'] ) || isset( $settings['layout']['wideSize'] ) ) { - $content_size = isset( $settings['layout']['contentSize'] ) ? $settings['layout']['contentSize'] : $settings['layout']['wideSize']; - $content_size = static::is_safe_css_declaration( 'max-width', $content_size ) ? $content_size : 'initial'; - $wide_size = isset( $settings['layout']['wideSize'] ) ? $settings['layout']['wideSize'] : $settings['layout']['contentSize']; - $wide_size = static::is_safe_css_declaration( 'max-width', $wide_size ) ? $wide_size : 'initial'; - $block_rules .= '--wp--style--global--content-size: ' . $content_size . ';'; - $block_rules .= '--wp--style--global--wide-size: ' . $wide_size . ';'; - } - - $block_rules .= ' }'; - } - - if ( static::ROOT_BLOCK_SELECTOR === $selector ) { - - if ( $use_root_padding ) { - $block_rules .= '.wp-site-blocks { padding-top: var(--wp--style--root--padding-top); padding-bottom: var(--wp--style--root--padding-bottom); }'; - $block_rules .= '.has-global-padding { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }'; - $block_rules .= '.has-global-padding > .alignfull { margin-right: calc(var(--wp--style--root--padding-right) * -1); margin-left: calc(var(--wp--style--root--padding-left) * -1); }'; - $block_rules .= '.has-global-padding > .alignfull > :where([class*="wp-block-"]:not(.alignfull):not(.alignfull):not([class*="__"]),p,h1,h2,h3,h4,h5,h6,ul,ol) { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }'; - } - - $block_rules .= '.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }'; - $block_rules .= '.wp-site-blocks > .alignright { float: right; margin-left: 2em; }'; - $block_rules .= '.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }'; - - $block_gap_value = _wp_array_get( $this->theme_json, array( 'styles', 'spacing', 'blockGap' ), '0.5em' ); - $has_block_gap_support = _wp_array_get( $this->theme_json, array( 'settings', 'spacing', 'blockGap' ) ) !== null; - if ( $has_block_gap_support ) { - $block_gap_value = static::get_property_value( $this->theme_json, array( 'styles', 'spacing', 'blockGap' ) ); - $block_rules .= '.wp-site-blocks > * { margin-block-start: 0; margin-block-end: 0; }'; - $block_rules .= ".wp-site-blocks > * + * { margin-block-start: $block_gap_value; }"; - - // For backwards compatibility, ensure the legacy block gap CSS variable is still available. - $block_rules .= "$selector { --wp--style--block-gap: $block_gap_value; }"; - } - $block_rules .= $this->get_layout_styles( $block_metadata ); - } - - return $block_rules; + * Gets the CSS rules for a particular block from theme.json. + * + * @since 6.1.0 + * + * @param array $block_metadata Metadata about the block to get styles for. + * + * @return string Styles for the block. + */ + public function get_styles_for_block( $block_metadata ) { + $node = _wp_array_get( $this->theme_json, $block_metadata['path'], array() ); + $use_root_padding = isset( $this->theme_json['settings']['useRootPaddingAwareAlignments'] ) && true === $this->theme_json['settings']['useRootPaddingAwareAlignments']; + $selector = $block_metadata['selector']; + $settings = _wp_array_get( $this->theme_json, array( 'settings' ) ); + + // Process style declarations for block support features the current + // block contains selectors for. Values for a feature with a custom + // selector are filtered from the theme.json node before it is + // processed as normal. + $feature_declarations = array(); + + if ( ! empty( $block_metadata['features'] ) ) { + foreach ( $block_metadata['features'] as $feature_name => $feature_selector ) { + if ( ! empty( $node[ $feature_name ] ) ) { + // Create temporary node containing only the feature data + // to leverage existing `compute_style_properties` function. + $feature = array( $feature_name => $node[ $feature_name ] ); + // Generate the feature's declarations only. + $new_feature_declarations = static::compute_style_properties( $feature, $settings, null, $this->theme_json ); + + // Merge new declarations with any that already exist for + // the feature selector. This may occur when multiple block + // support features use the same custom selector. + if ( isset( $feature_declarations[ $feature_selector ] ) ) { + $feature_declarations[ $feature_selector ] = array_merge( $feature_declarations[ $feature_selector ], $new_feature_declarations ); + } else { + $feature_declarations[ $feature_selector ] = $new_feature_declarations; + } + + // Remove the feature from the block's node now the + // styles will be included under the feature level selector. + unset( $node[ $feature_name ] ); + } + } + } + + // Get a reference to element name from path. + // $block_metadata['path'] = array('styles','elements','link'); + // Make sure that $block_metadata['path'] describes an element node, like ['styles', 'element', 'link']. + // Skip non-element paths like just ['styles']. + $is_processing_element = in_array( 'elements', $block_metadata['path'], true ); + + $current_element = $is_processing_element ? $block_metadata['path'][ count( $block_metadata['path'] ) - 1 ] : null; + + $element_pseudo_allowed = array_key_exists( $current_element, static::VALID_ELEMENT_PSEUDO_SELECTORS ) ? static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ] : array(); + + // Check for allowed pseudo classes (e.g. ":hover") from the $selector ("a:hover"). + // This also resets the array keys. + $pseudo_matches = array_values( + array_filter( + $element_pseudo_allowed, + function( $pseudo_selector ) use ( $selector ) { + return str_contains( $selector, $pseudo_selector ); + } + ) + ); + + $pseudo_selector = isset( $pseudo_matches[0] ) ? $pseudo_matches[0] : null; + + // If the current selector is a pseudo selector that's defined in the allow list for the current + // element then compute the style properties for it. + // Otherwise just compute the styles for the default selector as normal. + if ( $pseudo_selector && isset( $node[ $pseudo_selector ] ) && array_key_exists( $current_element, static::VALID_ELEMENT_PSEUDO_SELECTORS ) && in_array( $pseudo_selector, static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ], true ) ) { + $declarations = static::compute_style_properties( $node[ $pseudo_selector ], $settings, null, $this->theme_json, $selector, $use_root_padding ); + } else { + $declarations = static::compute_style_properties( $node, $settings, null, $this->theme_json, $selector, $use_root_padding ); + } + + $block_rules = ''; + + // 1. Separate the ones who use the general selector + // and the ones who use the duotone selector. + $declarations_duotone = array(); + foreach ( $declarations as $index => $declaration ) { + if ( 'filter' === $declaration['name'] ) { + unset( $declarations[ $index ] ); + $declarations_duotone[] = $declaration; + } + } + + // 2. Generate and append the rules that use the general selector. + $block_rules .= static::to_ruleset( $selector, $declarations ); + + // 3. Generate and append the rules that use the duotone selector. + if ( isset( $block_metadata['duotone'] ) && ! empty( $declarations_duotone ) ) { + $selector_duotone = static::scope_selector( $block_metadata['selector'], $block_metadata['duotone'] ); + $block_rules .= static::to_ruleset( $selector_duotone, $declarations_duotone ); + } + + // 4. Generate Layout block gap styles. + if ( + static::ROOT_BLOCK_SELECTOR !== $selector && + ! empty( $block_metadata['name'] ) + ) { + $block_rules .= $this->get_layout_styles( $block_metadata ); + } + + // 5. Generate and append the feature level rulesets. + foreach ( $feature_declarations as $feature_selector => $individual_feature_declarations ) { + $block_rules .= static::to_ruleset( $feature_selector, $individual_feature_declarations ); + } + + if ( static::ROOT_BLOCK_SELECTOR === $selector ) { + /* + * Reset default browser margin on the root body element. + * This is set on the root selector **before** generating the ruleset + * from the `theme.json`. This is to ensure that if the `theme.json` declares + * `margin` in its `spacing` declaration for the `body` element then these + * user-generated values take precedence in the CSS cascade. + * @link https://github.com/WordPress/gutenberg/issues/36147. + */ + $block_rules .= 'body { margin: 0;'; + + /* + * If there are content and wide widths in theme.json, output them + * as custom properties on the body element so all blocks can use them. + */ + if ( isset( $settings['layout']['contentSize'] ) || isset( $settings['layout']['wideSize'] ) ) { + $content_size = isset( $settings['layout']['contentSize'] ) ? $settings['layout']['contentSize'] : $settings['layout']['wideSize']; + $content_size = static::is_safe_css_declaration( 'max-width', $content_size ) ? $content_size : 'initial'; + $wide_size = isset( $settings['layout']['wideSize'] ) ? $settings['layout']['wideSize'] : $settings['layout']['contentSize']; + $wide_size = static::is_safe_css_declaration( 'max-width', $wide_size ) ? $wide_size : 'initial'; + $block_rules .= '--wp--style--global--content-size: ' . $content_size . ';'; + $block_rules .= '--wp--style--global--wide-size: ' . $wide_size . ';'; + } + + $block_rules .= ' }'; + } + + if ( static::ROOT_BLOCK_SELECTOR === $selector ) { + + if ( $use_root_padding ) { + $block_rules .= '.wp-site-blocks { padding-top: var(--wp--style--root--padding-top); padding-bottom: var(--wp--style--root--padding-bottom); }'; + $block_rules .= '.has-global-padding { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }'; + $block_rules .= '.has-global-padding > .alignfull { margin-right: calc(var(--wp--style--root--padding-right) * -1); margin-left: calc(var(--wp--style--root--padding-left) * -1); }'; + $block_rules .= '.has-global-padding > .alignfull > :where([class*="wp-block-"]:not(.alignfull):not(.alignfull):not([class*="__"]),p,h1,h2,h3,h4,h5,h6,ul,ol) { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }'; + } + + $block_rules .= '.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }'; + $block_rules .= '.wp-site-blocks > .alignright { float: right; margin-left: 2em; }'; + $block_rules .= '.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }'; + + $block_gap_value = _wp_array_get( $this->theme_json, array( 'styles', 'spacing', 'blockGap' ), '0.5em' ); + $has_block_gap_support = _wp_array_get( $this->theme_json, array( 'settings', 'spacing', 'blockGap' ) ) !== null; + if ( $has_block_gap_support ) { + $block_gap_value = static::get_property_value( $this->theme_json, array( 'styles', 'spacing', 'blockGap' ) ); + $block_rules .= '.wp-site-blocks > * { margin-block-start: 0; margin-block-end: 0; }'; + $block_rules .= ".wp-site-blocks > * + * { margin-block-start: $block_gap_value; }"; + + // For backwards compatibility, ensure the legacy block gap CSS variable is still available. + $block_rules .= "$selector { --wp--style--block-gap: $block_gap_value; }"; + } + $block_rules .= $this->get_layout_styles( $block_metadata ); + } + + return $block_rules; } /** diff --git a/tests/phpunit/tests/theme/wpThemeJson.php b/tests/phpunit/tests/theme/wpThemeJson.php index 2fba75e2ae40a..b7c22b9dced18 100644 --- a/tests/phpunit/tests/theme/wpThemeJson.php +++ b/tests/phpunit/tests/theme/wpThemeJson.php @@ -424,8 +424,8 @@ public function test_get_stylesheet_renders_enabled_protected_properties() { ); $expected = 'body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }.wp-site-blocks > * { margin-block-start: 0; margin-block-end: 0; }.wp-site-blocks > * + * { margin-block-start: 1em; }body { --wp--style--block-gap: 1em; }'; - $this->assertEquals( $expected, $theme_json->get_stylesheet() ); - $this->assertEquals( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) ); + $this->assertSame( $expected, $theme_json->get_stylesheet() ); + $this->assertSame( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) ); } /** @@ -2988,7 +2988,7 @@ public function test_get_property_value_valid() { ), ), ), - ) + ), ) ); @@ -3114,7 +3114,7 @@ public function test_get_stylesheet_generates_layout_styles( $layout_definitions ); // Results also include root site blocks styles. - $this->assertEquals( + $this->assertSame( 'body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }.wp-site-blocks > * { margin-block-start: 0; margin-block-end: 0; }.wp-site-blocks > * + * { margin-block-start: 1em; }body { --wp--style--block-gap: 1em; }body .is-layout-flow > *{margin-block-start: 0;margin-block-end: 0;}body .is-layout-flow > * + *{margin-block-start: 1em;margin-block-end: 0;}body .is-layout-flex{gap: 1em;}body .is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-flex{display: flex;}body .is-layout-flex{flex-wrap: wrap;align-items: center;}', $theme_json->get_stylesheet( array( 'styles' ) ) ); @@ -3149,7 +3149,7 @@ public function test_get_stylesheet_generates_layout_styles_with_spacing_presets ); // Results also include root site blocks styles. - $this->assertEquals( + $this->assertSame( 'body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }.wp-site-blocks > * { margin-block-start: 0; margin-block-end: 0; }.wp-site-blocks > * + * { margin-block-start: var(--wp--preset--spacing--60); }body { --wp--style--block-gap: var(--wp--preset--spacing--60); }body .is-layout-flow > *{margin-block-start: 0;margin-block-end: 0;}body .is-layout-flow > * + *{margin-block-start: var(--wp--preset--spacing--60);margin-block-end: 0;}body .is-layout-flex{gap: var(--wp--preset--spacing--60);}body .is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-flex{display: flex;}body .is-layout-flex{flex-wrap: wrap;align-items: center;}', $theme_json->get_stylesheet( array( 'styles' ) ) ); @@ -3185,7 +3185,7 @@ public function test_get_stylesheet_generates_fallback_gap_layout_styles( $layou $stylesheet = $theme_json->get_stylesheet( array( 'styles' ) ); // Results also include root site blocks styles. - $this->assertEquals( + $this->assertSame( 'body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }:where(.is-layout-flex){gap: 0.5em;}body .is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-flex{display: flex;}body .is-layout-flex{flex-wrap: wrap;align-items: center;}', $stylesheet ); @@ -3216,7 +3216,7 @@ public function test_get_stylesheet_generates_base_fallback_gap_layout_styles( $ $stylesheet = $theme_json->get_stylesheet( array( 'base-layout-styles' ) ); // Note the `base-layout-styles` includes a fallback gap for the Columns block for backwards compatibility. - $this->assertEquals( + $this->assertSame( ':where(.is-layout-flex){gap: 0.5em;}body .is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-flex{display: flex;}body .is-layout-flex{flex-wrap: wrap;align-items: center;}:where(.wp-block-columns.is-layout-flex){gap: 2em;}', $stylesheet ); @@ -3249,7 +3249,7 @@ public function test_get_stylesheet_skips_layout_styles( $layout_definitions ) { remove_theme_support( 'disable-layout-styles' ); // All Layout styles should be skipped. - $this->assertEquals( + $this->assertSame( '', $stylesheet ); @@ -3371,7 +3371,7 @@ function test_get_styles_for_block_with_padding_aware_alignments() { ); $expected = 'body{--wp--style--root--padding-top: 10px;--wp--style--root--padding-right: 12px;--wp--style--root--padding-bottom: 10px;--wp--style--root--padding-left: 12px;}body { margin: 0; }.wp-site-blocks { padding-top: var(--wp--style--root--padding-top); padding-bottom: var(--wp--style--root--padding-bottom); }.has-global-padding { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }.has-global-padding > .alignfull { margin-right: calc(var(--wp--style--root--padding-right) * -1); margin-left: calc(var(--wp--style--root--padding-left) * -1); }.has-global-padding > .alignfull > :where([class*="wp-block-"]:not(.alignfull):not(.alignfull):not([class*="__"]),p,h1,h2,h3,h4,h5,h6,ul,ol) { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }'; - $this->assertEquals( $expected, $theme_json->get_styles_for_block( $metadata ) ); + $this->assertSame( $expected, $theme_json->get_styles_for_block( $metadata ) ); } /** @@ -3402,7 +3402,7 @@ function test_get_styles_for_block_without_padding_aware_alignments() { ); $expected = 'body{padding-top: 10px;padding-right: 12px;padding-bottom: 10px;padding-left: 12px;}body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }'; - $this->assertEquals( $expected, $theme_json->get_styles_for_block( $metadata ) ); + $this->assertSame( $expected, $theme_json->get_styles_for_block( $metadata ) ); } /** @@ -3429,6 +3429,6 @@ function test_get_styles_for_block_with_content_width() { ); $expected = 'body { margin: 0;--wp--style--global--content-size: 800px;--wp--style--global--wide-size: 1000px; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }'; - $this->assertEquals( $expected, $theme_json->get_styles_for_block( $metadata ) ); + $this->assertSame( $expected, $theme_json->get_styles_for_block( $metadata ) ); } } From c9a0d3c784e3a9e6952cca4e79881c71c652630a Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Mon, 12 Sep 2022 16:10:32 +1000 Subject: [PATCH 11/16] Try migrating refactor of root layout rules --- src/wp-includes/class-wp-theme-json.php | 129 +++++++++++++--------- tests/phpunit/tests/theme/wpThemeJson.php | 28 +++-- 2 files changed, 95 insertions(+), 62 deletions(-) diff --git a/src/wp-includes/class-wp-theme-json.php b/src/wp-includes/class-wp-theme-json.php index e71c38d455480..d5c4dabc149ec 100644 --- a/src/wp-includes/class-wp-theme-json.php +++ b/src/wp-includes/class-wp-theme-json.php @@ -841,6 +841,11 @@ public function get_stylesheet( $types = array( 'variables', 'styles', 'presets' } if ( in_array( 'styles', $types, true ) ) { + $root_block_key = array_search( static::ROOT_BLOCK_SELECTOR, array_column( $style_nodes, 'selector' ), true ); + + if ( false !== $root_block_key ) { + $stylesheet .= $this->get_root_layout_rules( static::ROOT_BLOCK_SELECTOR, $style_nodes[ $root_block_key ] ); + } $stylesheet .= $this->get_block_classes( $style_nodes ); } elseif ( in_array( 'base-layout-styles', $types, true ) ) { // Base layout styles are provided as part of `styles`, so only output separately if explicitly requested. @@ -2024,60 +2029,82 @@ function( $pseudo_selector ) use ( $selector ) { $block_rules .= static::to_ruleset( $feature_selector, $individual_feature_declarations ); } - if ( static::ROOT_BLOCK_SELECTOR === $selector ) { - /* - * Reset default browser margin on the root body element. - * This is set on the root selector **before** generating the ruleset - * from the `theme.json`. This is to ensure that if the `theme.json` declares - * `margin` in its `spacing` declaration for the `body` element then these - * user-generated values take precedence in the CSS cascade. - * @link https://github.com/WordPress/gutenberg/issues/36147. - */ - $block_rules .= 'body { margin: 0;'; - - /* - * If there are content and wide widths in theme.json, output them - * as custom properties on the body element so all blocks can use them. - */ - if ( isset( $settings['layout']['contentSize'] ) || isset( $settings['layout']['wideSize'] ) ) { - $content_size = isset( $settings['layout']['contentSize'] ) ? $settings['layout']['contentSize'] : $settings['layout']['wideSize']; - $content_size = static::is_safe_css_declaration( 'max-width', $content_size ) ? $content_size : 'initial'; - $wide_size = isset( $settings['layout']['wideSize'] ) ? $settings['layout']['wideSize'] : $settings['layout']['contentSize']; - $wide_size = static::is_safe_css_declaration( 'max-width', $wide_size ) ? $wide_size : 'initial'; - $block_rules .= '--wp--style--global--content-size: ' . $content_size . ';'; - $block_rules .= '--wp--style--global--wide-size: ' . $wide_size . ';'; - } - - $block_rules .= ' }'; - } - - if ( static::ROOT_BLOCK_SELECTOR === $selector ) { - - if ( $use_root_padding ) { - $block_rules .= '.wp-site-blocks { padding-top: var(--wp--style--root--padding-top); padding-bottom: var(--wp--style--root--padding-bottom); }'; - $block_rules .= '.has-global-padding { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }'; - $block_rules .= '.has-global-padding > .alignfull { margin-right: calc(var(--wp--style--root--padding-right) * -1); margin-left: calc(var(--wp--style--root--padding-left) * -1); }'; - $block_rules .= '.has-global-padding > .alignfull > :where([class*="wp-block-"]:not(.alignfull):not(.alignfull):not([class*="__"]),p,h1,h2,h3,h4,h5,h6,ul,ol) { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }'; - } - - $block_rules .= '.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }'; - $block_rules .= '.wp-site-blocks > .alignright { float: right; margin-left: 2em; }'; - $block_rules .= '.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }'; + return $block_rules; + } - $block_gap_value = _wp_array_get( $this->theme_json, array( 'styles', 'spacing', 'blockGap' ), '0.5em' ); - $has_block_gap_support = _wp_array_get( $this->theme_json, array( 'settings', 'spacing', 'blockGap' ) ) !== null; - if ( $has_block_gap_support ) { - $block_gap_value = static::get_property_value( $this->theme_json, array( 'styles', 'spacing', 'blockGap' ) ); - $block_rules .= '.wp-site-blocks > * { margin-block-start: 0; margin-block-end: 0; }'; - $block_rules .= ".wp-site-blocks > * + * { margin-block-start: $block_gap_value; }"; + /** + * Outputs the CSS for layout rules on the root. + * + * @since 6.1.0 + * + * @param string $selector The root node selector. + * @param array $block_metadata The metadata for the root block. + * @return string The additional root rules CSS. + */ + public function get_root_layout_rules( $selector, $block_metadata ) { + $css = ''; + $settings = _wp_array_get( $this->theme_json, array( 'settings' ) ); + $use_root_padding = isset( $this->theme_json['settings']['useRootPaddingAwareAlignments'] ) && true === $this->theme_json['settings']['useRootPaddingAwareAlignments']; - // For backwards compatibility, ensure the legacy block gap CSS variable is still available. - $block_rules .= "$selector { --wp--style--block-gap: $block_gap_value; }"; - } - $block_rules .= $this->get_layout_styles( $block_metadata ); - } + /* + * Reset default browser margin on the root body element. + * This is set on the root selector **before** generating the ruleset + * from the `theme.json`. This is to ensure that if the `theme.json` declares + * `margin` in its `spacing` declaration for the `body` element then these + * user-generated values take precedence in the CSS cascade. + * @link https://github.com/WordPress/gutenberg/issues/36147. + */ + $css .= 'body { margin: 0;'; - return $block_rules; + /* + * If there are content and wide widths in theme.json, output them + * as custom properties on the body element so all blocks can use them. + */ + if ( isset( $settings['layout']['contentSize'] ) || isset( $settings['layout']['wideSize'] ) ) { + $content_size = isset( $settings['layout']['contentSize'] ) ? $settings['layout']['contentSize'] : $settings['layout']['wideSize']; + $content_size = static::is_safe_css_declaration( 'max-width', $content_size ) ? $content_size : 'initial'; + $wide_size = isset( $settings['layout']['wideSize'] ) ? $settings['layout']['wideSize'] : $settings['layout']['contentSize']; + $wide_size = static::is_safe_css_declaration( 'max-width', $wide_size ) ? $wide_size : 'initial'; + $css .= '--wp--style--global--content-size: ' . $content_size . ';'; + $css .= '--wp--style--global--wide-size: ' . $wide_size . ';'; + } + + $css .= '}'; + + if ( $use_root_padding ) { + // Top and bottom padding are applied to the outer block container. + $css .= '.wp-site-blocks { padding-top: var(--wp--style--root--padding-top); padding-bottom: var(--wp--style--root--padding-bottom); }'; + // Right and left padding are applied to the first container with `.has-global-padding` class. + $css .= '.has-global-padding { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }'; + // Nested containers with `.has-global-padding` class do not get padding. + $css .= '.has-global-padding :where(.has-global-padding) { padding-right: 0; padding-left: 0; }'; + // Alignfull children of the container with left and right padding have negative margins so they can still be full width. + $css .= '.has-global-padding > .alignfull { margin-right: calc(var(--wp--style--root--padding-right) * -1); margin-left: calc(var(--wp--style--root--padding-left) * -1); }'; + // The above rule is negated for alignfull children of nested containers. + $css .= '.has-global-padding :where(.has-global-padding) > .alignfull { margin-right: 0; margin-left: 0; }'; + // Some of the children of alignfull blocks without content width should also get padding: text blocks and non-alignfull container blocks. + $css .= '.has-global-padding > .alignfull:where(:not(.has-global-padding)) > :where([class*="wp-block-"]:not(.alignfull):not([class*="__"]),p,h1,h2,h3,h4,h5,h6,ul,ol) { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }'; + // The above rule also has to be negated for blocks inside nested `.has-global-padding` blocks. + $css .= '.has-global-padding :where(.has-global-padding) > .alignfull:where(:not(.has-global-padding)) > :where([class*="wp-block-"]:not(.alignfull):not([class*="__"]),p,h1,h2,h3,h4,h5,h6,ul,ol) { padding-right: 0; padding-left: 0; }'; + } + + $css .= '.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }'; + $css .= '.wp-site-blocks > .alignright { float: right; margin-left: 2em; }'; + $css .= '.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }'; + + $block_gap_value = _wp_array_get( $this->theme_json, array( 'styles', 'spacing', 'blockGap' ), '0.5em' ); + $has_block_gap_support = _wp_array_get( $this->theme_json, array( 'settings', 'spacing', 'blockGap' ) ) !== null; + if ( $has_block_gap_support ) { + $block_gap_value = static::get_property_value( $this->theme_json, array( 'styles', 'spacing', 'blockGap' ) ); + $css .= '.wp-site-blocks > * { margin-block-start: 0; margin-block-end: 0; }'; + $css .= ".wp-site-blocks > * + * { margin-block-start: $block_gap_value; }"; + + // For backwards compatibility, ensure the legacy block gap CSS variable is still available. + $css .= "$selector { --wp--style--block-gap: $block_gap_value; }"; + } + $css .= $this->get_layout_styles( $block_metadata ); + + return $css; } /** diff --git a/tests/phpunit/tests/theme/wpThemeJson.php b/tests/phpunit/tests/theme/wpThemeJson.php index b7c22b9dced18..27978f5f9a6f8 100644 --- a/tests/phpunit/tests/theme/wpThemeJson.php +++ b/tests/phpunit/tests/theme/wpThemeJson.php @@ -553,7 +553,7 @@ public function test_get_stylesheet() { ); $variables = 'body{--wp--preset--color--grey: grey;--wp--preset--font-family--small: 14px;--wp--preset--font-family--big: 41px;}.wp-block-group{--wp--custom--base-font: 16;--wp--custom--line-height--small: 1.2;--wp--custom--line-height--medium: 1.4;--wp--custom--line-height--large: 1.8;}'; - $styles = 'body { margin: 0; }body{color: var(--wp--preset--color--grey);}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }.wp-site-blocks > * { margin-block-start: 0; margin-block-end: 0; }.wp-site-blocks > * + * { margin-block-start: 24px; }body { --wp--style--block-gap: 24px; }a{background-color: #333;color: #111;}.wp-block-group{border-radius: 10px;padding: 24px;}.wp-block-group a{color: #111;}h1,h2,h3,h4,h5,h6{color: #123456;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a{background-color: #777;color: #555;}.wp-block-image{border-top-left-radius: 10px;border-bottom-right-radius: 1em;margin-bottom: 30px;}'; + $styles = 'body { margin: 0;}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }body{color: var(--wp--preset--color--grey);}a:where(:not(.wp-element-button)){background-color: #333;color: #111;}.wp-block-group{border-radius: 10px;padding: 24px;}.wp-block-group a:where(:not(.wp-element-button)){color: #111;}h1,h2,h3,h4,h5,h6{color: #123456;}h1 a:where(:not(.wp-element-button)),h2 a:where(:not(.wp-element-button)),h3 a:where(:not(.wp-element-button)),h4 a:where(:not(.wp-element-button)),h5 a:where(:not(.wp-element-button)),h6 a:where(:not(.wp-element-button)){background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a:where(:not(.wp-element-button)){background-color: #777;color: #555;}.wp-block-image{margin-bottom: 30px;}.wp-block-image img, .wp-block-image .wp-block-image__crop-area{border-top-left-radius: 10px;border-bottom-right-radius: 1em;}'; $presets = '.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}.has-small-font-family{font-family: var(--wp--preset--font-family--small) !important;}.has-big-font-family{font-family: var(--wp--preset--font-family--big) !important;}'; $all = $variables . $styles . $presets; $this->assertSame( $all, $theme_json->get_stylesheet() ); @@ -2992,7 +2992,7 @@ public function test_get_property_value_valid() { ) ); - $expected = 'body { margin: 0; }body{background-color: #ffffff;color: #000000;}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }.wp-element-button, .wp-block-button__link{background-color: #000000;color: #ffffff;}'; + $expected = 'body { margin: 0;}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }body{background-color: #ffffff;color: #000000;}.wp-element-button, .wp-block-button__link{background-color: #000000;color: #ffffff;}'; $this->assertSame( $expected, $theme_json->get_stylesheet() ); } @@ -3024,7 +3024,7 @@ public function test_get_property_value_loop() { ) ); - $expected = 'body { margin: 0; }body{background-color: #ffffff;}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }.wp-element-button, .wp-block-button__link{color: #ffffff;}'; + $expected = 'body { margin: 0;}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }body{background-color: #ffffff;}.wp-element-button, .wp-block-button__link{color: #ffffff;}'; $this->assertSame( $expected, $theme_json->get_stylesheet() ); } @@ -3056,7 +3056,7 @@ public function test_get_property_value_recursion() { ) ); - $expected = 'body { margin: 0; }body{background-color: #ffffff;color: #ffffff;}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }.wp-element-button, .wp-block-button__link{color: #ffffff;}'; + $expected = 'body { margin: 0;}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }body{background-color: #ffffff;color: #ffffff;}.wp-element-button, .wp-block-button__link{color: #ffffff;}'; $this->assertSame( $expected, $theme_json->get_stylesheet() ); } @@ -3080,7 +3080,7 @@ public function test_get_property_value_self() { ) ); - $expected = 'body { margin: 0; }body{background-color: #ffffff;}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }'; + $expected = 'body { margin: 0;}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }body{background-color: #ffffff;}'; $this->assertSame( $expected, $theme_json->get_stylesheet() ); } @@ -3370,8 +3370,10 @@ function test_get_styles_for_block_with_padding_aware_alignments() { 'selector' => 'body', ); - $expected = 'body{--wp--style--root--padding-top: 10px;--wp--style--root--padding-right: 12px;--wp--style--root--padding-bottom: 10px;--wp--style--root--padding-left: 12px;}body { margin: 0; }.wp-site-blocks { padding-top: var(--wp--style--root--padding-top); padding-bottom: var(--wp--style--root--padding-bottom); }.has-global-padding { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }.has-global-padding > .alignfull { margin-right: calc(var(--wp--style--root--padding-right) * -1); margin-left: calc(var(--wp--style--root--padding-left) * -1); }.has-global-padding > .alignfull > :where([class*="wp-block-"]:not(.alignfull):not(.alignfull):not([class*="__"]),p,h1,h2,h3,h4,h5,h6,ul,ol) { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }'; - $this->assertSame( $expected, $theme_json->get_styles_for_block( $metadata ) ); + $expected = 'body { margin: 0;}.wp-site-blocks { padding-top: var(--wp--style--root--padding-top); padding-bottom: var(--wp--style--root--padding-bottom); }.has-global-padding { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }.has-global-padding :where(.has-global-padding) { padding-right: 0; padding-left: 0; }.has-global-padding > .alignfull { margin-right: calc(var(--wp--style--root--padding-right) * -1); margin-left: calc(var(--wp--style--root--padding-left) * -1); }.has-global-padding :where(.has-global-padding) > .alignfull { margin-right: 0; margin-left: 0; }.has-global-padding > .alignfull:where(:not(.has-global-padding)) > :where([class*="wp-block-"]:not(.alignfull):not([class*="__"]),p,h1,h2,h3,h4,h5,h6,ul,ol) { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }.has-global-padding :where(.has-global-padding) > .alignfull:where(:not(.has-global-padding)) > :where([class*="wp-block-"]:not(.alignfull):not([class*="__"]),p,h1,h2,h3,h4,h5,h6,ul,ol) { padding-right: 0; padding-left: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }body{--wp--style--root--padding-top: 10px;--wp--style--root--padding-right: 12px;--wp--style--root--padding-bottom: 10px;--wp--style--root--padding-left: 12px;}'; + $root_rules = $theme_json->get_root_layout_rules( WP_Theme_JSON::ROOT_BLOCK_SELECTOR, $metadata ); + $style_rules = $theme_json->get_styles_for_block( $metadata ); + $this->assertSame( $expected, $root_rules . $style_rules ); } /** @@ -3401,8 +3403,10 @@ function test_get_styles_for_block_without_padding_aware_alignments() { 'selector' => 'body', ); - $expected = 'body{padding-top: 10px;padding-right: 12px;padding-bottom: 10px;padding-left: 12px;}body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }'; - $this->assertSame( $expected, $theme_json->get_styles_for_block( $metadata ) ); + $expected = 'body { margin: 0;}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }body{padding-top: 10px;padding-right: 12px;padding-bottom: 10px;padding-left: 12px;}'; + $root_rules = $theme_json->get_root_layout_rules( WP_Theme_JSON::ROOT_BLOCK_SELECTOR, $metadata ); + $style_rules = $theme_json->get_styles_for_block( $metadata ); + $this->assertSame( $expected, $root_rules . $style_rules ); } /** @@ -3428,7 +3432,9 @@ function test_get_styles_for_block_with_content_width() { 'selector' => 'body', ); - $expected = 'body { margin: 0;--wp--style--global--content-size: 800px;--wp--style--global--wide-size: 1000px; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }'; - $this->assertSame( $expected, $theme_json->get_styles_for_block( $metadata ) ); + $expected = 'body { margin: 0;--wp--style--global--content-size: 800px;--wp--style--global--wide-size: 1000px;}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }'; + $root_rules = $theme_json->get_root_layout_rules( WP_Theme_JSON::ROOT_BLOCK_SELECTOR, $metadata ); + $style_rules = $theme_json->get_styles_for_block( $metadata ); + $this->assertSame( $expected, $root_rules . $style_rules ); } } From c6ee98b49dc6023d2e09fcf6b5975f569ef77180 Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Mon, 12 Sep 2022 17:00:28 +1000 Subject: [PATCH 12/16] Add feature level selectors --- src/wp-includes/class-wp-theme-json.php | 110 +++++++++++++++++----- tests/phpunit/tests/theme/wpThemeJson.php | 14 +-- 2 files changed, 91 insertions(+), 33 deletions(-) diff --git a/src/wp-includes/class-wp-theme-json.php b/src/wp-includes/class-wp-theme-json.php index d5c4dabc149ec..e25b7d02e9d93 100644 --- a/src/wp-includes/class-wp-theme-json.php +++ b/src/wp-includes/class-wp-theme-json.php @@ -273,18 +273,19 @@ class WP_Theme_JSON { * added new properties for `border`, `color`, `spacing`, * and `typography`, and renamed others according to the new schema. * @since 6.0.0 Added `color.defaultDuotone`. - * @since 6.1.0 Added `layout.definitions`. + * @since 6.1.0 Added `layout.definitions` and `useRootPaddingAwareAlignments`. * @var array */ const VALID_SETTINGS = array( - 'appearanceTools' => null, - 'border' => array( + 'appearanceTools' => null, + 'useRootPaddingAwareAlignments' => null, + 'border' => array( 'color' => null, 'radius' => null, 'style' => null, 'width' => null, ), - 'color' => array( + 'color' => array( 'background' => null, 'custom' => null, 'customDuotone' => null, @@ -298,19 +299,19 @@ class WP_Theme_JSON { 'palette' => null, 'text' => null, ), - 'custom' => null, - 'layout' => array( + 'custom' => null, + 'layout' => array( 'contentSize' => null, 'definitions' => null, 'wideSize' => null, ), - 'spacing' => array( + 'spacing' => array( 'blockGap' => null, 'margin' => null, 'padding' => null, 'units' => null, ), - 'typography' => array( + 'typography' => array( 'customFontSize' => null, 'dropCap' => null, 'fontFamilies' => null, @@ -412,6 +413,20 @@ class WP_Theme_JSON { 'caption' => 'wp-element-caption', ); + /** + * List of block support features that can have their related styles + * generated under their own feature level selector rather than the block's. + * + * @since 6.1.0 + * @var string[] + */ + const BLOCK_SUPPORT_FEATURE_LEVEL_SELECTORS = array( + '__experimentalBorder' => 'border', + 'color' => 'color', + 'spacing' => 'spacing', + 'typography' => 'typography', + ); + /** * Returns a class name by an element name. * @@ -687,6 +702,7 @@ protected static function append_to_selector( $selector, $to_append, $position = * * @since 5.8.0 * @since 5.9.0 Added `duotone` key with CSS selector. + * @since 6.1.0 Added `features` key with block support feature level selectors. * * @return array Block metadata. */ @@ -716,6 +732,25 @@ protected static function get_blocks_metadata() { static::$blocks_metadata[ $block_name ]['duotone'] = $block_type->supports['color']['__experimentalDuotone']; } + // Generate block support feature level selectors if opted into + // for the current block. + $features = array(); + foreach ( static::BLOCK_SUPPORT_FEATURE_LEVEL_SELECTORS as $key => $feature ) { + if ( + isset( $block_type->supports[ $key ]['__experimentalSelector'] ) && + $block_type->supports[ $key ]['__experimentalSelector'] + ) { + $features[ $feature ] = static::scope_selector( + static::$blocks_metadata[ $block_name ]['selector'], + $block_type->supports[ $key ]['__experimentalSelector'] + ); + } + } + + if ( ! empty( $features ) ) { + static::$blocks_metadata[ $block_name ]['features'] = $features; + } + // Assign defaults, then overwrite those that the block sets by itself. // If the block selector is compounded, will append the element to each // individual block selector. @@ -1885,11 +1920,17 @@ private static function get_block_nodes( $theme_json ) { $duotone_selector = $selectors[ $name ]['duotone']; } + $feature_selectors = null; + if ( isset( $selectors[ $name ]['features'] ) ) { + $feature_selectors = $selectors[ $name ]['features']; + } + $nodes[] = array( 'name' => $name, 'path' => array( 'styles', 'blocks', $name ), 'selector' => $selector, 'duotone' => $duotone_selector, + 'features' => $feature_selectors, ); if ( isset( $theme_json['styles']['blocks'][ $name ]['elements'] ) ) { @@ -1932,10 +1973,12 @@ public function get_styles_for_block( $block_metadata ) { $selector = $block_metadata['selector']; $settings = _wp_array_get( $this->theme_json, array( 'settings' ) ); - // Process style declarations for block support features the current - // block contains selectors for. Values for a feature with a custom - // selector are filtered from the theme.json node before it is - // processed as normal. + /* + * Process style declarations for block support features the current + * block contains selectors for. Values for a feature with a custom + * selector are filtered from the theme.json node before it is + * processed as normal. + */ $feature_declarations = array(); if ( ! empty( $block_metadata['features'] ) ) { @@ -1963,18 +2006,26 @@ public function get_styles_for_block( $block_metadata ) { } } - // Get a reference to element name from path. - // $block_metadata['path'] = array('styles','elements','link'); - // Make sure that $block_metadata['path'] describes an element node, like ['styles', 'element', 'link']. - // Skip non-element paths like just ['styles']. + /* + * Get a reference to element name from path. + * $block_metadata['path'] = array( 'styles','elements','link' ); + * Make sure that $block_metadata['path'] describes an element node, like [ 'styles', 'element', 'link' ]. + * Skip non-element paths like just ['styles']. + */ $is_processing_element = in_array( 'elements', $block_metadata['path'], true ); $current_element = $is_processing_element ? $block_metadata['path'][ count( $block_metadata['path'] ) - 1 ] : null; - $element_pseudo_allowed = array_key_exists( $current_element, static::VALID_ELEMENT_PSEUDO_SELECTORS ) ? static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ] : array(); + $element_pseudo_allowed = array(); + + if ( array_key_exists( $current_element, static::VALID_ELEMENT_PSEUDO_SELECTORS ) ) { + $element_pseudo_allowed = static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ]; + } - // Check for allowed pseudo classes (e.g. ":hover") from the $selector ("a:hover"). - // This also resets the array keys. + /* + * Check for allowed pseudo classes (e.g. ":hover") from the $selector ("a:hover"). + * This also resets the array keys. + */ $pseudo_matches = array_values( array_filter( $element_pseudo_allowed, @@ -1986,10 +2037,15 @@ function( $pseudo_selector ) use ( $selector ) { $pseudo_selector = isset( $pseudo_matches[0] ) ? $pseudo_matches[0] : null; - // If the current selector is a pseudo selector that's defined in the allow list for the current - // element then compute the style properties for it. - // Otherwise just compute the styles for the default selector as normal. - if ( $pseudo_selector && isset( $node[ $pseudo_selector ] ) && array_key_exists( $current_element, static::VALID_ELEMENT_PSEUDO_SELECTORS ) && in_array( $pseudo_selector, static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ], true ) ) { + /* + * If the current selector is a pseudo selector that's defined in the allow list for the current + * element then compute the style properties for it. + * Otherwise just compute the styles for the default selector as normal. + */ + if ( $pseudo_selector && isset( $node[ $pseudo_selector ] ) && + array_key_exists( $current_element, static::VALID_ELEMENT_PSEUDO_SELECTORS ) && + in_array( $pseudo_selector, static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ], true ) + ) { $declarations = static::compute_style_properties( $node[ $pseudo_selector ], $settings, null, $this->theme_json, $selector, $use_root_padding ); } else { $declarations = static::compute_style_properties( $node, $settings, null, $this->theme_json, $selector, $use_root_padding ); @@ -1997,8 +2053,10 @@ function( $pseudo_selector ) use ( $selector ) { $block_rules = ''; - // 1. Separate the ones who use the general selector - // and the ones who use the duotone selector. + /* + * 1. Separate the declarations that use the general selector + * from the ones using the duotone selector. + */ $declarations_duotone = array(); foreach ( $declarations as $index => $declaration ) { if ( 'filter' === $declaration['name'] ) { @@ -2069,7 +2127,7 @@ public function get_root_layout_rules( $selector, $block_metadata ) { $css .= '--wp--style--global--wide-size: ' . $wide_size . ';'; } - $css .= '}'; + $css .= ' }'; if ( $use_root_padding ) { // Top and bottom padding are applied to the outer block container. diff --git a/tests/phpunit/tests/theme/wpThemeJson.php b/tests/phpunit/tests/theme/wpThemeJson.php index 27978f5f9a6f8..5a9f62d47002e 100644 --- a/tests/phpunit/tests/theme/wpThemeJson.php +++ b/tests/phpunit/tests/theme/wpThemeJson.php @@ -553,7 +553,7 @@ public function test_get_stylesheet() { ); $variables = 'body{--wp--preset--color--grey: grey;--wp--preset--font-family--small: 14px;--wp--preset--font-family--big: 41px;}.wp-block-group{--wp--custom--base-font: 16;--wp--custom--line-height--small: 1.2;--wp--custom--line-height--medium: 1.4;--wp--custom--line-height--large: 1.8;}'; - $styles = 'body { margin: 0;}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }body{color: var(--wp--preset--color--grey);}a:where(:not(.wp-element-button)){background-color: #333;color: #111;}.wp-block-group{border-radius: 10px;padding: 24px;}.wp-block-group a:where(:not(.wp-element-button)){color: #111;}h1,h2,h3,h4,h5,h6{color: #123456;}h1 a:where(:not(.wp-element-button)),h2 a:where(:not(.wp-element-button)),h3 a:where(:not(.wp-element-button)),h4 a:where(:not(.wp-element-button)),h5 a:where(:not(.wp-element-button)),h6 a:where(:not(.wp-element-button)){background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a:where(:not(.wp-element-button)){background-color: #777;color: #555;}.wp-block-image{margin-bottom: 30px;}.wp-block-image img, .wp-block-image .wp-block-image__crop-area{border-top-left-radius: 10px;border-bottom-right-radius: 1em;}'; + $styles = 'body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }body{color: var(--wp--preset--color--grey);}a:where(:not(.wp-element-button)){background-color: #333;color: #111;}.wp-block-group{border-radius: 10px;padding: 24px;}.wp-block-group a:where(:not(.wp-element-button)){color: #111;}h1,h2,h3,h4,h5,h6{color: #123456;}h1 a:where(:not(.wp-element-button)),h2 a:where(:not(.wp-element-button)),h3 a:where(:not(.wp-element-button)),h4 a:where(:not(.wp-element-button)),h5 a:where(:not(.wp-element-button)),h6 a:where(:not(.wp-element-button)){background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a:where(:not(.wp-element-button)){background-color: #777;color: #555;}.wp-block-image{margin-bottom: 30px;}.wp-block-image img, .wp-block-image .wp-block-image__crop-area{border-top-left-radius: 10px;border-bottom-right-radius: 1em;}'; $presets = '.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}.has-small-font-family{font-family: var(--wp--preset--font-family--small) !important;}.has-big-font-family{font-family: var(--wp--preset--font-family--big) !important;}'; $all = $variables . $styles . $presets; $this->assertSame( $all, $theme_json->get_stylesheet() ); @@ -2992,7 +2992,7 @@ public function test_get_property_value_valid() { ) ); - $expected = 'body { margin: 0;}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }body{background-color: #ffffff;color: #000000;}.wp-element-button, .wp-block-button__link{background-color: #000000;color: #ffffff;}'; + $expected = 'body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }body{background-color: #ffffff;color: #000000;}.wp-element-button, .wp-block-button__link{background-color: #000000;color: #ffffff;}'; $this->assertSame( $expected, $theme_json->get_stylesheet() ); } @@ -3024,7 +3024,7 @@ public function test_get_property_value_loop() { ) ); - $expected = 'body { margin: 0;}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }body{background-color: #ffffff;}.wp-element-button, .wp-block-button__link{color: #ffffff;}'; + $expected = 'body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }body{background-color: #ffffff;}.wp-element-button, .wp-block-button__link{color: #ffffff;}'; $this->assertSame( $expected, $theme_json->get_stylesheet() ); } @@ -3056,7 +3056,7 @@ public function test_get_property_value_recursion() { ) ); - $expected = 'body { margin: 0;}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }body{background-color: #ffffff;color: #ffffff;}.wp-element-button, .wp-block-button__link{color: #ffffff;}'; + $expected = 'body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }body{background-color: #ffffff;color: #ffffff;}.wp-element-button, .wp-block-button__link{color: #ffffff;}'; $this->assertSame( $expected, $theme_json->get_stylesheet() ); } @@ -3080,7 +3080,7 @@ public function test_get_property_value_self() { ) ); - $expected = 'body { margin: 0;}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }body{background-color: #ffffff;}'; + $expected = 'body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }body{background-color: #ffffff;}'; $this->assertSame( $expected, $theme_json->get_stylesheet() ); } @@ -3370,7 +3370,7 @@ function test_get_styles_for_block_with_padding_aware_alignments() { 'selector' => 'body', ); - $expected = 'body { margin: 0;}.wp-site-blocks { padding-top: var(--wp--style--root--padding-top); padding-bottom: var(--wp--style--root--padding-bottom); }.has-global-padding { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }.has-global-padding :where(.has-global-padding) { padding-right: 0; padding-left: 0; }.has-global-padding > .alignfull { margin-right: calc(var(--wp--style--root--padding-right) * -1); margin-left: calc(var(--wp--style--root--padding-left) * -1); }.has-global-padding :where(.has-global-padding) > .alignfull { margin-right: 0; margin-left: 0; }.has-global-padding > .alignfull:where(:not(.has-global-padding)) > :where([class*="wp-block-"]:not(.alignfull):not([class*="__"]),p,h1,h2,h3,h4,h5,h6,ul,ol) { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }.has-global-padding :where(.has-global-padding) > .alignfull:where(:not(.has-global-padding)) > :where([class*="wp-block-"]:not(.alignfull):not([class*="__"]),p,h1,h2,h3,h4,h5,h6,ul,ol) { padding-right: 0; padding-left: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }body{--wp--style--root--padding-top: 10px;--wp--style--root--padding-right: 12px;--wp--style--root--padding-bottom: 10px;--wp--style--root--padding-left: 12px;}'; + $expected = 'body { margin: 0; }.wp-site-blocks { padding-top: var(--wp--style--root--padding-top); padding-bottom: var(--wp--style--root--padding-bottom); }.has-global-padding { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }.has-global-padding :where(.has-global-padding) { padding-right: 0; padding-left: 0; }.has-global-padding > .alignfull { margin-right: calc(var(--wp--style--root--padding-right) * -1); margin-left: calc(var(--wp--style--root--padding-left) * -1); }.has-global-padding :where(.has-global-padding) > .alignfull { margin-right: 0; margin-left: 0; }.has-global-padding > .alignfull:where(:not(.has-global-padding)) > :where([class*="wp-block-"]:not(.alignfull):not([class*="__"]),p,h1,h2,h3,h4,h5,h6,ul,ol) { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }.has-global-padding :where(.has-global-padding) > .alignfull:where(:not(.has-global-padding)) > :where([class*="wp-block-"]:not(.alignfull):not([class*="__"]),p,h1,h2,h3,h4,h5,h6,ul,ol) { padding-right: 0; padding-left: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }body{--wp--style--root--padding-top: 10px;--wp--style--root--padding-right: 12px;--wp--style--root--padding-bottom: 10px;--wp--style--root--padding-left: 12px;}'; $root_rules = $theme_json->get_root_layout_rules( WP_Theme_JSON::ROOT_BLOCK_SELECTOR, $metadata ); $style_rules = $theme_json->get_styles_for_block( $metadata ); $this->assertSame( $expected, $root_rules . $style_rules ); @@ -3403,7 +3403,7 @@ function test_get_styles_for_block_without_padding_aware_alignments() { 'selector' => 'body', ); - $expected = 'body { margin: 0;}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }body{padding-top: 10px;padding-right: 12px;padding-bottom: 10px;padding-left: 12px;}'; + $expected = 'body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }body{padding-top: 10px;padding-right: 12px;padding-bottom: 10px;padding-left: 12px;}'; $root_rules = $theme_json->get_root_layout_rules( WP_Theme_JSON::ROOT_BLOCK_SELECTOR, $metadata ); $style_rules = $theme_json->get_styles_for_block( $metadata ); $this->assertSame( $expected, $root_rules . $style_rules ); From 2c87dfd03714f7fa8bbdc687dd27bf16d26c7983 Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Mon, 12 Sep 2022 17:30:20 +1000 Subject: [PATCH 13/16] Update tests --- src/wp-includes/class-wp-theme-json.php | 8 +++----- tests/phpunit/tests/theme/wpThemeJson.php | 8 ++++---- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/wp-includes/class-wp-theme-json.php b/src/wp-includes/class-wp-theme-json.php index e25b7d02e9d93..abf9a60a9c908 100644 --- a/src/wp-includes/class-wp-theme-json.php +++ b/src/wp-includes/class-wp-theme-json.php @@ -585,11 +585,9 @@ protected static function sanitize( $input, $valid_block_names, $valid_element_n */ $styles_non_top_level = static::VALID_STYLES; foreach ( array_keys( $styles_non_top_level ) as $section ) { - if ( array_key_exists( $section, $styles_non_top_level ) && is_array( $styles_non_top_level[ $section ] ) ) { - foreach ( array_keys( $styles_non_top_level[ $section ] ) as $prop ) { - if ( 'top' === $styles_non_top_level[ $section ][ $prop ] ) { - unset( $styles_non_top_level[ $section ][ $prop ] ); - } + foreach ( array_keys( $styles_non_top_level[ $section ] ) as $prop ) { + if ( 'top' === $styles_non_top_level[ $section ][ $prop ] ) { + unset( $styles_non_top_level[ $section ][ $prop ] ); } } } diff --git a/tests/phpunit/tests/theme/wpThemeJson.php b/tests/phpunit/tests/theme/wpThemeJson.php index 5a9f62d47002e..3c4b7af6430ba 100644 --- a/tests/phpunit/tests/theme/wpThemeJson.php +++ b/tests/phpunit/tests/theme/wpThemeJson.php @@ -553,7 +553,7 @@ public function test_get_stylesheet() { ); $variables = 'body{--wp--preset--color--grey: grey;--wp--preset--font-family--small: 14px;--wp--preset--font-family--big: 41px;}.wp-block-group{--wp--custom--base-font: 16;--wp--custom--line-height--small: 1.2;--wp--custom--line-height--medium: 1.4;--wp--custom--line-height--large: 1.8;}'; - $styles = 'body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }body{color: var(--wp--preset--color--grey);}a:where(:not(.wp-element-button)){background-color: #333;color: #111;}.wp-block-group{border-radius: 10px;padding: 24px;}.wp-block-group a:where(:not(.wp-element-button)){color: #111;}h1,h2,h3,h4,h5,h6{color: #123456;}h1 a:where(:not(.wp-element-button)),h2 a:where(:not(.wp-element-button)),h3 a:where(:not(.wp-element-button)),h4 a:where(:not(.wp-element-button)),h5 a:where(:not(.wp-element-button)),h6 a:where(:not(.wp-element-button)){background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a:where(:not(.wp-element-button)){background-color: #777;color: #555;}.wp-block-image{margin-bottom: 30px;}.wp-block-image img, .wp-block-image .wp-block-image__crop-area{border-top-left-radius: 10px;border-bottom-right-radius: 1em;}'; + $styles = 'body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }body{color: var(--wp--preset--color--grey);}a:where(:not(.wp-element-button)){background-color: #333;color: #111;}.wp-block-group{border-radius: 10px;padding: 24px;}.wp-block-group a:where(:not(.wp-element-button)){color: #111;}h1,h2,h3,h4,h5,h6{color: #123456;}h1 a:where(:not(.wp-element-button)),h2 a:where(:not(.wp-element-button)),h3 a:where(:not(.wp-element-button)),h4 a:where(:not(.wp-element-button)),h5 a:where(:not(.wp-element-button)),h6 a:where(:not(.wp-element-button)){background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a:where(:not(.wp-element-button)){background-color: #777;color: #555;}.wp-block-image{border-top-left-radius: 10px;border-bottom-right-radius: 1em;margin-bottom: 30px;}'; $presets = '.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}.has-small-font-family{font-family: var(--wp--preset--font-family--small) !important;}.has-big-font-family{font-family: var(--wp--preset--font-family--big) !important;}'; $all = $variables . $styles . $presets; $this->assertSame( $all, $theme_json->get_stylesheet() ); @@ -2678,8 +2678,8 @@ public function test_sanitization() { 'blocks' => array( 'core/group' => array( 'spacing' => array( - 'margin' => 'valid value', - 'blockGap' => 'url("https://wordpress.org/?invalid-value")', + 'margin' => 'valid value', + 'display' => 'none', ), ), ), @@ -3432,7 +3432,7 @@ function test_get_styles_for_block_with_content_width() { 'selector' => 'body', ); - $expected = 'body { margin: 0;--wp--style--global--content-size: 800px;--wp--style--global--wide-size: 1000px;}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }'; + $expected = 'body { margin: 0;--wp--style--global--content-size: 800px;--wp--style--global--wide-size: 1000px; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }'; $root_rules = $theme_json->get_root_layout_rules( WP_Theme_JSON::ROOT_BLOCK_SELECTOR, $metadata ); $style_rules = $theme_json->get_styles_for_block( $metadata ); $this->assertSame( $expected, $root_rules . $style_rules ); From 16e335cc5fb02dbfdbe8536e439f2614ce6943b1 Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Mon, 12 Sep 2022 17:39:49 +1000 Subject: [PATCH 14/16] Backport other fixes --- src/wp-includes/class-wp-theme-json.php | 11 +++-- tests/phpunit/tests/theme/wpThemeJson.php | 56 +++++++++++++++++++++++ 2 files changed, 62 insertions(+), 5 deletions(-) diff --git a/src/wp-includes/class-wp-theme-json.php b/src/wp-includes/class-wp-theme-json.php index abf9a60a9c908..08f786620b933 100644 --- a/src/wp-includes/class-wp-theme-json.php +++ b/src/wp-includes/class-wp-theme-json.php @@ -1047,10 +1047,11 @@ protected function get_layout_styles( $block_metadata ) { } } - if ( null !== $block_gap_value ) { + // If the block should have custom gap, add the gap styles. + if ( null !== $block_gap_value && false !== $block_gap_value && '' !== $block_gap_value ) { foreach ( $layout_definitions as $layout_definition_key => $layout_definition ) { - // Allow skipping default layout for themes that opt-in to block styles, but opt-out of blockGap. - if ( ! $has_block_gap_support && 'default' === $layout_definition_key ) { + // Allow outputting fallback gap styles for flex layout type when block gap support isn't available. + if ( ! $has_block_gap_support && 'flex' !== $layout_definition_key ) { continue; } @@ -1692,7 +1693,7 @@ protected static function compute_style_properties( $styles, $settings = array() * @return string|array Style property value. */ protected static function get_property_value( $styles, $path, $theme_json = null ) { - $value = _wp_array_get( $styles, $path, '' ); + $value = _wp_array_get( $styles, $path ); /* * This converts references to a path to the value at that path @@ -1725,7 +1726,7 @@ protected static function get_property_value( $styles, $path, $theme_json = null } } - if ( '' === $value || is_array( $value ) ) { + if ( is_array( $value ) ) { return $value; } diff --git a/tests/phpunit/tests/theme/wpThemeJson.php b/tests/phpunit/tests/theme/wpThemeJson.php index 3c4b7af6430ba..f1e3e1ae8b9c1 100644 --- a/tests/phpunit/tests/theme/wpThemeJson.php +++ b/tests/phpunit/tests/theme/wpThemeJson.php @@ -3255,6 +3255,62 @@ public function test_get_stylesheet_skips_layout_styles( $layout_definitions ) { ); } + /** + * @dataProvider data_get_layout_definitions + * + * @ticket 56467 + * + * @param array $layout_definitions Layout definitions as stored in core theme.json. + */ + public function test_get_stylesheet_generates_valid_block_gap_values_and_skips_null_or_false_values( $layout_definitions ) { + $theme_json = new WP_Theme_JSON( + array( + 'version' => WP_Theme_JSON::LATEST_SCHEMA, + 'settings' => array( + 'layout' => array( + 'definitions' => $layout_definitions, + ), + 'spacing' => array( + 'blockGap' => true, + ), + ), + 'styles' => array( + 'spacing' => array( + 'blockGap' => '1rem', + ), + 'blocks' => array( + 'core/post-content' => array( + 'color' => array( + 'text' => 'gray', // This value should not render block layout styles. + ), + ), + 'core/social-links' => array( + 'spacing' => array( + 'blockGap' => '0', // This value should render block layout gap as zero. + ), + ), + 'core/buttons' => array( + 'spacing' => array( + 'blockGap' => 0, // This value should render block layout gap as zero. + ), + ), + 'core/columns' => array( + 'spacing' => array( + 'blockGap' => false, // This value should be ignored. The block will use the global layout value. + ), + ), + ), + ), + ), + 'default' + ); + + $this->assertEquals( + 'body { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }.wp-site-blocks > * { margin-block-start: 0; margin-block-end: 0; }.wp-site-blocks > * + * { margin-block-start: 1rem; }body { --wp--style--block-gap: 1rem; }body .is-layout-flow > *{margin-block-start: 0;margin-block-end: 0;}body .is-layout-flow > * + *{margin-block-start: 1rem;margin-block-end: 0;}body .is-layout-flex{gap: 1rem;}body .is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-flex{display: flex;}body .is-layout-flex{flex-wrap: wrap;align-items: center;}.wp-block-post-content{color: gray;}.wp-block-social-links.is-layout-flow > *{margin-block-start: 0;margin-block-end: 0;}.wp-block-social-links.is-layout-flow > * + *{margin-block-start: 0;margin-block-end: 0;}.wp-block-social-links.is-layout-flex{gap: 0;}.wp-block-buttons.is-layout-flow > *{margin-block-start: 0;margin-block-end: 0;}.wp-block-buttons.is-layout-flow > * + *{margin-block-start: 0;margin-block-end: 0;}.wp-block-buttons.is-layout-flex{gap: 0;}', + $theme_json->get_stylesheet() + ); + } + /** * Data provider for layout tests. * From 7a1b21eb034739df4138f7fc70208cff67439e9e Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Mon, 12 Sep 2022 17:49:30 +1000 Subject: [PATCH 15/16] Revert change for shadow, as it is out of scope for this change --- src/wp-includes/class-wp-theme-json.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/wp-includes/class-wp-theme-json.php b/src/wp-includes/class-wp-theme-json.php index 08f786620b933..26cd3e95811a1 100644 --- a/src/wp-includes/class-wp-theme-json.php +++ b/src/wp-includes/class-wp-theme-json.php @@ -332,8 +332,7 @@ class WP_Theme_JSON { * @since 5.9.0 Renamed from `ALLOWED_STYLES` to `VALID_STYLES`, * added new properties for `border`, `filter`, `spacing`, * and `typography`. - * @since 6.1.0 Added new property for `shadow`, - * new side properties for `border`, + * @since 6.1.0 Added new side properties for `border`, * updated `blockGap` to be allowed at any level. * @var array */ @@ -353,7 +352,6 @@ class WP_Theme_JSON { 'gradient' => null, 'text' => null, ), - 'shadow' => null, 'filter' => array( 'duotone' => null, ), From 0f730b700591d270d1a9287a6fe498ec60219f64 Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Wed, 14 Sep 2022 11:56:23 +1000 Subject: [PATCH 16/16] Remove whitespace change --- src/wp-includes/class-wp-theme-json.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/class-wp-theme-json.php b/src/wp-includes/class-wp-theme-json.php index 26cd3e95811a1..6299b82ee40fd 100644 --- a/src/wp-includes/class-wp-theme-json.php +++ b/src/wp-includes/class-wp-theme-json.php @@ -2040,8 +2040,8 @@ function( $pseudo_selector ) use ( $selector ) { * Otherwise just compute the styles for the default selector as normal. */ if ( $pseudo_selector && isset( $node[ $pseudo_selector ] ) && - array_key_exists( $current_element, static::VALID_ELEMENT_PSEUDO_SELECTORS ) && - in_array( $pseudo_selector, static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ], true ) + array_key_exists( $current_element, static::VALID_ELEMENT_PSEUDO_SELECTORS ) + && in_array( $pseudo_selector, static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ], true ) ) { $declarations = static::compute_style_properties( $node[ $pseudo_selector ], $settings, null, $this->theme_json, $selector, $use_root_padding ); } else {