Make WordPress Core

Changeset 54162


Ignore:
Timestamp:
09/14/2022 06:42:04 PM (2 years ago)
Author:
hellofromTonya
Message:

Editor: Backport foundation for Layout block support refactor (part 1).

Backports the following changes from the Gutenberg repository:

  • [WordPress/gutenberg/40875 gutenberg/40875] Layout: Use semantic classnames, centralize layout definitions, reduce duplication, and fix blockGap in theme.json
  • [WordPress/gutenberg/42544 gutenberg/42544] Layout: Add a disable-layout-styles theme supports flag to opt out of all layout styles gutenberg/42544
  • [WordPress/gutenberg/42087 gutenberg/42087] Theme.json: Add block support feature level selectors for blocks gutenberg/42087
  • [WordPress/gutenberg/43792 gutenberg/43792] Global Styles: Split root layout rules into a different function gutenberg/43792
  • [WordPress/gutenberg/42544 gutenberg/42544] Layout: Add a disable-layout-styles theme supports flag to opt out of all layout styles gutenberg/42544
  • [WordPress/gutenberg/42665 gutenberg/42665] Layout: Reduce specificity of fallback blockGap styles gutenberg/42665
  • [WordPress/gutenberg/42085 gutenberg/42085] Core CSS support for root padding and alignfull blocks gutenberg/42085

Notes:

  • It doesn't entirely port over PR 40875 — the remaining PHP changes for that PR will be explored in a separate PR targeting layout.php.
  • [54159] was reverted in [54160] due to PHPUnit test failures for tests added by the commit. Later, tests passed when applied on top of trunk. There were various outages today of upstream wp-env dependencies, which likely were the root cause of the earlier failures. For historical tracking and to make sure, recommitting [54159] but instead on top of current trunk. See PR 3205 for more details.
  • Giving additional props for those who did a deep dive investigation into the failed tests.

Follow-up to [54160], [54159].

Props andrewserong, aaronrobertshaw, isabel_brison, bernhard-reiter, hellofromTonya.
See #56467.

Location:
trunk
Files:
9 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/block-editor.php

    r54160 r54162  
    210210        'disableCustomFontSizes'           => get_theme_support( 'disable-custom-font-sizes' ),
    211211        'disableCustomGradients'           => get_theme_support( 'disable-custom-gradients' ),
     212        'disableLayoutStyles'              => get_theme_support( 'disable-layout-styles' ),
    212213        'enableCustomLineHeight'           => get_theme_support( 'custom-line-height' ),
    213214        'enableCustomSpacing'              => get_theme_support( 'custom-spacing' ),
     
    418419            $global_styles[]      = $block_classes;
    419420        }
     421    } else {
     422        // If there is no `theme.json` file, ensure base layout styles are still available.
     423        $block_classes = array(
     424            'css'            => 'base-layout-styles',
     425            '__unstableType' => 'base-layout',
     426            'isGlobalStyles' => true,
     427        );
     428        $actual_css    = wp_get_global_stylesheet( array( $block_classes['css'] ) );
     429        if ( '' !== $actual_css ) {
     430            $block_classes['css'] = $actual_css;
     431            $global_styles[]  = $block_classes;
     432        }
    420433    }
    421434
     
    475488        unset( $editor_settings['__experimentalFeatures']['spacing']['padding'] );
    476489    }
     490    if ( isset( $editor_settings['__experimentalFeatures']['spacing']['customSpacingSize'] ) ) {
     491        $editor_settings['disableCustomSpacingSizes'] = ! $editor_ettings['__experimentalFeatures']['spacing']['customSpacingSize'];
     492        unset( $editor_settings['__experimentalFeatures']['spacing']['customSpacingSize'] );
     493    }
     494
     495    if ( isset( $editor_settings['__experimentalFeatures']['spacing']['spacingSizes'] ) ) {
     496        $spacing_sizes_by_origin  = $editor_settings['__experimentalFeatures']['spacing']['spacingSizes'];
     497        $editor_settings['spacingSizes'] = isset( $spacing_sizes_by_origin['custom'] ) ?
     498            $spacing_sizes_by_origin['custom'] : (
     499                isset( $spacing_sizes_by_origin['theme'] ) ?
     500                    $spacing_sizes_by_origin['theme'] :
     501                    $spacing_sizes_by_origin['default']
     502            );
     503    }
    477504
    478505    $editor_settings['__unstableResolvedAssets']         = _wp_get_iframed_editor_assets();
    479506    $editor_settings['localAutosaveInterval']            = 15;
     507    $editor_settings['disableLayoutStyles']              = current_theme_supports( 'disable-layout-styles' );
    480508    $editor_settings['__experimentalDiscussionSettings'] = array(
    481509        'commentOrder'         => get_option( 'comment_order' ),
  • trunk/src/wp-includes/class-wp-theme-json-resolver.php

    r54160 r54162  
    231231
    232232        return $with_theme_supports;
     233    }
     234
     235    /**
     236     * Gets the styles for blocks from the block.json file.
     237     *
     238     * @since 6.1.0
     239     *
     240     * @return WP_Theme_JSON
     241     */
     242    public static function get_block_data() {
     243        $registry = WP_Block_Type_Registry::get_instance();
     244        $blocks   = $registry->get_all_registered();
     245        $config   = array( 'version' => 1 );
     246        foreach ( $blocks as $block_name => $block_type ) {
     247            if ( isset( $block_type->supports['__experimentalStyle'] ) ) {
     248                $config['styles']['blocks'][ $block_name ] = static::remove_json_comments( $block_type->supports['__experimentalStyle'] );
     249            }
     250
     251            if (
     252                isset( $block_type->supports['spacing']['blockGap']['__experimentalDefault'] ) &&
     253                null === _wp_array_get( $config, array( 'styles', 'blocks', $block_name, 'spacing', 'blockGap' ), null )
     254            ) {
     255                // Ensure an empty placeholder value exists for the block, if it provides a default blockGap value.
     256                // The real blockGap value to be used will be determined when the styles are rendered for output.
     257                $config['styles']['blocks'][ $block_name ]['spacing']['blockGap'] = null;
     258            }
     259        }
     260
     261        // Core here means it's the lower level part of the styles chain.
     262        // It can be a core or a third-party block.
     263        return new WP_Theme_JSON( $config, 'core' );
     264    }
     265
     266    /**
     267     * When given an array, this will remove any keys with the name `//`.
     268     *
     269     * @param array $array The array to filter.
     270     * @return array The filtered array.
     271     */
     272    private static function remove_json_comments( $array ) {
     273        unset( $array['//'] );
     274        foreach ( $array as $k => $v ) {
     275            if ( is_array( $v ) ) {
     276                $array[ $k ] = static::remove_json_comments( $v );
     277            }
     278        }
     279
     280        return $array;
    233281    }
    234282
     
    371419     * @since 5.9.0 Added user data, removed the `$settings` parameter,
    372420     *              added the `$origin` parameter.
     421     * @since 6.1.0 Added block data.
    373422     *
    374423     * @param string $origin Optional. To what level should we merge data.
     
    383432        $result = new WP_Theme_JSON();
    384433        $result->merge( static::get_core_data() );
     434        $result->merge( static::get_block_data() );
    385435        $result->merge( static::get_theme_data() );
    386436
  • trunk/src/wp-includes/class-wp-theme-json.php

    r54160 r54162  
    178178     *              `text-decoration`, `text-transform`, and `filter` properties,
    179179     *              simplified the metadata structure.
     180     * @since 6.1.0 Added the `border-*-color`, `border-*-width`, `border-*-style`,
     181     *              `--wp--style--root--padding-*`, and `box-shadow` properties,
     182     *              removed the `--wp--style--block-gap` property.
    180183     * @var array
    181184     */
    182185    const PROPERTIES_METADATA = array(
    183         'background'                 => array( 'color', 'gradient' ),
    184         'background-color'           => array( 'color', 'background' ),
    185         'border-radius'              => array( 'border', 'radius' ),
    186         'border-top-left-radius'     => array( 'border', 'radius', 'topLeft' ),
    187         'border-top-right-radius'    => array( 'border', 'radius', 'topRight' ),
    188         'border-bottom-left-radius'  => array( 'border', 'radius', 'bottomLeft' ),
    189         'border-bottom-right-radius' => array( 'border', 'radius', 'bottomRight' ),
    190         'border-color'               => array( 'border', 'color' ),
    191         'border-width'               => array( 'border', 'width' ),
    192         'border-style'               => array( 'border', 'style' ),
    193         'color'                      => array( 'color', 'text' ),
    194         'font-family'                => array( 'typography', 'fontFamily' ),
    195         'font-size'                  => array( 'typography', 'fontSize' ),
    196         'font-style'                 => array( 'typography', 'fontStyle' ),
    197         'font-weight'                => array( 'typography', 'fontWeight' ),
    198         'letter-spacing'             => array( 'typography', 'letterSpacing' ),
    199         'line-height'                => array( 'typography', 'lineHeight' ),
    200         'margin'                     => array( 'spacing', 'margin' ),
    201         'margin-top'                 => array( 'spacing', 'margin', 'top' ),
    202         'margin-right'               => array( 'spacing', 'margin', 'right' ),
    203         'margin-bottom'              => array( 'spacing', 'margin', 'bottom' ),
    204         'margin-left'                => array( 'spacing', 'margin', 'left' ),
    205         'padding'                    => array( 'spacing', 'padding' ),
    206         'padding-top'                => array( 'spacing', 'padding', 'top' ),
    207         'padding-right'              => array( 'spacing', 'padding', 'right' ),
    208         'padding-bottom'             => array( 'spacing', 'padding', 'bottom' ),
    209         'padding-left'               => array( 'spacing', 'padding', 'left' ),
    210         '--wp--style--block-gap'     => array( 'spacing', 'blockGap' ),
    211         'text-decoration'            => array( 'typography', 'textDecoration' ),
    212         'text-transform'             => array( 'typography', 'textTransform' ),
    213         'filter'                     => array( 'filter', 'duotone' ),
     186        'background'                        => array( 'color', 'gradient' ),
     187        'background-color'                  => array( 'color', 'background' ),
     188        'border-radius'                     => array( 'border', 'radius' ),
     189        'border-top-left-radius'            => array( 'border', 'radius', 'topLeft' ),
     190        'border-top-right-radius'           => array( 'border', 'radius', 'topRight' ),
     191        'border-bottom-left-radius'         => array( 'border', 'radius', 'bottomLeft' ),
     192        'border-bottom-right-radius'        => array( 'border', 'radius', 'bottomRight' ),
     193        'border-color'                      => array( 'border', 'color' ),
     194        'border-width'                      => array( 'border', 'width' ),
     195        'border-style'                      => array( 'border', 'style' ),
     196        'border-top-color'                  => array( 'border', 'top', 'color' ),
     197        'border-top-width'                  => array( 'border', 'top', 'width' ),
     198        'border-top-style'                  => array( 'border', 'top', 'style' ),
     199        'border-right-color'                => array( 'border', 'right', 'color' ),
     200        'border-right-width'                => array( 'border', 'right', 'width' ),
     201        'border-right-style'                => array( 'border', 'right', 'style' ),
     202        'border-bottom-color'               => array( 'border', 'bottom', 'color' ),
     203        'border-bottom-width'               => array( 'border', 'bottom', 'width' ),
     204        'border-bottom-style'               => array( 'border', 'bottom', 'style' ),
     205        'border-left-color'                 => array( 'border', 'left', 'color' ),
     206        'border-left-width'                 => array( 'border', 'left', 'width' ),
     207        'border-left-style'                 => array( 'border', 'left', 'style' ),
     208        'color'                             => array( 'color', 'text' ),
     209        'font-family'                       => array( 'typography', 'fontFamily' ),
     210        'font-size'                         => array( 'typography', 'fontSize' ),
     211        'font-style'                        => array( 'typography', 'fontStyle' ),
     212        'font-weight'                       => array( 'typography', 'fontWeight' ),
     213        'letter-spacing'                    => array( 'typography', 'letterSpacing' ),
     214        'line-height'                       => array( 'typography', 'lineHeight' ),
     215        'margin'                            => array( 'spacing', 'margin' ),
     216        'margin-top'                        => array( 'spacing', 'margin', 'top' ),
     217        'margin-right'                      => array( 'spacing', 'margin', 'right' ),
     218        'margin-bottom'                     => array( 'spacing', 'margin', 'bottom' ),
     219        'margin-left'                       => array( 'spacing', 'margin', 'left' ),
     220        'padding'                           => array( 'spacing', 'padding' ),
     221        'padding-top'                       => array( 'spacing', 'padding', 'top' ),
     222        'padding-right'                     => array( 'spacing', 'padding', 'right' ),
     223        'padding-bottom'                    => array( 'spacing', 'padding', 'bottom' ),
     224        'padding-left'                      => array( 'spacing', 'padding', 'left' ),
     225        '--wp--style--root--padding'        => array( 'spacing', 'padding' ),
     226        '--wp--style--root--padding-top'    => array( 'spacing', 'padding', 'top' ),
     227        '--wp--style--root--padding-right'  => array( 'spacing', 'padding', 'right' ),
     228        '--wp--style--root--padding-bottom' => array( 'spacing', 'padding', 'bottom' ),
     229        '--wp--style--root--padding-left'   => array( 'spacing', 'padding', 'left' ),
     230        'text-decoration'                   => array( 'typography', 'textDecoration' ),
     231        'text-transform'                    => array( 'typography', 'textTransform' ),
     232        'filter'                            => array( 'filter', 'duotone' ),
     233        'box-shadow'                        => array( 'shadow' ),
    214234    );
    215235
     
    255275     *              and `typography`, and renamed others according to the new schema.
    256276     * @since 6.0.0 Added `color.defaultDuotone`.
     277     * @since 6.1.0 Added `layout.definitions` and `useRootPaddingAwareAlignments`.
    257278     * @var array
    258279     */
    259280    const VALID_SETTINGS = array(
    260         'appearanceTools' => null,
    261         'border'          => array(
     281        'appearanceTools'               => null,
     282        'useRootPaddingAwareAlignments' => null,
     283        'border'                        => array(
    262284            'color'  => null,
    263285            'radius' => null,
     
    265287            'width'  => null,
    266288        ),
    267         'color'           => array(
     289        'color'                         => array(
    268290            'background'       => null,
    269291            'custom'           => null,
     
    279301            'text'             => null,
    280302        ),
    281         'custom'          => null,
    282         'layout'          => array(
     303        'custom'                        => null,
     304        'layout'                        => array(
    283305            'contentSize' => null,
     306            'definitions' => null,
    284307            'wideSize'    => null,
    285308        ),
    286         'spacing'         => array(
     309        'spacing'                       => array(
    287310            'blockGap' => null,
    288311            'margin'   => null,
     
    290313            'units'    => null,
    291314        ),
    292         'typography'      => array(
     315        'typography'                    => array(
    293316            'customFontSize' => null,
    294317            'dropCap'        => null,
     
    311334     *              added new properties for `border`, `filter`, `spacing`,
    312335     *              and `typography`.
     336     * @since 6.1.0 Added new side properties for `border`,
     337     *              updated `blockGap` to be allowed at any level.
    313338     * @var array
    314339     */
     
    319344            'style'  => null,
    320345            'width'  => null,
     346            'top'    => null,
     347            'right'  => null,
     348            'bottom' => null,
     349            'left'   => null,
    321350        ),
    322351        'color'      => array(
     
    331360            'margin'   => null,
    332361            'padding'  => null,
    333             'blockGap' => 'top',
     362            'blockGap' => null,
    334363        ),
    335364        'typography' => array(
     
    382411        'button'  => 'wp-element-button',
    383412        'caption' => 'wp-element-caption',
     413    );
     414
     415    /**
     416     * List of block support features that can have their related styles
     417     * generated under their own feature level selector rather than the block's.
     418     *
     419     * @since 6.1.0
     420     * @var string[]
     421     */
     422    const BLOCK_SUPPORT_FEATURE_LEVEL_SELECTORS = array(
     423        '__experimentalBorder' => 'border',
     424        'color'                => 'color',
     425        'spacing'              => 'spacing',
     426        'typography'           => 'typography',
    384427    );
    385428
     
    657700     * @since 5.8.0
    658701     * @since 5.9.0 Added `duotone` key with CSS selector.
     702     * @since 6.1.0 Added `features` key with block support feature level selectors.
    659703     *
    660704     * @return array Block metadata.
     
    684728            ) {
    685729                static::$blocks_metadata[ $block_name ]['duotone'] = $block_type->supports['color']['__experimentalDuotone'];
     730            }
     731
     732            // Generate block support feature level selectors if opted into
     733            // for the current block.
     734            $features = array();
     735            foreach ( static::BLOCK_SUPPORT_FEATURE_LEVEL_SELECTORS as $key => $feature ) {
     736                if (
     737                    isset( $block_type->supports[ $key ]['__experimentalSelector'] ) &&
     738                    $block_type->supports[ $key ]['__experimentalSelector']
     739                ) {
     740                    $features[ $feature ] = static::scope_selector(
     741                        static::$blocks_metadata[ $block_name ]['selector'],
     742                        $block_type->supports[ $key ]['__experimentalSelector']
     743                    );
     744                }
     745            }
     746
     747            if ( ! empty( $features ) ) {
     748                static::$blocks_metadata[ $block_name ]['features'] = $features;
    686749            }
    687750
     
    811874
    812875        if ( in_array( 'styles', $types, true ) ) {
     876            $root_block_key = array_search( static::ROOT_BLOCK_SELECTOR, array_column( $style_nodes, 'selector' ), true );
     877
     878            if ( false !== $root_block_key ) {
     879                $stylesheet .= $this->get_root_layout_rules( static::ROOT_BLOCK_SELECTOR, $style_nodes[ $root_block_key ] );
     880            }
    813881            $stylesheet .= $this->get_block_classes( $style_nodes );
     882        } elseif ( in_array( 'base-layout-styles', $types, true ) ) {
     883            // Base layout styles are provided as part of `styles`, so only output separately if explicitly requested.
     884            // For backwards compatibility, the Columns block is explicitly included, to support a different default gap value.
     885            $base_styles_nodes = array(
     886                array(
     887                    'path'     => array( 'styles' ),
     888                    'selector' => static::ROOT_BLOCK_SELECTOR,
     889                ),
     890                array(
     891                    'path'     => array( 'styles', 'blocks', 'core/columns' ),
     892                    'selector' => '.wp-block-columns',
     893                    'name'     => 'core/columns',
     894                ),
     895            );
     896
     897            foreach ( $base_styles_nodes as $base_style_node ) {
     898                $stylesheet .= $this->get_layout_styles( $base_style_node );
     899            }
    814900        }
    815901
     
    885971     *              and no longer returns preset classes.
    886972     *              Removed the `$setting_nodes` parameter.
     973     * @since 6.1.0 Moved most internal logic to `get_styles_for_block()`.
    887974     *
    888975     * @param array $style_nodes Nodes with styles.
     
    899986        }
    900987
     988        return $block_rules;
     989    }
     990
     991    /**
     992     * Get the CSS layout rules for a particular block from theme.json layout definitions.
     993     *
     994     * @since 6.1.0
     995     *
     996     * @param array $block_metadata Metadata about the block to get styles for.
     997     *
     998     * @return string Layout styles for the block.
     999     */
     1000    protected function get_layout_styles( $block_metadata ) {
     1001        $block_rules = '';
     1002        $block_type  = null;
     1003
     1004        // Skip outputting layout styles if explicitly disabled.
     1005        if ( current_theme_supports( 'disable-layout-styles' ) ) {
     1006            return $block_rules;
     1007        }
     1008
     1009        if ( isset( $block_metadata['name'] ) ) {
     1010            $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block_metadata['name'] );
     1011            if ( ! block_has_support( $block_type, array( '__experimentalLayout' ), false ) ) {
     1012                return $block_rules;
     1013            }
     1014        }
     1015
     1016        $selector                 = isset( $block_metadata['selector'] ) ? $block_metadata['selector'] : '';
     1017        $has_block_gap_support    = _wp_array_get( $this->theme_json, array( 'settings', 'spacing', 'blockGap' ) ) !== null;
     1018        $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.
     1019        $node                     = _wp_array_get( $this->theme_json, $block_metadata['path'], array() );
     1020        $layout_definitions       = _wp_array_get( $this->theme_json, array( 'settings', 'layout', 'definitions' ), array() );
     1021        $layout_selector_pattern  = '/^[a-zA-Z0-9\-\.\ *+>:\(\)]*$/'; // Allow alphanumeric classnames, spaces, wildcard, sibling, child combinator and pseudo class selectors.
     1022
     1023        // Gap styles will only be output if the theme has block gap support, or supports a fallback gap.
     1024        // Default layout gap styles will be skipped for themes that do not explicitly opt-in to blockGap with a `true` or `false` value.
     1025        if ( $has_block_gap_support || $has_fallback_gap_support ) {
     1026            $block_gap_value = null;
     1027            // Use a fallback gap value if block gap support is not available.
     1028            if ( ! $has_block_gap_support ) {
     1029                $block_gap_value = static::ROOT_BLOCK_SELECTOR === $selector ? '0.5em' : null;
     1030                if ( ! empty( $block_type ) ) {
     1031                    $block_gap_value = _wp_array_get( $block_type->supports, array( 'spacing', 'blockGap', '__experimentalDefault' ), null );
     1032                }
     1033            } else {
     1034                $block_gap_value = static::get_property_value( $node, array( 'spacing', 'blockGap' ) );
     1035            }
     1036
     1037            // Support split row / column values and concatenate to a shorthand value.
     1038            if ( is_array( $block_gap_value ) ) {
     1039                if ( isset( $block_gap_value['top'] ) && isset( $block_gap_value['left'] ) ) {
     1040                    $gap_row         = static::get_property_value( $node, array( 'spacing', 'blockGap', 'top' ) );
     1041                    $gap_column      = static::get_property_value( $node, array( 'spacing', 'blockGap', 'left' ) );
     1042                    $block_gap_value = $gap_row === $gap_column ? $gap_row : $gap_row . ' ' . $gap_column;
     1043                } else {
     1044                    // Skip outputting gap value if not all sides are provided.
     1045                    $block_gap_value = null;
     1046                }
     1047            }
     1048
     1049            // If the block should have custom gap, add the gap styles.
     1050            if ( null !== $block_gap_value && false !== $block_gap_value && '' !== $block_gap_value ) {
     1051                foreach ( $layout_definitions as $layout_definition_key => $layout_definition ) {
     1052                    // Allow outputting fallback gap styles for flex layout type when block gap support isn't available.
     1053                    if ( ! $has_block_gap_support && 'flex' !== $layout_definition_key ) {
     1054                        continue;
     1055                    }
     1056
     1057                    $class_name    = sanitize_title( _wp_array_get( $layout_definition, array( 'className' ), false ) );
     1058                    $spacing_rules = _wp_array_get( $layout_definition, array( 'spacingStyles' ), array() );
     1059
     1060                    if (
     1061                        ! empty( $class_name ) &&
     1062                        ! empty( $spacing_rules )
     1063                    ) {
     1064                        foreach ( $spacing_rules as $spacing_rule ) {
     1065                            $declarations = array();
     1066                            if (
     1067                                isset( $spacing_rule['selector'] ) &&
     1068                                preg_match( $layout_selector_pattern, $spacing_rule['selector'] ) &&
     1069                                ! empty( $spacing_rule['rules'] )
     1070                            ) {
     1071                                // Iterate over each of the styling rules and substitute non-string values such as `null` with the real `blockGap` value.
     1072                                foreach ( $spacing_rule['rules'] as $css_property => $css_value ) {
     1073                                    $current_css_value = is_string( $css_value ) ? $css_value : $block_gap_value;
     1074                                    if ( static::is_safe_css_declaration( $css_property, $current_css_value ) ) {
     1075                                        $declarations[] = array(
     1076                                            'name'  => $css_property,
     1077                                            'value' => $current_css_value,
     1078                                        );
     1079                                    }
     1080                                }
     1081
     1082                                if ( ! $has_block_gap_support ) {
     1083                                    // For fallback gap styles, use lower specificity, to ensure styles do not unintentionally override theme styles.
     1084                                    $format          = static::ROOT_BLOCK_SELECTOR === $selector ? ':where(.%2$s%3$s)' : ':where(%1$s.%2$s%3$s)';
     1085                                    $layout_selector = sprintf(
     1086                                        $format,
     1087                                        $selector,
     1088                                        $class_name,
     1089                                        $spacing_rule['selector']
     1090                                    );
     1091                                } else {
     1092                                    $format          = static::ROOT_BLOCK_SELECTOR === $selector ? '%s .%s%s' : '%s.%s%s';
     1093                                    $layout_selector = sprintf(
     1094                                        $format,
     1095                                        $selector,
     1096                                        $class_name,
     1097                                        $spacing_rule['selector']
     1098                                    );
     1099                                }
     1100                                $block_rules .= static::to_ruleset( $layout_selector, $declarations );
     1101                            }
     1102                        }
     1103                    }
     1104                }
     1105            }
     1106        }
     1107
     1108        // Output base styles.
     1109        if (
     1110            static::ROOT_BLOCK_SELECTOR === $selector
     1111        ) {
     1112            $valid_display_modes = array( 'block', 'flex', 'grid' );
     1113            foreach ( $layout_definitions as $layout_definition ) {
     1114                $class_name       = sanitize_title( _wp_array_get( $layout_definition, array( 'className' ), false ) );
     1115                $base_style_rules = _wp_array_get( $layout_definition, array( 'baseStyles' ), array() );
     1116
     1117                if (
     1118                    ! empty( $class_name ) &&
     1119                    ! empty( $base_style_rules )
     1120                ) {
     1121                    // Output display mode. This requires special handling as `display` is not exposed in `safe_style_css_filter`.
     1122                    if (
     1123                        ! empty( $layout_definition['displayMode'] ) &&
     1124                        is_string( $layout_definition['displayMode'] ) &&
     1125                        in_array( $layout_definition['displayMode'], $valid_display_modes, true )
     1126                    ) {
     1127                        $layout_selector = sprintf(
     1128                            '%s .%s',
     1129                            $selector,
     1130                            $class_name
     1131                        );
     1132                        $block_rules    .= static::to_ruleset(
     1133                            $layout_selector,
     1134                            array(
     1135                                array(
     1136                                    'name'  => 'display',
     1137                                    'value' => $layout_definition['displayMode'],
     1138                                ),
     1139                            )
     1140                        );
     1141                    }
     1142
     1143                    foreach ( $base_style_rules as $base_style_rule ) {
     1144                        $declarations = array();
     1145
     1146                        if (
     1147                            isset( $base_style_rule['selector'] ) &&
     1148                            preg_match( $layout_selector_pattern, $base_style_rule['selector'] ) &&
     1149                            ! empty( $base_style_rule['rules'] )
     1150                        ) {
     1151                            foreach ( $base_style_rule['rules'] as $css_property => $css_value ) {
     1152                                if ( static::is_safe_css_declaration( $css_property, $css_value ) ) {
     1153                                    $declarations[] = array(
     1154                                        'name'  => $css_property,
     1155                                        'value' => $css_value,
     1156                                    );
     1157                                }
     1158                            }
     1159
     1160                            $layout_selector = sprintf(
     1161                                '%s .%s%s',
     1162                                $selector,
     1163                                $class_name,
     1164                                $base_style_rule['selector']
     1165                            );
     1166                            $block_rules    .= static::to_ruleset( $layout_selector, $declarations );
     1167                        }
     1168                    }
     1169                }
     1170            }
     1171        }
    9011172        return $block_rules;
    9021173    }
     
    13281599     * @since 5.8.0
    13291600     * @since 5.9.0 Added the `$settings` and `$properties` parameters.
    1330      * @since 6.1.0 Added the `$theme_json` parameter.
    1331      *
    1332      * @param array $styles    Styles to process.
    1333      * @param array $settings  Theme settings.
    1334      * @param array $properties Properties metadata.
    1335      * @param array $theme_json Theme JSON array.
    1336      * @return array Returns the modified $declarations.
    1337      */
    1338     protected static function compute_style_properties( $styles, $settings = array(), $properties = null, $theme_json = null ) {
     1601     * @since 6.1.0 Added `$theme_json`, `$selector`, and `$use_root_padding` parameters.
     1602     *
     1603     * @param array   $styles Styles to process.
     1604     * @param array   $settings Theme settings.
     1605     * @param array   $properties Properties metadata.
     1606     * @param array   $theme_json Theme JSON array.
     1607     * @param string  $selector The style block selector.
     1608     * @param boolean $use_root_padding Whether to add custom properties at root level.
     1609     * @return array  Returns the modified $declarations.
     1610     */
     1611    protected static function compute_style_properties( $styles, $settings = array(), $properties = null, $theme_json = null, $selector = null, $use_root_padding = null ) {
    13391612        if ( null === $properties ) {
    13401613            $properties = static::PROPERTIES_METADATA;
     
    13461619        }
    13471620
     1621        $root_variable_duplicates = array();
     1622
    13481623        foreach ( $properties as $css_property => $value_path ) {
    13491624            $value = static::get_property_value( $styles, $value_path, $theme_json );
     1625
     1626            if ( str_starts_with( $css_property, '--wp--style--root--' ) && ( static::ROOT_BLOCK_SELECTOR !== $selector || ! $use_root_padding ) ) {
     1627                continue;
     1628            }
     1629            // Root-level padding styles don't currently support strings with CSS shorthand values.
     1630            // This may change: https://github.com/WordPress/gutenberg/issues/40132.
     1631            if ( '--wp--style--root--padding' === $css_property && is_string( $value ) ) {
     1632                continue;
     1633            }
     1634
     1635            if ( str_starts_with( $css_property, '--wp--style--root--' ) && $use_root_padding ) {
     1636                $root_variable_duplicates[] = substr( $css_property, strlen( '--wp--style--root--' ) );
     1637            }
    13501638
    13511639            // Look up protected properties, keyed by value path.
     
    13731661        }
    13741662
     1663        // If a variable value is added to the root, the corresponding property should be removed.
     1664        foreach ( $root_variable_duplicates as $duplicate ) {
     1665            $discard = array_search( $duplicate, array_column( $declarations, 'name' ), true );
     1666            if ( is_numeric( $discard ) ) {
     1667                array_splice( $declarations, $discard, 1 );
     1668            }
     1669        }
     1670
    13751671        return $declarations;
    13761672    }
     
    13971693     */
    13981694    protected static function get_property_value( $styles, $path, $theme_json = null ) {
    1399         $value = _wp_array_get( $styles, $path, '' );
     1695        $value = _wp_array_get( $styles, $path );
    14001696
    14011697        /*
     
    14301726        }
    14311727
    1432         if ( '' === $value || is_array( $value ) ) {
     1728        if ( is_array( $value ) ) {
    14331729            return $value;
    14341730        }
     
    16231919            }
    16241920
     1921            $feature_selectors = null;
     1922            if ( isset( $selectors[ $name ]['features'] ) ) {
     1923                $feature_selectors = $selectors[ $name ]['features'];
     1924            }
     1925
    16251926            $nodes[] = array(
    16261927                'name'     => $name,
     
    16281929                'selector' => $selector,
    16291930                'duotone'  => $duotone_selector,
     1931                'features' => $feature_selectors,
    16301932            );
    16311933
     
    16601962     * @since 6.1.0
    16611963     *
    1662      * @param array $block_metadata Meta data about the block to get styles for.
    1663      * @return array Styles for the block.
     1964     * @param array $block_metadata Metadata about the block to get styles for.
     1965     *
     1966     * @return string Styles for the block.
    16641967     */
    16651968    public function get_styles_for_block( $block_metadata ) {
    1666 
    1667         $node = _wp_array_get( $this->theme_json, $block_metadata['path'], array() );
    1668 
    1669         $selector = $block_metadata['selector'];
    1670         $settings = _wp_array_get( $this->theme_json, array( 'settings' ) );
     1969        $node             = _wp_array_get( $this->theme_json, $block_metadata['path'], array() );
     1970        $use_root_padding = isset( $this->theme_json['settings']['useRootPaddingAwareAlignments'] ) && true === $this->theme_json['settings']['useRootPaddingAwareAlignments'];
     1971        $selector         = $block_metadata['selector'];
     1972        $settings         = _wp_array_get( $this->theme_json, array( 'settings' ) );
     1973
     1974        /*
     1975         * Process style declarations for block support features the current
     1976         * block contains selectors for. Values for a feature with a custom
     1977         * selector are filtered from the theme.json node before it is
     1978         * processed as normal.
     1979        */
     1980        $feature_declarations = array();
     1981
     1982        if ( ! empty( $block_metadata['features'] ) ) {
     1983            foreach ( $block_metadata['features'] as $feature_name => $feature_selector ) {
     1984                if ( ! empty( $node[ $feature_name ] ) ) {
     1985                    // Create temporary node containing only the feature data
     1986                    // to leverage existing `compute_style_properties` function.
     1987                    $feature = array( $feature_name => $node[ $feature_name ] );
     1988                    // Generate the feature's declarations only.
     1989                    $new_feature_declarations = static::compute_style_properties( $feature, $settings, null, $this->theme_json );
     1990
     1991                    // Merge new declarations with any that already exist for
     1992                    // the feature selector. This may occur when multiple block
     1993                    // support features use the same custom selector.
     1994                    if ( isset( $feature_declarations[ $feature_selector ] ) ) {
     1995                        $feature_declarations[ $feature_selector ] = array_merge( $feature_declarations[ $feature_selector ], $new_feature_declarations );
     1996                    } else {
     1997                        $feature_declarations[ $feature_selector ] = $new_feature_declarations;
     1998                    }
     1999
     2000                    // Remove the feature from the block's node now the
     2001                    // styles will be included under the feature level selector.
     2002                    unset( $node[ $feature_name ] );
     2003                }
     2004            }
     2005        }
    16712006
    16722007        /*
     
    17102045            && in_array( $pseudo_selector, static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ], true )
    17112046        ) {
    1712             $declarations = static::compute_style_properties( $node[ $pseudo_selector ], $settings, null, $this->theme_json );
     2047            $declarations = static::compute_style_properties( $node[ $pseudo_selector ], $settings, null, $this->theme_json, $selector, $use_root_padding );
    17132048        } else {
    1714             $declarations = static::compute_style_properties( $node, $settings, null, $this->theme_json );
     2049            $declarations = static::compute_style_properties( $node, $settings, null, $this->theme_json, $selector, $use_root_padding );
    17152050        }
    17162051
     
    17292064        }
    17302065
    1731         /*
    1732          * Reset default browser margin on the root body element.
    1733          * This is set on the root selector **before** generating the ruleset
    1734          * from the `theme.json`. This is to ensure that if the `theme.json` declares
    1735          * `margin` in its `spacing` declaration for the `body` element then these
    1736          * user-generated values take precedence in the CSS cascade.
    1737          * @link https://github.com/WordPress/gutenberg/issues/36147.
    1738          */
    1739         if ( static::ROOT_BLOCK_SELECTOR === $selector ) {
    1740             $block_rules .= 'body { margin: 0; }';
    1741         }
    1742 
    17432066        // 2. Generate and append the rules that use the general selector.
    17442067        $block_rules .= static::to_ruleset( $selector, $declarations );
     
    17502073        }
    17512074
    1752         if ( static::ROOT_BLOCK_SELECTOR === $selector ) {
    1753             $block_rules .= '.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }';
    1754             $block_rules .= '.wp-site-blocks > .alignright { float: right; margin-left: 2em; }';
    1755             $block_rules .= '.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }';
    1756 
    1757             $has_block_gap_support = _wp_array_get( $this->theme_json, array( 'settings', 'spacing', 'blockGap' ) ) !== null;
    1758             if ( $has_block_gap_support ) {
    1759                 $block_rules .= '.wp-site-blocks > * { margin-block-start: 0; margin-block-end: 0; }';
    1760                 $block_rules .= '.wp-site-blocks > * + * { margin-block-start: var( --wp--style--block-gap ); }';
    1761             }
     2075        // 4. Generate Layout block gap styles.
     2076        if (
     2077            static::ROOT_BLOCK_SELECTOR !== $selector &&
     2078            ! empty( $block_metadata['name'] )
     2079        ) {
     2080            $block_rules .= $this->get_layout_styles( $block_metadata );
     2081        }
     2082
     2083        // 5. Generate and append the feature level rulesets.
     2084        foreach ( $feature_declarations as $feature_selector => $individual_feature_declarations ) {
     2085            $block_rules .= static::to_ruleset( $feature_selector, $individual_feature_declarations );
    17622086        }
    17632087
    17642088        return $block_rules;
     2089    }
     2090
     2091    /**
     2092     * Outputs the CSS for layout rules on the root.
     2093     *
     2094     * @since 6.1.0
     2095     *
     2096     * @param string $selector The root node selector.
     2097     * @param array  $block_metadata The metadata for the root block.
     2098     * @return string The additional root rules CSS.
     2099     */
     2100    public function get_root_layout_rules( $selector, $block_metadata ) {
     2101        $css              = '';
     2102        $settings         = _wp_array_get( $this->theme_json, array( 'settings' ) );
     2103        $use_root_padding = isset( $this->theme_json['settings']['useRootPaddingAwareAlignments'] ) && true === $this->theme_json['settings']['useRootPaddingAwareAlignments'];
     2104
     2105        /*
     2106        * Reset default browser margin on the root body element.
     2107        * This is set on the root selector **before** generating the ruleset
     2108        * from the `theme.json`. This is to ensure that if the `theme.json` declares
     2109        * `margin` in its `spacing` declaration for the `body` element then these
     2110        * user-generated values take precedence in the CSS cascade.
     2111        * @link https://github.com/WordPress/gutenberg/issues/36147.
     2112        */
     2113        $css .= 'body { margin: 0;';
     2114
     2115        /*
     2116        * If there are content and wide widths in theme.json, output them
     2117        * as custom properties on the body element so all blocks can use them.
     2118        */
     2119        if ( isset( $settings['layout']['contentSize'] ) || isset( $settings['layout']['wideSize'] ) ) {
     2120            $content_size = isset( $settings['layout']['contentSize'] ) ? $settings['layout']['contentSize'] : $settings['layout']['wideSize'];
     2121            $content_size = static::is_safe_css_declaration( 'max-width', $content_size ) ? $content_size : 'initial';
     2122            $wide_size    = isset( $settings['layout']['wideSize'] ) ? $settings['layout']['wideSize'] : $settings['layout']['contentSize'];
     2123            $wide_size    = static::is_safe_css_declaration( 'max-width', $wide_size ) ? $wide_size : 'initial';
     2124            $css         .= '--wp--style--global--content-size: ' . $content_size . ';';
     2125            $css         .= '--wp--style--global--wide-size: ' . $wide_size . ';';
     2126        }
     2127
     2128        $css .= ' }';
     2129
     2130        if ( $use_root_padding ) {
     2131            // Top and bottom padding are applied to the outer block container.
     2132            $css .= '.wp-site-blocks { padding-top: var(--wp--style--root--padding-top); padding-bottom: var(--wp--style--root--padding-bottom); }';
     2133            // Right and left padding are applied to the first container with `.has-global-padding` class.
     2134            $css .= '.has-global-padding { padding-right: var(--wp--style--root--padding-right); padding-left: var(--wp--style--root--padding-left); }';
     2135            // Nested containers with `.has-global-padding` class do not get padding.
     2136            $css .= '.has-global-padding :where(.has-global-padding) { padding-right: 0; padding-left: 0; }';
     2137            // Alignfull children of the container with left and right padding have negative margins so they can still be full width.
     2138            $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); }';
     2139            // The above rule is negated for alignfull children of nested containers.
     2140            $css .= '.has-global-padding :where(.has-global-padding) > .alignfull { margin-right: 0; margin-left: 0; }';
     2141            // Some of the children of alignfull blocks without content width should also get padding: text blocks and non-alignfull container blocks.
     2142            $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); }';
     2143            // The above rule also has to be negated for blocks inside nested `.has-global-padding` blocks.
     2144            $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; }';
     2145        }
     2146
     2147        $css .= '.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }';
     2148        $css .= '.wp-site-blocks > .alignright { float: right; margin-left: 2em; }';
     2149        $css .= '.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }';
     2150
     2151        $block_gap_value       = _wp_array_get( $this->theme_json, array( 'styles', 'spacing', 'blockGap' ), '0.5em' );
     2152        $has_block_gap_support = _wp_array_get( $this->theme_json, array( 'settings', 'spacing', 'blockGap' ) ) !== null;
     2153        if ( $has_block_gap_support ) {
     2154            $block_gap_value = static::get_property_value( $this->theme_json, array( 'styles', 'spacing', 'blockGap' ) );
     2155            $css            .= '.wp-site-blocks > * { margin-block-start: 0; margin-block-end: 0; }';
     2156            $css            .= ".wp-site-blocks > * + * { margin-block-start: $block_gap_value; }";
     2157
     2158            // For backwards compatibility, ensure the legacy block gap CSS variable is still available.
     2159            $css .= "$selector { --wp--style--block-gap: $block_gap_value; }";
     2160        }
     2161        $css .= $this->get_layout_styles( $block_metadata );
     2162
     2163        return $css;
    17652164    }
    17662165
  • trunk/src/wp-includes/global-styles-and-settings.php

    r54160 r54162  
    108108    $supports_theme_json = WP_Theme_JSON_Resolver::theme_has_support();
    109109    if ( empty( $types ) && ! $supports_theme_json ) {
    110         $types = array( 'variables', 'presets' );
     110        $types = array( 'variables', 'presets', 'base-layout-styles' );
    111111    } elseif ( empty( $types ) ) {
    112112        $types = array( 'variables', 'styles', 'presets' );
  • trunk/src/wp-includes/theme.json

    r54160 r54162  
    33    "settings": {
    44        "appearanceTools": false,
     5        "useRootPaddingAwareAlignments": false,
    56        "border": {
    67            "color": false,
     
    1314            "custom": true,
    1415            "customDuotone": true,
    15             "customGradient": true,
    16             "defaultDuotone": true,
     16            "customGradient": true,
     17            "defaultDuotone": true,
    1718            "defaultGradients": true,
    1819            "defaultPalette": true,
     
    185186            ],
    186187            "text": true
     188        },
     189        "layout": {
     190            "definitions": {
     191                "default": {
     192                    "name": "default",
     193                    "slug": "flow",
     194                    "className": "is-layout-flow",
     195                    "baseStyles": [
     196                        {
     197                            "selector": " > .alignleft",
     198                            "rules": {
     199                                "float": "left",
     200                                "margin-inline-start": "0",
     201                                "margin-inline-end": "2em"
     202                            }
     203                        },
     204                        {
     205                            "selector": " > .alignright",
     206                            "rules": {
     207                                "float": "right",
     208                                "margin-inline-start": "2em",
     209                                "margin-inline-end": "0"
     210                            }
     211                        },
     212                        {
     213                            "selector": " > .aligncenter",
     214                            "rules": {
     215                                "margin-left": "auto !important",
     216                                "margin-right": "auto !important"
     217                            }
     218                        }
     219                    ],
     220                    "spacingStyles": [
     221                        {
     222                            "selector": " > *",
     223                            "rules": {
     224                                "margin-block-start": "0",
     225                                "margin-block-end": "0"
     226                            }
     227                        },
     228                        {
     229                            "selector": " > * + *",
     230                            "rules": {
     231                                "margin-block-start": null,
     232                                "margin-block-end": "0"
     233                            }
     234                        }
     235                    ]
     236                },
     237                "constrained": {
     238                    "name": "constrained",
     239                    "slug": "constrained",
     240                    "className": "is-layout-constrained",
     241                    "baseStyles": [
     242                        {
     243                            "selector": " > .alignleft",
     244                            "rules": {
     245                                "float": "left",
     246                                "margin-inline-start": "0",
     247                                "margin-inline-end": "2em"
     248                            }
     249                        },
     250                        {
     251                            "selector": " > .alignright",
     252                            "rules": {
     253                                "float": "right",
     254                                "margin-inline-start": "2em",
     255                                "margin-inline-end": "0"
     256                            }
     257                        },
     258                        {
     259                            "selector": " > .aligncenter",
     260                            "rules": {
     261                                "margin-left": "auto !important",
     262                                "margin-right": "auto !important"
     263                            }
     264                        },
     265                        {
     266                            "selector": " > :where(:not(.alignleft):not(.alignright):not(.alignfull))",
     267                            "rules": {
     268                                "max-width": "var(--wp--style--global--content-size)",
     269                                "margin-left": "auto !important",
     270                                "margin-right": "auto !important"
     271                            }
     272                        },
     273                        {
     274                            "selector": " > .alignwide",
     275                            "rules": {
     276                                "max-width": "var(--wp--style--global--wide-size)"
     277                            }
     278                        }
     279                    ],
     280                    "spacingStyles": [
     281                        {
     282                            "selector": " > *",
     283                            "rules": {
     284                                "margin-block-start": "0",
     285                                "margin-block-end": "0"
     286                            }
     287                        },
     288                        {
     289                            "selector": " > * + *",
     290                            "rules": {
     291                                "margin-block-start": null,
     292                                "margin-block-end": "0"
     293                            }
     294                        }
     295                    ]
     296                },
     297                "flex": {
     298                    "name": "flex",
     299                    "slug": "flex",
     300                    "className": "is-layout-flex",
     301                    "displayMode": "flex",
     302                    "baseStyles": [
     303                        {
     304                            "selector": "",
     305                            "rules": {
     306                                "flex-wrap": "wrap",
     307                                "align-items": "center"
     308                            }
     309                        },
     310                        {
     311                            "selector": " > *",
     312                            "rules": {
     313                                "margin": "0"
     314                            }
     315                        }
     316                    ],
     317                    "spacingStyles": [
     318                        {
     319                            "selector": "",
     320                            "rules": {
     321                                "gap": null
     322                            }
     323                        }
     324                    ]
     325                }
     326            }
    187327        },
    188328        "spacing": {
     
    190330            "margin": false,
    191331            "padding": false,
    192             "units": [ "px", "em", "rem", "vh", "vw", "%" ]
     332            "customSpacingSize": true,
     333            "units": [ "px", "em", "rem", "vh", "vw", "%" ],
     334            "spacingScale": {
     335                "operator": "*",
     336                "increment": 1.5,
     337                "steps": 7,
     338                "mediumStep": 1.5,
     339                "unit": "rem"
     340            }
    193341        },
    194342        "typography": {
     
    241389    },
    242390    "styles": {
    243         "spacing": { "blockGap": "24px" }
     391        "elements": {
     392            "button": {
     393                "color": {
     394                    "text": "#fff",
     395                    "background": "#32373c"
     396                },
     397                "spacing": {
     398                    "padding": "calc(0.667em + 2px) calc(1.333em + 2px)"
     399                },
     400                "typography": {
     401                    "fontSize": "inherit",
     402                    "fontFamily": "inherit",
     403                    "lineHeight": "inherit",
     404                    "textDecoration": "none"
     405                },
     406                "border": {
     407                    "width": "0"
     408                }
     409            },
     410            "link": {
     411                "typography": {
     412                    "textDecoration": "underline"
     413                }
     414            }
     415        },
     416        "spacing": {
     417            "blockGap": "24px",
     418            "padding": {
     419                "top": "0px",
     420                "right": "0px",
     421                "bottom": "0px",
     422                "left": "0px"
     423            }
     424        }
    244425    }
    245426}
  • trunk/src/wp-includes/theme.php

    r54160 r54162  
    40284028    );
    40294029    register_theme_feature(
     4030        'disable-layout-styles',
     4031        array(
     4032            'description'  => __( 'Whether the theme disables generated layout styles.' ),
     4033            'show_in_rest' => true,
     4034        )
     4035    );
     4036    register_theme_feature(
    40304037        'editor-color-palette',
    40314038        array(
  • trunk/tests/phpunit/tests/blocks/editor.php

    r54160 r54162  
    203203        $settings = get_default_block_editor_settings();
    204204
    205         $this->assertCount( 18, $settings );
     205        $this->assertCount( 19, $settings );
    206206        $this->assertFalse( $settings['alignWide'] );
    207207        $this->assertIsArray( $settings['allowedMimeTypes'] );
     
    250250        $this->assertFalse( $settings['disableCustomFontSizes'] );
    251251        $this->assertFalse( $settings['disableCustomGradients'] );
     252        $this->assertFalse( $settings['disableLayoutStyles'] );
    252253        $this->assertFalse( $settings['enableCustomLineHeight'] );
    253254        $this->assertFalse( $settings['enableCustomSpacing'] );
  • trunk/tests/phpunit/tests/rest-api/rest-themes-controller.php

    r54160 r54162  
    397397        $this->assertArrayHasKey( 'disable-custom-font-sizes', $theme_supports );
    398398        $this->assertArrayHasKey( 'disable-custom-gradients', $theme_supports );
     399        $this->assertArrayHasKey( 'disable-layout-styles', $theme_supports );
    399400        $this->assertArrayHasKey( 'editor-color-palette', $theme_supports );
    400401        $this->assertArrayHasKey( 'editor-font-sizes', $theme_supports );
     
    407408        $this->assertArrayHasKey( 'title-tag', $theme_supports );
    408409        $this->assertArrayHasKey( 'wp-block-styles', $theme_supports );
    409         $this->assertCount( 21, $theme_supports );
     410        $this->assertCount( 22, $theme_supports );
    410411    }
    411412
  • trunk/tests/phpunit/tests/theme/wpThemeJson.php

    r54160 r54162  
    424424        );
    425425
    426         $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 ); }';
     426        $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; }';
    427427        $this->assertSame( $expected, $theme_json->get_stylesheet() );
    428428        $this->assertSame( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) );
     
    545545                        ),
    546546                    ),
     547                    'spacing'  => array(
     548                        'blockGap' => '24px',
     549                    ),
    547550                ),
    548551                'misc'     => 'value',
     
    551554
    552555        $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;}';
    553         $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;}';
     556        $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;}';
    554557        $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;}';
    555558        $all       = $variables . $styles . $presets;
     
    26762679                        'core/group' => array(
    26772680                            'spacing' => array(
    2678                                 'margin'   => 'valid value',
    2679                                 'blockGap' => 'invalid value',
     2681                                'margin'  => 'valid value',
     2682                                'display' => 'none',
    26802683                            ),
    26812684                        ),
     
    29902993        );
    29912994
    2992         $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;}';
     2995        $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;}';
    29932996        $this->assertSame( $expected, $theme_json->get_stylesheet() );
    29942997    }
     
    30223025        );
    30233026
    3024         $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;}';
     3027        $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;}';
    30253028        $this->assertSame( $expected, $theme_json->get_stylesheet() );
    30263029    }
     
    30543057        );
    30553058
    3056         $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;}';
     3059        $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;}';
    30573060        $this->assertSame( $expected, $theme_json->get_stylesheet() );
    30583061    }
     
    30783081        );
    30793082
    3080         $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; }';
     3083        $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;}';
    30813084        $this->assertSame( $expected, $theme_json->get_stylesheet() );
    30823085    }
    30833086
     3087
     3088    /**
     3089     * @dataProvider data_get_layout_definitions
     3090     *
     3091     * @ticket 56467
     3092     *
     3093     * @param array $layout_definitions Layout definitions as stored in core theme.json.
     3094     */
     3095    public function test_get_stylesheet_generates_layout_styles( $layout_definitions ) {
     3096        $theme_json = new WP_Theme_JSON(
     3097            array(
     3098                'version'  => WP_Theme_JSON::LATEST_SCHEMA,
     3099                'settings' => array(
     3100                    'layout'  => array(
     3101                        'definitions' => $layout_definitions,
     3102                    ),
     3103                    'spacing' => array(
     3104                        'blockGap' => true,
     3105                    ),
     3106                ),
     3107                'styles'   => array(
     3108                    'spacing' => array(
     3109                        'blockGap' => '1em',
     3110                    ),
     3111                ),
     3112            ),
     3113            'default'
     3114        );
     3115
     3116        // Results also include root site blocks styles.
     3117        $this->assertSame(
     3118            '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;}',
     3119            $theme_json->get_stylesheet( array( 'styles' ) )
     3120        );
     3121    }
     3122
     3123    /**
     3124     * @dataProvider data_get_layout_definitions
     3125     *
     3126     * @ticket 56467
     3127     *
     3128     * @param array $layout_definitions Layout definitions as stored in core theme.json.
     3129     */
     3130    public function test_get_stylesheet_generates_layout_styles_with_spacing_presets( $layout_definitions ) {
     3131        $theme_json = new WP_Theme_JSON(
     3132            array(
     3133                'version'  => WP_Theme_JSON::LATEST_SCHEMA,
     3134                'settings' => array(
     3135                    'layout'  => array(
     3136                        'definitions' => $layout_definitions,
     3137                    ),
     3138                    'spacing' => array(
     3139                        'blockGap' => true,
     3140                    ),
     3141                ),
     3142                'styles'   => array(
     3143                    'spacing' => array(
     3144                        'blockGap' => 'var:preset|spacing|60',
     3145                    ),
     3146                ),
     3147            ),
     3148            'default'
     3149        );
     3150
     3151        // Results also include root site blocks styles.
     3152        $this->assertSame(
     3153            '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;}',
     3154            $theme_json->get_stylesheet( array( 'styles' ) )
     3155        );
     3156    }
     3157
     3158    /**
     3159     * @dataProvider data_get_layout_definitions
     3160     *
     3161     * @ticket 56467
     3162     *
     3163     * @param array $layout_definitions Layout definitions as stored in core theme.json.
     3164     */
     3165    public function test_get_stylesheet_generates_fallback_gap_layout_styles( $layout_definitions ) {
     3166        $theme_json = new WP_Theme_JSON(
     3167            array(
     3168                'version'  => WP_Theme_JSON::LATEST_SCHEMA,
     3169                'settings' => array(
     3170                    'layout'  => array(
     3171                        'definitions' => $layout_definitions,
     3172                    ),
     3173                    'spacing' => array(
     3174                        'blockGap' => null,
     3175                    ),
     3176                ),
     3177                'styles'   => array(
     3178                    'spacing' => array(
     3179                        'blockGap' => '1em',
     3180                    ),
     3181                ),
     3182            ),
     3183            'default'
     3184        );
     3185        $stylesheet = $theme_json->get_stylesheet( array( 'styles' ) );
     3186
     3187        // Results also include root site blocks styles.
     3188        $this->assertSame(
     3189            '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;}',
     3190            $stylesheet
     3191        );
     3192    }
     3193
     3194    /**
     3195     * @dataProvider data_get_layout_definitions
     3196     *
     3197     * @ticket 56467
     3198     *
     3199     * @param array $layout_definitions Layout definitions as stored in core theme.json.
     3200     */
     3201    public function test_get_stylesheet_generates_base_fallback_gap_layout_styles( $layout_definitions ) {
     3202        $theme_json = new WP_Theme_JSON(
     3203            array(
     3204                'version'  => WP_Theme_JSON::LATEST_SCHEMA,
     3205                'settings' => array(
     3206                    'layout'  => array(
     3207                        'definitions' => $layout_definitions,
     3208                    ),
     3209                    'spacing' => array(
     3210                        'blockGap' => null,
     3211                    ),
     3212                ),
     3213            ),
     3214            'default'
     3215        );
     3216        $stylesheet = $theme_json->get_stylesheet( array( 'base-layout-styles' ) );
     3217
     3218        // Note the `base-layout-styles` includes a fallback gap for the Columns block for backwards compatibility.
     3219        $this->assertSame(
     3220            ':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;}',
     3221            $stylesheet
     3222        );
     3223    }
     3224
     3225    /**
     3226     * @dataProvider data_get_layout_definitions
     3227     *
     3228     * @ticket 56467
     3229     *
     3230     * @param array $layout_definitions Layout definitions as stored in core theme.json.
     3231     */
     3232    public function test_get_stylesheet_skips_layout_styles( $layout_definitions ) {
     3233        add_theme_support( 'disable-layout-styles' );
     3234        $theme_json = new WP_Theme_JSON(
     3235            array(
     3236                'version'  => WP_Theme_JSON::LATEST_SCHEMA,
     3237                'settings' => array(
     3238                    'layout'  => array(
     3239                        'definitions' => $layout_definitions,
     3240                    ),
     3241                    'spacing' => array(
     3242                        'blockGap' => null,
     3243                    ),
     3244                ),
     3245            ),
     3246            'default'
     3247        );
     3248        $stylesheet = $theme_json->get_stylesheet( array( 'base-layout-styles' ) );
     3249        remove_theme_support( 'disable-layout-styles' );
     3250
     3251        // All Layout styles should be skipped.
     3252        $this->assertSame(
     3253            '',
     3254            $stylesheet
     3255        );
     3256    }
     3257
     3258    /**
     3259     * @dataProvider data_get_layout_definitions
     3260     *
     3261     * @ticket 56467
     3262     *
     3263     * @param array $layout_definitions Layout definitions as stored in core theme.json.
     3264     */
     3265    public function test_get_stylesheet_generates_valid_block_gap_values_and_skips_null_or_false_values( $layout_definitions ) {
     3266        $theme_json = new WP_Theme_JSON(
     3267            array(
     3268                'version'  => WP_Theme_JSON::LATEST_SCHEMA,
     3269                'settings' => array(
     3270                    'layout'  => array(
     3271                        'definitions' => $layout_definitions,
     3272                    ),
     3273                    'spacing' => array(
     3274                        'blockGap' => true,
     3275                    ),
     3276                ),
     3277                'styles'   => array(
     3278                    'spacing' => array(
     3279                        'blockGap' => '1rem',
     3280                    ),
     3281                    'blocks'  => array(
     3282                        'core/post-content' => array(
     3283                            'color' => array(
     3284                                'text' => 'gray', // This value should not render block layout styles.
     3285                            ),
     3286                        ),
     3287                        'core/social-links' => array(
     3288                            'spacing' => array(
     3289                                'blockGap' => '0', // This value should render block layout gap as zero.
     3290                            ),
     3291                        ),
     3292                        'core/buttons'      => array(
     3293                            'spacing' => array(
     3294                                'blockGap' => 0, // This value should render block layout gap as zero.
     3295                            ),
     3296                        ),
     3297                        'core/columns'      => array(
     3298                            'spacing' => array(
     3299                                'blockGap' => false, // This value should be ignored. The block will use the global layout value.
     3300                            ),
     3301                        ),
     3302                    ),
     3303                ),
     3304            ),
     3305            'default'
     3306        );
     3307
     3308        $this->assertEquals(
     3309            '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;}',
     3310            $theme_json->get_stylesheet()
     3311        );
     3312    }
     3313
     3314    /**
     3315     * Data provider for layout tests.
     3316     *
     3317     * @ticket 56467
     3318     *
     3319     * @return array
     3320     */
     3321    public function data_get_layout_definitions() {
     3322        return array(
     3323            'layout definitions' => array(
     3324                array(
     3325                    'default' => array(
     3326                        'name'          => 'default',
     3327                        'slug'          => 'flow',
     3328                        'className'     => 'is-layout-flow',
     3329                        'baseStyles'    => array(
     3330                            array(
     3331                                'selector' => ' > .alignleft',
     3332                                'rules'    => array(
     3333                                    'float'               => 'left',
     3334                                    'margin-inline-start' => '0',
     3335                                    'margin-inline-end'   => '2em',
     3336                                ),
     3337                            ),
     3338                            array(
     3339                                'selector' => ' > .alignright',
     3340                                'rules'    => array(
     3341                                    'float'               => 'right',
     3342                                    'margin-inline-start' => '2em',
     3343                                    'margin-inline-end'   => '0',
     3344                                ),
     3345                            ),
     3346                            array(
     3347                                'selector' => ' > .aligncenter',
     3348                                'rules'    => array(
     3349                                    'margin-left'  => 'auto !important',
     3350                                    'margin-right' => 'auto !important',
     3351                                ),
     3352                            ),
     3353                        ),
     3354                        'spacingStyles' => array(
     3355                            array(
     3356                                'selector' => ' > *',
     3357                                'rules'    => array(
     3358                                    'margin-block-start' => '0',
     3359                                    'margin-block-end'   => '0',
     3360                                ),
     3361                            ),
     3362                            array(
     3363                                'selector' => ' > * + *',
     3364                                'rules'    => array(
     3365                                    'margin-block-start' => null,
     3366                                    'margin-block-end'   => '0',
     3367                                ),
     3368                            ),
     3369                        ),
     3370                    ),
     3371                    'flex'    => array(
     3372                        'name'          => 'flex',
     3373                        'slug'          => 'flex',
     3374                        'className'     => 'is-layout-flex',
     3375                        'displayMode'   => 'flex',
     3376                        'baseStyles'    => array(
     3377                            array(
     3378                                'selector' => '',
     3379                                'rules'    => array(
     3380                                    'flex-wrap'   => 'wrap',
     3381                                    'align-items' => 'center',
     3382                                ),
     3383                            ),
     3384                        ),
     3385                        'spacingStyles' => array(
     3386                            array(
     3387                                'selector' => '',
     3388                                'rules'    => array(
     3389                                    'gap' => null,
     3390                                ),
     3391                            ),
     3392                        ),
     3393                    ),
     3394                ),
     3395            ),
     3396        );
     3397    }
     3398
     3399    /**
     3400     * @ticket 56467
     3401     */
     3402    function test_get_styles_for_block_with_padding_aware_alignments() {
     3403        $theme_json = new WP_Theme_JSON(
     3404            array(
     3405                'version'  => 2,
     3406                'styles'   => array(
     3407                    'spacing' => array(
     3408                        'padding' => array(
     3409                            'top'    => '10px',
     3410                            'right'  => '12px',
     3411                            'bottom' => '10px',
     3412                            'left'   => '12px',
     3413                        ),
     3414                    ),
     3415                ),
     3416                'settings' => array(
     3417                    'useRootPaddingAwareAlignments' => true,
     3418                ),
     3419            )
     3420        );
     3421
     3422        $metadata = array(
     3423            'path'     => array(
     3424                '0' => 'styles',
     3425            ),
     3426            'selector' => 'body',
     3427        );
     3428
     3429        $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;}';
     3430        $root_rules  = $theme_json->get_root_layout_rules( WP_Theme_JSON::ROOT_BLOCK_SELECTOR, $metadata );
     3431        $style_rules = $theme_json->get_styles_for_block( $metadata );
     3432        $this->assertSame( $expected, $root_rules . $style_rules );
     3433    }
     3434
     3435    /**
     3436     * @ticket 56467
     3437     */
     3438    function test_get_styles_for_block_without_padding_aware_alignments() {
     3439        $theme_json = new WP_Theme_JSON(
     3440            array(
     3441                'version' => 2,
     3442                'styles'  => array(
     3443                    'spacing' => array(
     3444                        'padding' => array(
     3445                            'top'    => '10px',
     3446                            'right'  => '12px',
     3447                            'bottom' => '10px',
     3448                            'left'   => '12px',
     3449                        ),
     3450                    ),
     3451                ),
     3452            )
     3453        );
     3454
     3455        $metadata = array(
     3456            'path'     => array(
     3457                '0' => 'styles',
     3458            ),
     3459            'selector' => 'body',
     3460        );
     3461
     3462        $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;}';
     3463        $root_rules  = $theme_json->get_root_layout_rules( WP_Theme_JSON::ROOT_BLOCK_SELECTOR, $metadata );
     3464        $style_rules = $theme_json->get_styles_for_block( $metadata );
     3465        $this->assertSame( $expected, $root_rules . $style_rules );
     3466    }
     3467
     3468    /**
     3469     * @ticket 56467
     3470     */
     3471    function test_get_styles_for_block_with_content_width() {
     3472        $theme_json = new WP_Theme_JSON(
     3473            array(
     3474                'version'  => 2,
     3475                'settings' => array(
     3476                    'layout' => array(
     3477                        'contentSize' => '800px',
     3478                        'wideSize'    => '1000px',
     3479                    ),
     3480                ),
     3481            )
     3482        );
     3483
     3484        $metadata = array(
     3485            'path'     => array(
     3486                '0' => 'settings',
     3487            ),
     3488            'selector' => 'body',
     3489        );
     3490
     3491        $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; }';
     3492        $root_rules  = $theme_json->get_root_layout_rules( WP_Theme_JSON::ROOT_BLOCK_SELECTOR, $metadata );
     3493        $style_rules = $theme_json->get_styles_for_block( $metadata );
     3494        $this->assertSame( $expected, $root_rules . $style_rules );
     3495    }
    30843496}
Note: See TracChangeset for help on using the changeset viewer.