From 4d77e49d524d1c8357cf954c5e363eb08420bcac Mon Sep 17 00:00:00 2001 From: ramon Date: Thu, 8 Feb 2024 11:11:39 +1100 Subject: [PATCH 01/20] First commit --- .../class-wp-style-engine-css-rule.php | 32 +++++++ .../class-wp-style-engine-processor.php | 93 +++++++++++++++---- .../style-engine/class-wp-style-engine.php | 8 +- packages/style-engine/style-engine.php | 24 +++-- .../class-wp-style-engine-processor-test.php | 59 ++++++++++++ phpunit/style-engine/style-engine-test.php | 65 +++++++++++++ tools/webpack/packages.js | 1 + 7 files changed, 254 insertions(+), 28 deletions(-) diff --git a/packages/style-engine/class-wp-style-engine-css-rule.php b/packages/style-engine/class-wp-style-engine-css-rule.php index 18d8c2f7ef7f0..4357efd2ae70b 100644 --- a/packages/style-engine/class-wp-style-engine-css-rule.php +++ b/packages/style-engine/class-wp-style-engine-css-rule.php @@ -23,6 +23,13 @@ class WP_Style_Engine_CSS_Rule { */ protected $selector; + /** + * The container. + * + * @var string + */ + protected $container; + /** * The selector declarations. * @@ -67,6 +74,30 @@ public function set_selector( $selector ) { return $this; } + /** + * Sets the container. + * + * @param string $container The CSS selector. + * + * @return WP_Style_Engine_CSS_Rule Returns the object to allow chaining of methods. + */ + public function set_container( $container ) { + if ( ! empty( $container ) ) { + $this->container = $container; + } + return $this; + } + + /** + * Gets the container. + * + * @return string Container. + */ + public function get_container() { + return $this->container; + } + + /** * Sets the declarations. * @@ -165,3 +196,4 @@ public function get_css( $should_prettify = false, $indent_count = 0 ) { } } } + diff --git a/packages/style-engine/class-wp-style-engine-processor.php b/packages/style-engine/class-wp-style-engine-processor.php index 3f8cef0a2cf31..2090b77391aba 100644 --- a/packages/style-engine/class-wp-style-engine-processor.php +++ b/packages/style-engine/class-wp-style-engine-processor.php @@ -65,30 +65,28 @@ public function add_rules( $css_rules ) { } foreach ( $css_rules as $rule ) { - $selector = $rule->get_selector(); - $at_rule = $rule->get_at_rule(); - - /** - * If there is an at_rule and it already exists in the css_rules array, - * add the rule to it. - * Otherwise, create a new entry for the at_rule - */ - if ( ! empty( $at_rule ) ) { - if ( isset( $this->css_rules[ "$at_rule $selector" ] ) ) { - $this->css_rules[ "$at_rule $selector" ]->add_declarations( $rule->get_declarations() ); - continue; + // Check for rules that need to be nested in containers. + $container = $rule->get_container(); + $selector = $rule->get_selector(); + + if ( ! empty( $selector ) && empty( $container ) ) { + if ( isset( $this->css_rules[ $selector ] ) ) { + $this->css_rules[ $selector ]->add_declarations( $rule->get_declarations() ); + } else { + $this->css_rules[ $rule->get_selector() ] = $rule; } - $this->css_rules[ "$at_rule $selector" ] = $rule; continue; } - // If the selector already exists, add the declarations to it. - if ( isset( $this->css_rules[ $selector ] ) ) { - $this->css_rules[ $selector ]->add_declarations( $rule->get_declarations() ); - continue; + if ( ! empty( $container ) ) { + if ( isset( $this->css_rules[ $container ] ) ) { + $this->css_rules[ $container ]->add_rule( $rule ); + } else { + $this->css_rules[ $container ] = new WP_Style_Engine_CSS_Rules_Container( $container, $rule ); + } } - $this->css_rules[ $rule->get_selector() ] = $rule; } + return $this; } @@ -126,7 +124,6 @@ public function get_css( $options = array() ) { // Build the CSS. $css = ''; foreach ( $this->css_rules as $rule ) { - // See class WP_Style_Engine_CSS_Rule for the get_css method. $css .= $rule->get_css( $options['prettify'] ); $css .= $options['prettify'] ? "\n" : ''; } @@ -171,3 +168,61 @@ private function combine_rules_selectors() { } } } + + +// @TODO new file and tests +// Should have same/similar interface to WP_Style_Engine_CSS_Rule or maybe even part of it? +class WP_Style_Engine_CSS_Rules_Container { + protected $container; + protected $rules = array(); + + public function __construct( $container = '', $rule ) { + $this->set_container( $container ); + // @TODO should be able to add multiple rules. + // @TODO check for instance of WP_Style_Engine_CSS_Rule + $this->add_rule( $rule ); + } + + public function set_container( $container ) { + $this->container = $container; + return $this; + } + + public function get_container() { + return $this->container; + } + + public function get_rules() { + return $this->rules; + } + + public function add_rule( $rule ) { + // @TODO should be able to add multiple rules. + // @TODO check for instance of WP_Style_Engine_CSS_Rule + $selector = $rule->get_selector(); + + if ( isset( $this->rules[ $selector ] ) ) { + $this->rules[ $selector ]->add_declarations( $rule->get_declarations() ); + } else { + $this->rules[ $selector ] = $rule; + } + return $this; + } + + public function get_css( $should_prettify = false, $indent_count = 0 ) { + $css = ''; + $indent_count = $should_prettify ? $indent_count + 1 : $indent_count; + $new_line = $should_prettify ? "\n" : ''; + $spacer = $should_prettify ? ' ' : ''; + foreach ( $this->rules as $rule ) { + $css .= $rule->get_css( $should_prettify, $indent_count ); + $css .= $should_prettify ? "\n" : ''; + } + + if ( empty( $css ) ) { + return ''; + } + + return "{$this->container}{$spacer}{{$new_line}{$css}}"; + } +} diff --git a/packages/style-engine/class-wp-style-engine.php b/packages/style-engine/class-wp-style-engine.php index a97e7cb8a0b21..e031da52e8420 100644 --- a/packages/style-engine/class-wp-style-engine.php +++ b/packages/style-engine/class-wp-style-engine.php @@ -626,7 +626,7 @@ protected static function get_url_or_value_css_declaration( $style_value, $style * * @return string A compiled CSS string. */ - public static function compile_css( $css_declarations, $css_selector ) { + public static function compile_css( $css_declarations, $css_selector, $container = '' ) { if ( empty( $css_declarations ) || ! is_array( $css_declarations ) ) { return ''; } @@ -634,6 +634,12 @@ public static function compile_css( $css_declarations, $css_selector ) { // Return an entire rule if there is a selector. if ( $css_selector ) { $css_rule = new WP_Style_Engine_CSS_Rule( $css_selector, $css_declarations ); + if ( $container ) { + $css_rule->set_container( $container ); + $css_container = new WP_Style_Engine_CSS_Rules_Container( $container, $css_rule ); + return $css_container->get_css(); + } + return $css_rule->get_css(); } diff --git a/packages/style-engine/style-engine.php b/packages/style-engine/style-engine.php index 034236ffef197..cc7420d01c7c4 100644 --- a/packages/style-engine/style-engine.php +++ b/packages/style-engine/style-engine.php @@ -43,6 +43,7 @@ function wp_style_engine_get_styles( $block_styles, $options = array() ) { $options, array( 'selector' => null, + 'container' => null, 'context' => null, 'convert_vars_to_classnames' => false, ) @@ -54,10 +55,13 @@ function wp_style_engine_get_styles( $block_styles, $options = array() ) { $styles_output = array(); if ( ! empty( $parsed_styles['declarations'] ) ) { - $styles_output['css'] = WP_Style_Engine::compile_css( $parsed_styles['declarations'], $options['selector'] ); + $styles_output['css'] = WP_Style_Engine::compile_css( $parsed_styles['declarations'], $options['selector'], $options['container'] ); $styles_output['declarations'] = $parsed_styles['declarations']; - if ( ! empty( $options['context'] ) ) { + if ( ! empty( $options['context'] ) && ! empty( $options['selector'] ) ) { + // @TODO Or could return the store from WP_Style_Engine::store_css_rule() and use it to get the rules. WP_Style_Engine::store_css_rule( $options['context'], $options['selector'], $parsed_styles['declarations'] ); + // @TODO WP_Style_Engine_CSS_Rules_Store could maybe do with a get_rule(). + WP_Style_Engine::get_store( $options['context'] )->add_rule( $options['selector'] )->set_container( $options['container'] ); } } @@ -83,7 +87,7 @@ function wp_style_engine_get_styles( $block_styles, $options = array() ) { * Required. A collection of CSS rules. * * @type array ...$0 { - * @type string $at_rule A CSS nested @rule, such as `@media (min-width: 80rem)` or `@layer module`. + * @type string $container A parent selector in the case or nested CSS, or a CSS nested @rule, such as `@media (min-width: 80rem)` or `@layer module`. * @type string $selector A CSS selector. * @type string[] $declarations An associative array of CSS definitions, e.g., array( "$property" => "$value", "$property" => "$value" ). * } @@ -116,14 +120,18 @@ function wp_style_engine_get_stylesheet_from_css_rules( $css_rules, $options = a if ( empty( $css_rule['selector'] ) || empty( $css_rule['declarations'] ) || ! is_array( $css_rule['declarations'] ) ) { continue; } - - $at_rule = ! empty( $css_rule['at_rule'] ) ? $css_rule['at_rule'] : ''; + // @TODO should this be in $options['container']? + $container = $css_rules['container'] ?? null; if ( ! empty( $options['context'] ) ) { - WP_Style_Engine::store_css_rule( $options['context'], $css_rule['selector'], $css_rule['declarations'], $at_rule ); + // @TODO how to combine rules with the same container? in a container_rule class? + WP_Style_Engine::store_css_rule( $options['context'], $css_rule['selector'], $css_rule['declarations'] ); + // @TODO WP_Style_Engine_CSS_Rules_Store could maybe do with a get_rule(). + WP_Style_Engine::get_store( $options['context'] )->add_rule( $css_rule['selector'] )->set_container( $container ); } - - $css_rule_objects[] = new WP_Style_Engine_CSS_Rule( $css_rule['selector'], $css_rule['declarations'], $at_rule ); + $new_rule = new WP_Style_Engine_CSS_Rule( $css_rule['selector'], $css_rule['declarations'] ); + $new_rule->set_container( $container ); + $css_rule_objects[] = $new_rule; } if ( empty( $css_rule_objects ) ) { diff --git a/phpunit/style-engine/class-wp-style-engine-processor-test.php b/phpunit/style-engine/class-wp-style-engine-processor-test.php index a1ec32a20977c..2ec5647d44f18 100644 --- a/phpunit/style-engine/class-wp-style-engine-processor-test.php +++ b/phpunit/style-engine/class-wp-style-engine-processor-test.php @@ -393,4 +393,63 @@ public function test_should_combine_previously_added_css_rules() { 'Return value of get_css() does not match expectations when combining 4 CSS rules' ); } + + public function test_should_return_store_rules_with_css_containers() { + $panda_store = WP_Style_Engine_CSS_Rules_Store_Gutenberg::get_store( 'panda' ); + // Add a nested rule. + $panda_store_rule_1 = $panda_store->add_rule( '.blueberry' )->set_container( '@container (width > 400px)' ); + $panda_store_rule_1->add_declarations( array( + 'background-color' => 'blue', + ) ); + + // Add a regular rule. + $panda_store_rule_2 = $panda_store->add_rule( '.wp-block-mushroom a:hover' ); + $panda_store_rule_2->add_declarations( array( + 'padding' => '100px', + ) ); + + + $a_panda_renderer = new WP_Style_Engine_Processor_Gutenberg(); + $a_panda_renderer->add_store( $panda_store ); + $this->assertSame( + '@container (width > 400px){.blueberry{background-color:blue;}}.wp-block-mushroom a:hover{padding:100px;}', + $a_panda_renderer->get_css( array( 'prettify' => false ) ), + 'Returns processed CSS rules with containers' + ); + + // Combine with a nested rule added directly to the processor. + $a_raspberry_rule = new WP_Style_Engine_CSS_Rule_Gutenberg( + '.raspberry', + array( + 'background-color' => 'red', + ) + ); + $a_raspberry_rule->set_container( '@container (width > 400px)' ); + + $a_panda_renderer->add_rules( $a_raspberry_rule ); + + $this->assertSame( + '@container (width > 400px){.blueberry{background-color:blue;}.raspberry{background-color:red;}}.wp-block-mushroom a:hover{padding:100px;}', + $a_panda_renderer->get_css( array( 'prettify' => false ) ), + 'Returns processed CSS rules with rules added to containers' + ); + + $expected_prettified = '@container (width > 400px) { + .blueberry { + background-color: blue; + } + .raspberry { + background-color: red; + } +} +.wp-block-mushroom a:hover { + padding: 100px; +} +'; + $this->assertSame( + $expected_prettified, + $a_panda_renderer->get_css( array( 'prettify' => true ) ), + 'Returns prettified processed CSS rules' + ); + } } diff --git a/phpunit/style-engine/style-engine-test.php b/phpunit/style-engine/style-engine-test.php index b66ccf5694bd6..26d4cfffbce91 100644 --- a/phpunit/style-engine/style-engine-test.php +++ b/phpunit/style-engine/style-engine-test.php @@ -236,6 +236,29 @@ public function data_wp_style_engine_get_styles() { ), ), + 'style_block_with_nested_selector' => array( + 'block_styles' => array( + 'spacing' => array( + 'padding' => array( + 'top' => '42px', + 'left' => '2%', + 'bottom' => '44px', + 'right' => '5rem', + ), + ), + ), + 'options' => array( 'selector' => '.wp-selector > p', 'container' => '@layer sandwich' ), + 'expected_output' => array( + 'css' => '@layer sandwich{.wp-selector > p{padding-top:42px;padding-left:2%;padding-bottom:44px;padding-right:5rem;}}', + 'declarations' => array( + 'padding-top' => '42px', + 'padding-left' => '2%', + 'padding-bottom' => '44px', + 'padding-right' => '5rem', + ), + ), + ), + 'elements_with_css_var_value' => array( 'block_styles' => array( 'color' => array( @@ -661,6 +684,48 @@ public function test_should_return_stylesheet_from_css_rules() { $this->assertSame( '.saruman{color:white;height:100px;border-style:solid;align-self:unset;}.gandalf{color:grey;height:90px;border-style:dotted;align-self:safe center;}.radagast{color:brown;height:60px;border-style:dashed;align-self:stretch;}', $compiled_stylesheet ); } + /** + * Tests returning a generated stylesheet from a set of nested rules. + */ + public function test_should_return_stylesheet_from_nested_css_rules() { + $css_rules = array( + array( + 'selector' => '.saruman', + 'container' => '@supports (align-self: stretch)', + 'declarations' => array( + 'color' => 'white', + 'height' => '100px', + 'border-style' => 'solid', + 'align-self' => 'stretch', + ), + ), + array( + 'selector' => '.gandalf', + 'container' => '@supports (border-style: dotted)', + 'declarations' => array( + 'color' => 'grey', + 'height' => '90px', + 'border-style' => 'dotted', + 'align-self' => 'safe center', + ), + ), + array( + 'selector' => '.radagast', + 'container' => '@supports (align-self: stretch)', + 'declarations' => array( + 'color' => 'brown', + 'height' => '60px', + 'border-style' => 'dashed', + 'align-self' => 'stretch', + ), + ), + ); + + $compiled_stylesheet = gutenberg_style_engine_get_stylesheet_from_css_rules( $css_rules, array( 'prettify' => false ) ); + + $this->assertSame( '.saruman{color:white;height:100px;border-style:solid;align-self:unset;}.gandalf{color:grey;height:90px;border-style:dotted;align-self:safe center;}.radagast{color:brown;height:60px;border-style:dashed;align-self:stretch;}', $compiled_stylesheet ); + } + /** * Tests that incoming styles are deduped and merged. * diff --git a/tools/webpack/packages.js b/tools/webpack/packages.js index 0a4b8cef57446..37cbe4955034d 100644 --- a/tools/webpack/packages.js +++ b/tools/webpack/packages.js @@ -44,6 +44,7 @@ const bundledPackagesPhpConfig = [ replaceClasses: [ 'WP_Style_Engine_CSS_Declarations', 'WP_Style_Engine_CSS_Rules_Store', + 'WP_Style_Engine_CSS_Rules_Container', 'WP_Style_Engine_CSS_Rule', 'WP_Style_Engine_Processor', 'WP_Style_Engine', From 17afecc5597a919b8f65c25b0f79fe6d017693b8 Mon Sep 17 00:00:00 2001 From: ramon Date: Thu, 8 Feb 2024 12:19:31 +1100 Subject: [PATCH 02/20] Tests --- packages/style-engine/style-engine.php | 3 ++- phpunit/style-engine/style-engine-test.php | 10 +++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/style-engine/style-engine.php b/packages/style-engine/style-engine.php index cc7420d01c7c4..03a9f4c282d72 100644 --- a/packages/style-engine/style-engine.php +++ b/packages/style-engine/style-engine.php @@ -121,7 +121,7 @@ function wp_style_engine_get_stylesheet_from_css_rules( $css_rules, $options = a continue; } // @TODO should this be in $options['container']? - $container = $css_rules['container'] ?? null; + $container = $css_rule['container'] ?? null; if ( ! empty( $options['context'] ) ) { // @TODO how to combine rules with the same container? in a container_rule class? @@ -131,6 +131,7 @@ function wp_style_engine_get_stylesheet_from_css_rules( $css_rules, $options = a } $new_rule = new WP_Style_Engine_CSS_Rule( $css_rule['selector'], $css_rule['declarations'] ); $new_rule->set_container( $container ); + $css_rule_objects[] = $new_rule; } diff --git a/phpunit/style-engine/style-engine-test.php b/phpunit/style-engine/style-engine-test.php index 26d4cfffbce91..75c75f3bbbf7c 100644 --- a/phpunit/style-engine/style-engine-test.php +++ b/phpunit/style-engine/style-engine-test.php @@ -699,6 +699,14 @@ public function test_should_return_stylesheet_from_nested_css_rules() { 'align-self' => 'stretch', ), ), + array( + 'selector' => '.saruman', + 'container' => '@supports (align-self: stretch)', + 'declarations' => array( + 'color' => 'black', + 'font-family' => 'The-Great-Eye', + ), + ), array( 'selector' => '.gandalf', 'container' => '@supports (border-style: dotted)', @@ -723,7 +731,7 @@ public function test_should_return_stylesheet_from_nested_css_rules() { $compiled_stylesheet = gutenberg_style_engine_get_stylesheet_from_css_rules( $css_rules, array( 'prettify' => false ) ); - $this->assertSame( '.saruman{color:white;height:100px;border-style:solid;align-self:unset;}.gandalf{color:grey;height:90px;border-style:dotted;align-self:safe center;}.radagast{color:brown;height:60px;border-style:dashed;align-self:stretch;}', $compiled_stylesheet ); + $this->assertSame( '@supports (align-self: stretch){.saruman{color:black;height:100px;border-style:solid;align-self:stretch;font-family:The-Great-Eye;}.radagast{color:brown;height:60px;border-style:dashed;align-self:stretch;}}@supports (border-style: dotted){.gandalf{color:grey;height:90px;border-style:dotted;align-self:safe center;}}', $compiled_stylesheet ); } /** From 794d4d94c7633f84681dc36eb632c39b5e85bc20 Mon Sep 17 00:00:00 2001 From: ramon Date: Fri, 9 Feb 2024 17:35:27 +1100 Subject: [PATCH 03/20] Extracting file # Clean up after rebase Clean up after rebase --- lib/load.php | 1 + .../class-wp-style-engine-css-rule.php | 58 ++---------- ...ss-wp-style-engine-css-rules-container.php | 69 +++++++++++++++ .../class-wp-style-engine-css-rules-store.php | 11 +-- .../class-wp-style-engine-processor.php | 88 +++++-------------- .../style-engine/class-wp-style-engine.php | 4 +- .../class-wp-style-engine-processor-test.php | 20 ++--- phpunit/style-engine/style-engine-test.php | 28 +++++- 8 files changed, 136 insertions(+), 143 deletions(-) create mode 100644 packages/style-engine/class-wp-style-engine-css-rules-container.php diff --git a/lib/load.php b/lib/load.php index 47d41fb50b3b5..3ff41e72709fd 100644 --- a/lib/load.php +++ b/lib/load.php @@ -195,6 +195,7 @@ function gutenberg_is_experiment_enabled( $name ) { // Copied package PHP files. if ( is_dir( __DIR__ . '/../build/style-engine' ) ) { + require_once __DIR__ . '/../build/style-engine/class-wp-style-engine-css-rules-container-gutenberg.php'; require_once __DIR__ . '/../build/style-engine/class-wp-style-engine-css-declarations-gutenberg.php'; require_once __DIR__ . '/../build/style-engine/class-wp-style-engine-css-rule-gutenberg.php'; require_once __DIR__ . '/../build/style-engine/class-wp-style-engine-css-rules-store-gutenberg.php'; diff --git a/packages/style-engine/class-wp-style-engine-css-rule.php b/packages/style-engine/class-wp-style-engine-css-rule.php index 4357efd2ae70b..184c2362abc98 100644 --- a/packages/style-engine/class-wp-style-engine-css-rule.php +++ b/packages/style-engine/class-wp-style-engine-css-rule.php @@ -15,7 +15,6 @@ * @access private */ class WP_Style_Engine_CSS_Rule { - /** * The selector. * @@ -24,7 +23,7 @@ class WP_Style_Engine_CSS_Rule { protected $selector; /** - * The container. + * A CSS selector or CSS nested @rule, such as `@media (min-width: 80rem)` or `@layer module`. * * @var string */ @@ -39,27 +38,16 @@ class WP_Style_Engine_CSS_Rule { */ protected $declarations; - /** - * The CSS nested @rule, such as `@media (min-width: 80rem)` or `@layer module`. - * - * @var string - */ - protected $at_rule; - - /** * Constructor * * @param string $selector The CSS selector. * @param string[]|WP_Style_Engine_CSS_Declarations $declarations An associative array of CSS definitions, e.g., array( "$property" => "$value", "$property" => "$value" ), * or a WP_Style_Engine_CSS_Declarations object. - * @param string $at_rule A CSS nested @rule, such as `@media (min-width: 80rem)` or `@layer module`. - * */ - public function __construct( $selector = '', $declarations = array(), $at_rule = '' ) { + public function __construct( $selector = '', $declarations = array() ) { $this->set_selector( $selector ); $this->add_declarations( $declarations ); - $this->set_at_rule( $at_rule ); } /** @@ -77,7 +65,7 @@ public function set_selector( $selector ) { /** * Sets the container. * - * @param string $container The CSS selector. + * @param string $container The CSS container selector or a CSS nested @rule, such as `@media (min-width: 80rem)` or `@layer module`. * * @return WP_Style_Engine_CSS_Rule Returns the object to allow chaining of methods. */ @@ -122,18 +110,6 @@ public function add_declarations( $declarations ) { return $this; } - /** - * Sets the at_rule. - * - * @param string $at_rule A CSS nested @rule, such as `@media (min-width: 80rem)` or `@layer module`. - * - * @return WP_Style_Engine_CSS_Rule Returns the object to allow chaining of methods. - */ - public function set_at_rule( $at_rule ) { - $this->at_rule = $at_rule; - return $this; - } - /** * Gets the declarations object. * @@ -152,15 +128,6 @@ public function get_selector() { return $this->selector; } - /** - * Gets the at_rule. - * - * @return string - */ - public function get_at_rule() { - return $this->at_rule; - } - /** * Gets the CSS. * @@ -170,28 +137,19 @@ public function get_at_rule() { * @return string */ public function get_css( $should_prettify = false, $indent_count = 0 ) { - $rule_indent = $should_prettify ? str_repeat( "\t", $indent_count ) : ''; - $nested_rule_indent = $should_prettify ? str_repeat( "\t", $indent_count + 1 ) : ''; - $declarations_indent = $should_prettify ? $indent_count + 1 : 0; - $nested_declarations_indent = $should_prettify ? $indent_count + 2 : 0; - $suffix = $should_prettify ? "\n" : ''; - $spacer = $should_prettify ? ' ' : ''; + $rule_indent = $should_prettify ? str_repeat( "\t", $indent_count ) : ''; + $declarations_indent = $should_prettify ? $indent_count + 1 : 0; + $suffix = $should_prettify ? "\n" : ''; + $spacer = $should_prettify ? ' ' : ''; // Trims any multiple selectors strings. $selector = $should_prettify ? implode( ',', array_map( 'trim', explode( ',', $this->get_selector() ) ) ) : $this->get_selector(); $selector = $should_prettify ? str_replace( array( ',' ), ",\n", $selector ) : $selector; - $at_rule = $this->get_at_rule(); - $has_at_rule = ! empty( $at_rule ); - $css_declarations = $this->declarations->get_declarations_string( $should_prettify, $has_at_rule ? $nested_declarations_indent : $declarations_indent ); + $css_declarations = $this->declarations->get_declarations_string( $should_prettify, $declarations_indent ); if ( empty( $css_declarations ) ) { return ''; } - if ( $has_at_rule ) { - $selector = "{$rule_indent}{$at_rule}{$spacer}{{$suffix}{$nested_rule_indent}{$selector}{$spacer}{{$suffix}{$css_declarations}{$suffix}{$nested_rule_indent}}{$suffix}{$rule_indent}}"; - return $selector; - } - return "{$rule_indent}{$selector}{$spacer}{{$suffix}{$css_declarations}{$suffix}{$rule_indent}}"; } } diff --git a/packages/style-engine/class-wp-style-engine-css-rules-container.php b/packages/style-engine/class-wp-style-engine-css-rules-container.php new file mode 100644 index 0000000000000..895b3071299f6 --- /dev/null +++ b/packages/style-engine/class-wp-style-engine-css-rules-container.php @@ -0,0 +1,69 @@ +set_container( $container ); + // @TODO should be able to add multiple rules. + // @TODO check for instance of WP_Style_Engine_CSS_Rule + $this->add_rule( $rule ); + } + + public function set_container( $container ) { + $this->container = $container; + return $this; + } + + public function get_container() { + return $this->container; + } + + public function get_rules() { + return $this->rules; + } + + public function get_rule( $selector ) { + return $this->rules[ $selector ] ?? null; + } + + public function add_rule( $rule ) { + // @TODO should be able to add multiple rules. + // @TODO should be able to return a rule and update its selectors + // @TODO check for instance of WP_Style_Engine_CSS_Rule + $selector = $rule->get_selector(); + + if ( isset( $this->rules[ $selector ] ) ) { + $this->rules[ $selector ]->add_declarations( $rule->get_declarations() ); + } else { + $this->rules[ $selector ] = $rule; + } + return $this; + } + + public function get_css( $should_prettify = false, $indent_count = 0 ) { + $css = ''; + $indent_count = $should_prettify ? $indent_count + 1 : $indent_count; + $new_line = $should_prettify ? "\n" : ''; + $spacer = $should_prettify ? ' ' : ''; + foreach ( $this->rules as $rule ) { + $css .= $rule->get_css( $should_prettify, $indent_count ); + $css .= $should_prettify ? "\n" : ''; + } + + if ( empty( $css ) ) { + return ''; + } + + return "{$this->container}{$spacer}{{$new_line}{$css}}"; + } +} diff --git a/packages/style-engine/class-wp-style-engine-css-rules-store.php b/packages/style-engine/class-wp-style-engine-css-rules-store.php index e199cda5da0fc..5a5c2f0dac2fa 100644 --- a/packages/style-engine/class-wp-style-engine-css-rules-store.php +++ b/packages/style-engine/class-wp-style-engine-css-rules-store.php @@ -110,26 +110,17 @@ public function get_all_rules() { * If the rule does not exist, it will be created. * * @param string $selector The CSS selector. - * @param string $at_rule The CSS nested @rule, such as `@media (min-width: 80rem)` or `@layer module`. * * @return WP_Style_Engine_CSS_Rule|void Returns a WP_Style_Engine_CSS_Rule object, or null if the selector is empty. */ - public function add_rule( $selector, $at_rule = '' ) { + public function add_rule( $selector ) { $selector = trim( $selector ); - $at_rule = trim( $at_rule ); // Bail early if there is no selector. if ( empty( $selector ) ) { return; } - if ( ! empty( $at_rule ) ) { - if ( empty( $this->rules[ "$at_rule $selector" ] ) ) { - $this->rules[ "$at_rule $selector" ] = new WP_Style_Engine_CSS_Rule( $selector, array(), $at_rule ); - } - return $this->rules[ "$at_rule $selector" ]; - } - // Create the rule if it doesn't exist. if ( empty( $this->rules[ $selector ] ) ) { $this->rules[ $selector ] = new WP_Style_Engine_CSS_Rule( $selector ); diff --git a/packages/style-engine/class-wp-style-engine-processor.php b/packages/style-engine/class-wp-style-engine-processor.php index 2090b77391aba..457c75956b643 100644 --- a/packages/style-engine/class-wp-style-engine-processor.php +++ b/packages/style-engine/class-wp-style-engine-processor.php @@ -30,6 +30,14 @@ class WP_Style_Engine_Processor { */ protected $css_rules = array(); + /** + * The set of nested CSS rules that this processor will work on. + * + * @var WP_Style_Engine_CSS_Rules_Container[] + */ + protected $css_containers = array(); + + /** * Add a store to the processor. * @@ -69,20 +77,20 @@ public function add_rules( $css_rules ) { $container = $rule->get_container(); $selector = $rule->get_selector(); - if ( ! empty( $selector ) && empty( $container ) ) { - if ( isset( $this->css_rules[ $selector ] ) ) { - $this->css_rules[ $selector ]->add_declarations( $rule->get_declarations() ); + if ( ! empty( $container ) ) { + if ( isset( $this->css_containers[ $container ] ) ) { + $this->css_containers[ $container ]->add_rule( $rule ); } else { - $this->css_rules[ $rule->get_selector() ] = $rule; + $this->css_containers[ $container ] = new WP_Style_Engine_CSS_Rules_Container( $container, $rule ); } continue; } - if ( ! empty( $container ) ) { - if ( isset( $this->css_rules[ $container ] ) ) { - $this->css_rules[ $container ]->add_rule( $rule ); + if ( ! empty( $selector ) ) { + if ( isset( $this->css_rules[ $selector ] ) ) { + $this->css_rules[ $selector ]->add_declarations( $rule->get_declarations() ); } else { - $this->css_rules[ $container ] = new WP_Style_Engine_CSS_Rules_Container( $container, $rule ); + $this->css_rules[ $rule->get_selector() ] = $rule; } } } @@ -123,10 +131,14 @@ public function get_css( $options = array() ) { // Build the CSS. $css = ''; - foreach ( $this->css_rules as $rule ) { + // Merge the rules and containers. Containers come last. + $merged_rules = array_merge( $this->css_rules, $this->css_containers ); + + foreach ( $merged_rules as $rule ) { $css .= $rule->get_css( $options['prettify'] ); $css .= $options['prettify'] ? "\n" : ''; } + return $css; } @@ -168,61 +180,3 @@ private function combine_rules_selectors() { } } } - - -// @TODO new file and tests -// Should have same/similar interface to WP_Style_Engine_CSS_Rule or maybe even part of it? -class WP_Style_Engine_CSS_Rules_Container { - protected $container; - protected $rules = array(); - - public function __construct( $container = '', $rule ) { - $this->set_container( $container ); - // @TODO should be able to add multiple rules. - // @TODO check for instance of WP_Style_Engine_CSS_Rule - $this->add_rule( $rule ); - } - - public function set_container( $container ) { - $this->container = $container; - return $this; - } - - public function get_container() { - return $this->container; - } - - public function get_rules() { - return $this->rules; - } - - public function add_rule( $rule ) { - // @TODO should be able to add multiple rules. - // @TODO check for instance of WP_Style_Engine_CSS_Rule - $selector = $rule->get_selector(); - - if ( isset( $this->rules[ $selector ] ) ) { - $this->rules[ $selector ]->add_declarations( $rule->get_declarations() ); - } else { - $this->rules[ $selector ] = $rule; - } - return $this; - } - - public function get_css( $should_prettify = false, $indent_count = 0 ) { - $css = ''; - $indent_count = $should_prettify ? $indent_count + 1 : $indent_count; - $new_line = $should_prettify ? "\n" : ''; - $spacer = $should_prettify ? ' ' : ''; - foreach ( $this->rules as $rule ) { - $css .= $rule->get_css( $should_prettify, $indent_count ); - $css .= $should_prettify ? "\n" : ''; - } - - if ( empty( $css ) ) { - return ''; - } - - return "{$this->container}{$spacer}{{$new_line}{$css}}"; - } -} diff --git a/packages/style-engine/class-wp-style-engine.php b/packages/style-engine/class-wp-style-engine.php index e031da52e8420..1524eb4a7dcb4 100644 --- a/packages/style-engine/class-wp-style-engine.php +++ b/packages/style-engine/class-wp-style-engine.php @@ -358,11 +358,11 @@ protected static function is_valid_style_value( $style_value ) { * * @return void. */ - public static function store_css_rule( $store_name, $css_selector, $css_declarations, $css_at_rule = '' ) { + public static function store_css_rule( $store_name, $css_selector, $css_declarations ) { if ( empty( $store_name ) || empty( $css_selector ) || empty( $css_declarations ) ) { return; } - static::get_store( $store_name )->add_rule( $css_selector, $css_at_rule )->add_declarations( $css_declarations ); + static::get_store( $store_name )->add_rule( $css_selector )->add_declarations( $css_declarations ); } /** diff --git a/phpunit/style-engine/class-wp-style-engine-processor-test.php b/phpunit/style-engine/class-wp-style-engine-processor-test.php index 2ec5647d44f18..facc2254df028 100644 --- a/phpunit/style-engine/class-wp-style-engine-processor-test.php +++ b/phpunit/style-engine/class-wp-style-engine-processor-test.php @@ -58,7 +58,7 @@ public function test_should_return_nested_rules_as_compiled_css() { 'background-color' => 'purple', ) ); - $a_nice_css_rule->set_at_rule( '@media (min-width: 80rem)' ); + $a_nice_css_rule->set_container( '@media (min-width: 80rem)' ); $a_nicer_css_rule = new WP_Style_Engine_CSS_Rule_Gutenberg( '.a-nicer-rule' ); $a_nicer_css_rule->add_declarations( @@ -68,7 +68,7 @@ public function test_should_return_nested_rules_as_compiled_css() { 'background-color' => 'purple', ) ); - $a_nicer_css_rule->set_at_rule( '@layer nicety' ); + $a_nicer_css_rule->set_container( '@layer nicety' ); $a_nice_processor = new WP_Style_Engine_Processor_Gutenberg(); $a_nice_processor->add_rules( array( $a_nice_css_rule, $a_nicer_css_rule ) ); @@ -143,7 +143,7 @@ public function test_should_return_prettified_nested_css_rules() { 'background-color' => 'orange', ) ); - $a_wonderful_css_rule->set_at_rule( '@media (min-width: 80rem)' ); + $a_wonderful_css_rule->set_container( '@media (min-width: 80rem)' ); $a_very_wonderful_css_rule = new WP_Style_Engine_CSS_Rule_Gutenberg( '.a-very_wonderful-rule' ); $a_very_wonderful_css_rule->add_declarations( @@ -152,7 +152,7 @@ public function test_should_return_prettified_nested_css_rules() { 'background-color' => 'orange', ) ); - $a_very_wonderful_css_rule->set_at_rule( '@layer wonderfulness' ); + $a_very_wonderful_css_rule->set_container( '@layer wonderfulness' ); $a_wonderful_processor = new WP_Style_Engine_Processor_Gutenberg(); $a_wonderful_processor->add_rules( array( $a_wonderful_css_rule, $a_very_wonderful_css_rule ) ); @@ -412,7 +412,7 @@ public function test_should_return_store_rules_with_css_containers() { $a_panda_renderer = new WP_Style_Engine_Processor_Gutenberg(); $a_panda_renderer->add_store( $panda_store ); $this->assertSame( - '@container (width > 400px){.blueberry{background-color:blue;}}.wp-block-mushroom a:hover{padding:100px;}', + '.wp-block-mushroom a:hover{padding:100px;}@container (width > 400px){.blueberry{background-color:blue;}}', $a_panda_renderer->get_css( array( 'prettify' => false ) ), 'Returns processed CSS rules with containers' ); @@ -429,12 +429,15 @@ public function test_should_return_store_rules_with_css_containers() { $a_panda_renderer->add_rules( $a_raspberry_rule ); $this->assertSame( - '@container (width > 400px){.blueberry{background-color:blue;}.raspberry{background-color:red;}}.wp-block-mushroom a:hover{padding:100px;}', + '.wp-block-mushroom a:hover{padding:100px;}@container (width > 400px){.blueberry{background-color:blue;}.raspberry{background-color:red;}}', $a_panda_renderer->get_css( array( 'prettify' => false ) ), 'Returns processed CSS rules with rules added to containers' ); - $expected_prettified = '@container (width > 400px) { + $expected_prettified = '.wp-block-mushroom a:hover { + padding: 100px; +} +@container (width > 400px) { .blueberry { background-color: blue; } @@ -442,9 +445,6 @@ public function test_should_return_store_rules_with_css_containers() { background-color: red; } } -.wp-block-mushroom a:hover { - padding: 100px; -} '; $this->assertSame( $expected_prettified, diff --git a/phpunit/style-engine/style-engine-test.php b/phpunit/style-engine/style-engine-test.php index 75c75f3bbbf7c..3c9f6c84cb3e8 100644 --- a/phpunit/style-engine/style-engine-test.php +++ b/phpunit/style-engine/style-engine-test.php @@ -687,11 +687,17 @@ public function test_should_return_stylesheet_from_css_rules() { /** * Tests returning a generated stylesheet from a set of nested rules. */ - public function test_should_return_stylesheet_from_nested_css_rules() { + public function test_should_return_stylesheet_with_combined_nested_css_rules_printed_after_non_nested() { $css_rules = array( array( 'selector' => '.saruman', - 'container' => '@supports (align-self: stretch)', + 'declarations' => array( + 'letter-spacing' => '1px', + ), + ), + array( + 'selector' => '.saruman', + 'container' => '@container (min-width: 700px)', 'declarations' => array( 'color' => 'white', 'height' => '100px', @@ -701,12 +707,26 @@ public function test_should_return_stylesheet_from_nested_css_rules() { ), array( 'selector' => '.saruman', - 'container' => '@supports (align-self: stretch)', + 'container' => '@container (min-width: 700px)', 'declarations' => array( 'color' => 'black', 'font-family' => 'The-Great-Eye', ), ), + array( + 'selector' => '.voldemort', + 'container' => '@supports (align-self: stretch)', + 'declarations' => array( + 'height' => '100px', + 'align-self' => 'stretch', + ), + ), + array( + 'selector' => '.gandalf', + 'declarations' => array( + 'letter-spacing' => '2px', + ), + ), array( 'selector' => '.gandalf', 'container' => '@supports (border-style: dotted)', @@ -731,7 +751,7 @@ public function test_should_return_stylesheet_from_nested_css_rules() { $compiled_stylesheet = gutenberg_style_engine_get_stylesheet_from_css_rules( $css_rules, array( 'prettify' => false ) ); - $this->assertSame( '@supports (align-self: stretch){.saruman{color:black;height:100px;border-style:solid;align-self:stretch;font-family:The-Great-Eye;}.radagast{color:brown;height:60px;border-style:dashed;align-self:stretch;}}@supports (border-style: dotted){.gandalf{color:grey;height:90px;border-style:dotted;align-self:safe center;}}', $compiled_stylesheet ); + $this->assertSame( '.saruman{letter-spacing:1px;}.gandalf{letter-spacing:2px;}@container (min-width: 700px){.saruman{color:black;height:100px;border-style:solid;align-self:stretch;font-family:The-Great-Eye;}}@supports (align-self: stretch){.voldemort{height:100px;align-self:stretch;}.radagast{color:brown;height:60px;border-style:dashed;align-self:stretch;}}@supports (border-style: dotted){.gandalf{color:grey;height:90px;border-style:dotted;align-self:safe center;}}', $compiled_stylesheet ); } /** From 5bea1a63136ef29a6bc118e34e7ad59f61358163 Mon Sep 17 00:00:00 2001 From: ramon Date: Fri, 9 Feb 2024 20:23:35 +1100 Subject: [PATCH 04/20] Refactor output public API methods in style-engine Ensuring we can add rule objects to store Tests new container class --- .../class-wp-style-engine-css-rule.php | 9 +- ...ss-wp-style-engine-css-rules-container.php | 181 +++++++++++++----- .../class-wp-style-engine-css-rules-store.php | 19 +- .../class-wp-style-engine-processor.php | 2 +- .../style-engine/class-wp-style-engine.php | 8 +- packages/style-engine/style-engine.php | 44 +++-- ...s-wp-style-engine-css-rules-store-test.php | 20 ++ 7 files changed, 205 insertions(+), 78 deletions(-) diff --git a/packages/style-engine/class-wp-style-engine-css-rule.php b/packages/style-engine/class-wp-style-engine-css-rule.php index 184c2362abc98..0d509130bb881 100644 --- a/packages/style-engine/class-wp-style-engine-css-rule.php +++ b/packages/style-engine/class-wp-style-engine-css-rule.php @@ -10,7 +10,7 @@ if ( ! class_exists( 'WP_Style_Engine_CSS_Rule' ) ) { /** - * Holds, sanitizes, processes and prints CSS declarations for the Style Engine. + * Holds, sanitizes, processes and prints CSS rules for the Style Engine. * * @access private */ @@ -23,7 +23,7 @@ class WP_Style_Engine_CSS_Rule { protected $selector; /** - * A CSS selector or CSS nested @rule, such as `@media (min-width: 80rem)` or `@layer module`. + * A parent CSS selector in the case of nested CSS, or a CSS nested @rule, such as `@media (min-width: 80rem)` or `@layer module`. * * @var string */ @@ -65,8 +65,8 @@ public function set_selector( $selector ) { /** * Sets the container. * - * @param string $container The CSS container selector or a CSS nested @rule, such as `@media (min-width: 80rem)` or `@layer module`. - * + * @param string $container A parent CSS selector in the case of nested CSS, or a CSS nested @rule, + * such as `@media (min-width: 80rem)` or `@layer module`. * @return WP_Style_Engine_CSS_Rule Returns the object to allow chaining of methods. */ public function set_container( $container ) { @@ -154,4 +154,3 @@ public function get_css( $should_prettify = false, $indent_count = 0 ) { } } } - diff --git a/packages/style-engine/class-wp-style-engine-css-rules-container.php b/packages/style-engine/class-wp-style-engine-css-rules-container.php index 895b3071299f6..9649fc3a1f343 100644 --- a/packages/style-engine/class-wp-style-engine-css-rules-container.php +++ b/packages/style-engine/class-wp-style-engine-css-rules-container.php @@ -8,62 +8,153 @@ */ // @TODO new file and tests // Should have same/similar interface to WP_Style_Engine_CSS_Rule or maybe even part of it? -class WP_Style_Engine_CSS_Rules_Container { - protected $container; - protected $rules = array(); - - public function __construct( $container = '', $rule ) { - $this->set_container( $container ); - // @TODO should be able to add multiple rules. - // @TODO check for instance of WP_Style_Engine_CSS_Rule - $this->add_rule( $rule ); - } - public function set_container( $container ) { - $this->container = $container; - return $this; - } +if ( ! class_exists( 'WP_Style_Engine_CSS_Rules_Container' ) ) { + /** + * Holds, sanitizes, processes and prints nested CSS rules for the Style Engine. + * + * @access private + */ + class WP_Style_Engine_CSS_Rules_Container { + /** + * A parent CSS selector in the case of nested CSS, or a CSS nested @rule, such as `@media (min-width: 80rem)` or `@layer module` + * + * @var string + */ + protected $container; - public function get_container() { - return $this->container; - } + /** + * The container declarations. + * + * Contains a WP_Style_Engine_CSS_Rule object. + * + * @var WP_Style_Engine_CSS_Rule[] + */ + protected $rules = array(); - public function get_rules() { - return $this->rules; - } + /** + * Constructor + * + * @param string $container A parent CSS selector in the case of nested CSS, or a CSS nested @rule, + * such as `@media (min-width: 80rem)` or `@layer module`. + * @param WP_Style_Engine_CSS_Rule[]|WP_Style_Engine_CSS_Rule $rule A WP_Style_Engine_CSS_Rule object. + */ + public function __construct( $container = '', $rule ) { + $this->set_container( $container ); + $this->add_rules( $rule ); + } - public function get_rule( $selector ) { - return $this->rules[ $selector ] ?? null; - } + /** + * Sets the container. + * + * @param string $container A parent CSS selector in the case of nested CSS, or a CSS nested @rule, + * such as `@media (min-width: 80rem)` or `@layer module`. + * + * @return WP_Style_Engine_CSS_Rules_Container Returns the object to allow chaining of methods. + */ + public function set_container( $container ) { + if ( ! empty( $container ) ) { + $this->container = $container; + } + return $this; + } - public function add_rule( $rule ) { - // @TODO should be able to add multiple rules. - // @TODO should be able to return a rule and update its selectors - // @TODO check for instance of WP_Style_Engine_CSS_Rule - $selector = $rule->get_selector(); + /** + * Gets the container. + * + * @return string Container. + */ + public function get_container() { + return $this->container; + } - if ( isset( $this->rules[ $selector ] ) ) { - $this->rules[ $selector ]->add_declarations( $rule->get_declarations() ); - } else { - $this->rules[ $selector ] = $rule; + /** + * Gets all nested rules. + * + * @return WP_Style_Engine_CSS_Rule[] + */ + public function get_rules() { + return $this->rules; } - return $this; - } - public function get_css( $should_prettify = false, $indent_count = 0 ) { - $css = ''; - $indent_count = $should_prettify ? $indent_count + 1 : $indent_count; - $new_line = $should_prettify ? "\n" : ''; - $spacer = $should_prettify ? ' ' : ''; - foreach ( $this->rules as $rule ) { - $css .= $rule->get_css( $should_prettify, $indent_count ); - $css .= $should_prettify ? "\n" : ''; + /** + * Gets a stored nested rules. + * + * @return WP_Style_Engine_CSS_Rule + */ + public function get_rule( $selector ) { + return $this->rules[ $selector ] ?? null; } - if ( empty( $css ) ) { - return ''; + /** + * Adds the rules. + * + * @param WP_Style_Engine_CSS_Rule|WP_Style_Engine_CSS_Rule[] $container_rules An array of declarations (property => value pairs), + * or a WP_Style_Engine_CSS_Declarations object. + * + * @return WP_Style_Engine_CSS_Rules_Container Returns the object to allow chaining of methods. + */ + public function add_rules( $container_rules ) { + if ( ! is_array( $container_rules ) ) { + $container_rules = array( $container_rules ); + } + + foreach ( $container_rules as $rule ) { + if ( ! $rule instanceof WP_Style_Engine_CSS_Rule ) { + _doing_it_wrong( + __METHOD__, + __( 'Rules passed to WP_Style_Engine_CSS_Rules_Containermust be an instance of WP_Style_Engine_CSS_Rule', 'default' ), + '6.6.0' + ); + continue; + } + + $container = $rule->get_container(); + + if ( $container !== $this->container ) { + _doing_it_wrong( + __METHOD__, + __( 'Rules passed to WP_Style_Engine_CSS_Rules_Container must have the same container as the container object', 'default' ), + '6.6.0' + ); + continue; + } + + $selector = $rule->get_selector(); + + if ( isset( $this->rules[ $selector ] ) ) { + $this->rules[ $selector ]->add_declarations( $rule->get_declarations() ); + } else { + $this->rules[ $selector ] = $rule; + } + } + + return $this; } - return "{$this->container}{$spacer}{{$new_line}{$css}}"; + /** + * Gets the nested CSS. + * + * @param bool $should_prettify Whether to add spacing, new lines and indents. + * @param number $indent_count The number of tab indents to apply to the rule. Applies if `prettify` is `true`. + * + * @return string + */ + public function get_css( $should_prettify = false, $indent_count = 0 ) { + $css = ''; + $indent_count = $should_prettify ? $indent_count + 1 : $indent_count; + $new_line = $should_prettify ? "\n" : ''; + $spacer = $should_prettify ? ' ' : ''; + foreach ( $this->rules as $rule ) { + $css .= $rule->get_css( $should_prettify, $indent_count ); + $css .= $should_prettify ? "\n" : ''; + } + + if ( empty( $css ) ) { + return ''; + } + + return "{$this->container}{$spacer}{{$new_line}{$css}}"; + } } } diff --git a/packages/style-engine/class-wp-style-engine-css-rules-store.php b/packages/style-engine/class-wp-style-engine-css-rules-store.php index 5a5c2f0dac2fa..13d82f62d9380 100644 --- a/packages/style-engine/class-wp-style-engine-css-rules-store.php +++ b/packages/style-engine/class-wp-style-engine-css-rules-store.php @@ -109,21 +109,30 @@ public function get_all_rules() { * Gets a WP_Style_Engine_CSS_Rule object by its selector. * If the rule does not exist, it will be created. * - * @param string $selector The CSS selector. + * @param string|WP_Style_Engine_CSS_Rule $rule The CSS selector or a WP_Style_Engine_CSS_Rule object. * * @return WP_Style_Engine_CSS_Rule|void Returns a WP_Style_Engine_CSS_Rule object, or null if the selector is empty. */ - public function add_rule( $selector ) { - $selector = trim( $selector ); + public function add_rule( $rule ) { + if ( empty( $rule ) ) { + return; + } + + if ( $rule instanceof WP_Style_Engine_CSS_Rule ) { + $selector = $rule->get_selector(); + } else { + $selector = trim( $rule ); + } - // Bail early if there is no selector. if ( empty( $selector ) ) { return; } // Create the rule if it doesn't exist. if ( empty( $this->rules[ $selector ] ) ) { - $this->rules[ $selector ] = new WP_Style_Engine_CSS_Rule( $selector ); + if ( ! empty( $selector ) ) { + $this->rules[ $selector ] = $rule instanceof WP_Style_Engine_CSS_Rule ? $rule : new WP_Style_Engine_CSS_Rule( $selector ); + } } return $this->rules[ $selector ]; diff --git a/packages/style-engine/class-wp-style-engine-processor.php b/packages/style-engine/class-wp-style-engine-processor.php index 457c75956b643..fd23a4681b0e6 100644 --- a/packages/style-engine/class-wp-style-engine-processor.php +++ b/packages/style-engine/class-wp-style-engine-processor.php @@ -79,7 +79,7 @@ public function add_rules( $css_rules ) { if ( ! empty( $container ) ) { if ( isset( $this->css_containers[ $container ] ) ) { - $this->css_containers[ $container ]->add_rule( $rule ); + $this->css_containers[ $container ]->add_rules( $rule ); } else { $this->css_containers[ $container ] = new WP_Style_Engine_CSS_Rules_Container( $container, $rule ); } diff --git a/packages/style-engine/class-wp-style-engine.php b/packages/style-engine/class-wp-style-engine.php index 1524eb4a7dcb4..b0c4b7f000242 100644 --- a/packages/style-engine/class-wp-style-engine.php +++ b/packages/style-engine/class-wp-style-engine.php @@ -626,7 +626,7 @@ protected static function get_url_or_value_css_declaration( $style_value, $style * * @return string A compiled CSS string. */ - public static function compile_css( $css_declarations, $css_selector, $container = '' ) { + public static function compile_css( $css_declarations, $css_selector ) { if ( empty( $css_declarations ) || ! is_array( $css_declarations ) ) { return ''; } @@ -634,12 +634,6 @@ public static function compile_css( $css_declarations, $css_selector, $container // Return an entire rule if there is a selector. if ( $css_selector ) { $css_rule = new WP_Style_Engine_CSS_Rule( $css_selector, $css_declarations ); - if ( $container ) { - $css_rule->set_container( $container ); - $css_container = new WP_Style_Engine_CSS_Rules_Container( $container, $css_rule ); - return $css_container->get_css(); - } - return $css_rule->get_css(); } diff --git a/packages/style-engine/style-engine.php b/packages/style-engine/style-engine.php index 03a9f4c282d72..a01059414768e 100644 --- a/packages/style-engine/style-engine.php +++ b/packages/style-engine/style-engine.php @@ -30,6 +30,8 @@ * @type bool $convert_vars_to_classnames Whether to skip converting incoming CSS var patterns, e.g., `var:preset||`, to var( --wp--preset--* ) values. Default `false`. * @type string $selector Optional. When a selector is passed, the value of `$css` in the return value will comprise a full CSS rule `$selector { ...$css_declarations }`, * otherwise, the value will be a concatenated string of CSS declarations. + * @type string $container Optional. A parent CSS selector in the case of nested CSS, or a CSS nested @rule, + * such as `@media (min-width: 80rem)` or `@layer module * } * * @return array { @@ -55,16 +57,31 @@ function wp_style_engine_get_styles( $block_styles, $options = array() ) { $styles_output = array(); if ( ! empty( $parsed_styles['declarations'] ) ) { - $styles_output['css'] = WP_Style_Engine::compile_css( $parsed_styles['declarations'], $options['selector'], $options['container'] ); - $styles_output['declarations'] = $parsed_styles['declarations']; - if ( ! empty( $options['context'] ) && ! empty( $options['selector'] ) ) { - // @TODO Or could return the store from WP_Style_Engine::store_css_rule() and use it to get the rules. - WP_Style_Engine::store_css_rule( $options['context'], $options['selector'], $parsed_styles['declarations'] ); - // @TODO WP_Style_Engine_CSS_Rules_Store could maybe do with a get_rule(). - WP_Style_Engine::get_store( $options['context'] )->add_rule( $options['selector'] )->set_container( $options['container'] ); + $container = $options['container'] ?? null; + $selector = $options['selector'] ?? null; + $css_declarations = new WP_Style_Engine_CSS_Declarations( $parsed_styles['declarations'] ); + $new_rule = null; + + if ( $selector ) { + $new_rule = new WP_Style_Engine_CSS_Rule( $options['selector'], $css_declarations ); + if ( $container ) { + $new_rule->set_container( $container ); + $css_container = new WP_Style_Engine_CSS_Rules_Container( $container, $new_rule ); + $styles_output['css'] = $css_container->get_css(); + } else { + $styles_output['css'] = $new_rule->get_css(); + } + } else { + $styles_output['css'] = $css_declarations->get_declarations_string(); + } + + if ( ! empty( $options['context'] ) && $new_rule ) { + WP_Style_Engine::get_store( $options['context'] )->add_rule( $new_rule ); } } + $styles_output['declarations'] = $parsed_styles['declarations']; + if ( ! empty( $parsed_styles['classnames'] ) ) { $styles_output['classnames'] = implode( ' ', array_unique( $parsed_styles['classnames'] ) ); } @@ -87,7 +104,7 @@ function wp_style_engine_get_styles( $block_styles, $options = array() ) { * Required. A collection of CSS rules. * * @type array ...$0 { - * @type string $container A parent selector in the case or nested CSS, or a CSS nested @rule, such as `@media (min-width: 80rem)` or `@layer module`. + * @type string $container A parent CSS selector in the case of nested CSS, or a CSS nested @rule, such as `@media (min-width: 80rem)` or `@layer module`. * @type string $selector A CSS selector. * @type string[] $declarations An associative array of CSS definitions, e.g., array( "$property" => "$value", "$property" => "$value" ). * } @@ -120,17 +137,14 @@ function wp_style_engine_get_stylesheet_from_css_rules( $css_rules, $options = a if ( empty( $css_rule['selector'] ) || empty( $css_rule['declarations'] ) || ! is_array( $css_rule['declarations'] ) ) { continue; } - // @TODO should this be in $options['container']? + $container = $css_rule['container'] ?? null; + $new_rule = new WP_Style_Engine_CSS_Rule( $css_rule['selector'], $css_rule['declarations'] ); + $new_rule->set_container( $container ); if ( ! empty( $options['context'] ) ) { - // @TODO how to combine rules with the same container? in a container_rule class? - WP_Style_Engine::store_css_rule( $options['context'], $css_rule['selector'], $css_rule['declarations'] ); - // @TODO WP_Style_Engine_CSS_Rules_Store could maybe do with a get_rule(). - WP_Style_Engine::get_store( $options['context'] )->add_rule( $css_rule['selector'] )->set_container( $container ); + WP_Style_Engine::get_store( $options['context'] )->add_rule( $new_rule ); } - $new_rule = new WP_Style_Engine_CSS_Rule( $css_rule['selector'], $css_rule['declarations'] ); - $new_rule->set_container( $container ); $css_rule_objects[] = $new_rule; } diff --git a/phpunit/style-engine/class-wp-style-engine-css-rules-store-test.php b/phpunit/style-engine/class-wp-style-engine-css-rules-store-test.php index 8529bff78e22c..5ffc8d4b8b19c 100644 --- a/phpunit/style-engine/class-wp-style-engine-css-rules-store-test.php +++ b/phpunit/style-engine/class-wp-style-engine-css-rules-store-test.php @@ -140,6 +140,26 @@ public function test_should_add_rule_to_existing_store() { $this->assertSame( $expected, $store_rule->get_css(), 'Return value of get_css() does not match expected CSS from existing store rules.' ); } + /** + * Tests adding rules to an existing store. + * + * @covers ::add_rule + */ + public function test_should_add_rule_object_to_existing_store() { + $new_hotdog_store = WP_Style_Engine_CSS_Rules_Store_Gutenberg::get_store( 'hotdog' ); + + $selector = '.wp-block-pickle a:hover'; + $pickle_rule = new WP_Style_Engine_CSS_Rule_Gutenberg( $selector, array( + 'color' => 'brown', + 'border-color' => 'yellow', + 'border-radius' => '10rem', + ) ); + $added_rule = $new_hotdog_store->add_rule( $pickle_rule ); + $expected = $pickle_rule->get_css(); + + $this->assertSame( $expected, $added_rule->get_css(), 'Return value of store rule get_css() matches passed rule object get_css().' ); + } + /** * Tests that all stored rule objects are returned. * From 08211cc66eb3f1ef2a4ff17ce8723d9ecbc2193b Mon Sep 17 00:00:00 2001 From: ramon Date: Sat, 10 Feb 2024 14:19:40 +1100 Subject: [PATCH 05/20] Store containers in the store. No need to store container names on the rules. --- ...ss-wp-style-engine-css-rules-container.php | 61 ++++----- .../class-wp-style-engine-css-rules-store.php | 18 ++- .../class-wp-style-engine-processor.php | 23 ++-- packages/style-engine/style-engine.php | 8 +- ...s-wp-style-engine-css-rules-store-test.php | 7 +- .../class-wp-style-engine-processor-test.php | 120 ++++++++++-------- 6 files changed, 124 insertions(+), 113 deletions(-) diff --git a/packages/style-engine/class-wp-style-engine-css-rules-container.php b/packages/style-engine/class-wp-style-engine-css-rules-container.php index 9649fc3a1f343..318ff6006c1b3 100644 --- a/packages/style-engine/class-wp-style-engine-css-rules-container.php +++ b/packages/style-engine/class-wp-style-engine-css-rules-container.php @@ -6,9 +6,8 @@ * * @package Gutenberg */ -// @TODO new file and tests -// Should have same/similar interface to WP_Style_Engine_CSS_Rule or maybe even part of it? - +// @TODO add unit tests +// @TODO Should have same/similar interface to WP_Style_Engine_CSS_Rule or maybe inherits from WP_Style_Engine_CSS_Rule? if ( ! class_exists( 'WP_Style_Engine_CSS_Rules_Container' ) ) { /** * Holds, sanitizes, processes and prints nested CSS rules for the Style Engine. @@ -21,7 +20,7 @@ class WP_Style_Engine_CSS_Rules_Container { * * @var string */ - protected $container; + protected $selector; /** * The container declarations. @@ -35,37 +34,37 @@ class WP_Style_Engine_CSS_Rules_Container { /** * Constructor * - * @param string $container A parent CSS selector in the case of nested CSS, or a CSS nested @rule, + * @param string $selector A parent CSS selector in the case of nested CSS, or a CSS nested @rule, * such as `@media (min-width: 80rem)` or `@layer module`. * @param WP_Style_Engine_CSS_Rule[]|WP_Style_Engine_CSS_Rule $rule A WP_Style_Engine_CSS_Rule object. */ - public function __construct( $container = '', $rule ) { - $this->set_container( $container ); + public function __construct( $selector = '', $rule ) { + $this->set_selector( $selector ); $this->add_rules( $rule ); } /** - * Sets the container. + * Sets the selector/container name. * - * @param string $container A parent CSS selector in the case of nested CSS, or a CSS nested @rule, + * @param string $selector A parent CSS selector in the case of nested CSS, or a CSS nested @rule, * such as `@media (min-width: 80rem)` or `@layer module`. * * @return WP_Style_Engine_CSS_Rules_Container Returns the object to allow chaining of methods. */ - public function set_container( $container ) { - if ( ! empty( $container ) ) { - $this->container = $container; + public function set_selector( $selector ) { + if ( ! empty( $selector ) ) { + $this->selector = $selector; } return $this; } /** - * Gets the container. + * Gets the selector/container name. * * @return string Container. */ - public function get_container() { - return $this->container; + public function get_selector() { + return $this->selector; } /** @@ -94,12 +93,12 @@ public function get_rule( $selector ) { * * @return WP_Style_Engine_CSS_Rules_Container Returns the object to allow chaining of methods. */ - public function add_rules( $container_rules ) { - if ( ! is_array( $container_rules ) ) { - $container_rules = array( $container_rules ); + public function add_rules( $rules ) { + if ( ! is_array( $rules ) ) { + $rules = array( $rules ); } - foreach ( $container_rules as $rule ) { + foreach ( $rules as $rule ) { if ( ! $rule instanceof WP_Style_Engine_CSS_Rule ) { _doing_it_wrong( __METHOD__, @@ -109,17 +108,6 @@ public function add_rules( $container_rules ) { continue; } - $container = $rule->get_container(); - - if ( $container !== $this->container ) { - _doing_it_wrong( - __METHOD__, - __( 'Rules passed to WP_Style_Engine_CSS_Rules_Container must have the same container as the container object', 'default' ), - '6.6.0' - ); - continue; - } - $selector = $rule->get_selector(); if ( isset( $this->rules[ $selector ] ) ) { @@ -141,20 +129,21 @@ public function add_rules( $container_rules ) { * @return string */ public function get_css( $should_prettify = false, $indent_count = 0 ) { - $css = ''; - $indent_count = $should_prettify ? $indent_count + 1 : $indent_count; - $new_line = $should_prettify ? "\n" : ''; - $spacer = $should_prettify ? ' ' : ''; + $css = ''; + $indent_count = $should_prettify ? $indent_count + 1 : $indent_count; + $new_line = $should_prettify ? "\n" : ''; + $spacer = $should_prettify ? ' ' : ''; + foreach ( $this->rules as $rule ) { $css .= $rule->get_css( $should_prettify, $indent_count ); $css .= $should_prettify ? "\n" : ''; } if ( empty( $css ) ) { - return ''; + return $css; } - return "{$this->container}{$spacer}{{$new_line}{$css}}"; + return "{$this->selector}{$spacer}{{$new_line}{$css}}"; } } } diff --git a/packages/style-engine/class-wp-style-engine-css-rules-store.php b/packages/style-engine/class-wp-style-engine-css-rules-store.php index 13d82f62d9380..a828aa9f8355b 100644 --- a/packages/style-engine/class-wp-style-engine-css-rules-store.php +++ b/packages/style-engine/class-wp-style-engine-css-rules-store.php @@ -109,7 +109,7 @@ public function get_all_rules() { * Gets a WP_Style_Engine_CSS_Rule object by its selector. * If the rule does not exist, it will be created. * - * @param string|WP_Style_Engine_CSS_Rule $rule The CSS selector or a WP_Style_Engine_CSS_Rule object. + * @param string|WP_Style_Engine_CSS_Rule|WP_Style_Engine_CSS_Rules_Container $rule The CSS selector or a WP_Style_Engine_CSS_Rule|WP_Style_Engine_CSS_Rules_Container object. * * @return WP_Style_Engine_CSS_Rule|void Returns a WP_Style_Engine_CSS_Rule object, or null if the selector is empty. */ @@ -118,9 +118,13 @@ public function add_rule( $rule ) { return; } - if ( $rule instanceof WP_Style_Engine_CSS_Rule ) { + $is_rules_object = $rule instanceof WP_Style_Engine_CSS_Rules_Container || $rule instanceof WP_Style_Engine_CSS_Rule; + + if ( $is_rules_object ) { $selector = $rule->get_selector(); - } else { + } + + if ( is_string( $rule ) ) { $selector = trim( $rule ); } @@ -128,11 +132,11 @@ public function add_rule( $rule ) { return; } - // Create the rule if it doesn't exist. + /* + Create a new WP_Style_Engine_CSS_Rule rule by default if it doesn't exist. + */ if ( empty( $this->rules[ $selector ] ) ) { - if ( ! empty( $selector ) ) { - $this->rules[ $selector ] = $rule instanceof WP_Style_Engine_CSS_Rule ? $rule : new WP_Style_Engine_CSS_Rule( $selector ); - } + $this->rules[ $selector ] = $is_rules_object ? $rule : new WP_Style_Engine_CSS_Rule( $selector ); } return $this->rules[ $selector ]; diff --git a/packages/style-engine/class-wp-style-engine-processor.php b/packages/style-engine/class-wp-style-engine-processor.php index fd23a4681b0e6..1dad913872a3f 100644 --- a/packages/style-engine/class-wp-style-engine-processor.php +++ b/packages/style-engine/class-wp-style-engine-processor.php @@ -63,7 +63,7 @@ public function add_store( $store ) { /** * Adds rules to be processed. * - * @param WP_Style_Engine_CSS_Rule|WP_Style_Engine_CSS_Rule[] $css_rules A single, or an array of, WP_Style_Engine_CSS_Rule objects from a store or otherwise. + * @param WP_Style_Engine_CSS_Rule|WP_Style_Engine_CSS_Rule[]|WP_Style_Engine_CSS_Rules_Container|WP_Style_Engine_CSS_Rules_Container[] $css_rules A single, or an array of, WP_Style_Engine_CSS_Rule objects from a store or otherwise. * * @return WP_Style_Engine_Processor Returns the object to allow chaining methods. */ @@ -74,23 +74,26 @@ public function add_rules( $css_rules ) { foreach ( $css_rules as $rule ) { // Check for rules that need to be nested in containers. - $container = $rule->get_container(); $selector = $rule->get_selector(); - if ( ! empty( $container ) ) { - if ( isset( $this->css_containers[ $container ] ) ) { - $this->css_containers[ $container ]->add_rules( $rule ); - } else { - $this->css_containers[ $container ] = new WP_Style_Engine_CSS_Rules_Container( $container, $rule ); - } + if ( empty( $selector ) ) { continue; } - if ( ! empty( $selector ) ) { + if ( $rule instanceof WP_Style_Engine_CSS_Rule ) { if ( isset( $this->css_rules[ $selector ] ) ) { $this->css_rules[ $selector ]->add_declarations( $rule->get_declarations() ); } else { - $this->css_rules[ $rule->get_selector() ] = $rule; + $this->css_rules[ $selector ] = $rule; + } + continue; + } + + if ( $rule instanceof WP_Style_Engine_CSS_Rules_Container ) { + if ( isset( $this->css_containers[ $selector ] ) ) { + $this->css_containers[ $selector ]->add_rules( $rule->get_rules() ); + } else { + $this->css_containers[ $selector ] = $rule; } } } diff --git a/packages/style-engine/style-engine.php b/packages/style-engine/style-engine.php index a01059414768e..449839637f986 100644 --- a/packages/style-engine/style-engine.php +++ b/packages/style-engine/style-engine.php @@ -65,7 +65,6 @@ function wp_style_engine_get_styles( $block_styles, $options = array() ) { if ( $selector ) { $new_rule = new WP_Style_Engine_CSS_Rule( $options['selector'], $css_declarations ); if ( $container ) { - $new_rule->set_container( $container ); $css_container = new WP_Style_Engine_CSS_Rules_Container( $container, $new_rule ); $styles_output['css'] = $css_container->get_css(); } else { @@ -139,8 +138,11 @@ function wp_style_engine_get_stylesheet_from_css_rules( $css_rules, $options = a } $container = $css_rule['container'] ?? null; - $new_rule = new WP_Style_Engine_CSS_Rule( $css_rule['selector'], $css_rule['declarations'] ); - $new_rule->set_container( $container ); + $new_rule = new WP_Style_Engine_CSS_Rule( $css_rule['selector'], $css_rule['declarations'] ); + + if ( $container ) { + $new_rule = new WP_Style_Engine_CSS_Rules_Container( $container, $new_rule ); + } if ( ! empty( $options['context'] ) ) { WP_Style_Engine::get_store( $options['context'] )->add_rule( $new_rule ); diff --git a/phpunit/style-engine/class-wp-style-engine-css-rules-store-test.php b/phpunit/style-engine/class-wp-style-engine-css-rules-store-test.php index 5ffc8d4b8b19c..87d2332a62a39 100644 --- a/phpunit/style-engine/class-wp-style-engine-css-rules-store-test.php +++ b/phpunit/style-engine/class-wp-style-engine-css-rules-store-test.php @@ -148,14 +148,15 @@ public function test_should_add_rule_to_existing_store() { public function test_should_add_rule_object_to_existing_store() { $new_hotdog_store = WP_Style_Engine_CSS_Rules_Store_Gutenberg::get_store( 'hotdog' ); - $selector = '.wp-block-pickle a:hover'; + $selector = '&.pickle'; $pickle_rule = new WP_Style_Engine_CSS_Rule_Gutenberg( $selector, array( 'color' => 'brown', 'border-color' => 'yellow', 'border-radius' => '10rem', ) ); - $added_rule = $new_hotdog_store->add_rule( $pickle_rule ); - $expected = $pickle_rule->get_css(); + $pickle_container = new WP_Style_Engine_CSS_Rules_Container_Gutenberg( '.hotdog', $pickle_rule ); + $added_rule = $new_hotdog_store->add_rule( $pickle_container ); + $expected = ".hotdog{&.pickle{color:brown;border-color:yellow;border-radius:10rem;}}"; $this->assertSame( $expected, $added_rule->get_css(), 'Return value of store rule get_css() matches passed rule object get_css().' ); } diff --git a/phpunit/style-engine/class-wp-style-engine-processor-test.php b/phpunit/style-engine/class-wp-style-engine-processor-test.php index facc2254df028..0081a960ffaab 100644 --- a/phpunit/style-engine/class-wp-style-engine-processor-test.php +++ b/phpunit/style-engine/class-wp-style-engine-processor-test.php @@ -44,41 +44,6 @@ public function test_should_return_rules_as_compiled_css() { ); } - /** - * Tests adding nested rules with at-rules and returning compiled CSS rules. - * - * @covers ::add_rules - * @covers ::get_css - */ - public function test_should_return_nested_rules_as_compiled_css() { - $a_nice_css_rule = new WP_Style_Engine_CSS_Rule_Gutenberg( '.a-nice-rule' ); - $a_nice_css_rule->add_declarations( - array( - 'color' => 'var(--nice-color)', - 'background-color' => 'purple', - ) - ); - $a_nice_css_rule->set_container( '@media (min-width: 80rem)' ); - - $a_nicer_css_rule = new WP_Style_Engine_CSS_Rule_Gutenberg( '.a-nicer-rule' ); - $a_nicer_css_rule->add_declarations( - array( - 'font-family' => 'Nice sans', - 'font-size' => '1em', - 'background-color' => 'purple', - ) - ); - $a_nicer_css_rule->set_container( '@layer nicety' ); - - $a_nice_processor = new WP_Style_Engine_Processor_Gutenberg(); - $a_nice_processor->add_rules( array( $a_nice_css_rule, $a_nicer_css_rule ) ); - - $this->assertSame( - '@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;}}', - $a_nice_processor->get_css( array( 'prettify' => false ) ) - ); - } - /** * Tests compiling CSS rules and formatting them with new lines and indents. * @@ -143,7 +108,8 @@ public function test_should_return_prettified_nested_css_rules() { 'background-color' => 'orange', ) ); - $a_wonderful_css_rule->set_container( '@media (min-width: 80rem)' ); + $a_wonderful_css_container = new WP_Style_Engine_CSS_Rules_Container_Gutenberg( '@media (min-width: 80rem)', $a_wonderful_css_rule ); + $a_very_wonderful_css_rule = new WP_Style_Engine_CSS_Rule_Gutenberg( '.a-very_wonderful-rule' ); $a_very_wonderful_css_rule->add_declarations( @@ -152,10 +118,11 @@ public function test_should_return_prettified_nested_css_rules() { 'background-color' => 'orange', ) ); - $a_very_wonderful_css_rule->set_container( '@layer wonderfulness' ); + $a_very_wonderful_css_container = new WP_Style_Engine_CSS_Rules_Container_Gutenberg( '@layer wonderfulness', $a_very_wonderful_css_rule ); + $a_wonderful_processor = new WP_Style_Engine_Processor_Gutenberg(); - $a_wonderful_processor->add_rules( array( $a_wonderful_css_rule, $a_very_wonderful_css_rule ) ); + $a_wonderful_processor->add_rules( array( $a_wonderful_css_container, $a_very_wonderful_css_container ) ); $expected = '@media (min-width: 80rem) { .a-wonderful-rule { @@ -394,17 +361,63 @@ public function test_should_combine_previously_added_css_rules() { ); } - public function test_should_return_store_rules_with_css_containers() { + /** + * Tests adding nested rules with at-rules and returning compiled CSS rules. + * + * @covers ::add_rules + * @covers ::get_css + */ + public function test_should_return_nested_rules_as_compiled_css() { + $a_nice_css_rule = new WP_Style_Engine_CSS_Rule_Gutenberg( '.a-nice-rule' ); + $a_nice_css_rule->add_declarations( + array( + 'color' => 'var(--nice-color)', + 'background-color' => 'purple', + ) + ); + $a_nice_css_container = new WP_Style_Engine_CSS_Rules_Container_Gutenberg( '@media (min-width: 80rem)', $a_nice_css_rule ); + + $a_nicer_css_rule = new WP_Style_Engine_CSS_Rule_Gutenberg( '.a-nicer-rule' ); + $a_nicer_css_rule->add_declarations( + array( + 'font-family' => 'Nice sans', + 'font-size' => '1em', + 'background-color' => 'purple', + ) + ); + $a_nicer_css_rule->set_container( '@layer nicety' ); + $a_nicer_css_container = new WP_Style_Engine_CSS_Rules_Container_Gutenberg( '@layer nicety', $a_nicer_css_rule ); + + $a_nice_processor = new WP_Style_Engine_Processor_Gutenberg(); + $a_nice_processor->add_rules( array( $a_nice_css_container, $a_nicer_css_container ) ); + + $this->assertSame( + '@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;}}', + $a_nice_processor->get_css( array( 'prettify' => false ) ) + ); + } + + /** + * Tests that incoming CSS rules are optimized and merged with existing CSS rules. + * + * @covers ::add_rules + * @covers ::get_rules + */ + public function test_should_return_compiled_css_with_nested_rules_last() { $panda_store = WP_Style_Engine_CSS_Rules_Store_Gutenberg::get_store( 'panda' ); // Add a nested rule. - $panda_store_rule_1 = $panda_store->add_rule( '.blueberry' )->set_container( '@container (width > 400px)' ); - $panda_store_rule_1->add_declarations( array( - 'background-color' => 'blue', - ) ); + $panda_store->add_rule( + new WP_Style_Engine_CSS_Rules_Container_Gutenberg( '@container (width > 400px)', new WP_Style_Engine_CSS_Rule_Gutenberg( + '.blueberry', + array( + 'background-color' => 'blue', + ) + ) ) ); + // Add a regular rule. - $panda_store_rule_2 = $panda_store->add_rule( '.wp-block-mushroom a:hover' ); - $panda_store_rule_2->add_declarations( array( + $panda_store_rule = $panda_store->add_rule( '.wp-block-mushroom a:hover' ); + $panda_store_rule->add_declarations( array( 'padding' => '100px', ) ); @@ -417,16 +430,15 @@ public function test_should_return_store_rules_with_css_containers() { 'Returns processed CSS rules with containers' ); - // Combine with a nested rule added directly to the processor. - $a_raspberry_rule = new WP_Style_Engine_CSS_Rule_Gutenberg( + // Combine existing nested rule with one added directly to the processor. + $a_panda_renderer->add_rules( + new WP_Style_Engine_CSS_Rules_Container_Gutenberg( '@container (width > 400px)', + new WP_Style_Engine_CSS_Rule_Gutenberg( '.raspberry', - array( - 'background-color' => 'red', - ) - ); - $a_raspberry_rule->set_container( '@container (width > 400px)' ); - - $a_panda_renderer->add_rules( $a_raspberry_rule ); + array( + 'background-color' => 'red', + ) + ) ) ); $this->assertSame( '.wp-block-mushroom a:hover{padding:100px;}@container (width > 400px){.blueberry{background-color:blue;}.raspberry{background-color:red;}}', From cafcbc64646a4b03d3c1bb83795b005079d8e949 Mon Sep 17 00:00:00 2001 From: ramon Date: Sat, 10 Feb 2024 14:24:58 +1100 Subject: [PATCH 06/20] Revert changes to WP_Style_Engine_CSS_Rule --- .../class-wp-style-engine-css-rule.php | 32 +------------------ 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/packages/style-engine/class-wp-style-engine-css-rule.php b/packages/style-engine/class-wp-style-engine-css-rule.php index 0d509130bb881..451dd94605538 100644 --- a/packages/style-engine/class-wp-style-engine-css-rule.php +++ b/packages/style-engine/class-wp-style-engine-css-rule.php @@ -15,6 +15,7 @@ * @access private */ class WP_Style_Engine_CSS_Rule { + /** * The selector. * @@ -22,13 +23,6 @@ class WP_Style_Engine_CSS_Rule { */ protected $selector; - /** - * A parent CSS selector in the case of nested CSS, or a CSS nested @rule, such as `@media (min-width: 80rem)` or `@layer module`. - * - * @var string - */ - protected $container; - /** * The selector declarations. * @@ -62,30 +56,6 @@ public function set_selector( $selector ) { return $this; } - /** - * Sets the container. - * - * @param string $container A parent CSS selector in the case of nested CSS, or a CSS nested @rule, - * such as `@media (min-width: 80rem)` or `@layer module`. - * @return WP_Style_Engine_CSS_Rule Returns the object to allow chaining of methods. - */ - public function set_container( $container ) { - if ( ! empty( $container ) ) { - $this->container = $container; - } - return $this; - } - - /** - * Gets the container. - * - * @return string Container. - */ - public function get_container() { - return $this->container; - } - - /** * Sets the declarations. * From 63ef366019fe96cdf1620ce7ea0b13b47b0eaf1c Mon Sep 17 00:00:00 2001 From: ramon Date: Sat, 10 Feb 2024 14:36:56 +1100 Subject: [PATCH 07/20] Formatting --- packages/style-engine/style-engine.php | 12 +- .../class-wp-style-engine-processor-test.php | 145 +++++++++--------- phpunit/style-engine/style-engine-test.php | 11 +- 3 files changed, 89 insertions(+), 79 deletions(-) diff --git a/packages/style-engine/style-engine.php b/packages/style-engine/style-engine.php index 449839637f986..389ace994f165 100644 --- a/packages/style-engine/style-engine.php +++ b/packages/style-engine/style-engine.php @@ -57,15 +57,15 @@ function wp_style_engine_get_styles( $block_styles, $options = array() ) { $styles_output = array(); if ( ! empty( $parsed_styles['declarations'] ) ) { - $container = $options['container'] ?? null; - $selector = $options['selector'] ?? null; - $css_declarations = new WP_Style_Engine_CSS_Declarations( $parsed_styles['declarations'] ); - $new_rule = null; + $container = $options['container'] ?? null; + $selector = $options['selector'] ?? null; + $css_declarations = new WP_Style_Engine_CSS_Declarations( $parsed_styles['declarations'] ); + $new_rule = null; if ( $selector ) { $new_rule = new WP_Style_Engine_CSS_Rule( $options['selector'], $css_declarations ); if ( $container ) { - $css_container = new WP_Style_Engine_CSS_Rules_Container( $container, $new_rule ); + $css_container = new WP_Style_Engine_CSS_Rules_Container( $container, $new_rule ); $styles_output['css'] = $css_container->get_css(); } else { $styles_output['css'] = $new_rule->get_css(); @@ -138,7 +138,7 @@ function wp_style_engine_get_stylesheet_from_css_rules( $css_rules, $options = a } $container = $css_rule['container'] ?? null; - $new_rule = new WP_Style_Engine_CSS_Rule( $css_rule['selector'], $css_rule['declarations'] ); + $new_rule = new WP_Style_Engine_CSS_Rule( $css_rule['selector'], $css_rule['declarations'] ); if ( $container ) { $new_rule = new WP_Style_Engine_CSS_Rules_Container( $container, $new_rule ); diff --git a/phpunit/style-engine/class-wp-style-engine-processor-test.php b/phpunit/style-engine/class-wp-style-engine-processor-test.php index 0081a960ffaab..f45194f3c3a88 100644 --- a/phpunit/style-engine/class-wp-style-engine-processor-test.php +++ b/phpunit/style-engine/class-wp-style-engine-processor-test.php @@ -95,54 +95,6 @@ public function test_should_return_prettified_css_rules() { ); } - /** - * Tests compiling nested CSS rules and formatting them with new lines and indents. - * - * @covers ::get_css - */ - public function test_should_return_prettified_nested_css_rules() { - $a_wonderful_css_rule = new WP_Style_Engine_CSS_Rule_Gutenberg( '.a-wonderful-rule' ); - $a_wonderful_css_rule->add_declarations( - array( - 'color' => 'var(--wonderful-color)', - 'background-color' => 'orange', - ) - ); - $a_wonderful_css_container = new WP_Style_Engine_CSS_Rules_Container_Gutenberg( '@media (min-width: 80rem)', $a_wonderful_css_rule ); - - - $a_very_wonderful_css_rule = new WP_Style_Engine_CSS_Rule_Gutenberg( '.a-very_wonderful-rule' ); - $a_very_wonderful_css_rule->add_declarations( - array( - 'color' => 'var(--wonderful-color)', - 'background-color' => 'orange', - ) - ); - $a_very_wonderful_css_container = new WP_Style_Engine_CSS_Rules_Container_Gutenberg( '@layer wonderfulness', $a_very_wonderful_css_rule ); - - - $a_wonderful_processor = new WP_Style_Engine_Processor_Gutenberg(); - $a_wonderful_processor->add_rules( array( $a_wonderful_css_container, $a_very_wonderful_css_container ) ); - - $expected = '@media (min-width: 80rem) { - .a-wonderful-rule { - color: var(--wonderful-color); - background-color: orange; - } -} -@layer wonderfulness { - .a-very_wonderful-rule { - color: var(--wonderful-color); - background-color: orange; - } -} -'; - $this->assertSame( - $expected, - $a_wonderful_processor->get_css( array( 'prettify' => true ) ) - ); - } - /** * Tests adding a store and compiling CSS rules from that store. * @@ -398,7 +350,55 @@ public function test_should_return_nested_rules_as_compiled_css() { } /** - * Tests that incoming CSS rules are optimized and merged with existing CSS rules. + * Tests compiling nested CSS rules and formatting them with new lines and indents. + * + * @covers ::get_css + */ + public function test_should_return_prettified_nested_css_rules() { + // Create nested CSS rule 1. + $a_wonderful_css_rule = new WP_Style_Engine_CSS_Rule_Gutenberg( '.a-wonderful-rule' ); + $a_wonderful_css_rule->add_declarations( + array( + 'color' => 'var(--wonderful-color)', + 'background-color' => 'orange', + ) + ); + $a_wonderful_css_container = new WP_Style_Engine_CSS_Rules_Container_Gutenberg( '@media (min-width: 80rem)', $a_wonderful_css_rule ); + + // Create nested CSS rule 2. + $a_very_wonderful_css_rule = new WP_Style_Engine_CSS_Rule_Gutenberg( '.a-very_wonderful-rule' ); + $a_very_wonderful_css_rule->add_declarations( + array( + 'color' => 'var(--wonderful-color)', + 'background-color' => 'orange', + ) + ); + $a_very_wonderful_css_container = new WP_Style_Engine_CSS_Rules_Container_Gutenberg( '@layer wonderfulness', $a_very_wonderful_css_rule ); + + $a_wonderful_processor = new WP_Style_Engine_Processor_Gutenberg(); + $a_wonderful_processor->add_rules( array( $a_wonderful_css_container, $a_very_wonderful_css_container ) ); + + $expected = '@media (min-width: 80rem) { + .a-wonderful-rule { + color: var(--wonderful-color); + background-color: orange; + } +} +@layer wonderfulness { + .a-very_wonderful-rule { + color: var(--wonderful-color); + background-color: orange; + } +} +'; + $this->assertSame( + $expected, + $a_wonderful_processor->get_css( array( 'prettify' => true ) ) + ); + } + + /** + * Tests that incoming CSS rules are merged with existing CSS rules and that containers appear at the end of the CSS output. * * @covers ::add_rules * @covers ::get_rules @@ -407,43 +407,50 @@ public function test_should_return_compiled_css_with_nested_rules_last() { $panda_store = WP_Style_Engine_CSS_Rules_Store_Gutenberg::get_store( 'panda' ); // Add a nested rule. $panda_store->add_rule( - new WP_Style_Engine_CSS_Rules_Container_Gutenberg( '@container (width > 400px)', new WP_Style_Engine_CSS_Rule_Gutenberg( - '.blueberry', - array( - 'background-color' => 'blue', + new WP_Style_Engine_CSS_Rules_Container_Gutenberg( + '@container (width > 400px)', + new WP_Style_Engine_CSS_Rule_Gutenberg( + '.blueberry', + array( + 'background-color' => 'blue', + ) ) - ) ) ); - + ) + ); // Add a regular rule. - $panda_store_rule = $panda_store->add_rule( '.wp-block-mushroom a:hover' ); - $panda_store_rule->add_declarations( array( - 'padding' => '100px', - ) ); - + $panda_store_rule = $panda_store->add_rule( '.wp-block-mushroom a:hover' ); + $panda_store_rule->add_declarations( + array( + 'padding' => '100px', + ) + ); $a_panda_renderer = new WP_Style_Engine_Processor_Gutenberg(); $a_panda_renderer->add_store( $panda_store ); $this->assertSame( '.wp-block-mushroom a:hover{padding:100px;}@container (width > 400px){.blueberry{background-color:blue;}}', $a_panda_renderer->get_css( array( 'prettify' => false ) ), - 'Returns processed CSS rules with containers' + 'Returns processed CSS rules with containers and containers at the end of the CSS output.' ); // Combine existing nested rule with one added directly to the processor. $a_panda_renderer->add_rules( - new WP_Style_Engine_CSS_Rules_Container_Gutenberg( '@container (width > 400px)', + new WP_Style_Engine_CSS_Rules_Container_Gutenberg( + '@container (width > 400px)', new WP_Style_Engine_CSS_Rule_Gutenberg( - '.raspberry', - array( - 'background-color' => 'red', - ) - ) ) ); + '.raspberry', + array( + 'background-color' => 'red', + ) + ) + ) + ); $this->assertSame( '.wp-block-mushroom a:hover{padding:100px;}@container (width > 400px){.blueberry{background-color:blue;}.raspberry{background-color:red;}}', $a_panda_renderer->get_css( array( 'prettify' => false ) ), - 'Returns processed CSS rules with rules added to containers' + 'Returns processed CSS rules with rules added to containers.' ); $expected_prettified = '.wp-block-mushroom a:hover { @@ -461,7 +468,7 @@ public function test_should_return_compiled_css_with_nested_rules_last() { $this->assertSame( $expected_prettified, $a_panda_renderer->get_css( array( 'prettify' => true ) ), - 'Returns prettified processed CSS rules' + 'Returns prettified processed CSS rules and nested rules' ); } } diff --git a/phpunit/style-engine/style-engine-test.php b/phpunit/style-engine/style-engine-test.php index 3c9f6c84cb3e8..d2b25e1d18d38 100644 --- a/phpunit/style-engine/style-engine-test.php +++ b/phpunit/style-engine/style-engine-test.php @@ -236,7 +236,7 @@ public function data_wp_style_engine_get_styles() { ), ), - 'style_block_with_nested_selector' => array( + 'style_block_with_nested_selector' => array( 'block_styles' => array( 'spacing' => array( 'padding' => array( @@ -247,7 +247,10 @@ public function data_wp_style_engine_get_styles() { ), ), ), - 'options' => array( 'selector' => '.wp-selector > p', 'container' => '@layer sandwich' ), + 'options' => array( + 'selector' => '.wp-selector > p', + 'container' => '@layer sandwich', + ), 'expected_output' => array( 'css' => '@layer sandwich{.wp-selector > p{padding-top:42px;padding-left:2%;padding-bottom:44px;padding-right:5rem;}}', 'declarations' => array( @@ -709,8 +712,8 @@ public function test_should_return_stylesheet_with_combined_nested_css_rules_pri 'selector' => '.saruman', 'container' => '@container (min-width: 700px)', 'declarations' => array( - 'color' => 'black', - 'font-family' => 'The-Great-Eye', + 'color' => 'black', + 'font-family' => 'The-Great-Eye', ), ), array( From 0514b536e8cf9ba35c06148be996e988b3d89605 Mon Sep 17 00:00:00 2001 From: ramon Date: Sat, 10 Feb 2024 15:15:31 +1100 Subject: [PATCH 08/20] Added container tests --- ...ss-wp-style-engine-css-rules-container.php | 8 +- ...-style-engine-css-rules-container-test.php | 193 ++++++++++++++++++ ...s-wp-style-engine-css-rules-store-test.php | 4 +- .../class-wp-style-engine-processor-test.php | 1 - 4 files changed, 201 insertions(+), 5 deletions(-) create mode 100644 phpunit/style-engine/class-wp-style-engine-css-rules-container-test.php diff --git a/packages/style-engine/class-wp-style-engine-css-rules-container.php b/packages/style-engine/class-wp-style-engine-css-rules-container.php index 318ff6006c1b3..163633adaa554 100644 --- a/packages/style-engine/class-wp-style-engine-css-rules-container.php +++ b/packages/style-engine/class-wp-style-engine-css-rules-container.php @@ -36,9 +36,9 @@ class WP_Style_Engine_CSS_Rules_Container { * * @param string $selector A parent CSS selector in the case of nested CSS, or a CSS nested @rule, * such as `@media (min-width: 80rem)` or `@layer module`. - * @param WP_Style_Engine_CSS_Rule[]|WP_Style_Engine_CSS_Rule $rule A WP_Style_Engine_CSS_Rule object. + * @param WP_Style_Engine_CSS_Rule[]|WP_Style_Engine_CSS_Rule $rule Optional. A WP_Style_Engine_CSS_Rule object. */ - public function __construct( $selector = '', $rule ) { + public function __construct( $selector = '', $rule = array() ) { $this->set_selector( $selector ); $this->add_rules( $rule ); } @@ -94,6 +94,10 @@ public function get_rule( $selector ) { * @return WP_Style_Engine_CSS_Rules_Container Returns the object to allow chaining of methods. */ public function add_rules( $rules ) { + if ( empty( $rules ) ) { + return $this; + } + if ( ! is_array( $rules ) ) { $rules = array( $rules ); } diff --git a/phpunit/style-engine/class-wp-style-engine-css-rules-container-test.php b/phpunit/style-engine/class-wp-style-engine-css-rules-container-test.php new file mode 100644 index 0000000000000..5f0a9d52ddf94 --- /dev/null +++ b/phpunit/style-engine/class-wp-style-engine-css-rules-container-test.php @@ -0,0 +1,193 @@ + '50% 50%', + 'color' => 'green', + ) + ); + $css_container = new WP_Style_Engine_CSS_Rules_Container_Gutenberg( $container_selector, $css_rule ); + + $this->assertSame( $container_selector, $css_container->get_selector(), 'Return value of get_selector() does not match value passed to constructor.' ); + + $expected = "$container_selector{{$css_rule->get_css()}}"; + + $this->assertSame( $expected, $css_container->get_css(), 'Value returned by get_css() does not match expected CSS string.' ); + } + + /** + * Tests that only WP_Style_Engine_CSS_Rule can be added to a container. + * + * @covers ::add_rules + * @covers ::get_rules + */ + public function test_should_only_add_rule_objects() { + $css_container = new WP_Style_Engine_CSS_Rules_Container_Gutenberg( '@media not all and (hover: hover)' ); + $selector = '.goanna'; + $css_rule_1 = new WP_Style_Engine_CSS_Rule_Gutenberg( + $selector, + array( + 'font-size' => '2rem', + ) + ); + $css_container->add_rules( '' ); + $this->assertSame( array(), $css_container->get_rules(), 'Return value of get_rules() does not match expected rules.' ); + } + + /** + * Tests that nested rule declaration properties are deduplicated. + * + * @covers ::add_rules + * @covers ::get_css + */ + public function test_should_dedupe_properties_in_rules() { + $css_container = new WP_Style_Engine_CSS_Rules_Container_Gutenberg( '@media not all and (hover: hover)' ); + $selector = '.goanna'; + $css_rule_1 = new WP_Style_Engine_CSS_Rule_Gutenberg( + $selector, + array( + 'font-size' => '2rem', + ) + ); + $css_container->add_rules( $css_rule_1 ); + + $this->assertSame( '@media not all and (hover: hover){.goanna{font-size:2rem;}}', $css_container->get_css(), 'Return value of get_css() does not match expected CSS container CSS.' ); + + $css_rule_2 = new WP_Style_Engine_CSS_Rule_Gutenberg( + $selector, + array( + 'font-size' => '4px', + ) + ); + $css_container->add_rules( $css_rule_2 ); + + $this->assertSame( '@media not all and (hover: hover){.goanna{font-size:4px;}}', $css_container->get_css(), 'Return value of get_css() does not match expected value with overwritten rule declaration.' ); + } + + /** + * Tests that rules can be added to existing containers. + * + * @covers ::add_rules + * @covers ::get_rule + * @covers ::get_css + */ + public function test_should_add_rules_to_existing_containers() { + $css_container = new WP_Style_Engine_CSS_Rules_Container_Gutenberg( '@media screen, print' ); + + $css_rule_1 = new WP_Style_Engine_CSS_Rule_Gutenberg( + 'body', + array( + 'line-height' => '0.1', + ) + ); + $css_container->add_rules( $css_rule_1 ); + + $css_rule_2 = new WP_Style_Engine_CSS_Rule_Gutenberg( + 'p', + array( + 'line-height' => '0.9', + ) + ); + $css_container->add_rules( $css_rule_2 ); + + $this->assertEquals( $css_rule_2, $css_container->get_rule( 'p' ), 'Return value of get_rule() does not match expected value.' ); + + $expected = '@media screen, print{body{line-height:0.1;}p{line-height:0.9;}}'; + + $this->assertSame( $expected, $css_container->get_css(), 'Return value of get_css() does not match expected value.' ); + } + + /** + * Tests setting a selector to a container. + * + * @covers ::set_selector + */ + public function test_should_set_selector() { + $css_container = new WP_Style_Engine_CSS_Rules_Container_Gutenberg( '@layer state' ); + + $this->assertSame( '@layer state', $css_container->get_selector(), 'Return value of get_selector() does not match value passed to constructor.' ); + + $css_container->set_selector( '@layer pony' ); + + $this->assertSame( '@layer pony', $css_container->get_selector(), 'Return value of get_selector() does not match value passed to set_selector().' ); + } + + /** + * Tests generating a CSS rule string. + * + * @covers ::get_css + */ + public function test_should_generate_css_rule_string() { + $selector = '.chips'; + $input_declarations = array( + 'margin-top' => '10px', + 'font-size' => '2rem', + ); + $css_declarations = new WP_Style_Engine_CSS_Declarations_Gutenberg( $input_declarations ); + $css_rule = new WP_Style_Engine_CSS_Rule_Gutenberg( $selector, $css_declarations ); + $css_container = new WP_Style_Engine_CSS_Rules_Container_Gutenberg( '@layer sauce', $css_rule ); + $expected = "@layer sauce{{$selector}{{$css_declarations->get_declarations_string()}}}"; + + $this->assertSame( $expected, $css_container->get_css() ); + } + + /** + * Tests that an empty string will be returned where there are no rules in a CSS container. + * + * @covers ::get_css + */ + public function test_should_return_empty_string_with_no_rules() { + $selector = '.holmes'; + $input_declarations = array(); + $css_declarations = new WP_Style_Engine_CSS_Declarations_Gutenberg( $input_declarations ); + $css_rule = new WP_Style_Engine_CSS_Rule_Gutenberg( $selector, $css_declarations ); + $css_container = new WP_Style_Engine_CSS_Rules_Container_Gutenberg( '@layer sauce', $css_rule ); + + $this->assertSame( '', $css_container->get_css() ); + } + + /** + * Tests that CSS containers are prettified. + * + * @covers ::get_css + */ + public function test_should_prettify_css_rule_output() { + $selector = '.baptiste'; + $input_declarations = array( + 'margin-left' => '0', + 'font-family' => 'Detective Sans', + ); + $css_declarations = new WP_Style_Engine_CSS_Declarations_Gutenberg( $input_declarations ); + $css_rule = new WP_Style_Engine_CSS_Rule_Gutenberg( $selector, $css_declarations ); + $expected = '@container (width < 650px) { + .baptiste { + margin-left: 0; + font-family: Detective Sans; + } +}'; + $css_container = new WP_Style_Engine_CSS_Rules_Container_Gutenberg( '@container (width < 650px)', $css_rule ); + + $this->assertSame( $expected, $css_container->get_css( true ) ); + } +} diff --git a/phpunit/style-engine/class-wp-style-engine-css-rules-store-test.php b/phpunit/style-engine/class-wp-style-engine-css-rules-store-test.php index 87d2332a62a39..83d93ef62122d 100644 --- a/phpunit/style-engine/class-wp-style-engine-css-rules-store-test.php +++ b/phpunit/style-engine/class-wp-style-engine-css-rules-store-test.php @@ -148,7 +148,7 @@ public function test_should_add_rule_to_existing_store() { public function test_should_add_rule_object_to_existing_store() { $new_hotdog_store = WP_Style_Engine_CSS_Rules_Store_Gutenberg::get_store( 'hotdog' ); - $selector = '&.pickle'; + $selector = '.pickle'; $pickle_rule = new WP_Style_Engine_CSS_Rule_Gutenberg( $selector, array( 'color' => 'brown', 'border-color' => 'yellow', @@ -156,7 +156,7 @@ public function test_should_add_rule_object_to_existing_store() { ) ); $pickle_container = new WP_Style_Engine_CSS_Rules_Container_Gutenberg( '.hotdog', $pickle_rule ); $added_rule = $new_hotdog_store->add_rule( $pickle_container ); - $expected = ".hotdog{&.pickle{color:brown;border-color:yellow;border-radius:10rem;}}"; + $expected = ".hotdog{.pickle{color:brown;border-color:yellow;border-radius:10rem;}}"; $this->assertSame( $expected, $added_rule->get_css(), 'Return value of store rule get_css() matches passed rule object get_css().' ); } diff --git a/phpunit/style-engine/class-wp-style-engine-processor-test.php b/phpunit/style-engine/class-wp-style-engine-processor-test.php index f45194f3c3a88..ba74713ec44f0 100644 --- a/phpunit/style-engine/class-wp-style-engine-processor-test.php +++ b/phpunit/style-engine/class-wp-style-engine-processor-test.php @@ -337,7 +337,6 @@ public function test_should_return_nested_rules_as_compiled_css() { 'background-color' => 'purple', ) ); - $a_nicer_css_rule->set_container( '@layer nicety' ); $a_nicer_css_container = new WP_Style_Engine_CSS_Rules_Container_Gutenberg( '@layer nicety', $a_nicer_css_rule ); $a_nice_processor = new WP_Style_Engine_Processor_Gutenberg(); From 332bc868f2f2657a1103a5481721d66050d5e81d Mon Sep 17 00:00:00 2001 From: ramon Date: Sat, 10 Feb 2024 15:24:01 +1100 Subject: [PATCH 09/20] linting --- .../class-wp-style-engine-processor.php | 2 +- ...-wp-style-engine-css-rules-container-test.php | 16 ++++++++-------- ...lass-wp-style-engine-css-rules-store-test.php | 15 +++++++++------ 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/packages/style-engine/class-wp-style-engine-processor.php b/packages/style-engine/class-wp-style-engine-processor.php index 1dad913872a3f..41541ab4c3b35 100644 --- a/packages/style-engine/class-wp-style-engine-processor.php +++ b/packages/style-engine/class-wp-style-engine-processor.php @@ -74,7 +74,7 @@ public function add_rules( $css_rules ) { foreach ( $css_rules as $rule ) { // Check for rules that need to be nested in containers. - $selector = $rule->get_selector(); + $selector = $rule->get_selector(); if ( empty( $selector ) ) { continue; diff --git a/phpunit/style-engine/class-wp-style-engine-css-rules-container-test.php b/phpunit/style-engine/class-wp-style-engine-css-rules-container-test.php index 5f0a9d52ddf94..5a9b3b6a27a6d 100644 --- a/phpunit/style-engine/class-wp-style-engine-css-rules-container-test.php +++ b/phpunit/style-engine/class-wp-style-engine-css-rules-container-test.php @@ -44,15 +44,15 @@ public function test_should_instantiate_with_selector_and_rules() { */ public function test_should_only_add_rule_objects() { $css_container = new WP_Style_Engine_CSS_Rules_Container_Gutenberg( '@media not all and (hover: hover)' ); - $selector = '.goanna'; - $css_rule_1 = new WP_Style_Engine_CSS_Rule_Gutenberg( - $selector, - array( - 'font-size' => '2rem', - ) - ); + $css_container->add_rules( '' ); - $this->assertSame( array(), $css_container->get_rules(), 'Return value of get_rules() does not match expected rules.' ); + $this->assertSame( array(), $css_container->get_rules(), 'Return value of get_rules() does not match expected rules when empty string added.' ); + + $css_container->add_rules( array() ); + $this->assertSame( array(), $css_container->get_rules(), 'Return value of get_rules() does not match expected rules when array() added.' ); + + $css_container->add_rules( new WP_Style_Engine_CSS_Rules_Container_Gutenberg( '@media not all and (hover: hover)' ) ); + $this->assertSame( array(), $css_container->get_rules(), 'Return value of get_rules() does not match expected rules when unknown class added.' ); } /** diff --git a/phpunit/style-engine/class-wp-style-engine-css-rules-store-test.php b/phpunit/style-engine/class-wp-style-engine-css-rules-store-test.php index 83d93ef62122d..2253af34de373 100644 --- a/phpunit/style-engine/class-wp-style-engine-css-rules-store-test.php +++ b/phpunit/style-engine/class-wp-style-engine-css-rules-store-test.php @@ -149,14 +149,17 @@ public function test_should_add_rule_object_to_existing_store() { $new_hotdog_store = WP_Style_Engine_CSS_Rules_Store_Gutenberg::get_store( 'hotdog' ); $selector = '.pickle'; - $pickle_rule = new WP_Style_Engine_CSS_Rule_Gutenberg( $selector, array( - 'color' => 'brown', - 'border-color' => 'yellow', - 'border-radius' => '10rem', - ) ); + $pickle_rule = new WP_Style_Engine_CSS_Rule_Gutenberg( + $selector, + array( + 'color' => 'brown', + 'border-color' => 'yellow', + 'border-radius' => '10rem', + ) + ); $pickle_container = new WP_Style_Engine_CSS_Rules_Container_Gutenberg( '.hotdog', $pickle_rule ); $added_rule = $new_hotdog_store->add_rule( $pickle_container ); - $expected = ".hotdog{.pickle{color:brown;border-color:yellow;border-radius:10rem;}}"; + $expected = '.hotdog{.pickle{color:brown;border-color:yellow;border-radius:10rem;}}'; $this->assertSame( $expected, $added_rule->get_css(), 'Return value of store rule get_css() matches passed rule object get_css().' ); } From 63b29de4626fc368888112834a3fa6e50e226335 Mon Sep 17 00:00:00 2001 From: ramon Date: Sat, 10 Feb 2024 15:47:07 +1100 Subject: [PATCH 10/20] Added empty test --- .../class-wp-style-engine-css-rules-container.php | 2 +- ...class-wp-style-engine-css-rules-container-test.php | 11 ++++------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/packages/style-engine/class-wp-style-engine-css-rules-container.php b/packages/style-engine/class-wp-style-engine-css-rules-container.php index 163633adaa554..82f9310a1a770 100644 --- a/packages/style-engine/class-wp-style-engine-css-rules-container.php +++ b/packages/style-engine/class-wp-style-engine-css-rules-container.php @@ -106,7 +106,7 @@ public function add_rules( $rules ) { if ( ! $rule instanceof WP_Style_Engine_CSS_Rule ) { _doing_it_wrong( __METHOD__, - __( 'Rules passed to WP_Style_Engine_CSS_Rules_Containermust be an instance of WP_Style_Engine_CSS_Rule', 'default' ), + __( 'Rules passed to WP_Style_Engine_CSS_Rules_Container must be an instance of WP_Style_Engine_CSS_Rule', 'default' ), '6.6.0' ); continue; diff --git a/phpunit/style-engine/class-wp-style-engine-css-rules-container-test.php b/phpunit/style-engine/class-wp-style-engine-css-rules-container-test.php index 5a9b3b6a27a6d..b08d2b33f36f3 100644 --- a/phpunit/style-engine/class-wp-style-engine-css-rules-container-test.php +++ b/phpunit/style-engine/class-wp-style-engine-css-rules-container-test.php @@ -37,22 +37,19 @@ public function test_should_instantiate_with_selector_and_rules() { } /** - * Tests that only WP_Style_Engine_CSS_Rule can be added to a container. + * Tests that empty values cannot be added. * * @covers ::add_rules * @covers ::get_rules */ - public function test_should_only_add_rule_objects() { + public function test_cannot_add_empty_values() { $css_container = new WP_Style_Engine_CSS_Rules_Container_Gutenberg( '@media not all and (hover: hover)' ); $css_container->add_rules( '' ); - $this->assertSame( array(), $css_container->get_rules(), 'Return value of get_rules() does not match expected rules when empty string added.' ); + $this->assertEmpty( $css_container->get_rules(), 'Return value of get_rules() does not match expected rules when empty string added.' ); $css_container->add_rules( array() ); - $this->assertSame( array(), $css_container->get_rules(), 'Return value of get_rules() does not match expected rules when array() added.' ); - - $css_container->add_rules( new WP_Style_Engine_CSS_Rules_Container_Gutenberg( '@media not all and (hover: hover)' ) ); - $this->assertSame( array(), $css_container->get_rules(), 'Return value of get_rules() does not match expected rules when unknown class added.' ); + $this->assertEmpty( $css_container->get_rules(), 'Return value of get_rules() does not match expected rules when array() added.' ); } /** From e6be2cc5def3d16dc6964aa327b9e6a4e7f637f9 Mon Sep 17 00:00:00 2001 From: ramon Date: Sat, 10 Feb 2024 15:49:58 +1100 Subject: [PATCH 11/20] Format --- .../style-engine/class-wp-style-engine-css-rules-container.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/style-engine/class-wp-style-engine-css-rules-container.php b/packages/style-engine/class-wp-style-engine-css-rules-container.php index 82f9310a1a770..494e093c715c2 100644 --- a/packages/style-engine/class-wp-style-engine-css-rules-container.php +++ b/packages/style-engine/class-wp-style-engine-css-rules-container.php @@ -112,7 +112,7 @@ public function add_rules( $rules ) { continue; } - $selector = $rule->get_selector(); + $selector = $rule->get_selector(); if ( isset( $this->rules[ $selector ] ) ) { $this->rules[ $selector ]->add_declarations( $rule->get_declarations() ); From 66063d3c1d43a41ad35dda88f975375bae7d09f1 Mon Sep 17 00:00:00 2001 From: ramon Date: Sat, 10 Feb 2024 15:57:43 +1100 Subject: [PATCH 12/20] Adding more test examples for wp_engine_get_stylesheet_from_css_rules --- phpunit/style-engine/style-engine-test.php | 29 ++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/phpunit/style-engine/style-engine-test.php b/phpunit/style-engine/style-engine-test.php index d2b25e1d18d38..a08c1931aac6e 100644 --- a/phpunit/style-engine/style-engine-test.php +++ b/phpunit/style-engine/style-engine-test.php @@ -616,6 +616,26 @@ public function test_should_not_store_block_styles_without_context() { */ public function test_should_get_stored_stylesheet_from_context() { $css_rules = array( + array( + 'selector' => '.pippin', + 'container' => '@container (min-width: 700px)', + 'declarations' => array( + 'color' => 'brown', + 'height' => '12px', + 'width' => '15px', + 'border-style' => 'dashed', + ), + ), + array( + 'selector' => '.merry', + 'container' => '@container (min-width: 700px)', + 'declarations' => array( + 'color' => 'khaki', + 'height' => '23px', + 'width' => '23px', + 'border-style' => 'solid', + ), + ), array( 'selector' => '.frodo', 'declarations' => array( @@ -634,7 +654,16 @@ public function test_should_get_stored_stylesheet_from_context() { 'border-style' => 'solid', ), ), + array( + 'selector' => '.pippin', + 'container' => '@container (min-width: 700px)', + 'declarations' => array( + 'color' => 'tan', + ), + ), ); + + // Unit tests for the `wp_style_engine_get_stylesheet_from_css_rules` function below. $compiled_stylesheet = gutenberg_style_engine_get_stylesheet_from_css_rules( $css_rules, array( From 081de82ccb9d0d9e510aeb14185910f1e5de8b35 Mon Sep 17 00:00:00 2001 From: ramon Date: Sat, 10 Feb 2024 15:59:09 +1100 Subject: [PATCH 13/20] Remove TODOs Store combines existing rules Store combines --- lib/load.php | 2 +- .../class-wp-style-engine-css-rule.php | 10 ++- ...ss-wp-style-engine-css-rules-container.php | 39 +++------ .../class-wp-style-engine-css-rules-store.php | 9 +- .../class-wp-style-engine-processor.php | 17 ++-- packages/style-engine/style-engine.php | 30 +++++-- ...s-wp-style-engine-css-rules-store-test.php | 47 ++++++++++ phpunit/style-engine/style-engine-test.php | 85 +++++++++++++++++-- 8 files changed, 184 insertions(+), 55 deletions(-) diff --git a/lib/load.php b/lib/load.php index 3ff41e72709fd..736a26f31d9ec 100644 --- a/lib/load.php +++ b/lib/load.php @@ -195,9 +195,9 @@ function gutenberg_is_experiment_enabled( $name ) { // Copied package PHP files. if ( is_dir( __DIR__ . '/../build/style-engine' ) ) { - require_once __DIR__ . '/../build/style-engine/class-wp-style-engine-css-rules-container-gutenberg.php'; require_once __DIR__ . '/../build/style-engine/class-wp-style-engine-css-declarations-gutenberg.php'; require_once __DIR__ . '/../build/style-engine/class-wp-style-engine-css-rule-gutenberg.php'; + require_once __DIR__ . '/../build/style-engine/class-wp-style-engine-css-rules-container-gutenberg.php'; require_once __DIR__ . '/../build/style-engine/class-wp-style-engine-css-rules-store-gutenberg.php'; require_once __DIR__ . '/../build/style-engine/class-wp-style-engine-processor-gutenberg.php'; require_once __DIR__ . '/../build/style-engine/class-wp-style-engine-gutenberg.php'; diff --git a/packages/style-engine/class-wp-style-engine-css-rule.php b/packages/style-engine/class-wp-style-engine-css-rule.php index 451dd94605538..c319225e4249e 100644 --- a/packages/style-engine/class-wp-style-engine-css-rule.php +++ b/packages/style-engine/class-wp-style-engine-css-rule.php @@ -52,7 +52,9 @@ public function __construct( $selector = '', $declarations = array() ) { * @return WP_Style_Engine_CSS_Rule Returns the object to allow chaining of methods. */ public function set_selector( $selector ) { - $this->selector = $selector; + if ( ! empty( $selector ) ) { + $this->selector = $selector; + } return $this; } @@ -65,6 +67,9 @@ public function set_selector( $selector ) { * @return WP_Style_Engine_CSS_Rule Returns the object to allow chaining of methods. */ public function add_declarations( $declarations ) { + if ( empty( $declarations ) ) { + return $this; + } $is_declarations_object = ! is_array( $declarations ); $declarations_array = $is_declarations_object ? $declarations->get_declarations() : $declarations; @@ -114,7 +119,8 @@ public function get_css( $should_prettify = false, $indent_count = 0 ) { // Trims any multiple selectors strings. $selector = $should_prettify ? implode( ',', array_map( 'trim', explode( ',', $this->get_selector() ) ) ) : $this->get_selector(); $selector = $should_prettify ? str_replace( array( ',' ), ",\n", $selector ) : $selector; - $css_declarations = $this->declarations->get_declarations_string( $should_prettify, $declarations_indent ); + $css_declarations = ! empty( $this->declarations ) ? $this->declarations->get_declarations_string( $should_prettify, $declarations_indent ) : ''; + if ( empty( $css_declarations ) ) { return ''; diff --git a/packages/style-engine/class-wp-style-engine-css-rules-container.php b/packages/style-engine/class-wp-style-engine-css-rules-container.php index 494e093c715c2..c2ffb712f9c75 100644 --- a/packages/style-engine/class-wp-style-engine-css-rules-container.php +++ b/packages/style-engine/class-wp-style-engine-css-rules-container.php @@ -6,15 +6,14 @@ * * @package Gutenberg */ -// @TODO add unit tests -// @TODO Should have same/similar interface to WP_Style_Engine_CSS_Rule or maybe inherits from WP_Style_Engine_CSS_Rule? + if ( ! class_exists( 'WP_Style_Engine_CSS_Rules_Container' ) ) { /** * Holds, sanitizes, processes and prints nested CSS rules for the Style Engine. * * @access private */ - class WP_Style_Engine_CSS_Rules_Container { + class WP_Style_Engine_CSS_Rules_Container extends WP_Style_Engine_CSS_Rule { /** * A parent CSS selector in the case of nested CSS, or a CSS nested @rule, such as `@media (min-width: 80rem)` or `@layer module` * @@ -22,6 +21,15 @@ class WP_Style_Engine_CSS_Rules_Container { */ protected $selector; + /** + * The selector declarations. + * + * Contains a WP_Style_Engine_CSS_Declarations object. + * + * @var WP_Style_Engine_CSS_Declarations + */ + protected $declarations; + /** * The container declarations. * @@ -43,30 +51,6 @@ public function __construct( $selector = '', $rule = array() ) { $this->add_rules( $rule ); } - /** - * Sets the selector/container name. - * - * @param string $selector A parent CSS selector in the case of nested CSS, or a CSS nested @rule, - * such as `@media (min-width: 80rem)` or `@layer module`. - * - * @return WP_Style_Engine_CSS_Rules_Container Returns the object to allow chaining of methods. - */ - public function set_selector( $selector ) { - if ( ! empty( $selector ) ) { - $this->selector = $selector; - } - return $this; - } - - /** - * Gets the selector/container name. - * - * @return string Container. - */ - public function get_selector() { - return $this->selector; - } - /** * Gets all nested rules. * @@ -137,6 +121,7 @@ public function get_css( $should_prettify = false, $indent_count = 0 ) { $indent_count = $should_prettify ? $indent_count + 1 : $indent_count; $new_line = $should_prettify ? "\n" : ''; $spacer = $should_prettify ? ' ' : ''; + $css .= ! empty( $this->declarations ) ? $this->declarations->get_declarations_string( $should_prettify, $indent_count ) : ''; foreach ( $this->rules as $rule ) { $css .= $rule->get_css( $should_prettify, $indent_count ); diff --git a/packages/style-engine/class-wp-style-engine-css-rules-store.php b/packages/style-engine/class-wp-style-engine-css-rules-store.php index a828aa9f8355b..8043b9d71d6ca 100644 --- a/packages/style-engine/class-wp-style-engine-css-rules-store.php +++ b/packages/style-engine/class-wp-style-engine-css-rules-store.php @@ -135,7 +135,14 @@ public function add_rule( $rule ) { /* Create a new WP_Style_Engine_CSS_Rule rule by default if it doesn't exist. */ - if ( empty( $this->rules[ $selector ] ) ) { + if ( isset( $this->rules[ $selector ] ) ) { + if ( $rule instanceof WP_Style_Engine_CSS_Rules_Container ) { + $this->rules[ $selector ]->add_rules( $rule->get_rules() ); + } + if ( $is_rules_object ) { + $this->rules[ $selector ]->add_declarations( $rule->get_declarations() ); + } + } else { $this->rules[ $selector ] = $is_rules_object ? $rule : new WP_Style_Engine_CSS_Rule( $selector ); } diff --git a/packages/style-engine/class-wp-style-engine-processor.php b/packages/style-engine/class-wp-style-engine-processor.php index 41541ab4c3b35..d6293fdd180a8 100644 --- a/packages/style-engine/class-wp-style-engine-processor.php +++ b/packages/style-engine/class-wp-style-engine-processor.php @@ -80,20 +80,21 @@ public function add_rules( $css_rules ) { continue; } - if ( $rule instanceof WP_Style_Engine_CSS_Rule ) { - if ( isset( $this->css_rules[ $selector ] ) ) { - $this->css_rules[ $selector ]->add_declarations( $rule->get_declarations() ); + if ( $rule instanceof WP_Style_Engine_CSS_Rules_Container ) { + if ( isset( $this->css_containers[ $selector ] ) ) { + $this->css_containers[ $selector ]->add_rules( $rule->get_rules() ); + $this->css_containers[ $selector ]->add_declarations( $rule->get_declarations() ); } else { - $this->css_rules[ $selector ] = $rule; + $this->css_containers[ $selector ] = $rule; } continue; } - if ( $rule instanceof WP_Style_Engine_CSS_Rules_Container ) { - if ( isset( $this->css_containers[ $selector ] ) ) { - $this->css_containers[ $selector ]->add_rules( $rule->get_rules() ); + if ( $rule instanceof WP_Style_Engine_CSS_Rule ) { + if ( isset( $this->css_rules[ $selector ] ) ) { + $this->css_rules[ $selector ]->add_declarations( $rule->get_declarations() ); } else { - $this->css_containers[ $selector ] = $rule; + $this->css_rules[ $selector ] = $rule; } } } diff --git a/packages/style-engine/style-engine.php b/packages/style-engine/style-engine.php index 389ace994f165..3744203bbaac5 100644 --- a/packages/style-engine/style-engine.php +++ b/packages/style-engine/style-engine.php @@ -62,14 +62,20 @@ function wp_style_engine_get_styles( $block_styles, $options = array() ) { $css_declarations = new WP_Style_Engine_CSS_Declarations( $parsed_styles['declarations'] ); $new_rule = null; - if ( $selector ) { - $new_rule = new WP_Style_Engine_CSS_Rule( $options['selector'], $css_declarations ); + // TODO: Extend WP_Style_Engine::compile_css to do this if block. + if ( $selector || $container ) { + if ( $selector ) { + $new_rule = new WP_Style_Engine_CSS_Rule( $options['selector'], $css_declarations ); + } + if ( $container ) { - $css_container = new WP_Style_Engine_CSS_Rules_Container( $container, $new_rule ); - $styles_output['css'] = $css_container->get_css(); - } else { - $styles_output['css'] = $new_rule->get_css(); + $new_rule = new WP_Style_Engine_CSS_Rules_Container( $container, $new_rule ); + if ( ! $selector ) { + $new_rule->add_declarations( $css_declarations ); + } } + + $styles_output['css'] = $new_rule->get_css(); } else { $styles_output['css'] = $css_declarations->get_declarations_string(); } @@ -133,15 +139,23 @@ function wp_style_engine_get_stylesheet_from_css_rules( $css_rules, $options = a $css_rule_objects = array(); foreach ( $css_rules as $css_rule ) { - if ( empty( $css_rule['selector'] ) || empty( $css_rule['declarations'] ) || ! is_array( $css_rule['declarations'] ) ) { + if ( empty( $css_rule['declarations'] ) || ! is_array( $css_rule['declarations'] ) ) { continue; } $container = $css_rule['container'] ?? null; - $new_rule = new WP_Style_Engine_CSS_Rule( $css_rule['selector'], $css_rule['declarations'] ); + $selector = $css_rule['selector'] ?? null; + $new_rule = null; + + if ( $selector ) { + $new_rule = new WP_Style_Engine_CSS_Rule( $css_rule['selector'], $css_rule['declarations'] ); + } if ( $container ) { $new_rule = new WP_Style_Engine_CSS_Rules_Container( $container, $new_rule ); + if ( ! $selector ) { + $new_rule->add_declarations( $css_rule['declarations'] ); + } } if ( ! empty( $options['context'] ) ) { diff --git a/phpunit/style-engine/class-wp-style-engine-css-rules-store-test.php b/phpunit/style-engine/class-wp-style-engine-css-rules-store-test.php index 2253af34de373..f464b21dd579a 100644 --- a/phpunit/style-engine/class-wp-style-engine-css-rules-store-test.php +++ b/phpunit/style-engine/class-wp-style-engine-css-rules-store-test.php @@ -140,6 +140,53 @@ public function test_should_add_rule_to_existing_store() { $this->assertSame( $expected, $store_rule->get_css(), 'Return value of get_css() does not match expected CSS from existing store rules.' ); } + /** + * Tests adding rules to an existing store. + * + * @covers ::add_rule + */ + public function test_should_combine_existing_rule_objects_to_store() { + $new_hotdog_store = WP_Style_Engine_CSS_Rules_Store_Gutenberg::get_store( 'hotdog' ); + + $selector = '.pickle'; + $pickle_rule = new WP_Style_Engine_CSS_Rule_Gutenberg( + $selector, + array( + 'color' => 'brown', + 'border-color' => 'yellow', + 'border-radius' => '10rem', + ) + ); + $pickle_container = new WP_Style_Engine_CSS_Rules_Container_Gutenberg( '.hotdog', $pickle_rule ); + $added_rule = $new_hotdog_store->add_rule( $pickle_container ); + $expected = '.hotdog{.pickle{color:brown;border-color:yellow;border-radius:10rem;}}'; + + $this->assertSame( $expected, $added_rule->get_css(), 'Return value of store rule get_css() matches passed rule object get_css().' ); + + $pickle_container_2 = new WP_Style_Engine_CSS_Rules_Container_Gutenberg( '.hotdog', array( + new WP_Style_Engine_CSS_Rule_Gutenberg( + '.pickle-2', + array( + 'color' => 'pink', + 'border-color' => 'blue', + 'border-radius' => '11rem', + ) + ), + new WP_Style_Engine_CSS_Rule_Gutenberg( + $selector, + array( + 'border-radius' => '1px', + ) + ) + ) + ); + $pickle_container->add_declarations( array( 'padding' => '100px' ) ); + $added_rule = $new_hotdog_store->add_rule( $pickle_container_2 ); + $expected = '.hotdog{padding:100px;.pickle{color:brown;border-color:yellow;border-radius:1px;}.pickle-2{color:pink;border-color:blue;border-radius:11rem;}}'; + $this->assertSame( $expected, $added_rule->get_css(), 'Return value of store rule get_css() matches passed rule object get_css().' ); + } + + /** * Tests adding rules to an existing store. * diff --git a/phpunit/style-engine/style-engine-test.php b/phpunit/style-engine/style-engine-test.php index a08c1931aac6e..9610129dce0d6 100644 --- a/phpunit/style-engine/style-engine-test.php +++ b/phpunit/style-engine/style-engine-test.php @@ -23,7 +23,7 @@ public function tear_down() { /** * Tests generating block styles and classnames based on various manifestations of the $block_styles argument. * - * @covers ::gutenberg_style_engine_get_styles + * @covers ::wp_style_engine_get_styles * @covers WP_Style_Engine_Gutenberg::parse_block_styles * @covers WP_Style_Engine_Gutenberg::compile_css * @@ -557,7 +557,7 @@ public function data_wp_style_engine_get_styles() { /** * Tests adding rules to a store and retrieving a generated stylesheet. * - * @covers ::gutenberg_style_engine_get_styles + * @covers ::wp_style_engine_get_styles * @covers WP_Style_Engine_Gutenberg::store_css_rule */ public function test_should_store_block_styles_using_context() { @@ -585,6 +585,49 @@ public function test_should_store_block_styles_using_context() { $this->assertSame( $generated_styles['css'], $rule->get_css() ); } + /** + * Tests adding nested rules/containers to a store and retrieving a generated stylesheet. + * + * @covers ::wp_style_engine_get_styles + * @covers ::wp_style_engine_get_stylesheet_from_context + * @covers WP_Style_Engine_Gutenberg::store_css_rule + */ + public function test_should_store_nested_block_styles_using_context() { + gutenberg_style_engine_get_styles( + array( + 'color' => array( + 'text' => 'var:preset|color|texas-flood', + ), + ), + array( + 'context' => 'block-supports', + 'container' => 'main' + ) + ); + + gutenberg_style_engine_get_styles( + array( + 'spacing' => array( + 'padding' => array( + 'top' => '42px', + 'left' => '2%', + 'bottom' => '44px', + 'right' => '5rem', + ), + ), + ), + array( + 'context' => 'block-supports', + 'selector' => '& .container', + 'container' => 'main' + ) + ); + + $store_css = gutenberg_style_engine_get_stylesheet_from_context( 'block-supports' ); + + $this->assertSame( 'main{color:var(--wp--preset--color--texas-flood);& .container{padding-top:42px;padding-left:2%;padding-bottom:44px;padding-right:5rem;}}', $store_css ); + } + /** * Tests that passing no context does not store styles. * @@ -615,7 +658,20 @@ public function test_should_not_store_block_styles_without_context() { * @covers ::wp_style_engine_get_stylesheet_from_context */ public function test_should_get_stored_stylesheet_from_context() { - $css_rules = array( + $css_rules = array( + array( + 'container' => '.gollem', + 'declarations' => array( + 'background-color' => 'green', + ), + ), + array( + 'container' => '.gollem', + 'selector' => '&.precious:hover', + 'declarations' => array( + 'background-color' => 'gold', + ), + ), array( 'selector' => '.pippin', 'container' => '@container (min-width: 700px)', @@ -658,7 +714,7 @@ public function test_should_get_stored_stylesheet_from_context() { 'selector' => '.pippin', 'container' => '@container (min-width: 700px)', 'declarations' => array( - 'color' => 'tan', + 'color' => 'tan', ), ), ); @@ -677,7 +733,7 @@ public function test_should_get_stored_stylesheet_from_context() { /** * Tests returning a generated stylesheet from a set of rules. * - * @covers ::gutenberg_style_engine_get_stylesheet_from_css_rules + * @covers ::wp_style_engine_get_stylesheet_from_css_rules * @covers WP_Style_Engine_Gutenberg::compile_stylesheet_from_css_rules */ public function test_should_return_stylesheet_from_css_rules() { @@ -721,6 +777,19 @@ public function test_should_return_stylesheet_from_css_rules() { */ public function test_should_return_stylesheet_with_combined_nested_css_rules_printed_after_non_nested() { $css_rules = array( + array( + 'container' => '.sauron', + 'declarations' => array( + 'text-transform' => 'uppercase', + ), + ), + array( + 'container' => '.sauron', + 'selector' => '.witch-king', + 'declarations' => array( + 'text-transform' => 'lowercase', + ), + ), array( 'selector' => '.saruman', 'declarations' => array( @@ -783,7 +852,7 @@ public function test_should_return_stylesheet_with_combined_nested_css_rules_pri $compiled_stylesheet = gutenberg_style_engine_get_stylesheet_from_css_rules( $css_rules, array( 'prettify' => false ) ); - $this->assertSame( '.saruman{letter-spacing:1px;}.gandalf{letter-spacing:2px;}@container (min-width: 700px){.saruman{color:black;height:100px;border-style:solid;align-self:stretch;font-family:The-Great-Eye;}}@supports (align-self: stretch){.voldemort{height:100px;align-self:stretch;}.radagast{color:brown;height:60px;border-style:dashed;align-self:stretch;}}@supports (border-style: dotted){.gandalf{color:grey;height:90px;border-style:dotted;align-self:safe center;}}', $compiled_stylesheet ); + $this->assertSame( '.saruman{letter-spacing:1px;}.gandalf{letter-spacing:2px;}.sauron{text-transform:uppercase;.witch-king{text-transform:lowercase;}}@container (min-width: 700px){.saruman{color:black;height:100px;border-style:solid;align-self:stretch;font-family:The-Great-Eye;}}@supports (align-self: stretch){.voldemort{height:100px;align-self:stretch;}.radagast{color:brown;height:60px;border-style:dashed;align-self:stretch;}}@supports (border-style: dotted){.gandalf{color:grey;height:90px;border-style:dotted;align-self:safe center;}}', $compiled_stylesheet ); } /** @@ -791,7 +860,7 @@ public function test_should_return_stylesheet_with_combined_nested_css_rules_pri * * @ticket 58811 * - * @covers ::gutenberg_style_engine_get_stylesheet_from_css_rules + * @covers ::wp_style_engine_get_stylesheet_from_css_rules * @covers WP_Style_Engine_Gutenberg::compile_stylesheet_from_css_rules */ public function test_should_dedupe_and_merge_css_rules() { @@ -841,7 +910,7 @@ public function test_should_dedupe_and_merge_css_rules() { * * This is testing this fix: https://github.com/WordPress/gutenberg/pull/49004 * - * @covers ::gutenberg_style_engine_get_stylesheet_from_css_rules + * @covers ::wp_style_engine_get_stylesheet_from_css_rules * @covers WP_Style_Engine_Gutenberg::compile_stylesheet_from_css_rules */ public function test_should_return_stylesheet_from_duotone_css_rules() { From 5276e3604fb2a9e389e852c06103b3a9980b9298 Mon Sep 17 00:00:00 2001 From: ramon Date: Sun, 11 Feb 2024 15:18:00 +1100 Subject: [PATCH 14/20] Remove unused properties of container class --- ...class-wp-style-engine-css-rules-container.php | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/packages/style-engine/class-wp-style-engine-css-rules-container.php b/packages/style-engine/class-wp-style-engine-css-rules-container.php index c2ffb712f9c75..18ae517a6ebd6 100644 --- a/packages/style-engine/class-wp-style-engine-css-rules-container.php +++ b/packages/style-engine/class-wp-style-engine-css-rules-container.php @@ -14,22 +14,6 @@ * @access private */ class WP_Style_Engine_CSS_Rules_Container extends WP_Style_Engine_CSS_Rule { - /** - * A parent CSS selector in the case of nested CSS, or a CSS nested @rule, such as `@media (min-width: 80rem)` or `@layer module` - * - * @var string - */ - protected $selector; - - /** - * The selector declarations. - * - * Contains a WP_Style_Engine_CSS_Declarations object. - * - * @var WP_Style_Engine_CSS_Declarations - */ - protected $declarations; - /** * The container declarations. * From eb5797c56d7b58ac0dcc86a08ef4fbb6a7c2f2fe Mon Sep 17 00:00:00 2001 From: ramon Date: Sun, 11 Feb 2024 15:57:32 +1100 Subject: [PATCH 15/20] Lint me --- .../class-wp-style-engine-css-rules-container.php | 2 +- packages/style-engine/style-engine.php | 2 +- .../class-wp-style-engine-css-rules-store-test.php | 10 ++++++---- phpunit/style-engine/style-engine-test.php | 4 ++-- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/style-engine/class-wp-style-engine-css-rules-container.php b/packages/style-engine/class-wp-style-engine-css-rules-container.php index 18ae517a6ebd6..244eed22db189 100644 --- a/packages/style-engine/class-wp-style-engine-css-rules-container.php +++ b/packages/style-engine/class-wp-style-engine-css-rules-container.php @@ -105,7 +105,7 @@ public function get_css( $should_prettify = false, $indent_count = 0 ) { $indent_count = $should_prettify ? $indent_count + 1 : $indent_count; $new_line = $should_prettify ? "\n" : ''; $spacer = $should_prettify ? ' ' : ''; - $css .= ! empty( $this->declarations ) ? $this->declarations->get_declarations_string( $should_prettify, $indent_count ) : ''; + $css .= ! empty( $this->declarations ) ? $this->declarations->get_declarations_string( $should_prettify, $indent_count ) : ''; foreach ( $this->rules as $rule ) { $css .= $rule->get_css( $should_prettify, $indent_count ); diff --git a/packages/style-engine/style-engine.php b/packages/style-engine/style-engine.php index 3744203bbaac5..9e6798c36a503 100644 --- a/packages/style-engine/style-engine.php +++ b/packages/style-engine/style-engine.php @@ -148,7 +148,7 @@ function wp_style_engine_get_stylesheet_from_css_rules( $css_rules, $options = a $new_rule = null; if ( $selector ) { - $new_rule = new WP_Style_Engine_CSS_Rule( $css_rule['selector'], $css_rule['declarations'] ); + $new_rule = new WP_Style_Engine_CSS_Rule( $css_rule['selector'], $css_rule['declarations'] ); } if ( $container ) { diff --git a/phpunit/style-engine/class-wp-style-engine-css-rules-store-test.php b/phpunit/style-engine/class-wp-style-engine-css-rules-store-test.php index f464b21dd579a..dd94d4e882f1d 100644 --- a/phpunit/style-engine/class-wp-style-engine-css-rules-store-test.php +++ b/phpunit/style-engine/class-wp-style-engine-css-rules-store-test.php @@ -163,7 +163,9 @@ public function test_should_combine_existing_rule_objects_to_store() { $this->assertSame( $expected, $added_rule->get_css(), 'Return value of store rule get_css() matches passed rule object get_css().' ); - $pickle_container_2 = new WP_Style_Engine_CSS_Rules_Container_Gutenberg( '.hotdog', array( + $pickle_container_2 = new WP_Style_Engine_CSS_Rules_Container_Gutenberg( + '.hotdog', + array( new WP_Style_Engine_CSS_Rule_Gutenberg( '.pickle-2', array( @@ -177,12 +179,12 @@ public function test_should_combine_existing_rule_objects_to_store() { array( 'border-radius' => '1px', ) - ) + ), ) ); $pickle_container->add_declarations( array( 'padding' => '100px' ) ); - $added_rule = $new_hotdog_store->add_rule( $pickle_container_2 ); - $expected = '.hotdog{padding:100px;.pickle{color:brown;border-color:yellow;border-radius:1px;}.pickle-2{color:pink;border-color:blue;border-radius:11rem;}}'; + $added_rule = $new_hotdog_store->add_rule( $pickle_container_2 ); + $expected = '.hotdog{padding:100px;.pickle{color:brown;border-color:yellow;border-radius:1px;}.pickle-2{color:pink;border-color:blue;border-radius:11rem;}}'; $this->assertSame( $expected, $added_rule->get_css(), 'Return value of store rule get_css() matches passed rule object get_css().' ); } diff --git a/phpunit/style-engine/style-engine-test.php b/phpunit/style-engine/style-engine-test.php index 9610129dce0d6..0da12f1b75b55 100644 --- a/phpunit/style-engine/style-engine-test.php +++ b/phpunit/style-engine/style-engine-test.php @@ -601,7 +601,7 @@ public function test_should_store_nested_block_styles_using_context() { ), array( 'context' => 'block-supports', - 'container' => 'main' + 'container' => 'main', ) ); @@ -619,7 +619,7 @@ public function test_should_store_nested_block_styles_using_context() { array( 'context' => 'block-supports', 'selector' => '& .container', - 'container' => 'main' + 'container' => 'main', ) ); From 4dcdc8bbc28c2b50c196a0a088aa801f8e9a4ba7 Mon Sep 17 00:00:00 2001 From: ramon Date: Sun, 11 Feb 2024 17:00:10 +1100 Subject: [PATCH 16/20] Some notes --- .../class-wp-style-engine-css-rules-container.php | 1 + .../style-engine/class-wp-style-engine-css-rules-store.php | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/style-engine/class-wp-style-engine-css-rules-container.php b/packages/style-engine/class-wp-style-engine-css-rules-container.php index 244eed22db189..c38f377bc0904 100644 --- a/packages/style-engine/class-wp-style-engine-css-rules-container.php +++ b/packages/style-engine/class-wp-style-engine-css-rules-container.php @@ -106,6 +106,7 @@ public function get_css( $should_prettify = false, $indent_count = 0 ) { $new_line = $should_prettify ? "\n" : ''; $spacer = $should_prettify ? ' ' : ''; $css .= ! empty( $this->declarations ) ? $this->declarations->get_declarations_string( $should_prettify, $indent_count ) : ''; + $css .= $should_prettify && $css ? "\n" : ''; foreach ( $this->rules as $rule ) { $css .= $rule->get_css( $should_prettify, $indent_count ); diff --git a/packages/style-engine/class-wp-style-engine-css-rules-store.php b/packages/style-engine/class-wp-style-engine-css-rules-store.php index 8043b9d71d6ca..0587cf8809edf 100644 --- a/packages/style-engine/class-wp-style-engine-css-rules-store.php +++ b/packages/style-engine/class-wp-style-engine-css-rules-store.php @@ -136,7 +136,11 @@ public function add_rule( $rule ) { Create a new WP_Style_Engine_CSS_Rule rule by default if it doesn't exist. */ if ( isset( $this->rules[ $selector ] ) ) { - if ( $rule instanceof WP_Style_Engine_CSS_Rules_Container ) { + // @TODO Create a unit test to check if an incoming rule is a container too. Don't overwrite existing containers? + // @TODO Maybe have a helper function on the class to check if the rule is a container? + // Or store them in the store in different containers? + // Or an ->merge() method? + if ( $this->rules[ $selector ] instanceof WP_Style_Engine_CSS_Rules_Container && $rule instanceof WP_Style_Engine_CSS_Rules_Container ) { $this->rules[ $selector ]->add_rules( $rule->get_rules() ); } if ( $is_rules_object ) { From b10ef349d645fcb187a53d1fd6e3ffbe6a12c64e Mon Sep 17 00:00:00 2001 From: ramon Date: Sun, 11 Feb 2024 17:05:02 +1100 Subject: [PATCH 17/20] linty --- packages/style-engine/class-wp-style-engine-css-rule.php | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/style-engine/class-wp-style-engine-css-rule.php b/packages/style-engine/class-wp-style-engine-css-rule.php index c319225e4249e..92410009c2338 100644 --- a/packages/style-engine/class-wp-style-engine-css-rule.php +++ b/packages/style-engine/class-wp-style-engine-css-rule.php @@ -121,7 +121,6 @@ public function get_css( $should_prettify = false, $indent_count = 0 ) { $selector = $should_prettify ? str_replace( array( ',' ), ",\n", $selector ) : $selector; $css_declarations = ! empty( $this->declarations ) ? $this->declarations->get_declarations_string( $should_prettify, $declarations_indent ) : ''; - if ( empty( $css_declarations ) ) { return ''; } From 1737f72dfa752a2ab56ccab8edf7e4f756c39656 Mon Sep 17 00:00:00 2001 From: ramon Date: Sun, 11 Feb 2024 18:45:54 +1100 Subject: [PATCH 18/20] Tidy up store and add tests --- .../class-wp-style-engine-css-rules-store.php | 34 ++++------- .../class-wp-style-engine-processor.php | 5 ++ ...s-wp-style-engine-css-rules-store-test.php | 59 +++++++++++++++++++ 3 files changed, 76 insertions(+), 22 deletions(-) diff --git a/packages/style-engine/class-wp-style-engine-css-rules-store.php b/packages/style-engine/class-wp-style-engine-css-rules-store.php index 0587cf8809edf..04c3ccab89d96 100644 --- a/packages/style-engine/class-wp-style-engine-css-rules-store.php +++ b/packages/style-engine/class-wp-style-engine-css-rules-store.php @@ -118,36 +118,26 @@ public function add_rule( $rule ) { return; } - $is_rules_object = $rule instanceof WP_Style_Engine_CSS_Rules_Container || $rule instanceof WP_Style_Engine_CSS_Rule; - - if ( $is_rules_object ) { - $selector = $rule->get_selector(); - } - if ( is_string( $rule ) ) { $selector = trim( $rule ); + /* + Create a new WP_Style_Engine_CSS_Rule rule by default if it doesn't exist. + */ + if ( ! isset( $this->rules[ $selector ] ) ) { + $rule = new WP_Style_Engine_CSS_Rule( $selector ); + } else { + return $this->rules[ $selector ]; + } } - if ( empty( $selector ) ) { - return; - } - - /* - Create a new WP_Style_Engine_CSS_Rule rule by default if it doesn't exist. - */ + $selector = $rule->get_selector(); if ( isset( $this->rules[ $selector ] ) ) { - // @TODO Create a unit test to check if an incoming rule is a container too. Don't overwrite existing containers? - // @TODO Maybe have a helper function on the class to check if the rule is a container? - // Or store them in the store in different containers? - // Or an ->merge() method? - if ( $this->rules[ $selector ] instanceof WP_Style_Engine_CSS_Rules_Container && $rule instanceof WP_Style_Engine_CSS_Rules_Container ) { + if ( $rule instanceof WP_Style_Engine_CSS_Rules_Container && $this->rules[ $selector ] instanceof WP_Style_Engine_CSS_Rules_Container ) { $this->rules[ $selector ]->add_rules( $rule->get_rules() ); } - if ( $is_rules_object ) { - $this->rules[ $selector ]->add_declarations( $rule->get_declarations() ); - } + $this->rules[ $selector ]->add_declarations( $rule->get_declarations() ); } else { - $this->rules[ $selector ] = $is_rules_object ? $rule : new WP_Style_Engine_CSS_Rule( $selector ); + $this->rules[ $selector ] = $rule; } return $this->rules[ $selector ]; diff --git a/packages/style-engine/class-wp-style-engine-processor.php b/packages/style-engine/class-wp-style-engine-processor.php index d6293fdd180a8..3d2aaf81b2143 100644 --- a/packages/style-engine/class-wp-style-engine-processor.php +++ b/packages/style-engine/class-wp-style-engine-processor.php @@ -80,6 +80,11 @@ public function add_rules( $css_rules ) { continue; } + /* + * Merge existing rule and container objects or create new ones. + * Containers and rules are stored in separate arrays to allow for + * separate processing. + */ if ( $rule instanceof WP_Style_Engine_CSS_Rules_Container ) { if ( isset( $this->css_containers[ $selector ] ) ) { $this->css_containers[ $selector ]->add_rules( $rule->get_rules() ); diff --git a/phpunit/style-engine/class-wp-style-engine-css-rules-store-test.php b/phpunit/style-engine/class-wp-style-engine-css-rules-store-test.php index dd94d4e882f1d..a1c2c2d79b8a0 100644 --- a/phpunit/style-engine/class-wp-style-engine-css-rules-store-test.php +++ b/phpunit/style-engine/class-wp-style-engine-css-rules-store-test.php @@ -140,6 +140,65 @@ public function test_should_add_rule_to_existing_store() { $this->assertSame( $expected, $store_rule->get_css(), 'Return value of get_css() does not match expected CSS from existing store rules.' ); } + /** + * Tests separation of stores. + * + * @covers ::add_rule + * @covers ::get_store + */ + public function test_should_add_rules_to_separate_stores() { + $store_one = WP_Style_Engine_CSS_Rules_Store_Gutenberg::get_store( 'one' ); + $store_one_rule = $store_one->add_rule( '.one' ); + $store_one_container = $store_one->add_rule( new WP_Style_Engine_CSS_Rules_Container_Gutenberg( '.one_container' ) ); + + $store_two = WP_Style_Engine_CSS_Rules_Store_Gutenberg::get_store( 'two' ); + $store_rule_two = $store_two->add_rule( '.two' ); + + $this->assertSame( + array( + '.one' => $store_one_rule, + '.one_container' => $store_one_container, + ), + WP_Style_Engine_Gutenberg::get_store( 'one' )->get_all_rules(), + 'get_all_rules() does not return expected array of rules for store one.' + ); + + $this->assertSame( + array( + '.two' => $store_rule_two, + ), + WP_Style_Engine_Gutenberg::get_store( 'two' )->get_all_rules(), + 'get_all_rules() does not return expected array of rules for store two.' + ); + } + + /** + * Tests adding identical selectors. + * + * @covers ::add_rule + */ + public function test_should_not_overwrite_existing_rules() { + $store_one = WP_Style_Engine_CSS_Rules_Store_Gutenberg::get_store( 'one' ); + $store_one_container = $store_one->add_rule( new WP_Style_Engine_CSS_Rules_Container_Gutenberg( '.one' ) ); + $store_one_rule = $store_one->add_rule( '.one' ); + + $store_two = WP_Style_Engine_CSS_Rules_Store_Gutenberg::get_store( 'two' ); + $store_rule_two = $store_two->add_rule( '.two' ); + $store_two_container = $store_two->add_rule( new WP_Style_Engine_CSS_Rules_Container_Gutenberg( '.two' ) ); + + $this->assertSame( + $store_one_rule, + $store_one_container, + 'add_rule() does not return already existing return .one rule.' + ); + + $this->assertSame( + $store_two_container, + $store_rule_two, + 'add_rule() does not return already existing return .two rule.' + ); + } + /** * Tests adding rules to an existing store. * From 84f0f4ce0d3f3374b575c6ab682a5802c699499d Mon Sep 17 00:00:00 2001 From: ramon Date: Mon, 12 Feb 2024 10:05:40 +1100 Subject: [PATCH 19/20] Test for nesting other at-rules --- phpunit/style-engine/style-engine-test.php | 46 ++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/phpunit/style-engine/style-engine-test.php b/phpunit/style-engine/style-engine-test.php index 0da12f1b75b55..11bb2a639d6a8 100644 --- a/phpunit/style-engine/style-engine-test.php +++ b/phpunit/style-engine/style-engine-test.php @@ -855,6 +855,52 @@ public function test_should_return_stylesheet_with_combined_nested_css_rules_pri $this->assertSame( '.saruman{letter-spacing:1px;}.gandalf{letter-spacing:2px;}.sauron{text-transform:uppercase;.witch-king{text-transform:lowercase;}}@container (min-width: 700px){.saruman{color:black;height:100px;border-style:solid;align-self:stretch;font-family:The-Great-Eye;}}@supports (align-self: stretch){.voldemort{height:100px;align-self:stretch;}.radagast{color:brown;height:60px;border-style:dashed;align-self:stretch;}}@supports (border-style: dotted){.gandalf{color:grey;height:90px;border-style:dotted;align-self:safe center;}}', $compiled_stylesheet ); } + /** + * Tests returning a generated stylesheet from a set of nested rules. + */ + public function test_should_return_stylesheet_with_nested_at_rules() { + $css_rules = array( + array( + 'container' => '.foo', + 'declarations' => array( + 'background-color' => 'red', + ), + ), + array( + 'container' => '.foo', + 'selector' => '@media (orientation: landscape)', + 'declarations' => array( + 'background-color' => 'blue', + ), + ), + array( + 'container' => '.foo', + 'selector' => '@media (min-width > 1024px)', + 'declarations' => array( + 'background-color' => 'cotton-blue', + ), + ), + ); + + $compiled_stylesheet = gutenberg_style_engine_get_stylesheet_from_css_rules( $css_rules, array( 'prettify' => false ) ); + + $this->assertSame( '.foo{background-color:red;@media (orientation: landscape){background-color:blue;}@media (min-width > 1024px){background-color:cotton-blue;}}', $compiled_stylesheet ); + } + + /* + .foo { + display: grid; + + @media (orientation: landscape) { + grid-auto-flow: column; + + @media (min-width > 1024px) { + max-inline-size: 1024px; + } + } +} + */ + /** * Tests that incoming styles are deduped and merged. * From e69c10dac5f737bfa14ef5993439d5050b560551 Mon Sep 17 00:00:00 2001 From: ramon Date: Mon, 12 Feb 2024 10:08:55 +1100 Subject: [PATCH 20/20] Remove comments --- phpunit/style-engine/style-engine-test.php | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/phpunit/style-engine/style-engine-test.php b/phpunit/style-engine/style-engine-test.php index 11bb2a639d6a8..4cb463d3a5a27 100644 --- a/phpunit/style-engine/style-engine-test.php +++ b/phpunit/style-engine/style-engine-test.php @@ -887,20 +887,6 @@ public function test_should_return_stylesheet_with_nested_at_rules() { $this->assertSame( '.foo{background-color:red;@media (orientation: landscape){background-color:blue;}@media (min-width > 1024px){background-color:cotton-blue;}}', $compiled_stylesheet ); } - /* - .foo { - display: grid; - - @media (orientation: landscape) { - grid-auto-flow: column; - - @media (min-width > 1024px) { - max-inline-size: 1024px; - } - } -} - */ - /** * Tests that incoming styles are deduped and merged. *