From a90494e29954e617b8f612093d58b38cb89bcc30 Mon Sep 17 00:00:00 2001 From: ramonjd Date: Wed, 6 Apr 2022 15:43:47 +1000 Subject: [PATCH 1/6] Initial commit: add typography to style engine --- lib/block-supports/typography.php | 126 ++++++++++-------- .../style-engine/class-wp-style-engine.php | 55 +++++++- .../phpunit/class-wp-style-engine-test.php | 21 ++- 3 files changed, 141 insertions(+), 61 deletions(-) diff --git a/lib/block-supports/typography.php b/lib/block-supports/typography.php index 9661277c1c7b9b..6c20a375a011ff 100644 --- a/lib/block-supports/typography.php +++ b/lib/block-supports/typography.php @@ -92,117 +92,135 @@ function gutenberg_apply_typography_support( $block_type, $block_attributes ) { $has_text_decoration_support = _wp_array_get( $typography_supports, array( '__experimentalTextDecoration' ), false ); $has_text_transform_support = _wp_array_get( $typography_supports, array( '__experimentalTextTransform' ), false ); - if ( $has_font_size_support && ! gutenberg_should_skip_block_supports_serialization( $block_type, 'typography', 'fontSize' ) ) { + // Whether to skip individual block support features. + $should_skip_font_size = gutenberg_should_skip_block_supports_serialization( $block_type, 'typography', 'fontSize' ); + $should_skip_font_family = gutenberg_should_skip_block_supports_serialization( $block_type, 'typography', 'fontFamily' ); + $should_skip_font_style = gutenberg_should_skip_block_supports_serialization( $block_type, 'typography', 'fontStyle' ); + $should_skip_font_weight = gutenberg_should_skip_block_supports_serialization( $block_type, 'typography', 'fontWeight' ); + $should_skip_line_height = gutenberg_should_skip_block_supports_serialization( $block_type, 'typography', 'lineHeight' ); + $should_skip_text_decoration = gutenberg_should_skip_block_supports_serialization( $block_type, 'typography', 'textDecoration' ); + $should_skip_text_transform = gutenberg_should_skip_block_supports_serialization( $block_type, 'typography', 'textTransform' ); + $should_skip_letter_spacing = gutenberg_should_skip_block_supports_serialization( $block_type, 'typography', 'letterSpacing' ); + + if ( $has_font_size_support && ! $should_skip_font_size ) { $has_named_font_size = array_key_exists( 'fontSize', $block_attributes ); $has_custom_font_size = isset( $block_attributes['style']['typography']['fontSize'] ); if ( $has_named_font_size ) { $classes[] = sprintf( 'has-%s-font-size', _wp_to_kebab_case( $block_attributes['fontSize'] ) ); } elseif ( $has_custom_font_size ) { - $styles[] = sprintf( 'font-size: %s;', $block_attributes['style']['typography']['fontSize'] ); + $styles['fontSize'] = $block_attributes['style']['typography']['fontSize']; } } - if ( $has_font_family_support && ! gutenberg_should_skip_block_supports_serialization( $block_type, 'typography', 'fontFamily' ) ) { + if ( $has_font_family_support && ! $should_skip_font_family ) { $has_named_font_family = array_key_exists( 'fontFamily', $block_attributes ); $has_custom_font_family = isset( $block_attributes['style']['typography']['fontFamily'] ); if ( $has_named_font_family ) { $classes[] = sprintf( 'has-%s-font-family', _wp_to_kebab_case( $block_attributes['fontFamily'] ) ); } elseif ( $has_custom_font_family ) { - // Before using classes, the value was serialized as a CSS Custom Property. - // We don't need this code path when it lands in core. $font_family_custom = $block_attributes['style']['typography']['fontFamily']; - if ( strpos( $font_family_custom, 'var:preset|font-family' ) !== false ) { - $index_to_splice = strrpos( $font_family_custom, '|' ) + 1; - $font_family_slug = _wp_to_kebab_case( substr( $font_family_custom, $index_to_splice ) ); - $font_family_custom = sprintf( 'var(--wp--preset--font-family--%s)', $font_family_slug ); - } - $styles[] = sprintf( 'font-family: %s;', $font_family_custom ); + // Before using classes, the value was serialized as a CSS Custom Property. + // We don't need to check for a preset when it lands in core. + $font_family_value = gutenberg_typography_get_preset_inline_style_value( $font_family_custom, 'font-family' ); + $styles['fontFamily'] = $font_family_value; } } - if ( $has_font_style_support && ! gutenberg_should_skip_block_supports_serialization( $block_type, 'typography', 'fontStyle' ) ) { - $font_style = gutenberg_typography_get_css_variable_inline_style( $block_attributes, 'fontStyle', 'font-style' ); - if ( $font_style ) { - $styles[] = $font_style; + if ( $has_font_style_support && ! $should_skip_font_style ) { + $font_style = _wp_array_get( $block_attributes, array( 'style', 'typography', 'fontStyle' ), null ); + $font_style_value = gutenberg_typography_get_preset_inline_style_value( $font_style, 'font-style' ); + if ( $font_style_value ) { + $styles['fontStyle'] = $font_style_value; } } - if ( $has_font_weight_support && ! gutenberg_should_skip_block_supports_serialization( $block_type, 'typography', 'fontWeight' ) ) { - $font_weight = gutenberg_typography_get_css_variable_inline_style( $block_attributes, 'fontWeight', 'font-weight' ); - if ( $font_weight ) { - $styles[] = $font_weight; + if ( $has_font_weight_support && ! $should_skip_font_weight ) { + $font_weight = _wp_array_get( $block_attributes, array( 'style', 'typography', 'fontWeight' ), null ); + $font_weight_value = gutenberg_typography_get_preset_inline_style_value( $font_weight, 'font-weight' ); + if ( $font_weight_value ) { + $styles['fontWeight'] = $font_weight_value; } } - if ( $has_line_height_support && ! gutenberg_should_skip_block_supports_serialization( $block_type, 'typography', 'lineHeight' ) ) { + if ( $has_line_height_support && ! $should_skip_line_height ) { $has_line_height = isset( $block_attributes['style']['typography']['lineHeight'] ); if ( $has_line_height ) { - $styles[] = sprintf( 'line-height: %s;', $block_attributes['style']['typography']['lineHeight'] ); + $styles['lineHeight'] = $block_attributes['style']['typography']['lineHeight']; } } - if ( $has_text_decoration_support && ! gutenberg_should_skip_block_supports_serialization( $block_type, 'typography', 'textDecoration' ) ) { - $text_decoration_style = gutenberg_typography_get_css_variable_inline_style( $block_attributes, 'textDecoration', 'text-decoration' ); - if ( $text_decoration_style ) { - $styles[] = $text_decoration_style; + if ( $has_text_decoration_support && ! $should_skip_text_decoration ) { + $text_decoration = _wp_array_get( $block_attributes, array( 'style', 'typography', 'textDecoration' ), null ); + $text_decoration_value = gutenberg_typography_get_preset_inline_style_value( $text_decoration, 'text-decoration' ); + if ( $text_decoration_value ) { + $styles['textDecoration'] = $text_decoration_value; } } - if ( $has_text_transform_support && ! gutenberg_should_skip_block_supports_serialization( $block_type, 'typography', 'textTransform' ) ) { - $text_transform_style = gutenberg_typography_get_css_variable_inline_style( $block_attributes, 'textTransform', 'text-transform' ); - if ( $text_transform_style ) { - $styles[] = $text_transform_style; + if ( $has_text_transform_support && ! $should_skip_text_transform ) { + $text_transform = _wp_array_get( $block_attributes, array( 'style', 'typography', 'textTransform' ), null ); + $text_transform_value = gutenberg_typography_get_preset_inline_style_value( $text_transform, 'text-transform' ); + if ( $text_transform_value ) { + $styles['textTransform'] = $text_transform_value; } } - if ( $has_letter_spacing_support && ! gutenberg_should_skip_block_supports_serialization( $block_type, 'typography', 'letterSpacing' ) ) { - $letter_spacing_style = gutenberg_typography_get_css_variable_inline_style( $block_attributes, 'letterSpacing', 'letter-spacing' ); - if ( $letter_spacing_style ) { - $styles[] = $letter_spacing_style; + if ( $has_letter_spacing_support && ! $should_skip_letter_spacing ) { + $letter_spacing = _wp_array_get( $block_attributes, array( 'style', 'typography', 'letterSpacing' ), null ); + $letter_spacing_value = gutenberg_typography_get_preset_inline_style_value( $letter_spacing, 'letter-spacing' ); + if ( $letter_spacing_value ) { + $styles['letterSpacing'] = $letter_spacing_value; } } if ( ! empty( $classes ) ) { $attributes['class'] = implode( ' ', $classes ); } - if ( ! empty( $styles ) ) { - $attributes['style'] = implode( ' ', $styles ); + + $style_engine = WP_Style_Engine_Gutenberg::get_instance(); + $inline_styles = $style_engine->generate( + array( 'typography' => $styles ), + array( + 'inline' => true, + ) + ); + + if ( ! empty( $inline_styles ) ) { + $attributes['style'] = $inline_styles; } return $attributes; } /** - * Generates an inline style for a typography feature e.g. text decoration, + * Generates an inline style value for a typography feature e.g. text decoration, * text transform, and font style. * - * @param array $attributes Block's attributes. - * @param string $feature Key for the feature within the typography styles. - * @param string $css_property Slug for the CSS property the inline style sets. + * @param string $style_value A raw style value for a single typography feature from a block's style attribute. + * @param string $css_property Slug for the CSS property the inline style sets. * - * @return string CSS inline style. + * @return string? A CSS inline style value. */ -function gutenberg_typography_get_css_variable_inline_style( $attributes, $feature, $css_property ) { - // Retrieve current attribute value or skip if not found. - $style_value = _wp_array_get( $attributes, array( 'style', 'typography', $feature ), false ); - if ( ! $style_value ) { - return; - } - - // If we don't have a preset CSS variable, we'll assume it's a regular CSS value. - if ( strpos( $style_value, "var:preset|{$css_property}|" ) === false ) { - return sprintf( '%s:%s;', $css_property, $style_value ); +function gutenberg_typography_get_preset_inline_style_value( $style_value, $css_property ) { + // If the style value is not a preset CSS variable go no further. + if ( empty( $style_value ) || strpos( $style_value, "var:preset|{$css_property}|" ) === false ) { + return $style_value; } + // For backwards compatibility. + // Presets were removed in https://github.com/WordPress/gutenberg/pull/27555. // We have a preset CSS variable as the style. // Get the style value from the string and return CSS style. $index_to_splice = strrpos( $style_value, '|' ) + 1; - $slug = substr( $style_value, $index_to_splice ); + // @TODO + // Font family requires the slugs to be converted to kebab case. Should this be optional in this method? + // Let's test with some older blocks. + $slug = _wp_to_kebab_case( substr( $style_value, $index_to_splice ) ); - // Return the actual CSS inline style e.g. `text-decoration:var(--wp--preset--text-decoration--underline);`. - return sprintf( '%s:var(--wp--preset--%s--%s);', $css_property, $css_property, $slug ); + // Return the actual CSS inline style value e.g. `var(--wp--preset--text-decoration--underline);`. + return sprintf( 'var(--wp--preset--%s--%s);', $css_property, $slug ); } // Register the block support. @@ -213,5 +231,3 @@ function gutenberg_typography_get_css_variable_inline_style( $attributes, $featu 'apply' => 'gutenberg_apply_typography_support', ) ); - - diff --git a/packages/style-engine/class-wp-style-engine.php b/packages/style-engine/class-wp-style-engine.php index c07075184c41bc..766b06ea1df4ac 100644 --- a/packages/style-engine/class-wp-style-engine.php +++ b/packages/style-engine/class-wp-style-engine.php @@ -36,16 +36,58 @@ class WP_Style_Engine { * For example, `'padding' => 'array( 'top' => '1em' )` will return `array( 'padding-top' => '1em' )` */ const BLOCK_STYLE_DEFINITIONS_METADATA = array( - 'spacing' => array( + 'spacing' => array( 'padding' => array( 'property_key' => 'padding', 'path' => array( 'spacing', 'padding' ), - 'value_func' => 'static::get_css_box_rules', + 'value_func' => 'static::get_css_rules', ), 'margin' => array( 'property_key' => 'margin', 'path' => array( 'spacing', 'margin' ), - 'value_func' => 'static::get_css_box_rules', + 'value_func' => 'static::get_css_rules', + ), + ), + 'typography' => array( + 'fontSize' => array( + 'property_key' => 'font-size', + 'path' => array( 'typography', 'fontSize' ), + 'value_func' => 'static::get_css_rules', + ), + 'fontFamily' => array( + 'property_key' => 'font-family', + 'path' => array( 'typography', 'fontFamily' ), + 'value_func' => 'static::get_css_rules', + ), + 'fontStyle' => array( + 'property_key' => 'font-style', + 'path' => array( 'typography', 'fontStyle' ), + 'value_func' => 'static::get_css_rules', + ), + 'fontWeight' => array( + 'property_key' => 'font-weight', + 'path' => array( 'typography', 'fontWeight' ), + 'value_func' => 'static::get_css_rules', + ), + 'lineHeight' => array( + 'property_key' => 'line-height', + 'path' => array( 'typography', 'lineHeight' ), + 'value_func' => 'static::get_css_rules', + ), + 'textDecoration' => array( + 'property_key' => 'text-decoration', + 'path' => array( 'typography', 'textDecoration' ), + 'value_func' => 'static::get_css_rules', + ), + 'textTransform' => array( + 'property_key' => 'text-transform', + 'path' => array( 'typography', 'textTransform' ), + 'value_func' => 'static::get_css_rules', + ), + 'letterSpacing' => array( + 'property_key' => 'letter-spacing', + 'path' => array( 'typography', 'letterSpacing' ), + 'value_func' => 'static::get_css_rules', ), ), ); @@ -145,20 +187,23 @@ public function generate( $block_styles, $options = array() ) { } /** - * Returns a CSS ruleset for box model styles such as margins, padding, and borders. + * Default style value parser that returns a CSS ruleset. + * If the input contains an array, it will treated like a box model + * for styles such as margins, padding, and borders * * @param string|array $style_value A single raw Gutenberg style attributes value for a CSS property. * @param string $style_property The CSS property for which we're creating a rule. * * @return array The class name for the added style. */ - public static function get_css_box_rules( $style_value, $style_property ) { + public static function get_css_rules( $style_value, $style_property ) { $rules = array(); if ( ! $style_value ) { return $rules; } + // We assume box model-like properties. if ( is_array( $style_value ) ) { foreach ( $style_value as $key => $value ) { $rules[ "$style_property-$key" ] = $value; diff --git a/packages/style-engine/phpunit/class-wp-style-engine-test.php b/packages/style-engine/phpunit/class-wp-style-engine-test.php index 2e0e9afe17bfc2..a7fe6b0e8ab810 100644 --- a/packages/style-engine/phpunit/class-wp-style-engine-test.php +++ b/packages/style-engine/phpunit/class-wp-style-engine-test.php @@ -131,7 +131,7 @@ public function data_block_styles_fixtures() { 'expected_output' => 'padding-top:42px;padding-left:2%;padding-bottom:44px;padding-right:5rem;', ), - 'inline_valid_multiple_style' => array( + 'inline_valid_multiple_spacing_style' => array( 'block_styles' => array( 'spacing' => array( 'padding' => array( @@ -153,6 +153,25 @@ public function data_block_styles_fixtures() { ), 'expected_output' => 'padding-top:42px;padding-left:2%;padding-bottom:44px;padding-right:5rem;margin-top:12rem;margin-left:2vh;margin-bottom:2px;margin-right:10em;', ), + + 'inline_valid_multiple_typography_style' => array( + 'block_styles' => array( + 'typography' => array( + 'fontSize' => 'clamp(2em, 2vw, 4em)', + 'fontFamily' => 'Roboto,Oxygen-Sans,Ubuntu,sans-serif', + 'fontStyle' => 'italic', + 'fontWeight' => '800', + 'lineHeight' => '1.3', + 'textDecoration' => 'underline', + 'textTransform' => 'uppercase', + 'letterSpacing' => '2', + ), + ), + 'options' => array( + 'inline' => true, + ), + 'expected_output' => 'font-family:Roboto,Oxygen-Sans,Ubuntu,sans-serif;font-style:italic;font-weight:800;line-height:1.3;text-decoration:underline;text-transform:uppercase;letter-spacing:2;', + ), ); } } From 886caffd32eab3e5653f9cd71b220537add4ea82 Mon Sep 17 00:00:00 2001 From: ramonjd Date: Thu, 7 Apr 2022 12:29:14 +1000 Subject: [PATCH 2/6] Adding whitespace to inline output --- packages/style-engine/class-wp-style-engine.php | 6 +++--- .../style-engine/phpunit/class-wp-style-engine-test.php | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/style-engine/class-wp-style-engine.php b/packages/style-engine/class-wp-style-engine.php index 766b06ea1df4ac..fe850ba220a875 100644 --- a/packages/style-engine/class-wp-style-engine.php +++ b/packages/style-engine/class-wp-style-engine.php @@ -175,15 +175,15 @@ public function generate( $block_styles, $options = array() ) { // Generate inline style rules. if ( isset( $options['inline'] ) && true === $options['inline'] ) { foreach ( $rules as $rule => $value ) { - $filtered_css = esc_html( safecss_filter_attr( "{$rule}:{$value}" ) ); + $filtered_css = esc_html( safecss_filter_attr( "{$rule}: {$value}" ) ); if ( ! empty( $filtered_css ) ) { - $output .= $filtered_css . ';'; + $output .= $filtered_css . '; '; } } } } - return $output; + return trim( $output ); } /** diff --git a/packages/style-engine/phpunit/class-wp-style-engine-test.php b/packages/style-engine/phpunit/class-wp-style-engine-test.php index a7fe6b0e8ab810..9fd262d49ced73 100644 --- a/packages/style-engine/phpunit/class-wp-style-engine-test.php +++ b/packages/style-engine/phpunit/class-wp-style-engine-test.php @@ -104,7 +104,7 @@ public function data_block_styles_fixtures() { 'path' => array( 'spacing', 'margin' ), 'inline' => true, ), - 'expected_output' => 'margin:111px;', + 'expected_output' => 'margin: 111px;', ), 'inline_valid_single_style' => array( @@ -128,7 +128,7 @@ public function data_block_styles_fixtures() { 'path' => array( 'spacing', 'padding' ), 'inline' => true, ), - 'expected_output' => 'padding-top:42px;padding-left:2%;padding-bottom:44px;padding-right:5rem;', + 'expected_output' => 'padding-top: 42px; padding-left: 2%; padding-bottom: 44px; padding-right: 5rem;', ), 'inline_valid_multiple_spacing_style' => array( @@ -151,7 +151,7 @@ public function data_block_styles_fixtures() { 'options' => array( 'inline' => true, ), - 'expected_output' => 'padding-top:42px;padding-left:2%;padding-bottom:44px;padding-right:5rem;margin-top:12rem;margin-left:2vh;margin-bottom:2px;margin-right:10em;', + 'expected_output' => 'padding-top: 42px; padding-left: 2%; padding-bottom: 44px; padding-right: 5rem; margin-top: 12rem; margin-left: 2vh; margin-bottom: 2px; margin-right: 10em;', ), 'inline_valid_multiple_typography_style' => array( @@ -170,7 +170,7 @@ public function data_block_styles_fixtures() { 'options' => array( 'inline' => true, ), - 'expected_output' => 'font-family:Roboto,Oxygen-Sans,Ubuntu,sans-serif;font-style:italic;font-weight:800;line-height:1.3;text-decoration:underline;text-transform:uppercase;letter-spacing:2;', + 'expected_output' => 'font-family: Roboto,Oxygen-Sans,Ubuntu,sans-serif; font-style: italic; font-weight: 800; line-height: 1.3; text-decoration: underline; text-transform: uppercase; letter-spacing: 2;', ), ); } From 4e9ed63575ea130537d66e7fc4afc88c7ba65bcc Mon Sep 17 00:00:00 2001 From: ramonjd Date: Thu, 7 Apr 2022 13:27:09 +1000 Subject: [PATCH 3/6] Introduce spacing to inline styles First stab at generating classnames based on slugs --- lib/block-supports/typography.php | 19 ++++--- .../style-engine/class-wp-style-engine.php | 51 ++++++++++++++++--- phpunit/block-supports/spacing-test.php | 4 +- 3 files changed, 60 insertions(+), 14 deletions(-) diff --git a/lib/block-supports/typography.php b/lib/block-supports/typography.php index 6c20a375a011ff..8b930cd7bd28ca 100644 --- a/lib/block-supports/typography.php +++ b/lib/block-supports/typography.php @@ -107,7 +107,7 @@ function gutenberg_apply_typography_support( $block_type, $block_attributes ) { $has_custom_font_size = isset( $block_attributes['style']['typography']['fontSize'] ); if ( $has_named_font_size ) { - $classes[] = sprintf( 'has-%s-font-size', _wp_to_kebab_case( $block_attributes['fontSize'] ) ); + $classes['fontSize'] = _wp_to_kebab_case( $block_attributes['fontSize'] ); } elseif ( $has_custom_font_size ) { $styles['fontSize'] = $block_attributes['style']['typography']['fontSize']; } @@ -118,7 +118,7 @@ function gutenberg_apply_typography_support( $block_type, $block_attributes ) { $has_custom_font_family = isset( $block_attributes['style']['typography']['fontFamily'] ); if ( $has_named_font_family ) { - $classes[] = sprintf( 'has-%s-font-family', _wp_to_kebab_case( $block_attributes['fontFamily'] ) ); + $classes['fontFamily'] = _wp_to_kebab_case( $block_attributes['fontFamily'] ); } elseif ( $has_custom_font_family ) { $font_family_custom = $block_attributes['style']['typography']['fontFamily']; // Before using classes, the value was serialized as a CSS Custom Property. @@ -175,10 +175,6 @@ function gutenberg_apply_typography_support( $block_type, $block_attributes ) { } } - if ( ! empty( $classes ) ) { - $attributes['class'] = implode( ' ', $classes ); - } - $style_engine = WP_Style_Engine_Gutenberg::get_instance(); $inline_styles = $style_engine->generate( array( 'typography' => $styles ), @@ -187,6 +183,17 @@ function gutenberg_apply_typography_support( $block_type, $block_attributes ) { ) ); + $classnames = $style_engine->generate( + array( 'typography' => $classes ), + array( + 'classnames' => true, + ) + ); + + if ( ! empty( $classnames ) ) { + $attributes['class'] = $classnames; + } + if ( ! empty( $inline_styles ) ) { $attributes['style'] = $inline_styles; } diff --git a/packages/style-engine/class-wp-style-engine.php b/packages/style-engine/class-wp-style-engine.php index fe850ba220a875..e441d4dc1f9d74 100644 --- a/packages/style-engine/class-wp-style-engine.php +++ b/packages/style-engine/class-wp-style-engine.php @@ -50,14 +50,16 @@ class WP_Style_Engine { ), 'typography' => array( 'fontSize' => array( - 'property_key' => 'font-size', - 'path' => array( 'typography', 'fontSize' ), - 'value_func' => 'static::get_css_rules', + 'property_key' => 'font-size', + 'path' => array( 'typography', 'fontSize' ), + 'value_func' => 'static::get_css_rules', + 'classname_pattern' => 'has-%s-font-size', ), 'fontFamily' => array( - 'property_key' => 'font-family', - 'path' => array( 'typography', 'fontFamily' ), - 'value_func' => 'static::get_css_rules', + 'property_key' => 'font-family', + 'path' => array( 'typography', 'fontFamily' ), + 'value_func' => 'static::get_css_rules', + 'classname_pattern' => 'has-%s-font-family', ), 'fontStyle' => array( 'property_key' => 'font-style', @@ -130,6 +132,26 @@ protected function get_block_style_css_rules( $style_value, $path ) { return array(); } + /** + * Returns a classname built using the classname_pattern in BLOCK_STYLE_DEFINITIONS_METADATA. + * + * @param string $classname_slug A single slug to be inserted into the classname pattern. + * @param array $path An array of strings representing a path to the style value. + * + * @return string A CSS classname. + */ + protected function get_block_classname( $classname_slug, $path ) { + $style_definition = _wp_array_get( static::BLOCK_STYLE_DEFINITIONS_METADATA, $path, null ); + + if ( ! empty( $style_definition ) ) { + if ( isset( $style_definition['classname_pattern'] ) ) { + return sprintf( $style_definition['classname_pattern'], $classname_slug ); + } + } + + return ''; + } + /** * Returns an CSS ruleset. * Styles are bundled based on the instructions in BLOCK_STYLE_DEFINITIONS_METADATA. @@ -149,6 +171,23 @@ public function generate( $block_styles, $options = array() ) { return $output; } + if ( isset( $options['classnames'] ) && true === $options['classnames'] ) { + + $classnames = array(); + foreach ( self::BLOCK_STYLE_DEFINITIONS_METADATA as $definition_group ) { + foreach ( $definition_group as $style_definition ) { + $classname_value = _wp_array_get( $block_styles, $style_definition['path'], null ); + + if ( empty( $classname_value ) ) { + continue; + } + + $classnames[] = $this->get_block_classname( $classname_value, $style_definition['path'] ); + } + } + return implode( ' ', $classnames ); + } + $rules = array(); // If a path to a specific block style is defined, only return rules for that style. diff --git a/phpunit/block-supports/spacing-test.php b/phpunit/block-supports/spacing-test.php index 618c809b84f6ad..613f415557cfdd 100644 --- a/phpunit/block-supports/spacing-test.php +++ b/phpunit/block-supports/spacing-test.php @@ -62,7 +62,7 @@ function test_spacing_style_is_applied() { $actual = gutenberg_apply_spacing_support( $block_type, $block_atts ); $expected = array( - 'style' => 'padding:111px;margin-top:1px;margin-right:2px;margin-bottom:3px;margin-left:4px;', + 'style' => 'padding: 111px; margin-top: 1px; margin-right: 2px; margin-bottom: 3px; margin-left: 4px;', ); $this->assertSame( $expected, $actual ); @@ -152,7 +152,7 @@ function test_margin_with_individual_skipped_serialization_block_supports() { $actual = gutenberg_apply_spacing_support( $block_type, $block_atts ); $expected = array( - 'style' => 'padding-top:1px;padding-right:2px;padding-bottom:3px;padding-left:4px;', + 'style' => 'padding-top: 1px; padding-right: 2px; padding-bottom: 3px; padding-left: 4px;', ); $this->assertSame( $expected, $actual ); From e1cda2cdc94684740b670a06a236d6b9fc1abfd3 Mon Sep 17 00:00:00 2001 From: ramonjd Date: Thu, 7 Apr 2022 15:39:36 +1000 Subject: [PATCH 4/6] Removing generating styles based on an options path since we're not using it yet. Adding a new public function to generate_classnames. Updated tests. --- lib/block-supports/spacing.php | 2 +- lib/block-supports/typography.php | 6 +- .../style-engine/class-wp-style-engine.php | 92 ++++++++---------- .../phpunit/class-wp-style-engine-test.php | 95 ++++++++++--------- 4 files changed, 94 insertions(+), 101 deletions(-) diff --git a/lib/block-supports/spacing.php b/lib/block-supports/spacing.php index 86bb96598af446..d2530293d24537 100644 --- a/lib/block-supports/spacing.php +++ b/lib/block-supports/spacing.php @@ -52,7 +52,7 @@ function gutenberg_apply_spacing_support( $block_type, $block_attributes ) { return $attributes; } - $style_engine = WP_Style_Engine_Gutenberg::get_instance(); + $style_engine = gutenberg_get_style_engine(); $skip_padding = gutenberg_should_skip_block_supports_serialization( $block_type, 'spacing', 'padding' ); $skip_margin = gutenberg_should_skip_block_supports_serialization( $block_type, 'spacing', 'margin' ); $spacing_block_styles = array(); diff --git a/lib/block-supports/typography.php b/lib/block-supports/typography.php index 8b930cd7bd28ca..63b841fb0558d8 100644 --- a/lib/block-supports/typography.php +++ b/lib/block-supports/typography.php @@ -175,7 +175,7 @@ function gutenberg_apply_typography_support( $block_type, $block_attributes ) { } } - $style_engine = WP_Style_Engine_Gutenberg::get_instance(); + $style_engine = gutenberg_get_style_engine(); $inline_styles = $style_engine->generate( array( 'typography' => $styles ), array( @@ -183,10 +183,10 @@ function gutenberg_apply_typography_support( $block_type, $block_attributes ) { ) ); - $classnames = $style_engine->generate( + $classnames = $style_engine->get_classnames( array( 'typography' => $classes ), array( - 'classnames' => true, + 'use_schema' => true, ) ); diff --git a/packages/style-engine/class-wp-style-engine.php b/packages/style-engine/class-wp-style-engine.php index e441d4dc1f9d74..44054d4291eb48 100644 --- a/packages/style-engine/class-wp-style-engine.php +++ b/packages/style-engine/class-wp-style-engine.php @@ -50,16 +50,16 @@ class WP_Style_Engine { ), 'typography' => array( 'fontSize' => array( - 'property_key' => 'font-size', - 'path' => array( 'typography', 'fontSize' ), - 'value_func' => 'static::get_css_rules', - 'classname_pattern' => 'has-%s-font-size', + 'property_key' => 'font-size', + 'path' => array( 'typography', 'fontSize' ), + 'value_func' => 'static::get_css_rules', + 'classname_schema' => 'has-%s-font-size', ), 'fontFamily' => array( - 'property_key' => 'font-family', - 'path' => array( 'typography', 'fontFamily' ), - 'value_func' => 'static::get_css_rules', - 'classname_pattern' => 'has-%s-font-family', + 'property_key' => 'font-family', + 'path' => array( 'typography', 'fontFamily' ), + 'value_func' => 'static::get_css_rules', + 'classname_schema' => 'has-%s-font-family', ), 'fontStyle' => array( 'property_key' => 'font-style', @@ -133,23 +133,42 @@ protected function get_block_style_css_rules( $style_value, $path ) { } /** - * Returns a classname built using the classname_pattern in BLOCK_STYLE_DEFINITIONS_METADATA. + * Returns a classname built using a provided schema. * - * @param string $classname_slug A single slug to be inserted into the classname pattern. - * @param array $path An array of strings representing a path to the style value. + * @param array $block_styles An array of styles from a block's attributes. + * Some of the values may contain slugs that need to be parsed using a schema. + * @param array $options = array( + * 'use_schema' => (boolean) Whether to use the internal classname schema in BLOCK_STYLE_DEFINITIONS_METADATA. + * );. * * @return string A CSS classname. */ - protected function get_block_classname( $classname_slug, $path ) { - $style_definition = _wp_array_get( static::BLOCK_STYLE_DEFINITIONS_METADATA, $path, null ); + public function get_classnames( $block_styles, $options = array() ) { + $output = ''; - if ( ! empty( $style_definition ) ) { - if ( isset( $style_definition['classname_pattern'] ) ) { - return sprintf( $style_definition['classname_pattern'], $classname_slug ); + if ( empty( $block_styles ) ) { + return $output; + } + + $classnames = array(); + $use_schema = isset( $options['use_schema'] ) ? $options['use_schema'] : false; + + foreach ( self::BLOCK_STYLE_DEFINITIONS_METADATA as $definition_group ) { + foreach ( $definition_group as $style_definition ) { + $classname_value = _wp_array_get( $block_styles, $style_definition['path'], null ); + + if ( empty( $classname_value ) ) { + continue; + } + + $style_definition = _wp_array_get( static::BLOCK_STYLE_DEFINITIONS_METADATA, $style_definition['path'], null ); + // If there is no stored schema we could generate classnames based on other properties such as the path or value or other prefix passed to options. + $schema = $use_schema ? $style_definition['classname_schema'] : ''; + $classnames[] = sprintf( $schema, $classname_value ); } } - return ''; + return implode( ' ', $classnames ); } /** @@ -171,42 +190,15 @@ public function generate( $block_styles, $options = array() ) { return $output; } - if ( isset( $options['classnames'] ) && true === $options['classnames'] ) { - - $classnames = array(); - foreach ( self::BLOCK_STYLE_DEFINITIONS_METADATA as $definition_group ) { - foreach ( $definition_group as $style_definition ) { - $classname_value = _wp_array_get( $block_styles, $style_definition['path'], null ); - - if ( empty( $classname_value ) ) { - continue; - } - - $classnames[] = $this->get_block_classname( $classname_value, $style_definition['path'] ); - } - } - return implode( ' ', $classnames ); - } - $rules = array(); - // If a path to a specific block style is defined, only return rules for that style. - if ( isset( $options['path'] ) && is_array( $options['path'] ) ) { - $style_value = _wp_array_get( $block_styles, $options['path'], null ); - if ( empty( $style_value ) ) { - return $output; - } - $rules = array_merge( $rules, $this->get_block_style_css_rules( $style_value, $options['path'] ) ); - } else { - // Otherwise build them all. - foreach ( self::BLOCK_STYLE_DEFINITIONS_METADATA as $definition_group ) { - foreach ( $definition_group as $style_definition ) { - $style_value = _wp_array_get( $block_styles, $style_definition['path'], null ); - if ( empty( $style_value ) ) { - continue; - } - $rules = array_merge( $rules, $this->get_block_style_css_rules( $style_value, $style_definition['path'] ) ); + foreach ( self::BLOCK_STYLE_DEFINITIONS_METADATA as $definition_group ) { + foreach ( $definition_group as $style_definition ) { + $style_value = _wp_array_get( $block_styles, $style_definition['path'], null ); + if ( empty( $style_value ) ) { + continue; } + $rules = array_merge( $rules, $this->get_block_style_css_rules( $style_value, $style_definition['path'] ) ); } } diff --git a/packages/style-engine/phpunit/class-wp-style-engine-test.php b/packages/style-engine/phpunit/class-wp-style-engine-test.php index 9fd262d49ced73..be3c7630013d67 100644 --- a/packages/style-engine/phpunit/class-wp-style-engine-test.php +++ b/packages/style-engine/phpunit/class-wp-style-engine-test.php @@ -13,12 +13,12 @@ */ class WP_Style_Engine_Test extends WP_UnitTestCase { /** - * Tests various manifestations of the $block_styles argument. + * Tests generating styles based on various manifestations of the $block_styles argument. * - * @dataProvider data_block_styles_fixtures + * @dataProvider data_generate_css_fixtures */ function test_generate_css( $block_styles, $options, $expected_output ) { - $style_engine = WP_Style_Engine::get_instance(); + $style_engine = wp_get_style_engine(); $generated_styles = $style_engine->generate( $block_styles, $options @@ -31,7 +31,7 @@ function test_generate_css( $block_styles, $options, $expected_output ) { * * @return array */ - public function data_block_styles_fixtures() { + public function data_generate_css_fixtures() { return array( 'default_return_value' => array( 'block_styles' => array(), @@ -42,7 +42,6 @@ public function data_block_styles_fixtures() { 'inline_invalid_block_styles_empty' => array( 'block_styles' => array(), 'options' => array( - 'path' => array( 'spacing', 'padding' ), 'inline' => true, ), 'expected_output' => '', @@ -63,7 +62,6 @@ public function data_block_styles_fixtures() { 'pageBreakAfter' => 'verso', ), 'options' => array( - 'path' => array( 'pageBreakAfter', 'verso' ), 'inline' => true, ), 'expected_output' => '', @@ -76,62 +74,24 @@ public function data_block_styles_fixtures() { ), ), 'options' => array( - 'path' => array( 'spacing', 'padding' ), 'inline' => true, ), 'expected_output' => '', ), - 'inline_invalid_multiple_style_unknown_property' => array( - 'block_styles' => array( - 'spacing' => array( - 'gavin' => '1000vw', - ), - ), - 'options' => array( - 'inline' => true, - ), - 'expected_output' => '', - ), - - 'inline_valid_single_style_string' => array( + 'inline_valid_style_string' => array( 'block_styles' => array( 'spacing' => array( 'margin' => '111px', ), ), 'options' => array( - 'path' => array( 'spacing', 'margin' ), 'inline' => true, ), 'expected_output' => 'margin: 111px;', ), - 'inline_valid_single_style' => array( - 'block_styles' => array( - 'spacing' => array( - 'padding' => array( - 'top' => '42px', - 'left' => '2%', - 'bottom' => '44px', - 'right' => '5rem', - ), - 'margin' => array( - 'top' => '12rem', - 'left' => '2vh', - 'bottom' => '2px', - 'right' => '10em', - ), - ), - ), - 'options' => array( - 'path' => array( 'spacing', 'padding' ), - 'inline' => true, - ), - 'expected_output' => 'padding-top: 42px; padding-left: 2%; padding-bottom: 44px; padding-right: 5rem;', - ), - - 'inline_valid_multiple_spacing_style' => array( + 'inline_valid_box_model_style' => array( 'block_styles' => array( 'spacing' => array( 'padding' => array( @@ -154,7 +114,7 @@ public function data_block_styles_fixtures() { 'expected_output' => 'padding-top: 42px; padding-left: 2%; padding-bottom: 44px; padding-right: 5rem; margin-top: 12rem; margin-left: 2vh; margin-bottom: 2px; margin-right: 10em;', ), - 'inline_valid_multiple_typography_style' => array( + 'inline_valid_typography_style' => array( 'block_styles' => array( 'typography' => array( 'fontSize' => 'clamp(2em, 2vw, 4em)', @@ -174,4 +134,45 @@ public function data_block_styles_fixtures() { ), ); } + + /** + * Tests generating classnames based on various manifestations of the $block_styles argument. + * + * @dataProvider data_get_classnames_fixtures + */ + function test_get_classnames( $block_styles, $options, $expected_output ) { + $style_engine = wp_get_style_engine(); + $generated_styles = $style_engine->get_classnames( + $block_styles, + $options + ); + $this->assertSame( $expected_output, $generated_styles ); + } + + /** + * Data provider. + * + * @return array + */ + public function data_get_classnames_fixtures() { + return array( + 'default_return_value' => array( + 'block_styles' => array(), + 'options' => null, + 'expected_output' => '', + ), + 'valid_classnames_use_schema' => array( + 'block_styles' => array( + 'typography' => array( + 'fontSize' => 'fantastic', + 'fontFamily' => 'totally-awesome', + ), + ), + 'options' => array( + 'use_schema' => true, + ), + 'expected_output' => 'has-fantastic-font-size has-totally-awesome-font-family', + ), + ); + } } From 9dff8851e8c8c3f13e90087d9dfd6118a332e690 Mon Sep 17 00:00:00 2001 From: ramonjd Date: Wed, 13 Apr 2022 14:38:46 +1000 Subject: [PATCH 5/6] Perform kebab case normalization in the style engine. Remove value_func for common style definitions since I'm pretty sure we'll be using the get_css_rules method for 80%++ of cases. --- lib/block-supports/typography.php | 9 +++---- .../style-engine/class-wp-style-engine.php | 24 +++++++------------ 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/lib/block-supports/typography.php b/lib/block-supports/typography.php index 63b841fb0558d8..68ed7e64130790 100644 --- a/lib/block-supports/typography.php +++ b/lib/block-supports/typography.php @@ -107,7 +107,7 @@ function gutenberg_apply_typography_support( $block_type, $block_attributes ) { $has_custom_font_size = isset( $block_attributes['style']['typography']['fontSize'] ); if ( $has_named_font_size ) { - $classes['fontSize'] = _wp_to_kebab_case( $block_attributes['fontSize'] ); + $classes['fontSize'] = $block_attributes['fontSize']; } elseif ( $has_custom_font_size ) { $styles['fontSize'] = $block_attributes['style']['typography']['fontSize']; } @@ -118,7 +118,7 @@ function gutenberg_apply_typography_support( $block_type, $block_attributes ) { $has_custom_font_family = isset( $block_attributes['style']['typography']['fontFamily'] ); if ( $has_named_font_family ) { - $classes['fontFamily'] = _wp_to_kebab_case( $block_attributes['fontFamily'] ); + $classes['fontFamily'] = $block_attributes['fontFamily']; } elseif ( $has_custom_font_family ) { $font_family_custom = $block_attributes['style']['typography']['fontFamily']; // Before using classes, the value was serialized as a CSS Custom Property. @@ -184,10 +184,7 @@ function gutenberg_apply_typography_support( $block_type, $block_attributes ) { ); $classnames = $style_engine->get_classnames( - array( 'typography' => $classes ), - array( - 'use_schema' => true, - ) + array( 'typography' => $classes ) ); if ( ! empty( $classnames ) ) { diff --git a/packages/style-engine/class-wp-style-engine.php b/packages/style-engine/class-wp-style-engine.php index 44054d4291eb48..4ccd16b804faa9 100644 --- a/packages/style-engine/class-wp-style-engine.php +++ b/packages/style-engine/class-wp-style-engine.php @@ -40,56 +40,46 @@ class WP_Style_Engine { 'padding' => array( 'property_key' => 'padding', 'path' => array( 'spacing', 'padding' ), - 'value_func' => 'static::get_css_rules', ), 'margin' => array( 'property_key' => 'margin', 'path' => array( 'spacing', 'margin' ), - 'value_func' => 'static::get_css_rules', ), ), 'typography' => array( 'fontSize' => array( 'property_key' => 'font-size', 'path' => array( 'typography', 'fontSize' ), - 'value_func' => 'static::get_css_rules', 'classname_schema' => 'has-%s-font-size', ), 'fontFamily' => array( 'property_key' => 'font-family', 'path' => array( 'typography', 'fontFamily' ), - 'value_func' => 'static::get_css_rules', 'classname_schema' => 'has-%s-font-family', ), 'fontStyle' => array( 'property_key' => 'font-style', 'path' => array( 'typography', 'fontStyle' ), - 'value_func' => 'static::get_css_rules', ), 'fontWeight' => array( 'property_key' => 'font-weight', 'path' => array( 'typography', 'fontWeight' ), - 'value_func' => 'static::get_css_rules', ), 'lineHeight' => array( 'property_key' => 'line-height', 'path' => array( 'typography', 'lineHeight' ), - 'value_func' => 'static::get_css_rules', ), 'textDecoration' => array( 'property_key' => 'text-decoration', 'path' => array( 'typography', 'textDecoration' ), - 'value_func' => 'static::get_css_rules', ), 'textTransform' => array( 'property_key' => 'text-transform', 'path' => array( 'typography', 'textTransform' ), - 'value_func' => 'static::get_css_rules', ), 'letterSpacing' => array( 'property_key' => 'letter-spacing', 'path' => array( 'typography', 'letterSpacing' ), - 'value_func' => 'static::get_css_rules', ), ), ); @@ -121,11 +111,14 @@ protected function get_block_style_css_rules( $style_value, $path ) { $style_definition = _wp_array_get( static::BLOCK_STYLE_DEFINITIONS_METADATA, $path, null ); if ( ! empty( $style_definition ) ) { + // Style definitions can define a function to generate custom CSS rules. if ( isset( $style_definition['value_func'] ) && is_callable( $style_definition['value_func'] ) ) { return call_user_func( $style_definition['value_func'], $style_value, $style_definition['property_key'] ); + } else { + return static::get_css_rules( $style_value, $style_definition['property_key'] ); } } @@ -151,8 +144,6 @@ public function get_classnames( $block_styles, $options = array() ) { } $classnames = array(); - $use_schema = isset( $options['use_schema'] ) ? $options['use_schema'] : false; - foreach ( self::BLOCK_STYLE_DEFINITIONS_METADATA as $definition_group ) { foreach ( $definition_group as $style_definition ) { $classname_value = _wp_array_get( $block_styles, $style_definition['path'], null ); @@ -161,10 +152,13 @@ public function get_classnames( $block_styles, $options = array() ) { continue; } + $classname_value = _wp_to_kebab_case( $classname_value ); $style_definition = _wp_array_get( static::BLOCK_STYLE_DEFINITIONS_METADATA, $style_definition['path'], null ); - // If there is no stored schema we could generate classnames based on other properties such as the path or value or other prefix passed to options. - $schema = $use_schema ? $style_definition['classname_schema'] : ''; - $classnames[] = sprintf( $schema, $classname_value ); + // Right now we expect a classname pattern to be stored in BLOCK_STYLE_DEFINITIONS_METADATA. + // One day, if there are no stored schemata, we could allow custom patterns or + // generate classnames based on other properties + // such as a path or a value or a prefix passed in options. + $classnames[] = isset( $style_definition['classname_schema'] ) ? sprintf( $style_definition['classname_schema'], $classname_value ) : $classname_value; } } From 3123b021fbd37f1941d3141ac7ffcb221674425c Mon Sep 17 00:00:00 2001 From: ramonjd Date: Wed, 13 Apr 2022 14:47:13 +1000 Subject: [PATCH 6/6] Remove unused arg $options --- packages/style-engine/class-wp-style-engine.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/style-engine/class-wp-style-engine.php b/packages/style-engine/class-wp-style-engine.php index 4ccd16b804faa9..148b208c51c258 100644 --- a/packages/style-engine/class-wp-style-engine.php +++ b/packages/style-engine/class-wp-style-engine.php @@ -129,14 +129,11 @@ protected function get_block_style_css_rules( $style_value, $path ) { * Returns a classname built using a provided schema. * * @param array $block_styles An array of styles from a block's attributes. - * Some of the values may contain slugs that need to be parsed using a schema. - * @param array $options = array( - * 'use_schema' => (boolean) Whether to use the internal classname schema in BLOCK_STYLE_DEFINITIONS_METADATA. - * );. + * Some values may contain slugs that need to be parsed using a schema. * * @return string A CSS classname. */ - public function get_classnames( $block_styles, $options = array() ) { + public function get_classnames( $block_styles ) { $output = ''; if ( empty( $block_styles ) ) {