From c64788048b923d3cda04b9aadfe69deca970026f Mon Sep 17 00:00:00 2001 From: ramonjd Date: Fri, 2 Sep 2022 07:37:38 +1000 Subject: [PATCH 01/10] initial commit: this commit adds a new class to the style engine that manages extensions of the block style definitions adding tests --- lib/load.php | 3 + .../class-wp-style-engine-block-supports.php | 133 ++++++++++ .../style-engine/class-wp-style-engine.php | 4 +- ...ss-wp-style-engine-block-supports-test.php | 247 ++++++++++++++++++ tools/webpack/packages.js | 1 + 5 files changed, 386 insertions(+), 2 deletions(-) create mode 100644 packages/style-engine/class-wp-style-engine-block-supports.php create mode 100644 phpunit/style-engine/class-wp-style-engine-block-supports-test.php diff --git a/lib/load.php b/lib/load.php index ef8da334debe6e..799dbef9f9b819 100644 --- a/lib/load.php +++ b/lib/load.php @@ -215,6 +215,9 @@ function gutenberg_is_experiment_enabled( $name ) { 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'; } +if ( file_exists( __DIR__ . '/../build/style-engine/class-wp-style-engine-block-supports-gutenberg.php' ) ) { + require_once __DIR__ . '/../build/style-engine/class-wp-style-engine-block-supports-gutenberg.php'; +} // Block supports overrides. require __DIR__ . '/block-supports/settings.php'; diff --git a/packages/style-engine/class-wp-style-engine-block-supports.php b/packages/style-engine/class-wp-style-engine-block-supports.php new file mode 100644 index 00000000000000..f40e712e28ba8e --- /dev/null +++ b/packages/style-engine/class-wp-style-engine-block-supports.php @@ -0,0 +1,133 @@ + $definition_group_style ) { + if ( ! is_array( $definition_group_style ) || empty( $definition_group_style ) ) { + continue; + } + + if ( ! array_key_exists( $definition_group_key, static::$merged_block_support_metadata ) ) { + static::$merged_block_support_metadata[ $definition_group_key ] = array(); + } + + foreach ( $definition_group_style as $style_definition_key => $style_definition ) { + if ( ! is_array( $style_definition ) || empty( $style_definition ) ) { + continue; + } + + $array_to_extend = array_key_exists( $style_definition_key, static::$merged_block_support_metadata[ $definition_group_key ] ) ? static::$merged_block_support_metadata[ $definition_group_key ][ $style_definition_key ] : array(); + $merged_style_definition = static::merge_custom_style_definitions_metadata( $array_to_extend, $style_definition ); + + if ( $merged_style_definition ) { + static::$merged_block_support_metadata[ $definition_group_key ][ $style_definition_key ] = $merged_style_definition; + } + } + } + } + + /** + * Get the metadata array. + * + * @param array $path A path to an array item in static::$merged_block_support_metadata. + * @return array + */ + public static function get_metadata( $path = array() ) { + // Assigns value to $merged_block_support_metadata if not already set. + if ( empty( static::$merged_block_support_metadata ) ) { + static::reset_metadata(); + } + if ( ! empty( $path ) ) { + return _wp_array_get( static::$merged_block_support_metadata, $path, null ); + } + return static::$merged_block_support_metadata; + } + + /** + * Resets the de-referenced metadata array. + * + * @return void + */ + public static function reset_metadata() { + static::$merged_block_support_metadata = wp_json_decode( wp_json_encode( WP_Style_Engine::BLOCK_STYLE_DEFINITIONS_METADATA ), true ); + } + + /** + * Merges single style definitions with incoming custom style definitions. + * + * @param array $style_definition The internal style definition metadata. + * @param array $custom_definition The custom style definition metadata to be merged. + * + * @return array|void The merged definition metadata. + */ + protected static function merge_custom_style_definitions_metadata( $style_definition, $custom_definition = array() ) { + // Required metadata. + if ( ! isset( $style_definition['path'] ) && ! isset( $custom_definition['path'] ) && ! is_array( $custom_definition['path'] ) ) { + return; + } + + // Only allow strings for valid property keys. + if ( ! isset( $custom_definition['property_keys']['default'] ) && ! is_string( $custom_definition['property_keys']['default'] ) ) { + return; + } + + // Only allow strings for valid property keys. + if ( isset( $custom_definition['property_keys']['individual'] ) && ! is_string( $custom_definition['property_keys']['individual'] ) ) { + return; + } + + // Only allow specific value_func. + if ( isset( $custom_definition['value_func'] ) && 'WP_Style_Engine::get_individual_property_css_declarations' !== $custom_definition['value_func'] ) { + return; + } + + $custom_definition['property_keys']['default'] = sanitize_key( $custom_definition['property_keys']['default'] ); + + $valid_keys = array( 'path', 'property_keys', 'css_vars', 'classnames' ); + foreach ( $valid_keys as $key ) { + if ( isset( $custom_definition[ $key ] ) && is_array( $custom_definition[ $key ] ) ) { + if ( ! isset( $style_definition[ $key ] ) ) { + $style_definition[ $key ] = array(); + } + $style_definition[ $key ] = array_merge( $style_definition[ $key ], $custom_definition[ $key ] ); + } + } + + return $style_definition; + } +} diff --git a/packages/style-engine/class-wp-style-engine.php b/packages/style-engine/class-wp-style-engine.php index 283601e551c98f..c8bcc660f89cee 100644 --- a/packages/style-engine/class-wp-style-engine.php +++ b/packages/style-engine/class-wp-style-engine.php @@ -364,7 +364,7 @@ public static function parse_block_styles( $block_styles, $options ) { } // Collect CSS and classnames. - foreach ( static::BLOCK_STYLE_DEFINITIONS_METADATA as $definition_group_key => $definition_group_style ) { + foreach ( WP_Style_Engine_Block_Supports::get_metadata() as $definition_group_key => $definition_group_style ) { if ( empty( $block_styles[ $definition_group_key ] ) ) { continue; } @@ -527,7 +527,7 @@ protected static function get_individual_property_css_declarations( $style_value // Build a path to the individual rules in definitions. $style_definition_path = array( $definition_group_key, $css_property ); - $style_definition = _wp_array_get( static::BLOCK_STYLE_DEFINITIONS_METADATA, $style_definition_path, null ); + $style_definition = _wp_array_get( WP_Style_Engine_Block_Supports::get_metadata(), $style_definition_path, null ); if ( $style_definition && isset( $style_definition['property_keys']['individual'] ) ) { // Set a CSS var if there is a valid preset value. diff --git a/phpunit/style-engine/class-wp-style-engine-block-supports-test.php b/phpunit/style-engine/class-wp-style-engine-block-supports-test.php new file mode 100644 index 00000000000000..e4f55c0ffe61da --- /dev/null +++ b/phpunit/style-engine/class-wp-style-engine-block-supports-test.php @@ -0,0 +1,247 @@ +assertEquals( WP_Style_Engine_Gutenberg::BLOCK_STYLE_DEFINITIONS_METADATA, $metadata, 'Returning all default definitions' ); + + $color_metadata = WP_Style_Engine_Block_Supports_Gutenberg::get_metadata( array( 'color' ) ); + $this->assertEquals( WP_Style_Engine_Gutenberg::BLOCK_STYLE_DEFINITIONS_METADATA['color'], $color_metadata, 'Returning top-level color definition' ); + + $color_metadata = WP_Style_Engine_Block_Supports_Gutenberg::get_metadata( array( 'color', 'background' ) ); + $this->assertEquals( WP_Style_Engine_Gutenberg::BLOCK_STYLE_DEFINITIONS_METADATA['color']['background'], $color_metadata, 'Returning second-level color > background definition' ); + + $null_metadata = WP_Style_Engine_Block_Supports_Gutenberg::get_metadata( array( 'something', 'background' ) ); + $this->assertNull( $null_metadata, 'Returning `null` where the path is invalid' ); + } + + /** + * Tests adding metadata to the block styles definition. + * + * @covers ::add_metadata + */ + public function test_should_add_new_top_level_metadata() { + $new_metadata = array( + 'layout' => array( + 'float' => array( + 'property_keys' => array( + 'default' => 'float', + ), + 'path' => array( 'layout', 'float' ), + 'css_vars' => array( + 'layout' => '--wp--preset--float--$slug', + ), + 'classnames' => array( + 'has-float-layout' => true, + 'has-$slug-float' => 'layout', + ), + ), + 'width' => array( + 'property_keys' => array( + 'default' => 'width', + 'individual' => '%s-width', + ), + 'path' => array( 'layout', 'width' ), + 'classnames' => array( + 'has-$slug-width' => 'layout', + ), + ), + ), + ); + WP_Style_Engine_Block_Supports_Gutenberg::add_metadata( $new_metadata ); + $this->assertEquals( $new_metadata['layout'], WP_Style_Engine_Block_Supports_Gutenberg::get_metadata( array( 'layout' ) ), 'A new style definition for `layout` should be registered' ); + + $block_styles = array( + 'layout' => array( + 'float' => 'var:preset|layout|left', + 'width' => array( + 'max' => '100px', + 'min' => '20px', + ), + ), + ); + $expected_styles = array( + 'css' => 'float:var(--wp--preset--float--left);max-width:100px;min-width:20px;', + 'declarations' => array( + 'float' => 'var(--wp--preset--float--left)', + 'max-width' => '100px', + 'min-width' => '20px', + ), + 'classnames' => 'has-float-layout has-left-float', + ); + $generated_styles = gutenberg_style_engine_get_styles( $block_styles ); + + $this->assertSame( $expected_styles, $generated_styles, 'CSS should be generated using the newly-added metadata' ); + } + + /** + * Tests adding new second-level property metadata to the block styles definition. + * + * @covers ::add_metadata + */ + public function test_should_add_new_style_property_metadata() { + $new_metadata = array( + 'typography' => array( + 'textIndent' => array( + 'property_keys' => array( + 'default' => 'text-indent', + ), + 'path' => array( 'typography', 'textIndent' ), + 'classnames' => array( + 'has-text-indent' => true, + ), + ), + ), + ); + WP_Style_Engine_Block_Supports_Gutenberg::add_metadata( $new_metadata ); + $this->assertEquals( $new_metadata['typography']['textIndent'], WP_Style_Engine_Block_Supports_Gutenberg::get_metadata( array( 'typography', 'textIndent' ) ) ); + + $block_styles = array( + 'typography' => array( + 'textIndent' => '1rem', + ), + ); + $expected_styles = array( + 'css' => 'text-indent:1rem;', + 'declarations' => array( + 'text-indent' => '1rem', + ), + 'classnames' => 'has-text-indent', + ); + $generated_styles = gutenberg_style_engine_get_styles( $block_styles ); + + $this->assertSame( $expected_styles, $generated_styles, 'CSS should be generated using the newly-added property metadata' ); + } + + /** + * Tests merging metadata to the block styles definition. + * + * @covers ::add_metadata + */ + public function test_should_overwrite_style_property_metadata() { + $new_metadata = array( + 'spacing' => array( + 'padding' => array( + 'property_keys' => array( + 'default' => 'columns', + ), + 'css_vars' => array( + 'spacing' => '--wp--preset--column--$slug', + ), + ), + ), + ); + $expected_merged_metadata = array( + 'spacing' => array( + 'padding' => array( + 'property_keys' => array( + 'default' => 'columns', + 'individual' => 'padding-%s', + ), + 'path' => array( 'spacing', 'padding' ), + 'css_vars' => array( + 'spacing' => '--wp--preset--column--$slug', + ), + ), + ), + ); + + WP_Style_Engine_Block_Supports_Gutenberg::add_metadata( $new_metadata ); + $this->assertEquals( + $expected_merged_metadata['spacing']['padding'], + WP_Style_Engine_Block_Supports_Gutenberg::get_metadata( array( 'spacing', 'padding' ) ), + 'The newly-merged property metadata should be present' + ); + + $block_styles = array( + 'spacing' => array( + 'padding' => '1rem', + ), + ); + $expected_styles = array( + 'css' => 'columns:1rem;', + 'declarations' => array( + 'columns' => '1rem', + ), + ); + $generated_styles = gutenberg_style_engine_get_styles( $block_styles ); + + $this->assertSame( $expected_styles, $generated_styles, 'CSS should be generated using the newly-merged property metadata' ); + } + + /** + * Tests merging metadata to the block styles definition. + * + * @covers ::add_metadata + */ + public function test_should_get_dereferenced_array() { + $new_metadata = array( + 'spacing' => array( + 'padding' => array( + 'property_keys' => array( + 'default' => 'columns', + ), + 'css_vars' => array( + 'spacing' => '--wp--preset--column--$slug', + ), + ), + ), + ); + $expected_merged_metadata = array( + 'spacing' => array( + 'padding' => array( + 'property_keys' => array( + 'default' => 'columns', + 'individual' => 'padding-%s', + ), + 'path' => array( 'spacing', 'padding' ), + 'css_vars' => array( + 'spacing' => '--wp--preset--column--$slug', + ), + ), + ), + ); + $expected_original_metadata = array( + 'spacing' => array( + 'padding' => array( + 'property_keys' => array( + 'default' => 'padding', + 'individual' => 'padding-%s', + ), + 'path' => array( 'spacing', 'padding' ), + 'css_vars' => array( + 'spacing' => '--wp--preset--spacing--$slug', + ), + ), + ), + ); + + WP_Style_Engine_Block_Supports_Gutenberg::add_metadata( $new_metadata ); + $this->assertEquals( $expected_merged_metadata['spacing']['padding'], WP_Style_Engine_Block_Supports_Gutenberg::get_metadata( array( 'spacing', 'padding' ) ), 'Should merge' ); + $this->assertEquals( $expected_original_metadata['spacing']['padding'], WP_Style_Engine_Gutenberg::BLOCK_STYLE_DEFINITIONS_METADATA['spacing']['padding'], 'Should not affect original' ); + } +} diff --git a/tools/webpack/packages.js b/tools/webpack/packages.js index e5bb74abdb0a1f..55c9140a682873 100644 --- a/tools/webpack/packages.js +++ b/tools/webpack/packages.js @@ -32,6 +32,7 @@ const bundledPackagesPhpConfig = [ from: './packages/style-engine/', to: 'build/style-engine/', replaceClasses: [ + 'WP_Style_Engine_Block_Supports', 'WP_Style_Engine_CSS_Declarations', 'WP_Style_Engine_CSS_Rules_Store', 'WP_Style_Engine_CSS_Rule', From 6dd326c13b8c3675427acc82482741720462e5bd Mon Sep 17 00:00:00 2001 From: ramonjd Date: Wed, 26 Oct 2022 16:58:07 +1100 Subject: [PATCH 02/10] No such thing as wp_json_decode --- packages/style-engine/class-wp-style-engine-block-supports.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/style-engine/class-wp-style-engine-block-supports.php b/packages/style-engine/class-wp-style-engine-block-supports.php index f40e712e28ba8e..25bc0a6d1324db 100644 --- a/packages/style-engine/class-wp-style-engine-block-supports.php +++ b/packages/style-engine/class-wp-style-engine-block-supports.php @@ -84,7 +84,7 @@ public static function get_metadata( $path = array() ) { * @return void */ public static function reset_metadata() { - static::$merged_block_support_metadata = wp_json_decode( wp_json_encode( WP_Style_Engine::BLOCK_STYLE_DEFINITIONS_METADATA ), true ); + static::$merged_block_support_metadata = json_decode( wp_json_encode( WP_Style_Engine::BLOCK_STYLE_DEFINITIONS_METADATA ), true ); } /** From ffdc7daf398f5af412520501b9abf7df7c5cdd69 Mon Sep 17 00:00:00 2001 From: ramonjd Date: Wed, 26 Oct 2022 17:06:37 +1100 Subject: [PATCH 03/10] Rename to styles metadata --- lib/load.php | 4 +-- ...-wp-style-engine-block-style-metadata.php} | 6 ++-- .../style-engine/class-wp-style-engine.php | 4 +-- ...tyle-engine-block-style-metadata-test.php} | 32 +++++++++---------- tools/webpack/packages.js | 2 +- 5 files changed, 24 insertions(+), 24 deletions(-) rename packages/style-engine/{class-wp-style-engine-block-supports.php => class-wp-style-engine-block-style-metadata.php} (96%) rename phpunit/style-engine/{class-wp-style-engine-block-supports-test.php => class-wp-style-engine-block-style-metadata-test.php} (81%) diff --git a/lib/load.php b/lib/load.php index 799dbef9f9b819..001b1fa5ffb89e 100644 --- a/lib/load.php +++ b/lib/load.php @@ -215,8 +215,8 @@ function gutenberg_is_experiment_enabled( $name ) { 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'; } -if ( file_exists( __DIR__ . '/../build/style-engine/class-wp-style-engine-block-supports-gutenberg.php' ) ) { - require_once __DIR__ . '/../build/style-engine/class-wp-style-engine-block-supports-gutenberg.php'; +if ( file_exists( __DIR__ . '/../build/style-engine/class-wp-style-engine-block-style-metadata-gutenberg.php' ) ) { + require_once __DIR__ . '/../build/style-engine/class-wp-style-engine-block-style-metadata-gutenberg.php'; } // Block supports overrides. diff --git a/packages/style-engine/class-wp-style-engine-block-supports.php b/packages/style-engine/class-wp-style-engine-block-style-metadata.php similarity index 96% rename from packages/style-engine/class-wp-style-engine-block-supports.php rename to packages/style-engine/class-wp-style-engine-block-style-metadata.php index 25bc0a6d1324db..7b27368b0a7a46 100644 --- a/packages/style-engine/class-wp-style-engine-block-supports.php +++ b/packages/style-engine/class-wp-style-engine-block-style-metadata.php @@ -1,13 +1,13 @@ $definition_group_style ) { + foreach ( WP_Style_Engine_Block_Style_Metadata::get_metadata() as $definition_group_key => $definition_group_style ) { if ( empty( $block_styles[ $definition_group_key ] ) ) { continue; } @@ -527,7 +527,7 @@ protected static function get_individual_property_css_declarations( $style_value // Build a path to the individual rules in definitions. $style_definition_path = array( $definition_group_key, $css_property ); - $style_definition = _wp_array_get( WP_Style_Engine_Block_Supports::get_metadata(), $style_definition_path, null ); + $style_definition = _wp_array_get( WP_Style_Engine_Block_Style_Metadata::get_metadata(), $style_definition_path, null ); if ( $style_definition && isset( $style_definition['property_keys']['individual'] ) ) { // Set a CSS var if there is a valid preset value. diff --git a/phpunit/style-engine/class-wp-style-engine-block-supports-test.php b/phpunit/style-engine/class-wp-style-engine-block-style-metadata-test.php similarity index 81% rename from phpunit/style-engine/class-wp-style-engine-block-supports-test.php rename to phpunit/style-engine/class-wp-style-engine-block-style-metadata-test.php index e4f55c0ffe61da..cb7d736c4929d1 100644 --- a/phpunit/style-engine/class-wp-style-engine-block-supports-test.php +++ b/phpunit/style-engine/class-wp-style-engine-block-style-metadata-test.php @@ -1,6 +1,6 @@ assertEquals( WP_Style_Engine_Gutenberg::BLOCK_STYLE_DEFINITIONS_METADATA, $metadata, 'Returning all default definitions' ); - $color_metadata = WP_Style_Engine_Block_Supports_Gutenberg::get_metadata( array( 'color' ) ); + $color_metadata = WP_Style_Engine_Block_Style_Metadata_Gutenberg::get_metadata( array( 'color' ) ); $this->assertEquals( WP_Style_Engine_Gutenberg::BLOCK_STYLE_DEFINITIONS_METADATA['color'], $color_metadata, 'Returning top-level color definition' ); - $color_metadata = WP_Style_Engine_Block_Supports_Gutenberg::get_metadata( array( 'color', 'background' ) ); + $color_metadata = WP_Style_Engine_Block_Style_Metadata_Gutenberg::get_metadata( array( 'color', 'background' ) ); $this->assertEquals( WP_Style_Engine_Gutenberg::BLOCK_STYLE_DEFINITIONS_METADATA['color']['background'], $color_metadata, 'Returning second-level color > background definition' ); - $null_metadata = WP_Style_Engine_Block_Supports_Gutenberg::get_metadata( array( 'something', 'background' ) ); + $null_metadata = WP_Style_Engine_Block_Style_Metadata_Gutenberg::get_metadata( array( 'something', 'background' ) ); $this->assertNull( $null_metadata, 'Returning `null` where the path is invalid' ); } @@ -72,8 +72,8 @@ public function test_should_add_new_top_level_metadata() { ), ), ); - WP_Style_Engine_Block_Supports_Gutenberg::add_metadata( $new_metadata ); - $this->assertEquals( $new_metadata['layout'], WP_Style_Engine_Block_Supports_Gutenberg::get_metadata( array( 'layout' ) ), 'A new style definition for `layout` should be registered' ); + WP_Style_Engine_Block_Style_Metadata_Gutenberg::add_metadata( $new_metadata ); + $this->assertEquals( $new_metadata['layout'], WP_Style_Engine_Block_Style_Metadata_Gutenberg::get_metadata( array( 'layout' ) ), 'A new style definition for `layout` should be registered' ); $block_styles = array( 'layout' => array( @@ -117,8 +117,8 @@ public function test_should_add_new_style_property_metadata() { ), ), ); - WP_Style_Engine_Block_Supports_Gutenberg::add_metadata( $new_metadata ); - $this->assertEquals( $new_metadata['typography']['textIndent'], WP_Style_Engine_Block_Supports_Gutenberg::get_metadata( array( 'typography', 'textIndent' ) ) ); + WP_Style_Engine_Block_Style_Metadata_Gutenberg::add_metadata( $new_metadata ); + $this->assertEquals( $new_metadata['typography']['textIndent'], WP_Style_Engine_Block_Style_Metadata_Gutenberg::get_metadata( array( 'typography', 'textIndent' ) ) ); $block_styles = array( 'typography' => array( @@ -170,10 +170,10 @@ public function test_should_overwrite_style_property_metadata() { ), ); - WP_Style_Engine_Block_Supports_Gutenberg::add_metadata( $new_metadata ); + WP_Style_Engine_Block_Style_Metadata_Gutenberg::add_metadata( $new_metadata ); $this->assertEquals( $expected_merged_metadata['spacing']['padding'], - WP_Style_Engine_Block_Supports_Gutenberg::get_metadata( array( 'spacing', 'padding' ) ), + WP_Style_Engine_Block_Style_Metadata_Gutenberg::get_metadata( array( 'spacing', 'padding' ) ), 'The newly-merged property metadata should be present' ); @@ -240,8 +240,8 @@ public function test_should_get_dereferenced_array() { ), ); - WP_Style_Engine_Block_Supports_Gutenberg::add_metadata( $new_metadata ); - $this->assertEquals( $expected_merged_metadata['spacing']['padding'], WP_Style_Engine_Block_Supports_Gutenberg::get_metadata( array( 'spacing', 'padding' ) ), 'Should merge' ); + WP_Style_Engine_Block_Style_Metadata_Gutenberg::add_metadata( $new_metadata ); + $this->assertEquals( $expected_merged_metadata['spacing']['padding'], WP_Style_Engine_Block_Style_Metadata_Gutenberg::get_metadata( array( 'spacing', 'padding' ) ), 'Should merge' ); $this->assertEquals( $expected_original_metadata['spacing']['padding'], WP_Style_Engine_Gutenberg::BLOCK_STYLE_DEFINITIONS_METADATA['spacing']['padding'], 'Should not affect original' ); } } diff --git a/tools/webpack/packages.js b/tools/webpack/packages.js index 55c9140a682873..5f17c733a21616 100644 --- a/tools/webpack/packages.js +++ b/tools/webpack/packages.js @@ -32,7 +32,7 @@ const bundledPackagesPhpConfig = [ from: './packages/style-engine/', to: 'build/style-engine/', replaceClasses: [ - 'WP_Style_Engine_Block_Supports', + 'WP_Style_Engine_Block_Style_Metadata', 'WP_Style_Engine_CSS_Declarations', 'WP_Style_Engine_CSS_Rules_Store', 'WP_Style_Engine_CSS_Rule', From 596862f48484c4d37852a93d35168f75d2078e04 Mon Sep 17 00:00:00 2001 From: ramonjd Date: Wed, 26 Oct 2022 17:59:53 +1100 Subject: [PATCH 04/10] Switching to class instantiation instead of a static class --- packages/style-engine/README.md | 36 +++++ ...s-wp-style-engine-block-style-metadata.php | 60 +++++---- .../style-engine/class-wp-style-engine.php | 4 +- packages/style-engine/style-engine.php | 6 +- ...style-engine-block-style-metadata-test.php | 123 +++++++++++------- 5 files changed, 154 insertions(+), 75 deletions(-) diff --git a/packages/style-engine/README.md b/packages/style-engine/README.md index 290e04ee60ae83..990789b902f0bd 100644 --- a/packages/style-engine/README.md +++ b/packages/style-engine/README.md @@ -36,6 +36,7 @@ _Parameters_ - _context_ `string` An identifier describing the origin of the style object, e.g., 'block-supports' or 'global-styles'. Default is 'block-supports'. When both `context` and `selector` are set, the Style Engine will store the CSS rules using the `context` as a key. - _convert_vars_to_classnames_ `boolean` Whether to skip converting CSS var:? values to var( --wp--preset--\* ) values. Default is `false`. - _selector_ `string` When a selector is passed, `generate()` will return a full CSS rule `$selector { ...rules }`, otherwise a concatenated string of properties and values. + - _metadata_ `array` An associate array in the format of WP_Style_Engine::BLOCK_STYLE_DEFINITIONS_METADATA that extends the latter. _Returns_ `array|null` @@ -87,6 +88,41 @@ array( */ ``` +The default block style support can be extended by passing metadata in the options. + +```php +$block_attributes = array( + 'style' => array( + 'layout' => array( 'float' => 'left' ), + ), +); + +$styles = wp_style_engine_get_styles( + $block_attributes['style'], + array( + 'selector' => '.a-selector', + 'metadata' => array( + 'layout' => array( + 'float' => array( + 'property_keys' => array( + 'default' => 'float', + ), + 'path' => array( 'layout', 'float' ), + ), + ), + ) + ) +); +print_r( $styles ); + +/* +array( + 'css' => '.a-selector{float:left}' + 'declarations' => array( 'float' => 'left' ) +) +*/ +``` + ### wp_style_engine_get_stylesheet_from_css_rules() Use this function to compile and return a stylesheet for any CSS rules. The Style Engine will automatically merge declarations and combine selectors. diff --git a/packages/style-engine/class-wp-style-engine-block-style-metadata.php b/packages/style-engine/class-wp-style-engine-block-style-metadata.php index 7b27368b0a7a46..b2a8361ba6f315 100644 --- a/packages/style-engine/class-wp-style-engine-block-style-metadata.php +++ b/packages/style-engine/class-wp-style-engine-block-style-metadata.php @@ -16,34 +16,49 @@ * * @access private */ -final class WP_Style_Engine_Block_Style_Metadata { +class WP_Style_Engine_Block_Style_Metadata { + /** + * The original metadata. + * + * @var array + */ + protected $base_metadata = array(); + /** * The merged metadata. * * @var array */ - protected static $merged_block_support_metadata = array(); + protected $merged_block_support_metadata = array(); + + /** + * Constructor for this object. + * + * If a `$declarations` array is passed, it will be used to populate + * the initial $declarations prop of the object by calling add_declarations(). + * + * @param array $base_metadata An associative array of block style metadata to extend. + */ + public function __construct( $base_metadata = array() ) { + $this->base_metadata = $base_metadata; + $this->reset_metadata(); + } /** * Add block style metadata. * * @param array $metadata The $metadata. * - * @return void + * @return WP_Style_Engine_Block_Style_Metadata Returns the object to allow chaining methods. */ - public static function add_metadata( $metadata = array() ) { - // Assigns value to $merged_block_support_metadata if not already set. - if ( empty( static::$merged_block_support_metadata ) ) { - static::reset_metadata(); - } - + public function add_metadata( $metadata = array() ) { foreach ( $metadata as $definition_group_key => $definition_group_style ) { if ( ! is_array( $definition_group_style ) || empty( $definition_group_style ) ) { continue; } - if ( ! array_key_exists( $definition_group_key, static::$merged_block_support_metadata ) ) { - static::$merged_block_support_metadata[ $definition_group_key ] = array(); + if ( ! array_key_exists( $definition_group_key, $this->merged_block_support_metadata ) ) { + $this->merged_block_support_metadata[ $definition_group_key ] = array(); } foreach ( $definition_group_style as $style_definition_key => $style_definition ) { @@ -51,14 +66,15 @@ public static function add_metadata( $metadata = array() ) { continue; } - $array_to_extend = array_key_exists( $style_definition_key, static::$merged_block_support_metadata[ $definition_group_key ] ) ? static::$merged_block_support_metadata[ $definition_group_key ][ $style_definition_key ] : array(); - $merged_style_definition = static::merge_custom_style_definitions_metadata( $array_to_extend, $style_definition ); + $array_to_extend = array_key_exists( $style_definition_key, $this->merged_block_support_metadata[ $definition_group_key ] ) ? $this->merged_block_support_metadata[ $definition_group_key ][ $style_definition_key ] : array(); + $merged_style_definition = $this->merge_custom_style_definitions_metadata( $array_to_extend, $style_definition ); if ( $merged_style_definition ) { - static::$merged_block_support_metadata[ $definition_group_key ][ $style_definition_key ] = $merged_style_definition; + $this->merged_block_support_metadata[ $definition_group_key ][ $style_definition_key ] = $merged_style_definition; } } } + return $this; } /** @@ -67,15 +83,11 @@ public static function add_metadata( $metadata = array() ) { * @param array $path A path to an array item in static::$merged_block_support_metadata. * @return array */ - public static function get_metadata( $path = array() ) { - // Assigns value to $merged_block_support_metadata if not already set. - if ( empty( static::$merged_block_support_metadata ) ) { - static::reset_metadata(); - } + public function get_metadata( $path = array() ) { if ( ! empty( $path ) ) { - return _wp_array_get( static::$merged_block_support_metadata, $path, null ); + return _wp_array_get( $this->merged_block_support_metadata, $path, null ); } - return static::$merged_block_support_metadata; + return $this->merged_block_support_metadata; } /** @@ -83,8 +95,8 @@ public static function get_metadata( $path = array() ) { * * @return void */ - public static function reset_metadata() { - static::$merged_block_support_metadata = json_decode( wp_json_encode( WP_Style_Engine::BLOCK_STYLE_DEFINITIONS_METADATA ), true ); + public function reset_metadata() { + $this->merged_block_support_metadata = json_decode( wp_json_encode( $this->base_metadata ), true ); } /** @@ -95,7 +107,7 @@ public static function reset_metadata() { * * @return array|void The merged definition metadata. */ - protected static function merge_custom_style_definitions_metadata( $style_definition, $custom_definition = array() ) { + protected function merge_custom_style_definitions_metadata( $style_definition, $custom_definition = array() ) { // Required metadata. if ( ! isset( $style_definition['path'] ) && ! isset( $custom_definition['path'] ) && ! is_array( $custom_definition['path'] ) ) { return; diff --git a/packages/style-engine/class-wp-style-engine.php b/packages/style-engine/class-wp-style-engine.php index 92f4c256eadea4..036c7f27853350 100644 --- a/packages/style-engine/class-wp-style-engine.php +++ b/packages/style-engine/class-wp-style-engine.php @@ -364,7 +364,7 @@ public static function parse_block_styles( $block_styles, $options ) { } // Collect CSS and classnames. - foreach ( WP_Style_Engine_Block_Style_Metadata::get_metadata() as $definition_group_key => $definition_group_style ) { + foreach ( $options['metadata'] as $definition_group_key => $definition_group_style ) { if ( empty( $block_styles[ $definition_group_key ] ) ) { continue; } @@ -527,7 +527,7 @@ protected static function get_individual_property_css_declarations( $style_value // Build a path to the individual rules in definitions. $style_definition_path = array( $definition_group_key, $css_property ); - $style_definition = _wp_array_get( WP_Style_Engine_Block_Style_Metadata::get_metadata(), $style_definition_path, null ); + $style_definition = _wp_array_get( $options['metadata'], $style_definition_path, null ); if ( $style_definition && isset( $style_definition['property_keys']['individual'] ) ) { // Set a CSS var if there is a valid preset value. diff --git a/packages/style-engine/style-engine.php b/packages/style-engine/style-engine.php index 4571a2fcce4ffe..ce0047a44dcc04 100644 --- a/packages/style-engine/style-engine.php +++ b/packages/style-engine/style-engine.php @@ -30,6 +30,7 @@ * @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 array $metadata An associate array in the format of WP_Style_Engine::BLOCK_STYLE_DEFINITIONS_METADATA that extends the latter. * } * * @return array { @@ -45,10 +46,13 @@ function wp_style_engine_get_styles( $block_styles, $options = array() ) { 'selector' => null, 'context' => null, 'convert_vars_to_classnames' => false, + 'metadata' => array(), ) ); - $parsed_styles = WP_Style_Engine::parse_block_styles( $block_styles, $options ); + $block_style_metadata = new WP_Style_Engine_Block_Style_Metadata( WP_Style_Engine::BLOCK_STYLE_DEFINITIONS_METADATA ); + $options['metadata'] = $block_style_metadata->add_metadata( $options['metadata'] )->get_metadata(); + $parsed_styles = WP_Style_Engine::parse_block_styles( $block_styles, $options ); // Output. $styles_output = array(); diff --git a/phpunit/style-engine/class-wp-style-engine-block-style-metadata-test.php b/phpunit/style-engine/class-wp-style-engine-block-style-metadata-test.php index cb7d736c4929d1..4dcc639b4de7a0 100644 --- a/phpunit/style-engine/class-wp-style-engine-block-style-metadata-test.php +++ b/phpunit/style-engine/class-wp-style-engine-block-style-metadata-test.php @@ -12,30 +12,24 @@ * @coversDefaultClass WP_Style_Engine_Block_Style_Metadata_Gutenberg */ class WP_Style_Engine_Block_Style_Metadata_Test extends WP_UnitTestCase { - /** - * Resets metadata after each test. - */ - public function tear_down() { - WP_Style_Engine_Block_Style_Metadata_Gutenberg::reset_metadata(); - parent::tear_down(); - } - /** * Tests getting metadata. * * @covers ::get_metadata */ public function test_should_get_metadata() { - $metadata = WP_Style_Engine_Block_Style_Metadata_Gutenberg::get_metadata(); - $this->assertEquals( WP_Style_Engine_Gutenberg::BLOCK_STYLE_DEFINITIONS_METADATA, $metadata, 'Returning all default definitions' ); + $block_style_metadata = new WP_Style_Engine_Block_Style_Metadata_Gutenberg( WP_Style_Engine_Gutenberg::BLOCK_STYLE_DEFINITIONS_METADATA ); + + $full_metadata = $block_style_metadata->get_metadata(); + $this->assertEquals( WP_Style_Engine_Gutenberg::BLOCK_STYLE_DEFINITIONS_METADATA, $full_metadata, 'Returning all default definitions' ); - $color_metadata = WP_Style_Engine_Block_Style_Metadata_Gutenberg::get_metadata( array( 'color' ) ); + $color_metadata = $block_style_metadata->get_metadata( array( 'color' ) ); $this->assertEquals( WP_Style_Engine_Gutenberg::BLOCK_STYLE_DEFINITIONS_METADATA['color'], $color_metadata, 'Returning top-level color definition' ); - $color_metadata = WP_Style_Engine_Block_Style_Metadata_Gutenberg::get_metadata( array( 'color', 'background' ) ); + $color_metadata = $block_style_metadata->get_metadata( array( 'color', 'background' ) ); $this->assertEquals( WP_Style_Engine_Gutenberg::BLOCK_STYLE_DEFINITIONS_METADATA['color']['background'], $color_metadata, 'Returning second-level color > background definition' ); - $null_metadata = WP_Style_Engine_Block_Style_Metadata_Gutenberg::get_metadata( array( 'something', 'background' ) ); + $null_metadata = $block_style_metadata->get_metadata( array( 'something', 'background' ) ); $this->assertNull( $null_metadata, 'Returning `null` where the path is invalid' ); } @@ -45,7 +39,8 @@ public function test_should_get_metadata() { * @covers ::add_metadata */ public function test_should_add_new_top_level_metadata() { - $new_metadata = array( + $block_style_metadata = new WP_Style_Engine_Block_Style_Metadata_Gutenberg( WP_Style_Engine_Gutenberg::BLOCK_STYLE_DEFINITIONS_METADATA ); + $new_metadata = array( 'layout' => array( 'float' => array( 'property_keys' => array( @@ -72,8 +67,11 @@ public function test_should_add_new_top_level_metadata() { ), ), ); - WP_Style_Engine_Block_Style_Metadata_Gutenberg::add_metadata( $new_metadata ); - $this->assertEquals( $new_metadata['layout'], WP_Style_Engine_Block_Style_Metadata_Gutenberg::get_metadata( array( 'layout' ) ), 'A new style definition for `layout` should be registered' ); + $this->assertEquals( + $new_metadata['layout'], + $block_style_metadata->add_metadata( $new_metadata )->get_metadata( array( 'layout' ) ), + 'A new style definition for `layout` should be registered' + ); $block_styles = array( 'layout' => array( @@ -93,9 +91,18 @@ public function test_should_add_new_top_level_metadata() { ), 'classnames' => 'has-float-layout has-left-float', ); - $generated_styles = gutenberg_style_engine_get_styles( $block_styles ); + $generated_styles = gutenberg_style_engine_get_styles( + $block_styles, + array( + 'metadata' => $new_metadata, + ) + ); - $this->assertSame( $expected_styles, $generated_styles, 'CSS should be generated using the newly-added metadata' ); + $this->assertSame( + $expected_styles, + $generated_styles, + 'CSS should be generated using the newly-added metadata' + ); } /** @@ -104,7 +111,8 @@ public function test_should_add_new_top_level_metadata() { * @covers ::add_metadata */ public function test_should_add_new_style_property_metadata() { - $new_metadata = array( + $block_style_metadata = new WP_Style_Engine_Block_Style_Metadata_Gutenberg( WP_Style_Engine_Gutenberg::BLOCK_STYLE_DEFINITIONS_METADATA ); + $new_metadata = array( 'typography' => array( 'textIndent' => array( 'property_keys' => array( @@ -117,8 +125,12 @@ public function test_should_add_new_style_property_metadata() { ), ), ); - WP_Style_Engine_Block_Style_Metadata_Gutenberg::add_metadata( $new_metadata ); - $this->assertEquals( $new_metadata['typography']['textIndent'], WP_Style_Engine_Block_Style_Metadata_Gutenberg::get_metadata( array( 'typography', 'textIndent' ) ) ); + $block_style_metadata->add_metadata( $new_metadata ); + $this->assertEquals( + $new_metadata['typography']['textIndent'], + $block_style_metadata->get_metadata( array( 'typography', 'textIndent' ) ), + 'The new style property should match expected.' + ); $block_styles = array( 'typography' => array( @@ -132,9 +144,18 @@ public function test_should_add_new_style_property_metadata() { ), 'classnames' => 'has-text-indent', ); - $generated_styles = gutenberg_style_engine_get_styles( $block_styles ); + $generated_styles = gutenberg_style_engine_get_styles( + $block_styles, + array( + 'metadata' => $new_metadata, + ) + ); - $this->assertSame( $expected_styles, $generated_styles, 'CSS should be generated using the newly-added property metadata' ); + $this->assertSame( + $expected_styles, + $generated_styles, + 'CSS should be generated using the newly-added property metadata' + ); } /** @@ -143,6 +164,7 @@ public function test_should_add_new_style_property_metadata() { * @covers ::add_metadata */ public function test_should_overwrite_style_property_metadata() { + $block_style_metadata = new WP_Style_Engine_Block_Style_Metadata_Gutenberg( WP_Style_Engine_Gutenberg::BLOCK_STYLE_DEFINITIONS_METADATA ); $new_metadata = array( 'spacing' => array( 'padding' => array( @@ -170,10 +192,10 @@ public function test_should_overwrite_style_property_metadata() { ), ); - WP_Style_Engine_Block_Style_Metadata_Gutenberg::add_metadata( $new_metadata ); + $block_style_metadata->add_metadata( $new_metadata ); $this->assertEquals( $expected_merged_metadata['spacing']['padding'], - WP_Style_Engine_Block_Style_Metadata_Gutenberg::get_metadata( array( 'spacing', 'padding' ) ), + $block_style_metadata->get_metadata( array( 'spacing', 'padding' ) ), 'The newly-merged property metadata should be present' ); @@ -188,18 +210,28 @@ public function test_should_overwrite_style_property_metadata() { 'columns' => '1rem', ), ); - $generated_styles = gutenberg_style_engine_get_styles( $block_styles ); + $generated_styles = gutenberg_style_engine_get_styles( + $block_styles, + array( + 'metadata' => $new_metadata, + ) + ); - $this->assertSame( $expected_styles, $generated_styles, 'CSS should be generated using the newly-merged property metadata' ); + $this->assertSame( + $expected_styles, + $generated_styles, + 'CSS should be generated using the newly-merged property metadata' + ); } /** - * Tests merging metadata to the block styles definition. + * Tests resetting metadata to the original block styles definition. * - * @covers ::add_metadata + * @covers ::reset_metadata */ - public function test_should_get_dereferenced_array() { - $new_metadata = array( + public function test_should_reset_metadata() { + $block_style_metadata = new WP_Style_Engine_Block_Style_Metadata_Gutenberg( WP_Style_Engine_Gutenberg::BLOCK_STYLE_DEFINITIONS_METADATA ); + $new_metadata = array( 'spacing' => array( 'padding' => array( 'property_keys' => array( @@ -211,7 +243,7 @@ public function test_should_get_dereferenced_array() { ), ), ); - $expected_merged_metadata = array( + $expected_merged_metadata = array( 'spacing' => array( 'padding' => array( 'property_keys' => array( @@ -225,23 +257,18 @@ public function test_should_get_dereferenced_array() { ), ), ); - $expected_original_metadata = array( - 'spacing' => array( - 'padding' => array( - 'property_keys' => array( - 'default' => 'padding', - 'individual' => 'padding-%s', - ), - 'path' => array( 'spacing', 'padding' ), - 'css_vars' => array( - 'spacing' => '--wp--preset--spacing--$slug', - ), - ), - ), + + $this->assertEquals( + $expected_merged_metadata['spacing']['padding'], + $block_style_metadata->add_metadata( $new_metadata )->get_metadata( array( 'spacing', 'padding' ) ), + 'Should merge' ); - WP_Style_Engine_Block_Style_Metadata_Gutenberg::add_metadata( $new_metadata ); - $this->assertEquals( $expected_merged_metadata['spacing']['padding'], WP_Style_Engine_Block_Style_Metadata_Gutenberg::get_metadata( array( 'spacing', 'padding' ) ), 'Should merge' ); - $this->assertEquals( $expected_original_metadata['spacing']['padding'], WP_Style_Engine_Gutenberg::BLOCK_STYLE_DEFINITIONS_METADATA['spacing']['padding'], 'Should not affect original' ); + $block_style_metadata->reset_metadata(); + $this->assertEquals( + WP_Style_Engine_Gutenberg::BLOCK_STYLE_DEFINITIONS_METADATA['spacing']['padding'], + $block_style_metadata->get_metadata( array( 'spacing', 'padding' ) ), + 'Should be equal to original' + ); } } From cb0a71058d71abc0cabb6276fc5248ef90a97504 Mon Sep 17 00:00:00 2001 From: ramonjd Date: Wed, 26 Oct 2022 18:01:43 +1100 Subject: [PATCH 05/10] Changelog --- packages/style-engine/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/style-engine/CHANGELOG.md b/packages/style-engine/CHANGELOG.md index 48030434c19496..1de7c41282697a 100644 --- a/packages/style-engine/CHANGELOG.md +++ b/packages/style-engine/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Enhancement + +- Style engine: provide a way to extend default block support style definitions [#45296](https://github.com/WordPress/gutenberg/pull/45296) + ## 1.24.0 (2023-08-31) ## 1.23.0 (2023-08-16) From 8ede0356bda80707dd7589ba95c2d77abbada155 Mon Sep 17 00:00:00 2001 From: ramonjd Date: Fri, 28 Oct 2022 14:17:13 +1100 Subject: [PATCH 06/10] Fixing up comments and README.md --- packages/style-engine/README.md | 4 +++- .../class-wp-style-engine-block-style-metadata.php | 9 +++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/style-engine/README.md b/packages/style-engine/README.md index 990789b902f0bd..2c2f380d93e712 100644 --- a/packages/style-engine/README.md +++ b/packages/style-engine/README.md @@ -88,7 +88,7 @@ array( */ ``` -The default block style support can be extended by passing metadata in the options. +The default block style support definitions can be extended by passing metadata in the options. ```php $block_attributes = array( @@ -123,6 +123,8 @@ array( */ ``` +Note: CSS properties must be supported by [safecss_filter_attr](https://developer.wordpress.org/reference/functions/safecss_filter_attr/), otherwise you can allow non-supported CSS using the filter [safecss_filter_attr_allow_css](https://developer.wordpress.org/reference/hooks/safecss_filter_attr_allow_css/). + ### wp_style_engine_get_stylesheet_from_css_rules() Use this function to compile and return a stylesheet for any CSS rules. The Style Engine will automatically merge declarations and combine selectors. diff --git a/packages/style-engine/class-wp-style-engine-block-style-metadata.php b/packages/style-engine/class-wp-style-engine-block-style-metadata.php index b2a8361ba6f315..47fa4005c75d64 100644 --- a/packages/style-engine/class-wp-style-engine-block-style-metadata.php +++ b/packages/style-engine/class-wp-style-engine-block-style-metadata.php @@ -18,7 +18,7 @@ */ class WP_Style_Engine_Block_Style_Metadata { /** - * The original metadata. + * A variable to cache original metadata. * * @var array */ @@ -34,9 +34,6 @@ class WP_Style_Engine_Block_Style_Metadata { /** * Constructor for this object. * - * If a `$declarations` array is passed, it will be used to populate - * the initial $declarations prop of the object by calling add_declarations(). - * * @param array $base_metadata An associative array of block style metadata to extend. */ public function __construct( $base_metadata = array() ) { @@ -45,7 +42,7 @@ public function __construct( $base_metadata = array() ) { } /** - * Add block style metadata. + * Adds block style metadata. * * @param array $metadata The $metadata. * @@ -78,7 +75,7 @@ public function add_metadata( $metadata = array() ) { } /** - * Get the metadata array. + * Returns merged metadata. * * @param array $path A path to an array item in static::$merged_block_support_metadata. * @return array From ba1c6d5366f6e06c1d3723f47aa00b78b2a8192c Mon Sep 17 00:00:00 2001 From: ramonjd Date: Fri, 28 Oct 2022 15:21:14 +1100 Subject: [PATCH 07/10] - only allow adding new styles, not overwriting original styles - moving CSS style output tests to style-engine-test.php --- ...s-wp-style-engine-block-style-metadata.php | 12 +- ...style-engine-block-style-metadata-test.php | 150 +++--------------- phpunit/style-engine/style-engine-test.php | 65 ++++++++ 3 files changed, 98 insertions(+), 129 deletions(-) diff --git a/packages/style-engine/class-wp-style-engine-block-style-metadata.php b/packages/style-engine/class-wp-style-engine-block-style-metadata.php index 47fa4005c75d64..b8bde570f5861f 100644 --- a/packages/style-engine/class-wp-style-engine-block-style-metadata.php +++ b/packages/style-engine/class-wp-style-engine-block-style-metadata.php @@ -59,6 +59,12 @@ public function add_metadata( $metadata = array() ) { } foreach ( $definition_group_style as $style_definition_key => $style_definition ) { + // Bails early if merging metadata is attempting to overwrite existing, original style metadata. + if ( array_key_exists( $definition_group_key, $this->base_metadata ) + && array_key_exists( $style_definition_key, $this->base_metadata[ $definition_group_key ] ) ) { + continue; + } + if ( ! is_array( $style_definition ) || empty( $style_definition ) ) { continue; } @@ -120,13 +126,9 @@ protected function merge_custom_style_definitions_metadata( $style_definition, $ return; } - // Only allow specific value_func. - if ( isset( $custom_definition['value_func'] ) && 'WP_Style_Engine::get_individual_property_css_declarations' !== $custom_definition['value_func'] ) { - return; - } - $custom_definition['property_keys']['default'] = sanitize_key( $custom_definition['property_keys']['default'] ); + // A white list of keys that may be merged. Note the absence of the callable `value_func`. $valid_keys = array( 'path', 'property_keys', 'css_vars', 'classnames' ); foreach ( $valid_keys as $key ) { if ( isset( $custom_definition[ $key ] ) && is_array( $custom_definition[ $key ] ) ) { diff --git a/phpunit/style-engine/class-wp-style-engine-block-style-metadata-test.php b/phpunit/style-engine/class-wp-style-engine-block-style-metadata-test.php index 4dcc639b4de7a0..1d225e4263aa59 100644 --- a/phpunit/style-engine/class-wp-style-engine-block-style-metadata-test.php +++ b/phpunit/style-engine/class-wp-style-engine-block-style-metadata-test.php @@ -72,37 +72,6 @@ public function test_should_add_new_top_level_metadata() { $block_style_metadata->add_metadata( $new_metadata )->get_metadata( array( 'layout' ) ), 'A new style definition for `layout` should be registered' ); - - $block_styles = array( - 'layout' => array( - 'float' => 'var:preset|layout|left', - 'width' => array( - 'max' => '100px', - 'min' => '20px', - ), - ), - ); - $expected_styles = array( - 'css' => 'float:var(--wp--preset--float--left);max-width:100px;min-width:20px;', - 'declarations' => array( - 'float' => 'var(--wp--preset--float--left)', - 'max-width' => '100px', - 'min-width' => '20px', - ), - 'classnames' => 'has-float-layout has-left-float', - ); - $generated_styles = gutenberg_style_engine_get_styles( - $block_styles, - array( - 'metadata' => $new_metadata, - ) - ); - - $this->assertSame( - $expected_styles, - $generated_styles, - 'CSS should be generated using the newly-added metadata' - ); } /** @@ -110,7 +79,7 @@ public function test_should_add_new_top_level_metadata() { * * @covers ::add_metadata */ - public function test_should_add_new_style_property_metadata() { + public function test_should_add_new_style_property_metadata_keys() { $block_style_metadata = new WP_Style_Engine_Block_Style_Metadata_Gutenberg( WP_Style_Engine_Gutenberg::BLOCK_STYLE_DEFINITIONS_METADATA ); $new_metadata = array( 'typography' => array( @@ -118,54 +87,37 @@ public function test_should_add_new_style_property_metadata() { 'property_keys' => array( 'default' => 'text-indent', ), + 'css_vars' => array( + 'spacing' => '--wp--preset--spacing--$slug', + ), 'path' => array( 'typography', 'textIndent' ), 'classnames' => array( 'has-text-indent' => true, ), + 'value_func' => 'Test::function', ), ), ); $block_style_metadata->add_metadata( $new_metadata ); + + // Remove ignored property keys. + unset( $new_metadata['typography']['textIndent']['value_func'] ); + $this->assertEquals( $new_metadata['typography']['textIndent'], $block_style_metadata->get_metadata( array( 'typography', 'textIndent' ) ), 'The new style property should match expected.' ); - - $block_styles = array( - 'typography' => array( - 'textIndent' => '1rem', - ), - ); - $expected_styles = array( - 'css' => 'text-indent:1rem;', - 'declarations' => array( - 'text-indent' => '1rem', - ), - 'classnames' => 'has-text-indent', - ); - $generated_styles = gutenberg_style_engine_get_styles( - $block_styles, - array( - 'metadata' => $new_metadata, - ) - ); - - $this->assertSame( - $expected_styles, - $generated_styles, - 'CSS should be generated using the newly-added property metadata' - ); } /** - * Tests merging metadata to the block styles definition. + * Tests that merging style metadata to the block styles definitions does not work. * * @covers ::add_metadata */ - public function test_should_overwrite_style_property_metadata() { - $block_style_metadata = new WP_Style_Engine_Block_Style_Metadata_Gutenberg( WP_Style_Engine_Gutenberg::BLOCK_STYLE_DEFINITIONS_METADATA ); - $new_metadata = array( + public function test_should_not_overwrite_style_property_metadata() { + $block_style_metadata = new WP_Style_Engine_Block_Style_Metadata_Gutenberg( WP_Style_Engine_Gutenberg::BLOCK_STYLE_DEFINITIONS_METADATA ); + $new_metadata = array( 'spacing' => array( 'padding' => array( 'property_keys' => array( @@ -177,51 +129,13 @@ public function test_should_overwrite_style_property_metadata() { ), ), ); - $expected_merged_metadata = array( - 'spacing' => array( - 'padding' => array( - 'property_keys' => array( - 'default' => 'columns', - 'individual' => 'padding-%s', - ), - 'path' => array( 'spacing', 'padding' ), - 'css_vars' => array( - 'spacing' => '--wp--preset--column--$slug', - ), - ), - ), - ); $block_style_metadata->add_metadata( $new_metadata ); $this->assertEquals( - $expected_merged_metadata['spacing']['padding'], + WP_Style_Engine_Gutenberg::BLOCK_STYLE_DEFINITIONS_METADATA['spacing']['padding'], $block_style_metadata->get_metadata( array( 'spacing', 'padding' ) ), 'The newly-merged property metadata should be present' ); - - $block_styles = array( - 'spacing' => array( - 'padding' => '1rem', - ), - ); - $expected_styles = array( - 'css' => 'columns:1rem;', - 'declarations' => array( - 'columns' => '1rem', - ), - ); - $generated_styles = gutenberg_style_engine_get_styles( - $block_styles, - array( - 'metadata' => $new_metadata, - ) - ); - - $this->assertSame( - $expected_styles, - $generated_styles, - 'CSS should be generated using the newly-merged property metadata' - ); } /** @@ -230,44 +144,32 @@ public function test_should_overwrite_style_property_metadata() { * @covers ::reset_metadata */ public function test_should_reset_metadata() { - $block_style_metadata = new WP_Style_Engine_Block_Style_Metadata_Gutenberg( WP_Style_Engine_Gutenberg::BLOCK_STYLE_DEFINITIONS_METADATA ); - $new_metadata = array( - 'spacing' => array( - 'padding' => array( - 'property_keys' => array( - 'default' => 'columns', - ), - 'css_vars' => array( - 'spacing' => '--wp--preset--column--$slug', - ), - ), - ), - ); - $expected_merged_metadata = array( + $block_style_metadata = new WP_Style_Engine_Block_Style_Metadata_Gutenberg( WP_Style_Engine_Gutenberg::BLOCK_STYLE_DEFINITIONS_METADATA ); + $new_metadata = array( 'spacing' => array( - 'padding' => array( + 'gap' => array( 'property_keys' => array( - 'default' => 'columns', - 'individual' => 'padding-%s', + 'default' => 'gap', + 'individual' => 'gap-%', ), - 'path' => array( 'spacing', 'padding' ), + 'path' => array( 'spacing', 'gap' ), 'css_vars' => array( - 'spacing' => '--wp--preset--column--$slug', + 'spacing' => '--wp--preset--spacing--$slug', ), ), ), ); $this->assertEquals( - $expected_merged_metadata['spacing']['padding'], - $block_style_metadata->add_metadata( $new_metadata )->get_metadata( array( 'spacing', 'padding' ) ), - 'Should merge' + $new_metadata['spacing']['gap'], + $block_style_metadata->add_metadata( $new_metadata )->get_metadata( array( 'spacing', 'gap' ) ), + 'Should successfully merge metadata' ); $block_style_metadata->reset_metadata(); $this->assertEquals( - WP_Style_Engine_Gutenberg::BLOCK_STYLE_DEFINITIONS_METADATA['spacing']['padding'], - $block_style_metadata->get_metadata( array( 'spacing', 'padding' ) ), + WP_Style_Engine_Gutenberg::BLOCK_STYLE_DEFINITIONS_METADATA['spacing'], + $block_style_metadata->get_metadata( array( 'spacing' ) ), 'Should be equal to original' ); } diff --git a/phpunit/style-engine/style-engine-test.php b/phpunit/style-engine/style-engine-test.php index fae95b995ee44d..279b051aa83ee2 100644 --- a/phpunit/style-engine/style-engine-test.php +++ b/phpunit/style-engine/style-engine-test.php @@ -493,6 +493,71 @@ public function data_wp_style_engine_get_styles() { ), ), ), + + 'extend_block_style_definitions_with_metadata' => array( + 'block_styles' => array( + 'layout' => array( + 'float' => 'var:preset|layout|left', + 'width' => array( + 'max' => '100px', + 'min' => '20px', + ), + ), + 'typography' => array( + 'textIndent' => '1rem', + ), + ), + 'options' => array( + 'metadata' => array( + 'layout' => array( + 'float' => array( + 'property_keys' => array( + 'default' => 'float', + ), + 'path' => array( 'layout', 'float' ), + 'css_vars' => array( + 'layout' => '--wp--preset--float--$slug', + ), + 'classnames' => array( + 'has-float-layout' => true, + 'has-$slug-float' => 'layout', + ), + ), + 'width' => array( + 'property_keys' => array( + 'default' => 'width', + 'individual' => '%s-width', + ), + 'path' => array( 'layout', 'width' ), + 'classnames' => array( + 'has-$slug-width' => 'layout', + ), + ), + ), + 'typography' => array( + 'textIndent' => array( + 'property_keys' => array( + 'default' => 'text-indent', + ), + 'path' => array( 'typography', 'textIndent' ), + 'classnames' => array( + 'has-text-indent' => true, + ), + ), + ), + ), + ), + 'expected_output' => array( + 'css' => 'text-indent:1rem;float:var(--wp--preset--float--left);max-width:100px;min-width:20px;', + 'declarations' => array( + 'text-indent' => '1rem', + 'float' => 'var(--wp--preset--float--left)', + 'max-width' => '100px', + 'min-width' => '20px', + ), + 'classnames' => 'has-text-indent has-float-layout has-left-float', + ), + ), ); } From 18504364b1bc9a4ec2e9502b89ccfb912e76a09e Mon Sep 17 00:00:00 2001 From: ramonjd Date: Fri, 18 Nov 2022 14:00:53 +1100 Subject: [PATCH 08/10] Readme needed some love --- packages/style-engine/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/style-engine/README.md b/packages/style-engine/README.md index 2c2f380d93e712..1cc9a2aad2619d 100644 --- a/packages/style-engine/README.md +++ b/packages/style-engine/README.md @@ -88,7 +88,7 @@ array( */ ``` -The default block style support definitions can be extended by passing metadata in the options. +The default block style support definitions can be extended by passing metadata in the options. ```php $block_attributes = array( From 466127ffed6f48a4371b2ad75e4866ae90c0d5fb Mon Sep 17 00:00:00 2001 From: ramon Date: Mon, 19 Dec 2022 14:55:03 +1100 Subject: [PATCH 09/10] Replace array_key_exists with isset for speed Update var name --- ...s-wp-style-engine-block-style-metadata.php | 20 ++++++++++++------- ...style-engine-block-style-metadata-test.php | 2 +- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/packages/style-engine/class-wp-style-engine-block-style-metadata.php b/packages/style-engine/class-wp-style-engine-block-style-metadata.php index b8bde570f5861f..ac7d9e9f45680e 100644 --- a/packages/style-engine/class-wp-style-engine-block-style-metadata.php +++ b/packages/style-engine/class-wp-style-engine-block-style-metadata.php @@ -44,24 +44,29 @@ public function __construct( $base_metadata = array() ) { /** * Adds block style metadata. * - * @param array $metadata The $metadata. + * @param array $new_metadata The $metadata to be added. * * @return WP_Style_Engine_Block_Style_Metadata Returns the object to allow chaining methods. */ - public function add_metadata( $metadata = array() ) { - foreach ( $metadata as $definition_group_key => $definition_group_style ) { + public function add_metadata( $new_metadata = array() ) { + if ( empty( $new_metadata ) ) { + return $this; + } + + foreach ( $new_metadata as $definition_group_key => $definition_group_style ) { if ( ! is_array( $definition_group_style ) || empty( $definition_group_style ) ) { continue; } - if ( ! array_key_exists( $definition_group_key, $this->merged_block_support_metadata ) ) { + // Adds a new top-level group if it doesn't exist already. + if ( ! isset( $this->merged_block_support_metadata[ $definition_group_key ] ) ) { $this->merged_block_support_metadata[ $definition_group_key ] = array(); } foreach ( $definition_group_style as $style_definition_key => $style_definition ) { // Bails early if merging metadata is attempting to overwrite existing, original style metadata. - if ( array_key_exists( $definition_group_key, $this->base_metadata ) - && array_key_exists( $style_definition_key, $this->base_metadata[ $definition_group_key ] ) ) { + if ( isset( $this->base_metadata[ $definition_group_key ] ) + && isset( $this->base_metadata[ $definition_group_key ][ $style_definition_key ] ) ) { continue; } @@ -69,7 +74,8 @@ public function add_metadata( $metadata = array() ) { continue; } - $array_to_extend = array_key_exists( $style_definition_key, $this->merged_block_support_metadata[ $definition_group_key ] ) ? $this->merged_block_support_metadata[ $definition_group_key ][ $style_definition_key ] : array(); + $array_to_extend = isset( $this->merged_block_support_metadata[ $definition_group_key ][ $style_definition_key ] ) + ? $this->merged_block_support_metadata[ $definition_group_key ][ $style_definition_key ] : array(); $merged_style_definition = $this->merge_custom_style_definitions_metadata( $array_to_extend, $style_definition ); if ( $merged_style_definition ) { diff --git a/phpunit/style-engine/class-wp-style-engine-block-style-metadata-test.php b/phpunit/style-engine/class-wp-style-engine-block-style-metadata-test.php index 1d225e4263aa59..9e4218e6c2e463 100644 --- a/phpunit/style-engine/class-wp-style-engine-block-style-metadata-test.php +++ b/phpunit/style-engine/class-wp-style-engine-block-style-metadata-test.php @@ -75,7 +75,7 @@ public function test_should_add_new_top_level_metadata() { } /** - * Tests adding new second-level property metadata to the block styles definition. + * Tests adding new second-level property metadata to the block styles definition and ignore `value_func` values. * * @covers ::add_metadata */ From eb569b9d6bac1e1c854cd445d58d040f6367f7dd Mon Sep 17 00:00:00 2001 From: ramon Date: Mon, 19 Dec 2022 16:30:03 +1100 Subject: [PATCH 10/10] Remove readme doc for now since it won't be in core for a while. --- packages/style-engine/README.md | 38 --------------------------------- 1 file changed, 38 deletions(-) diff --git a/packages/style-engine/README.md b/packages/style-engine/README.md index 1cc9a2aad2619d..290e04ee60ae83 100644 --- a/packages/style-engine/README.md +++ b/packages/style-engine/README.md @@ -36,7 +36,6 @@ _Parameters_ - _context_ `string` An identifier describing the origin of the style object, e.g., 'block-supports' or 'global-styles'. Default is 'block-supports'. When both `context` and `selector` are set, the Style Engine will store the CSS rules using the `context` as a key. - _convert_vars_to_classnames_ `boolean` Whether to skip converting CSS var:? values to var( --wp--preset--\* ) values. Default is `false`. - _selector_ `string` When a selector is passed, `generate()` will return a full CSS rule `$selector { ...rules }`, otherwise a concatenated string of properties and values. - - _metadata_ `array` An associate array in the format of WP_Style_Engine::BLOCK_STYLE_DEFINITIONS_METADATA that extends the latter. _Returns_ `array|null` @@ -88,43 +87,6 @@ array( */ ``` -The default block style support definitions can be extended by passing metadata in the options. - -```php -$block_attributes = array( - 'style' => array( - 'layout' => array( 'float' => 'left' ), - ), -); - -$styles = wp_style_engine_get_styles( - $block_attributes['style'], - array( - 'selector' => '.a-selector', - 'metadata' => array( - 'layout' => array( - 'float' => array( - 'property_keys' => array( - 'default' => 'float', - ), - 'path' => array( 'layout', 'float' ), - ), - ), - ) - ) -); -print_r( $styles ); - -/* -array( - 'css' => '.a-selector{float:left}' - 'declarations' => array( 'float' => 'left' ) -) -*/ -``` - -Note: CSS properties must be supported by [safecss_filter_attr](https://developer.wordpress.org/reference/functions/safecss_filter_attr/), otherwise you can allow non-supported CSS using the filter [safecss_filter_attr_allow_css](https://developer.wordpress.org/reference/hooks/safecss_filter_attr_allow_css/). - ### wp_style_engine_get_stylesheet_from_css_rules() Use this function to compile and return a stylesheet for any CSS rules. The Style Engine will automatically merge declarations and combine selectors.