Make WordPress Core

Changeset 58089


Ignore:
Timestamp:
05/03/2024 04:45:20 AM (3 months ago)
Author:
isabel_brison
Message:

Editor: add Style Engine support for nested CSS rules.

Adds support for passing a $rules_group string to wp_style_engine_get_stylesheet_from_css_rules(), so rules can be nested under a media query, layer or other rule.

Props isabel_brison, ramonopoly.
Fixes #61099.

Location:
trunk
Files:
9 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/style-engine.php

    r56574 r58089  
    114114 *
    115115 * @since 6.1.0
     116 * @since 6.6.0 Added support for `$rules_group` in the `$css_rules` array.
    116117 *
    117118 * @param array $css_rules {
     
    119120 *
    120121 *     @type array ...$0 {
     122 *         @type string   $rules_group  A parent CSS selector in the case of nested CSS,
     123 *                                      or a CSS nested @rule, such as `@media (min-width: 80rem)` or `@layer module`.
    121124 *         @type string   $selector     A CSS selector.
    122125 *         @type string[] $declarations An associative array of CSS definitions,
     
    155158        }
    156159
     160        $rules_group = $css_rule['rules_group'] ?? null;
    157161        if ( ! empty( $options['context'] ) ) {
    158             WP_Style_Engine::store_css_rule( $options['context'], $css_rule['selector'], $css_rule['declarations'] );
     162            WP_Style_Engine::store_css_rule( $options['context'], $css_rule['selector'], $css_rule['declarations'], $rules_group );
    159163        }
    160164
    161         $css_rule_objects[] = new WP_Style_Engine_CSS_Rule( $css_rule['selector'], $css_rule['declarations'] );
     165        $css_rule_objects[] = new WP_Style_Engine_CSS_Rule( $css_rule['selector'], $css_rule['declarations'], $rules_group );
    162166    }
    163167
  • trunk/src/wp-includes/style-engine/class-wp-style-engine-css-rule.php

    r55819 r58089  
    3737
    3838    /**
     39     * A parent CSS selector in the case of nested CSS, or a CSS nested @rule,
     40     * such as `@media (min-width: 80rem)` or `@layer module`.
     41     *
     42     * @since 6.6.0
     43     * @var string
     44     */
     45    protected $rules_group;
     46
     47    /**
    3948     * Constructor.
    4049     *
    4150     * @since 6.1.0
     51     * @since 6.6.0 Added the `$rules_group` parameter.
    4252     *
    4353     * @param string                                    $selector     Optional. The CSS selector. Default empty string.
     
    4656     *                                                                or a WP_Style_Engine_CSS_Declarations object.
    4757     *                                                                Default empty array.
     58     * @param string                                    $rules_group  A parent CSS selector in the case of nested CSS, or a CSS nested @rule,
     59     *                                                                such as `@media (min-width: 80rem)` or `@layer module`.
    4860     */
    49     public function __construct( $selector = '', $declarations = array() ) {
     61    public function __construct( $selector = '', $declarations = array(), $rules_group = '' ) {
    5062        $this->set_selector( $selector );
    5163        $this->add_declarations( $declarations );
     64        $this->set_rules_group( $rules_group );
    5265    }
    5366
     
    91104
    92105    /**
     106     * Sets the rules group.
     107     *
     108     * @since 6.6.0
     109     *
     110     * @param string $rules_group A parent CSS selector in the case of nested CSS, or a CSS nested @rule,
     111     *                            such as `@media (min-width: 80rem)` or `@layer module`.
     112     * @return WP_Style_Engine_CSS_Rule Returns the object to allow chaining of methods.
     113     */
     114    public function set_rules_group( $rules_group ) {
     115        $this->rules_group = $rules_group;
     116        return $this;
     117    }
     118
     119    /**
     120     * Gets the rules group.
     121     *
     122     * @since 6.6.0
     123     *
     124     * @return string
     125     */
     126    public function get_rules_group() {
     127        return $this->rules_group;
     128    }
     129
     130    /**
    93131     * Gets the declarations object.
    94132     *
     
    116154     *
    117155     * @since 6.1.0
     156     * @since 6.6.0 Added support for nested CSS with rules groups.
    118157     *
    119158     * @param bool $should_prettify Optional. Whether to add spacing, new lines and indents.
     
    124163     */
    125164    public function get_css( $should_prettify = false, $indent_count = 0 ) {
    126         $rule_indent         = $should_prettify ? str_repeat( "\t", $indent_count ) : '';
    127         $declarations_indent = $should_prettify ? $indent_count + 1 : 0;
    128         $suffix              = $should_prettify ? "\n" : '';
    129         $spacer              = $should_prettify ? ' ' : '';
    130         $selector            = $should_prettify ? str_replace( ',', ",\n", $this->get_selector() ) : $this->get_selector();
    131         $css_declarations    = $this->declarations->get_declarations_string( $should_prettify, $declarations_indent );
     165        $rule_indent                = $should_prettify ? str_repeat( "\t", $indent_count ) : '';
     166        $nested_rule_indent         = $should_prettify ? str_repeat( "\t", $indent_count + 1 ) : '';
     167        $declarations_indent        = $should_prettify ? $indent_count + 1 : 0;
     168        $nested_declarations_indent = $should_prettify ? $indent_count + 2 : 0;
     169        $suffix                     = $should_prettify ? "\n" : '';
     170        $spacer                     = $should_prettify ? ' ' : '';
     171        // Trims any multiple selectors strings.
     172        $selector         = $should_prettify ? implode( ',', array_map( 'trim', explode( ',', $this->get_selector() ) ) ) : $this->get_selector();
     173        $selector         = $should_prettify ? str_replace( array( ',' ), ",\n", $selector ) : $selector;
     174        $rules_group      = $this->get_rules_group();
     175        $has_rules_group  = ! empty( $rules_group );
     176        $css_declarations = $this->declarations->get_declarations_string( $should_prettify, $has_rules_group ? $nested_declarations_indent : $declarations_indent );
    132177
    133178        if ( empty( $css_declarations ) ) {
     
    135180        }
    136181
     182        if ( $has_rules_group ) {
     183            $selector = "{$rule_indent}{$rules_group}{$spacer}{{$suffix}{$nested_rule_indent}{$selector}{$spacer}{{$suffix}{$css_declarations}{$suffix}{$nested_rule_indent}}{$suffix}{$rule_indent}}";
     184            return $selector;
     185        }
     186
    137187        return "{$rule_indent}{$selector}{$spacer}{{$suffix}{$css_declarations}{$suffix}{$rule_indent}}";
    138188    }
  • trunk/src/wp-includes/style-engine/class-wp-style-engine-css-rules-store.php

    r55819 r58089  
    122122     *
    123123     * @since 6.1.0
     124     * @since 6.6.0 Added the $rules_group parameter.
    124125     *
    125126     * @param string $selector The CSS selector.
     127     * @param string $rules_group A parent CSS selector in the case of nested CSS, or a CSS nested @rule,
     128     *                            such as `@media (min-width: 80rem)` or `@layer module`.
    126129     * @return WP_Style_Engine_CSS_Rule|void Returns a WP_Style_Engine_CSS_Rule object,
    127130     *                                       or void if the selector is empty.
    128131     */
    129     public function add_rule( $selector ) {
    130         $selector = trim( $selector );
     132    public function add_rule( $selector, $rules_group = '' ) {
     133        $selector    = $selector ? trim( $selector ) : '';
     134        $rules_group = $rules_group ? trim( $rules_group ) : '';
    131135
    132136        // Bail early if there is no selector.
    133137        if ( empty( $selector ) ) {
    134138            return;
     139        }
     140
     141        if ( ! empty( $rules_group ) ) {
     142            if ( empty( $this->rules[ "$rules_group $selector" ] ) ) {
     143                $this->rules[ "$rules_group $selector" ] = new WP_Style_Engine_CSS_Rule( $selector, array(), $rules_group );
     144            }
     145            return $this->rules[ "$rules_group $selector" ];
    135146        }
    136147
  • trunk/src/wp-includes/style-engine/class-wp-style-engine-processor.php

    r56574 r58089  
    5959     *
    6060     * @since 6.1.0
     61     * @since 6.6.0 Added support for rules_group.
    6162     *
    6263     * @param WP_Style_Engine_CSS_Rule|WP_Style_Engine_CSS_Rule[] $css_rules A single, or an array of,
     
    7172
    7273        foreach ( $css_rules as $rule ) {
    73             $selector = $rule->get_selector();
     74            $selector    = $rule->get_selector();
     75            $rules_group = $rule->get_rules_group();
     76
     77            /**
     78             * If there is a rules_group and it already exists in the css_rules array,
     79             * add the rule to it.
     80             * Otherwise, create a new entry for the rules_group.
     81             */
     82            if ( ! empty( $rules_group ) ) {
     83                if ( isset( $this->css_rules[ "$rules_group $selector" ] ) ) {
     84                    $this->css_rules[ "$rules_group $selector" ]->add_declarations( $rule->get_declarations() );
     85                    continue;
     86                }
     87                $this->css_rules[ "$rules_group $selector" ] = $rule;
     88                continue;
     89            }
     90
     91            // If the selector already exists, add the declarations to it.
    7492            if ( isset( $this->css_rules[ $selector ] ) ) {
    7593                $this->css_rules[ $selector ]->add_declarations( $rule->get_declarations() );
     
    118136        $css = '';
    119137        foreach ( $this->css_rules as $rule ) {
     138            // See class WP_Style_Engine_CSS_Rule for the get_css method.
    120139            $css .= $rule->get_css( $options['prettify'] );
    121140            $css .= $options['prettify'] ? "\n" : '';
  • trunk/src/wp-includes/style-engine/class-wp-style-engine.php

    r57491 r58089  
    365365     *
    366366     * @since 6.1.0
     367     * @since 6.6.0 Added the `$rules_group` parameter.
    367368     *
    368369     * @param string   $store_name       A valid store key.
     
    372373     * @param string[] $css_declarations An associative array of CSS definitions,
    373374     *                                   e.g. `array( "$property" => "$value", "$property" => "$value" )`.
    374      */
    375     public static function store_css_rule( $store_name, $css_selector, $css_declarations ) {
     375     * @param string $rules_group        Optional. A parent CSS selector in the case of nested CSS, or a CSS nested @rule,
     376     *                                   such as `@media (min-width: 80rem)` or `@layer module`.
     377     */
     378    public static function store_css_rule( $store_name, $css_selector, $css_declarations, $rules_group = '' ) {
    376379        if ( empty( $store_name ) || empty( $css_selector ) || empty( $css_declarations ) ) {
    377380            return;
    378381        }
    379         static::get_store( $store_name )->add_rule( $css_selector )->add_declarations( $css_declarations );
     382        static::get_store( $store_name )->add_rule( $css_selector, $rules_group )->add_declarations( $css_declarations );
    380383    }
    381384
  • trunk/tests/phpunit/tests/style-engine/styleEngine.php

    r57491 r58089  
    750750        $this->assertSame( '.gandalf{color:white;height:190px;border-style:dotted;padding:10px;margin-bottom:100px;}.dumbledore{color:grey;height:90px;border-style:dotted;}.rincewind{color:grey;height:90px;border-style:dotted;}', $compiled_stylesheet );
    751751    }
     752
     753    /**
     754     * Tests returning a generated stylesheet from a set of nested rules and merging their declarations.
     755     *
     756     * @ticket 61099
     757     *
     758     * @covers ::wp_style_engine_get_stylesheet_from_css_rules
     759     */
     760    public function test_should_merge_declarations_for_rules_groups() {
     761        $css_rules = array(
     762            array(
     763                'selector'     => '.saruman',
     764                'rules_group'  => '@container (min-width: 700px)',
     765                'declarations' => array(
     766                    'color'        => 'white',
     767                    'height'       => '100px',
     768                    'border-style' => 'solid',
     769                    'align-self'   => 'stretch',
     770                ),
     771            ),
     772            array(
     773                'selector'     => '.saruman',
     774                'rules_group'  => '@container (min-width: 700px)',
     775                'declarations' => array(
     776                    'color'       => 'black',
     777                    'font-family' => 'The-Great-Eye',
     778                ),
     779            ),
     780        );
     781
     782        $compiled_stylesheet = wp_style_engine_get_stylesheet_from_css_rules( $css_rules, array( 'prettify' => false ) );
     783
     784        $this->assertSame( '@container (min-width: 700px){.saruman{color:black;height:100px;border-style:solid;align-self:stretch;font-family:The-Great-Eye;}}', $compiled_stylesheet );
     785    }
     786
     787    /**
     788     * Tests returning a generated stylesheet from a set of nested rules.
     789     *
     790     * @ticket 61099
     791     *
     792     * @covers ::wp_style_engine_get_stylesheet_from_css_rules
     793     */
     794    public function test_should_return_stylesheet_with_nested_rules() {
     795        $css_rules = array(
     796            array(
     797                'rules_group'  => '.foo',
     798                'selector'     => '@media (orientation: landscape)',
     799                'declarations' => array(
     800                    'background-color' => 'blue',
     801                ),
     802            ),
     803            array(
     804                'rules_group'  => '.foo',
     805                'selector'     => '@media (min-width > 1024px)',
     806                'declarations' => array(
     807                    'background-color' => 'cotton-blue',
     808                ),
     809            ),
     810        );
     811
     812        $compiled_stylesheet = wp_style_engine_get_stylesheet_from_css_rules( $css_rules, array( 'prettify' => false ) );
     813
     814        $this->assertSame( '.foo{@media (orientation: landscape){background-color:blue;}}.foo{@media (min-width > 1024px){background-color:cotton-blue;}}', $compiled_stylesheet );
     815    }
    752816}
  • trunk/tests/phpunit/tests/style-engine/wpStyleEngineCssRule.php

    r54394 r58089  
    3636
    3737        $this->assertSame( $expected, $css_rule->get_css(), 'Value returned by get_css() does not match expected declarations string.' );
     38    }
     39
     40    /**
     41     * Tests setting and getting a rules group.
     42     *
     43     * @ticket 61099
     44     *
     45     * @covers ::set_rules_group
     46     * @covers ::get_rules_group
     47     */
     48    public function test_should_set_rules_group() {
     49        $rule = new WP_Style_Engine_CSS_Rule( '.heres-johnny', array(), '@layer state' );
     50
     51        $this->assertSame( '@layer state', $rule->get_rules_group(), 'Return value of get_rules_group() does not match value passed to constructor.' );
     52
     53        $rule->set_rules_group( '@layer pony' );
     54
     55        $this->assertSame( '@layer pony', $rule->get_rules_group(), 'Return value of get_rules_group() does not match value passed to set_rules_group().' );
    3856    }
    3957
  • trunk/tests/phpunit/tests/style-engine/wpStyleEngineCssRulesStore.php

    r54402 r58089  
    188188        $this->assertSame( $expected, $new_pizza_store->get_all_rules(), 'Return value for get_all_rules() does not match expectations after adding new rules to store.' );
    189189    }
     190
     191    /**
     192     * Tests adding rules group keys to store.
     193     *
     194     * @ticket 61099
     195     *
     196     * @covers ::add_rule
     197     */
     198    public function test_should_store_as_concatenated_rules_groups_and_selector() {
     199        $store_one      = WP_Style_Engine_CSS_Rules_Store::get_store( 'one' );
     200        $store_one_rule = $store_one->add_rule( '.tony', '.one' );
     201
     202        $this->assertSame(
     203            '.one .tony',
     204            "{$store_one_rule->get_rules_group()} {$store_one_rule->get_selector()}",
     205            'add_rule() does not concatenate rules group and selector.'
     206        );
     207    }
    190208}
  • trunk/tests/phpunit/tests/style-engine/wpStyleEngineProcessor.php

    r56574 r58089  
    5050
    5151    /**
     52     * Tests adding nested rules with at-rules and returning compiled CSS rules.
     53     *
     54     * @ticket 61099
     55     *
     56     * @covers ::add_rules
     57     * @covers ::get_css
     58     */
     59    public function test_should_return_nested_rules_as_compiled_css() {
     60        $a_nice_css_rule = new WP_Style_Engine_CSS_Rule( '.a-nice-rule' );
     61        $a_nice_css_rule->add_declarations(
     62            array(
     63                'color'            => 'var(--nice-color)',
     64                'background-color' => 'purple',
     65            )
     66        );
     67        $a_nice_css_rule->set_rules_group( '@media (min-width: 80rem)' );
     68
     69        $a_nicer_css_rule = new WP_Style_Engine_CSS_Rule( '.a-nicer-rule' );
     70        $a_nicer_css_rule->add_declarations(
     71            array(
     72                'font-family'      => 'Nice sans',
     73                'font-size'        => '1em',
     74                'background-color' => 'purple',
     75            )
     76        );
     77        $a_nicer_css_rule->set_rules_group( '@layer nicety' );
     78
     79        $a_nice_processor = new WP_Style_Engine_Processor();
     80        $a_nice_processor->add_rules( array( $a_nice_css_rule, $a_nicer_css_rule ) );
     81
     82        $this->assertSame(
     83            '@media (min-width: 80rem){.a-nice-rule{color:var(--nice-color);background-color:purple;}}@layer nicety{.a-nicer-rule{font-family:Nice sans;font-size:1em;background-color:purple;}}',
     84            $a_nice_processor->get_css( array( 'prettify' => false ) )
     85        );
     86    }
     87
     88    /**
    5289     * Tests compiling CSS rules and formatting them with new lines and indents.
    5390     *
     
    97134';
    98135        $this->assertSameIgnoreEOL(
     136            $expected,
     137            $a_wonderful_processor->get_css( array( 'prettify' => true ) )
     138        );
     139    }
     140
     141    /**
     142     * Tests compiling nested CSS rules and formatting them with new lines and indents.
     143     *
     144     * @ticket 61099
     145     *
     146     * @covers ::get_css
     147     */
     148    public function test_should_return_prettified_nested_css_rules() {
     149        $a_wonderful_css_rule = new WP_Style_Engine_CSS_Rule( '.a-wonderful-rule' );
     150        $a_wonderful_css_rule->add_declarations(
     151            array(
     152                'color'            => 'var(--wonderful-color)',
     153                'background-color' => 'orange',
     154            )
     155        );
     156        $a_wonderful_css_rule->set_rules_group( '@media (min-width: 80rem)' );
     157
     158        $a_very_wonderful_css_rule = new WP_Style_Engine_CSS_Rule( '.a-very_wonderful-rule' );
     159        $a_very_wonderful_css_rule->add_declarations(
     160            array(
     161                'color'            => 'var(--wonderful-color)',
     162                'background-color' => 'orange',
     163            )
     164        );
     165        $a_very_wonderful_css_rule->set_rules_group( '@layer wonderfulness' );
     166
     167        $a_wonderful_processor = new WP_Style_Engine_Processor();
     168        $a_wonderful_processor->add_rules( array( $a_wonderful_css_rule, $a_very_wonderful_css_rule ) );
     169
     170        $expected = '@media (min-width: 80rem) {
     171    .a-wonderful-rule {
     172        color: var(--wonderful-color);
     173        background-color: orange;
     174    }
     175}
     176@layer wonderfulness {
     177    .a-very_wonderful-rule {
     178        color: var(--wonderful-color);
     179        background-color: orange;
     180    }
     181}
     182';
     183        $this->assertSame(
    99184            $expected,
    100185            $a_wonderful_processor->get_css( array( 'prettify' => true ) )
Note: See TracChangeset for help on using the changeset viewer.