Skip to content

Commit

Permalink
Experiment: Style Engine Rules & Store objects (#42222)
Browse files Browse the repository at this point in the history
* Add a CSS_Rule object

* Add a rules store

* some inline docs & cleanup on the store

* typos & fixes

* remove parent relations for rules

* escape the selector

* Allow for multiple named instances of the store

* Use a single declarations object per rule

* Allow chaining methods

* Remove esc_html

* Add a default value for store_name

* use static()

* Adding tests

* rename get_rule to add_rule

* add a remove_rule method

* minor performance improvement

* cleanup & added a return doc

Co-authored-by: ramonjd <ramonjd@gmail.com>
  • Loading branch information
aristath and ramonjd authored Jul 15, 2022
1 parent e8d2de7 commit 3136704
Show file tree
Hide file tree
Showing 6 changed files with 428 additions and 0 deletions.
6 changes: 6 additions & 0 deletions lib/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,12 @@ function gutenberg_is_experiment_enabled( $name ) {
if ( file_exists( __DIR__ . '/../build/style-engine/class-wp-style-engine-css-declarations-gutenberg.php' ) ) {
require_once __DIR__ . '/../build/style-engine/class-wp-style-engine-css-declarations-gutenberg.php';
}
if ( file_exists( __DIR__ . '/../build/style-engine/class-wp-style-engine-css-rule-gutenberg.php' ) ) {
require_once __DIR__ . '/../build/style-engine/class-wp-style-engine-css-rule-gutenberg.php';
}
if ( file_exists( __DIR__ . '/../build/style-engine/class-wp-style-engine-css-rules-store-gutenberg.php' ) ) {
require_once __DIR__ . '/../build/style-engine/class-wp-style-engine-css-rules-store-gutenberg.php';
}

// Block supports overrides.
require __DIR__ . '/block-supports/utils.php';
Expand Down
115 changes: 115 additions & 0 deletions packages/style-engine/class-wp-style-engine-css-rule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
<?php
/**
* WP_Style_Engine_CSS_Rule
*
* An object for CSS rules.
*
* @package Gutenberg
*/

if ( class_exists( 'WP_Style_Engine_CSS_Rule' ) ) {
return;
}

/**
* Holds, sanitizes, processes and prints CSS declarations for the style engine.
*
* @access private
*/
class WP_Style_Engine_CSS_Rule {

/**
* The selector.
*
* @var string
*/
protected $selector;

/**
* The selector declarations.
*
* Contains a WP_Style_Engine_CSS_Declarations object.
*
* @var WP_Style_Engine_CSS_Declarations
*/
protected $declarations;

/**
* Constructor
*
* @param string $selector The CSS selector.
* @param array|WP_Style_Engine_CSS_Declarations $declarations An array of declarations (property => value pairs),
* or a WP_Style_Engine_CSS_Declarations object.
*/
public function __construct( $selector = '', $declarations = array() ) {
$this->set_selector( $selector );
$this->set_declarations( $declarations );
}

/**
* Set the selector.
*
* @param string $selector The CSS selector.
*
* @return WP_Style_Engine_CSS_Rule Returns the object to allow chaining of methods.
*/
public function set_selector( $selector ) {
if ( empty( $selector ) ) {
return;
}
$this->selector = $selector;

return $this;
}

/**
* Set the declarations.
*
* @param array|WP_Style_Engine_CSS_Declarations $declarations An array of declarations (property => value pairs),
* or a WP_Style_Engine_CSS_Declarations object.
*
* @return WP_Style_Engine_CSS_Rule Returns the object to allow chaining of methods.
*/
public function set_declarations( $declarations ) {
$is_declarations_object = ! is_array( $declarations );
$declarations_array = $is_declarations_object ? $declarations->get_declarations() : $declarations;

if ( null === $this->declarations && $is_declarations_object ) {
$this->declarations = $declarations;
return $this;
}
if ( null === $this->declarations ) {
$this->declarations = new WP_Style_Engine_CSS_Declarations( $declarations_array );
}
$this->declarations->add_declarations( $declarations_array );

return $this;
}

/**
* Get the declarations object.
*
* @return WP_Style_Engine_CSS_Declarations
*/
public function get_declarations() {
return $this->declarations;
}

/**
* Get the full selector.
*
* @return string
*/
public function get_selector() {
return $this->selector;
}

/**
* Get the CSS.
*
* @return string
*/
public function get_css() {
return $this->get_selector() . ' {' . $this->declarations->get_declarations_string() . '}';
}
}
94 changes: 94 additions & 0 deletions packages/style-engine/class-wp-style-engine-css-rules-store.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?php
/**
* WP_Style_Engine_CSS_Rules_Store
*
* A store for WP_Style_Engine_CSS_Rule objects.
*
* @package Gutenberg
*/

if ( class_exists( 'WP_Style_Engine_CSS_Rules_Store' ) ) {
return;
}

/**
* Holds, sanitizes, processes and prints CSS declarations for the style engine.
*
* @access private
*/
class WP_Style_Engine_CSS_Rules_Store {

/**
* An array of named WP_Style_Engine_CSS_Rules_Store objects.
*
* @static
*
* @var WP_Style_Engine_CSS_Rules_Store[]
*/
protected static $stores = array();

/**
* An array of CSS Rules objects assigned to the store.
*
* @var WP_Style_Engine_CSS_Rule[]
*/
protected $rules = array();

/**
* Get an instance of the store.
*
* @param string $store_name The name of the store.
*
* @return WP_Style_Engine_CSS_Rules_Store
*/
public static function get_store( $store_name = 'default' ) {
if ( ! isset( static::$stores[ $store_name ] ) ) {
static::$stores[ $store_name ] = new static();
}
return static::$stores[ $store_name ];
}
/**
* Get an array of all rules.
*
* @return WP_Style_Engine_CSS_Rule[]
*/
public function get_all_rules() {
return $this->rules;
}

/**
* Get 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.
*
* @return WP_Style_Engine_CSS_Rule|null Returns a WP_Style_Engine_CSS_Rule object, or null if the selector is empty.
*/
public function add_rule( $selector ) {

$selector = trim( $selector );

// 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 );
}

return $this->rules[ $selector ];
}

/**
* Remove a selector from the store.
*
* @param string $selector The CSS selector.
*
* @return void
*/
public function remove_rule( $selector ) {
unset( $this->rules[ $selector ] );
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?php
/**
* Tests the Style Engine CSS Rule class.
*
* @package Gutenberg
* @subpackage style-engine
*/

require __DIR__ . '/../class-wp-style-engine-css-rule.php';
require __DIR__ . '/../class-wp-style-engine-css-declarations.php';

/**
* Tests for registering, storing and generating CSS declarations.
*/
class WP_Style_Engine_CSS_Rule_Test extends WP_UnitTestCase {
/**
* Should set declarations on instantiation.
*/
public function test_instantiate_with_selector_and_rules() {
$selector = '.law-and-order';
$input_declarations = array(
'margin-top' => '10px',
'font-size' => '2rem',
);
$css_declarations = new WP_Style_Engine_CSS_Declarations( $input_declarations );
$css_rule = new WP_Style_Engine_CSS_Rule( $selector, $css_declarations );

$this->assertSame( $selector, $css_rule->get_selector() );

$expected = "$selector {{$css_declarations->get_declarations_string()}}";
$this->assertSame( $expected, $css_rule->get_css() );
}

/**
* Test dedupe declaration properties.
*/
public function test_dedupe_properties_in_rules() {
$selector = '.taggart';
$first_declaration = array(
'font-size' => '2rem',
);
$overwrite_first_declaration = array(
'font-size' => '4px',
);
$css_rule = new WP_Style_Engine_CSS_Rule( $selector, $first_declaration );
$css_rule->set_declarations( new WP_Style_Engine_CSS_Declarations( $overwrite_first_declaration ) );

$expected = '.taggart {font-size: 4px;}';
$this->assertSame( $expected, $css_rule->get_css() );
}

/**
* Should set selector and rules on instantiation.
*/
public function test_set_declarations() {
// Declarations using a WP_Style_Engine_CSS_Declarations object.
$some_css_declarations = new WP_Style_Engine_CSS_Declarations( array( 'margin-top' => '10px' ) );
// Declarations using a property => value array.
$some_more_css_declarations = array( 'font-size' => '1rem' );
$css_rule = new WP_Style_Engine_CSS_Rule( '.hill-street-blues', $some_css_declarations );
$css_rule->set_declarations( $some_more_css_declarations );

$expected = '.hill-street-blues {margin-top: 10px; font-size: 1rem;}';
$this->assertSame( $expected, $css_rule->get_css() );
}

/**
* Should set selector and rules on instantiation.
*/
public function test_set_selector() {
$selector = '.taggart';
$css_rule = new WP_Style_Engine_CSS_Rule( $selector );

$this->assertSame( $selector, $css_rule->get_selector() );

$css_rule->set_selector( '.law-and-order' );

$this->assertSame( '.law-and-order', $css_rule->get_selector() );
}

/**
* Should set selector and rules on instantiation.
*/
public function test_get_css() {
$selector = '.chips';
$input_declarations = array(
'margin-top' => '10px',
'font-size' => '2rem',
);
$css_declarations = new WP_Style_Engine_CSS_Declarations( $input_declarations );
$css_rule = new WP_Style_Engine_CSS_Rule( $selector, $css_declarations );
$expected = "$selector {{$css_declarations->get_declarations_string()}}";

$this->assertSame( $expected, $css_rule->get_css() );
}
}
Loading

0 comments on commit 3136704

Please sign in to comment.