Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 41 additions & 26 deletions lib/class-wp-theme-json-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -721,7 +721,14 @@ protected static function sanitize( $input, $valid_block_names, $valid_element_n
$schema_settings_blocks = array();
foreach ( $valid_block_names as $block ) {
// Build the schema for each block style variation.
$style_variation_names = isset( $input['styles']['blocks'][ $block ]['variations'] ) ? array_keys( $input['styles']['blocks'][ $block ]['variations'] ) : array();
$style_variation_names = array();
if (
! empty( $input['styles']['blocks'][ $block ]['variations'] ) &&
is_array( $input['styles']['blocks'][ $block ]['variations'] )
) {
$style_variation_names = array_keys( $input['styles']['blocks'][ $block ]['variations'] );
}

$schema_styles_variations = array();
if ( ! empty( $style_variation_names ) ) {
$schema_styles_variations = array_fill_keys( $style_variation_names, $styles_non_top_level );
Expand Down Expand Up @@ -2315,33 +2322,41 @@ public function get_styles_for_block( $block_metadata ) {

// If the block has feature selectors, generate the declarations for them within the current style variation.
if ( ! empty( $block_metadata['features'] ) ) {
$clean_style_variation_selector = trim( $style_variation_selector );
foreach ( $block_metadata['features'] as $feature_name => $feature_selector ) {
if ( ! empty( $style_variation_node[ $feature_name ] ) ) {
// Prepend the variation selector to the feature selector.
$split_feature_selectors = explode( ',', $feature_selector );
$feature_selectors = array_map(
function( $split_feature_selector ) use ( $style_variation_selector ) {
return trim( $style_variation_selector ) . trim( $split_feature_selector );
},
$split_feature_selectors
);
$combined_feature_selectors = implode( ',', $feature_selectors );

// Compute declarations for the feature.
$new_feature_declarations = static::compute_style_properties( array( $feature_name => $style_variation_node[ $feature_name ] ), $settings, null, $this->theme_json );

// Merge new declarations with any that already exist for
// the feature selector. This may occur when multiple block
// support features use the same custom selector.
if ( isset( $style_variation_declarations[ $combined_feature_selectors ] ) ) {
$style_variation_declarations[ $combined_feature_selectors ] = array_merge( $style_variation_declarations[ $combined_feature_selectors ], $new_feature_declarations );
} else {
$style_variation_declarations[ $combined_feature_selectors ] = $new_feature_declarations;
}
// Remove the feature from the variation's node now the
// styles will be included under the feature level selector.
unset( $style_variation_node[ $feature_name ] );
if ( empty( $style_variation_node[ $feature_name ] ) ) {
continue;
}
// Prepend the variation selector to the feature selector.
$split_feature_selectors = explode( ',', $feature_selector );
$feature_selectors = array_map(
static function( $split_feature_selector ) use ( $clean_style_variation_selector ) {
return $clean_style_variation_selector . trim( $split_feature_selector );
},
$split_feature_selectors
);
$combined_feature_selectors = implode( ',', $feature_selectors );

// Compute declarations for the feature.
$new_feature_declarations = static::compute_style_properties( array( $feature_name => $style_variation_node[ $feature_name ] ), $settings, null, $this->theme_json );

/*
* Merge new declarations with any that already exist for
* the feature selector. This may occur when multiple block
* support features use the same custom selector.
*/
if ( isset( $style_variation_declarations[ $combined_feature_selectors ] ) ) {
$style_variation_declarations[ $combined_feature_selectors ] = array_merge( $style_variation_declarations[ $combined_feature_selectors ], $new_feature_declarations );
} else {
$style_variation_declarations[ $combined_feature_selectors ] = $new_feature_declarations;
}

/*
* Remove the feature from the variation's node now the
* styles will be included under the feature level selector.
*/
unset( $style_variation_node[ $feature_name ] );

}
}
// Compute declarations for remaining styles not covered by feature level selectors.
Expand Down
294 changes: 294 additions & 0 deletions phpunit/class-wp-theme-json-test.php
Original file line number Diff line number Diff line change
Expand Up @@ -1504,6 +1504,300 @@ public function test_get_styles_for_block_with_content_width() {
$this->assertEquals( $expected, $root_rules . $style_rules );
}

/**
* @dataProvider data_sanitize_for_block_with_style_variations
*
* @param array $theme_json_variations Theme.json variations to test.
* @param array $expected_sanitized Expected results after sanitizing.
*/
public function test_sanitize_for_block_with_style_variations( $theme_json_variations, $expected_sanitized ) {
$theme_json = new WP_Theme_JSON_Gutenberg(
array(
'version' => 2,
'styles' => array(
'blocks' => array(
'core/quote' => $theme_json_variations,
),
),
)
);

// Validate structure is sanitized.
$sanitized_theme_json = $theme_json->get_raw_data();
$this->assertIsArray( $sanitized_theme_json, 'Sanitized theme.json is not an array data type' );
$this->assertArrayHasKey( 'styles', $sanitized_theme_json, 'Sanitized theme.json does not have an "styles" key' );
$this->assertSameSetsWithIndex( $expected_sanitized, $sanitized_theme_json['styles'], 'Sanitized theme.json styles does not match' );
}

/**
* Data provider.
*
* @return array
*/
public function data_sanitize_for_block_with_style_variations() {
return array(
'1 variation with 1 invalid property' => array(
'theme_json_variations' => array(
'variations' => array(
'plain' => array(
'color' => array(
'background' => 'hotpink',
),
),
),
),
'expected_sanitized' => array(
'blocks' => array(
'core/quote' => array(
'variations' => array(
'plain' => array(
'color' => array(
'background' => 'hotpink',
),
),
),
),
),
),
),
'1 variation with 2 invalid properties' => array(
'theme_json_variations' => array(
'variations' => array(
'plain' => array(
'color' => array(
'background' => 'hotpink',
),
'invalidProperty1' => 'value1',
'invalidProperty2' => 'value2',
),
),
),
'expected_sanitized' => array(
'blocks' => array(
'core/quote' => array(
'variations' => array(
'plain' => array(
'color' => array(
'background' => 'hotpink',
),
),
),
),
),
),
),
'2 variations with 1 invalid property' => array(
'theme_json_variations' => array(
'variations' => array(
'plain' => array(
'color' => array(
'background' => 'hotpink',
),
'invalidProperty1' => 'value1',
),
'basic' => array(
'color' => array(
'background' => '#ffffff',
'text' => '#000000',
),
'foo' => 'bar',
),
),
),
'expected_sanitized' => array(
'blocks' => array(
'core/quote' => array(
'variations' => array(
'plain' => array(
'color' => array(
'background' => 'hotpink',
),
),
'basic' => array(
'color' => array(
'background' => '#ffffff',
'text' => '#000000',
),
),
),
),
),
),
),
);
}

/**
* @dataProvider data_sanitize_with_invalid_style_variation
*
* @param array $theme_json_variations The theme.json variations to test.
*/
public function test_sanitize_with_invalid_style_variation( $theme_json_variations ) {
$theme_json = new WP_Theme_JSON_Gutenberg(
array(
'version' => 2,
'styles' => array(
'blocks' => array(
'core/quote' => $theme_json_variations,
),
),
)
);

// Validate structure is sanitized.
$sanitized_theme_json = $theme_json->get_raw_data();
$this->assertIsArray( $sanitized_theme_json, 'Sanitized theme.json is not an array data type' );
$this->assertArrayNotHasKey( 'styles', $sanitized_theme_json, 'Sanitized theme.json should not have a "styles" key' );

}

/**
* Data provider.
*
* @return array
*/
public function data_sanitize_with_invalid_style_variation() {
return array(
'empty string variation' => array(
array(
'variations' => '',
),
),
'boolean variation' => array(
array(
'variations' => false,
),
),
);
}

/**
* @dataProvider data_get_styles_for_block_with_style_variations
*
* @param array $theme_json_variations Theme.json variations to test.
* @param string $metadata_variations Style variations to test.
* @param string $expected Expected results for styling.
*/
public function test_get_styles_for_block_with_style_variations( $theme_json_variations, $metadata_variations, $expected ) {
$theme_json = new WP_Theme_JSON_Gutenberg(
array(
'version' => 2,
'styles' => array(
'blocks' => array(
'core/quote' => $theme_json_variations,
),
),
)
);

// Validate styles are generated properly.
$metadata = array(
'path' => array( 'styles', 'blocks', 'core/quote' ),
'selector' => '.wp-block-quote',
'variations' => $metadata_variations,
);
$actual_styles = $theme_json->get_styles_for_block( $metadata );
$this->assertSame( $expected, $actual_styles );
}

/**
* Data provider.
*
* @return array
*/
public function data_get_styles_for_block_with_style_variations() {
$plain = array(
'metadata' => array(
'path' => array( 'styles', 'blocks', 'core/quote', 'variations', 'plain' ),
'selector' => '.is-style-plain.is-style-plain.wp-block-quote',
),
'styles' => '.is-style-plain.is-style-plain.wp-block-quote{background-color: hotpink;}',
);
$basic = array(
'metadata' => array(
'path' => array( 'styles', 'blocks', 'core/quote', 'variations', 'basic' ),
'selector' => '.is-style-basic.is-style-basic.wp-block-quote',
),
'styles' => '.is-style-basic.is-style-basic.wp-block-quote{background-color: #ffffff;color: #000000;}',
);

return array(
'1 variation with 1 invalid property' => array(
'theme_json_variations' => array(
'variations' => array(
'plain' => array(
'color' => array(
'background' => 'hotpink',
),
),
),
),
'metadata_variation' => array( $plain['metadata'] ),
'expected' => $plain['styles'],
),
'1 variation with 2 invalid properties' => array(
'theme_json_variations' => array(
'variations' => array(
'plain' => array(
'color' => array(
'background' => 'hotpink',
),
'invalidProperty1' => 'value1',
'invalidProperty2' => 'value2',
),
),
),
'metadata_variation' => array( $plain['metadata'] ),
'expected' => $plain['styles'],
),
'2 variations with 1 invalid property' => array(
'theme_json_variations' => array(
'variations' => array(
'plain' => array(
'color' => array(
'background' => 'hotpink',
),
'invalidProperty1' => 'value1',
),
'basic' => array(
'color' => array(
'background' => '#ffffff',
'text' => '#000000',
),
'foo' => 'bar',
),
),
),
'metadata_variation' => array( $plain['metadata'], $basic['metadata'] ),
'expected_styles' => $plain['styles'] . $basic['styles'],
),
'2 variations with multiple invalid properties' => array(
'theme_json_variations' => array(
'variations' => array(
'plain' => array(
'color' => array(
'background' => 'hotpink',
),
'invalidProperty1' => 'value1',
'invalidProperty2' => 'value2',
),
'basic' => array(
'foo' => 'foo',
'color' => array(
'background' => '#ffffff',
'text' => '#000000',
),
'bar' => 'bar',
'baz' => 'baz',
),
),
),
'metadata_variation' => array( $plain['metadata'], $basic['metadata'] ),
'expected_styles' => $plain['styles'] . $basic['styles'],
),
);
}

public function test_update_separator_declarations() {
// If only background is defined, test that includes border-color to the style so it is applied on the front end.
$theme_json = new WP_Theme_JSON_Gutenberg(
Expand Down