Skip to content

Commit

Permalink
Section Styles: Resolve ref values in variations data (WordPress#63172)
Browse files Browse the repository at this point in the history
Co-authored-by: aaronrobertshaw <[email protected]>
Co-authored-by: andrewserong <[email protected]>
Co-authored-by: ramonjd <[email protected]>
Co-authored-by: daviedR <[email protected]>
  • Loading branch information
5 people authored Jul 8, 2024
1 parent 6d3282e commit 0aa455b
Show file tree
Hide file tree
Showing 5 changed files with 286 additions and 3 deletions.
3 changes: 3 additions & 0 deletions backport-changelog/6.6/6989.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
https://github.com/WordPress/wordpress-develop/pull/6989

* https://github.com/WordPress/gutenberg/pull/63172
40 changes: 40 additions & 0 deletions lib/block-supports/block-style-variations.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,42 @@ function gutenberg_get_block_style_variation_name_from_class( $class_string ) {
return $matches[1] ?? null;
}

/**
* Recursively resolves any `ref` values within a block style variation's data.
*
* @since 6.6.0
*
* @param array $variation_data Reference to the variation data being processed.
* @param array $theme_json Theme.json data to retrieve referenced values from.
*/
function gutenberg_resolve_block_style_variation_ref_values( &$variation_data, $theme_json ) {
foreach ( $variation_data as $key => &$value ) {
// Only need to potentially process arrays.
if ( is_array( $value ) ) {
// If ref value is set, attempt to find its matching value and update it.
if ( array_key_exists( 'ref', $value ) ) {
// Clean up any invalid ref value.
if ( empty( $value['ref'] ) || ! is_string( $value['ref'] ) ) {
unset( $variation_data[ $key ] );
}

$value_path = explode( '.', $value['ref'] ?? '' );
$ref_value = _wp_array_get( $theme_json, $value_path );

// Only update the current value if the referenced path matched a value.
if ( null === $ref_value ) {
unset( $variation_data[ $key ] );
} else {
$value = $ref_value;
}
} else {
// Recursively look for ref instances.
gutenberg_resolve_block_style_variation_ref_values( $value, $theme_json );
}
}
}
}

/**
* Render the block style variation's styles.
*
Expand Down Expand Up @@ -79,6 +115,10 @@ function gutenberg_render_block_style_variation_support_styles( $parsed_block )
return $parsed_block;
}

// Recursively resolve any ref values with the appropriate value within the
// theme_json data.
gutenberg_resolve_block_style_variation_ref_values( $variation_data, $theme_json );

$variation_instance = gutenberg_create_block_style_variation_instance_name( $parsed_block, $variation );
$class_name = "is-style-$variation_instance";
$updated_class_name = $parsed_block['attrs']['className'] . " $class_name";
Expand Down
83 changes: 80 additions & 3 deletions packages/block-editor/src/hooks/block-style-variation.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
getBlockSelectors,
} from '../components/global-styles';
import { useStyleOverride } from './utils';
import { getValueFromObjectPath } from '../utils/object';
import { store as blockEditorStore } from '../store';
import { globalStylesDataKey } from '../store/private-keys';
import { unlock } from '../lock-unlock';
Expand Down Expand Up @@ -180,6 +181,77 @@ export function __unstableBlockStyleVariationOverridesWithConfig( { config } ) {
);
}

/**
* Retrieves any variation styles data and resolves any referenced values.
*
* @param {Object} globalStyles A complete global styles object, containing settings and styles.
* @param {string} name The name of the desired block type.
* @param {variation} variation The of the block style variation to retrieve data for.
*
* @return {Object|undefined} The global styles data for the specified variation.
*/
export function getVariationStylesWithRefValues(
globalStyles,
name,
variation
) {
if ( ! globalStyles?.styles?.blocks?.[ name ]?.variations?.[ variation ] ) {
return;
}

// Helper to recursively look for `ref` values to resolve.
const replaceRefs = ( variationStyles ) => {
Object.keys( variationStyles ).forEach( ( key ) => {
const value = variationStyles[ key ];

// Only process objects.
if ( typeof value === 'object' && value !== null ) {
// Process `ref` value if present.
if ( value.ref !== undefined ) {
if (
typeof value.ref !== 'string' ||
value.ref.trim() === ''
) {
// Remove invalid ref.
delete variationStyles[ key ];
} else {
// Resolve `ref` value.
const refValue = getValueFromObjectPath(
globalStyles,
value.ref
);

if ( refValue ) {
variationStyles[ key ] = refValue;
} else {
delete variationStyles[ key ];
}
}
} else {
// Recursively resolve `ref` values in nested objects.
replaceRefs( value );

// After recursion, if value is empty due to explicitly
// `undefined` ref value, remove it.
if ( Object.keys( value ).length === 0 ) {
delete variationStyles[ key ];
}
}
}
} );
};

// Deep clone variation node to avoid mutating it within global styles and losing refs.
const styles = JSON.parse(
JSON.stringify(
globalStyles.styles.blocks[ name ].variations[ variation ]
)
);
replaceRefs( styles );

return styles;
}

function useBlockStyleVariation( name, variation, clientId ) {
// Prefer global styles data in GlobalStylesContext, which are available
// if in the site editor. Otherwise fall back to whatever is in the
Expand All @@ -194,9 +266,14 @@ function useBlockStyleVariation( name, variation, clientId ) {
}, [] );

return useMemo( () => {
const styles = mergedConfig?.styles ?? globalStyles;
const variationStyles =
styles?.blocks?.[ name ]?.variations?.[ variation ];
const variationStyles = getVariationStylesWithRefValues(
{
settings: mergedConfig?.settings ?? globalSettings,
styles: mergedConfig?.styles ?? globalStyles,
},
name,
variation
);

return {
settings: mergedConfig?.settings ?? globalSettings,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/**
* Internal dependencies
*/
import { getVariationStylesWithRefValues } from '../block-style-variation';

describe( 'getVariationStylesWithRefValues', () => {
it( 'should resolve ref values correctly', () => {
const globalStyles = {
styles: {
color: { background: 'red' },
blocks: {
'core/heading': {
color: { text: 'blue' },
},
'core/group': {
variations: {
custom: {
color: {
text: { ref: 'styles.does-not-exist' },
background: {
ref: 'styles.color.background',
},
},
blocks: {
'core/heading': {
color: {
text: {
ref: 'styles.blocks.core/heading.color.text',
},
background: { ref: '' },
},
},
},
elements: {
link: {
color: {
text: {
ref: 'styles.elements.link.color.text',
},
background: { ref: undefined },
},
':hover': {
color: {
text: {
ref: 'styles.elements.link.:hover.color.text',
},
},
},
},
},
},
},
},
},
elements: {
link: {
color: { text: 'green' },
':hover': {
color: { text: 'yellow' },
},
},
},
},
};

expect(
getVariationStylesWithRefValues(
globalStyles,
'core/group',
'custom'
)
).toEqual( {
color: { background: 'red' },
blocks: {
'core/heading': {
color: { text: 'blue' },
},
},
elements: {
link: {
color: {
text: 'green',
},
':hover': {
color: { text: 'yellow' },
},
},
},
} );
} );
} );
72 changes: 72 additions & 0 deletions phpunit/block-supports/block-style-variations-test.php
Original file line number Diff line number Diff line change
Expand Up @@ -189,4 +189,76 @@ public function test_add_registered_block_styles_to_theme_data() {

$this->assertSameSetsWithIndex( $expected, $group_styles, 'Variation data does not match' );
}

/**
* Tests that block style variations resolve any `ref` values when generating styles.
*/
public function test_block_style_variation_ref_values() {
switch_theme( 'block-theme' );

$variation_data = array(
'color' => array(
'text' => array(
'ref' => 'styles.does-not-exist',
),
'background' => array(
'ref' => 'styles.blocks.core/group.variations.block-style-variation-a.color.text',
),
),
'blocks' => array(
'core/heading' => array(
'color' => array(
'text' => array(
'ref' => 'styles.blocks.core/group.variations.block-style-variation-a.color.background',
),
'background' => array(
'ref' => '',
),
),
),
),
'elements' => array(
'link' => array(
'color' => array(
'text' => array(
'ref' => 'styles.blocks.core/group.variations.block-style-variation-b.color.text',
),
'background' => array(
'ref' => null,
),
),
':hover' => array(
'color' => array(
'text' => array(
'ref' => 'styles.blocks.core/group.variations.block-style-variation-b.color.background',
),
),
),
),
),
);

$theme_json = WP_Theme_JSON_Resolver_Gutenberg::get_theme_data()->get_raw_data();

gutenberg_resolve_block_style_variation_ref_values( $variation_data, $theme_json );

$expected = array(
'color' => array( 'background' => 'plum' ),
'blocks' => array(
'core/heading' => array(
'color' => array( 'text' => 'indigo' ),
),
),
'elements' => array(
'link' => array(
'color' => array( 'text' => 'lightblue' ),
':hover' => array(
'color' => array( 'text' => 'midnightblue' ),
),
),
),
);

$this->assertSameSetsWithIndex( $expected, $variation_data, 'Variation data with resolved ref values does not match' );
}
}

0 comments on commit 0aa455b

Please sign in to comment.