Skip to content

Commit

Permalink
Block Styles: Fix block style variation selector generation (#58051)
Browse files Browse the repository at this point in the history
  • Loading branch information
aaronrobertshaw authored Jan 30, 2024
1 parent 2719637 commit 8895a51
Show file tree
Hide file tree
Showing 6 changed files with 248 additions and 3 deletions.
35 changes: 34 additions & 1 deletion lib/class-wp-theme-json-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -1024,7 +1024,7 @@ protected static function get_blocks_metadata() {
if ( ! empty( $block_type->styles ) ) {
$style_selectors = array();
foreach ( $block_type->styles as $style ) {
$style_selectors[ $style['name'] ] = static::append_to_selector( '.is-style-' . $style['name'], static::$blocks_metadata[ $block_name ]['selector'] );
$style_selectors[ $style['name'] ] = static::get_block_style_variation_selector( $style['name'], static::$blocks_metadata[ $block_name ]['selector'] );
}
static::$blocks_metadata[ $block_name ]['styleVariations'] = $style_selectors;
}
Expand Down Expand Up @@ -3880,4 +3880,37 @@ function ( $carry, $item ) {
$theme_json->theme_json['styles'] = self::convert_variables_to_value( $styles, $vars );
return $theme_json;
}

/**
* Generates a selector for a block style variation.
*
* @param string $variation_name Name of the block style variation.
* @param string $block_selector CSS selector for the block.
*
* @return string Block selector with block style variation selector added to it.
*/
protected static function get_block_style_variation_selector( $variation_name, $block_selector ) {
$variation_class = ".is-style-$variation_name";

if ( ! $block_selector ) {
return $variation_class;
}

$limit = 1;
$selector_parts = explode( ',', $block_selector );
$result = array();

foreach ( $selector_parts as $part ) {
$result[] = preg_replace_callback(
'/((?::\([^)]+\))?\s*)([^\s:]+)/',
function ( $matches ) use ( $variation_class ) {
return $matches[1] . $matches[2] . $variation_class;
},
$part,
$limit
);
}

return implode( ',', $result );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -816,7 +816,7 @@ describe( 'global styles renderer', () => {
filter: { duotone: 'img' },
},
styleVariationSelectors: {
foo: '.is-style-foo.my-image',
foo: '.my-image.is-style-foo',
},
hasLayoutSupport: false,
},
Expand Down
86 changes: 86 additions & 0 deletions packages/block-editor/src/components/global-styles/test/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*/
import {
areGlobalStyleConfigsEqual,
getBlockStyleVariationSelector,
getPresetVariableFromValue,
getValueFromVariable,
} from '../utils';
Expand Down Expand Up @@ -259,4 +260,89 @@ describe( 'editor utils', () => {
}
);
} );

describe( 'getBlockStyleVariationSelector', () => {
test.each( [
{ type: 'empty', selector: '', expected: '.is-style-custom' },
{
type: 'class',
selector: '.wp-block',
expected: '.wp-block.is-style-custom',
},
{
type: 'id',
selector: '#wp-block',
expected: '#wp-block.is-style-custom',
},
{
type: 'element tag',
selector: 'p',
expected: 'p.is-style-custom',
},
{
type: 'attribute',
selector: '[style*="color"]',
expected: '[style*="color"].is-style-custom',
},
{
type: 'descendant',
selector: '.wp-block .inner',
expected: '.wp-block.is-style-custom .inner',
},
{
type: 'comma-separated',
selector: '.wp-block .inner, .wp-block .alternative',
expected:
'.wp-block.is-style-custom .inner, .wp-block.is-style-custom .alternative',
},
{
type: 'pseudo',
selector: 'div:first-child',
expected: 'div.is-style-custom:first-child',
},
{
type: ':is',
selector: '.wp-block:is(.outer .inner:first-child)',
expected:
'.wp-block.is-style-custom:is(.outer .inner:first-child)',
},
{
type: ':not',
selector: '.wp-block:not(.outer .inner:first-child)',
expected:
'.wp-block.is-style-custom:not(.outer .inner:first-child)',
},
{
type: ':has',
selector: '.wp-block:has(.outer .inner:first-child)',
expected:
'.wp-block.is-style-custom:has(.outer .inner:first-child)',
},
{
type: ':where',
selector: '.wp-block:where(.outer .inner:first-child)',
expected:
'.wp-block.is-style-custom:where(.outer .inner:first-child)',
},
{
type: 'wrapping :where',
selector: ':where(.outer .inner:first-child)',
expected: ':where(.outer.is-style-custom .inner:first-child)',
},
{
type: 'complex',
selector:
'.wp:where(.something):is(.test:not(.nothing p)):has(div[style]) .content, .wp:where(.nothing):not(.test:is(.something div)):has(span[style]) .inner',
expected:
'.wp.is-style-custom:where(.something):is(.test:not(.nothing p)):has(div[style]) .content, .wp.is-style-custom:where(.nothing):not(.test:is(.something div)):has(span[style]) .inner',
},
] )(
'should add variation class to ancestor in $type selector',
( { selector, expected } ) => {
expect(
getBlockStyleVariationSelector( 'custom', selector )
).toBe( expected );
}
);
} );
} );
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
ROOT_BLOCK_SELECTOR,
scopeSelector,
appendToSelector,
getBlockStyleVariationSelector,
} from './utils';
import { getBlockCSSSelector } from './get-block-css-selector';
import {
Expand Down Expand Up @@ -1077,7 +1078,10 @@ export const getBlockSelectors = ( blockTypes, getBlockStyles ) => {
const styleVariationSelectors = {};
if ( blockStyleVariations?.length ) {
blockStyleVariations.forEach( ( variation ) => {
const styleVariationSelector = `.is-style-${ variation.name }${ selector }`;
const styleVariationSelector = getBlockStyleVariationSelector(
variation.name,
selector
);
styleVariationSelectors[ variation.name ] =
styleVariationSelector;
} );
Expand Down
36 changes: 36 additions & 0 deletions packages/block-editor/src/components/global-styles/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -446,3 +446,39 @@ export function areGlobalStyleConfigsEqual( original, variation ) {
fastDeepEqual( original?.settings, variation?.settings )
);
}

/**
* Generates the selector for a block style variation by creating the
* appropriate CSS class and adding it to the ancestor portion of the block's
* selector.
*
* For example, take the Button block which has a compound selector:
* `.wp-block-button .wp-block-button__link`. With a variation named 'custom',
* the class `.is-style-custom` should be added to the `.wp-block-button`
* ancestor only.
*
* This function will take into account comma separated and complex selectors.
*
* @param {string} variation Name for the variation.
* @param {string} blockSelector CSS selector for the block.
*
* @return {string} CSS selector for the block style variation.
*/
export function getBlockStyleVariationSelector( variation, blockSelector ) {
const variationClass = `.is-style-${ variation }`;

if ( ! blockSelector ) {
return variationClass;
}

const ancestorRegex = /((?::\([^)]+\))?\s*)([^\s:]+)/;
const addVariationClass = ( _match, group1, group2 ) => {
return group1 + group2 + variationClass;
};

const result = blockSelector
.split( ',' )
.map( ( part ) => part.replace( ancestorRegex, addVariationClass ) );

return result.join( ',' );
}
86 changes: 86 additions & 0 deletions phpunit/class-wp-theme-json-test.php
Original file line number Diff line number Diff line change
Expand Up @@ -2475,4 +2475,90 @@ public function test_get_stylesheet_custom_root_selector() {
$actual
);
}

/**
* Tests the correct application of a block style variation's selector to
* a block's selector.
*
* @dataProvider data_get_block_style_variation_selector
*
* @param string $selector CSS selector.
* @param string $expected Expected block style variation CSS selector.
*/
public function test_get_block_style_variation_selector( $selector, $expected ) {
$theme_json = new ReflectionClass( 'WP_Theme_JSON_Gutenberg' );

$func = $theme_json->getMethod( 'get_block_style_variation_selector' );
$func->setAccessible( true );

$actual = $func->invoke( null, 'custom', $selector );

$this->assertEquals( $expected, $actual );
}

/**
* Data provider for generating block style variation selectors.
*
* @return array[]
*/
public function data_get_block_style_variation_selector() {
return array(
'empty block selector' => array(
'selector' => '',
'expected' => '.is-style-custom',
),
'class selector' => array(
'selector' => '.wp-block',
'expected' => '.wp-block.is-style-custom',
),
'id selector' => array(
'selector' => '#wp-block',
'expected' => '#wp-block.is-style-custom',
),
'element tag selector' => array(
'selector' => 'p',
'expected' => 'p.is-style-custom',
),
'attribute selector' => array(
'selector' => '[style*="color"]',
'expected' => '[style*="color"].is-style-custom',
),
'descendant selector' => array(
'selector' => '.wp-block .inner',
'expected' => '.wp-block.is-style-custom .inner',
),
'comma separated selector' => array(
'selector' => '.wp-block .inner, .wp-block .alternative',
'expected' => '.wp-block.is-style-custom .inner, .wp-block.is-style-custom .alternative',
),
'pseudo selector' => array(
'selector' => 'div:first-child',
'expected' => 'div.is-style-custom:first-child',
),
':is selector' => array(
'selector' => '.wp-block:is(.outer .inner:first-child)',
'expected' => '.wp-block.is-style-custom:is(.outer .inner:first-child)',
),
':not selector' => array(
'selector' => '.wp-block:not(.outer .inner:first-child)',
'expected' => '.wp-block.is-style-custom:not(.outer .inner:first-child)',
),
':has selector' => array(
'selector' => '.wp-block:has(.outer .inner:first-child)',
'expected' => '.wp-block.is-style-custom:has(.outer .inner:first-child)',
),
':where selector' => array(
'selector' => '.wp-block:where(.outer .inner:first-child)',
'expected' => '.wp-block.is-style-custom:where(.outer .inner:first-child)',
),
'wrapping :where selector' => array(
'selector' => ':where(.outer .inner:first-child)',
'expected' => ':where(.outer.is-style-custom .inner:first-child)',
),
'complex' => array(
'selector' => '.wp:where(.something):is(.test:not(.nothing p)):has(div[style]) .content, .wp:where(.nothing):not(.test:is(.something div)):has(span[style]) .inner',
'expected' => '.wp.is-style-custom:where(.something):is(.test:not(.nothing p)):has(div[style]) .content, .wp.is-style-custom:where(.nothing):not(.test:is(.something div)):has(span[style]) .inner',
),
);
}
}

0 comments on commit 8895a51

Please sign in to comment.