Skip to content

Commit

Permalink
Block Variations: Compare objects based on given properties (#62272)
Browse files Browse the repository at this point in the history
If a block variation has an isActive property that is an array of strings (attribute names), and one of the attributes referenced therein is an object, compare that attribute based on the object properties specified by the variation, rather than by reference.

Co-authored-by: ockham <bernhard-reiter@git.wordpress.org>
Co-authored-by: ntsekouras <ntsekouras@git.wordpress.org>
Co-authored-by: cbravobernal <cbravobernal@git.wordpress.org>
Co-authored-by: tyxla <tyxla@git.wordpress.org>
Co-authored-by: gziolo <gziolo@git.wordpress.org
  • Loading branch information
5 people authored and vcanales committed Jun 5, 2024
1 parent 803c373 commit 2f2677a
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 2 deletions.
7 changes: 5 additions & 2 deletions packages/blocks/src/store/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { RichTextData } from '@wordpress/rich-text';
/**
* Internal dependencies
*/
import { getValueFromObjectPath } from './utils';
import { getValueFromObjectPath, matchesAttributes } from './utils';

/** @typedef {import('../api/registration').WPBlockVariation} WPBlockVariation */
/** @typedef {import('../api/registration').WPBlockVariationScope} WPBlockVariationScope */
Expand Down Expand Up @@ -277,7 +277,10 @@ export function getActiveBlockVariation( state, blockName, attributes, scope ) {
if ( blockAttributeValue instanceof RichTextData ) {
blockAttributeValue = blockAttributeValue.toHTMLString();
}
return variationAttributeValue === blockAttributeValue;
return matchesAttributes(
blockAttributeValue,
variationAttributeValue
);
} );
if ( isMatch && definedAttributesLength > maxMatchedAttributes ) {
match = variation;
Expand Down
77 changes: 77 additions & 0 deletions packages/blocks/src/store/test/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,83 @@ describe( 'selectors', () => {
} )
).toEqual( variations[ 1 ] );
} );
it( 'should compare object attributes in the isActive array based on given properties', () => {
const variations = [
{
name: 'variation-1',
attributes: {
firstTestAttribute: {
nestedProperty: 1,
secondNestedProperty: 10,
},
secondTestAttribute: {
nestedProperty: {
firstDeeplyNestedProperty: 'a1',
secondDeeplyNestedProperty: 'a2',
},
},
},
isActive: [
'firstTestAttribute',
'secondTestAttribute.nestedProperty',
],
},
{
name: 'variation-2',
attributes: {
firstTestAttribute: {
nestedProperty: 2,
secondNestedProperty: 20,
},
secondTestAttribute: {
nestedProperty: {
firstDeeplyNestedProperty: 'b1',
secondDeeplyNestedProperty: 'b2',
},
},
},
isActive: [
'firstTestAttribute',
'secondTestAttribute.nestedProperty',
],
},
];
const state =
createBlockVariationsStateWithTestBlockType( variations );

expect(
getActiveBlockVariation( state, blockName, {
firstTestAttribute: {
nestedProperty: 1,
secondNestedProperty: 10,
otherNestedProperty: 5555,
},
secondTestAttribute: {
nestedProperty: {
firstDeeplyNestedProperty: 'a1',
secondDeeplyNestedProperty: 'a2',
otherDeeplyNestedProperty: 'ffff',
},
},
} )
).toEqual( variations[ 0 ] );
expect(
getActiveBlockVariation( state, blockName, {
firstTestAttribute: {
nestedProperty: 2,
secondNestedProperty: 20,
otherNestedProperty: 5555,
},
secondTestAttribute: {
nestedProperty: {
firstDeeplyNestedProperty: 'b1',
secondDeeplyNestedProperty: 'b2',
otherDeeplyNestedProperty: 'ffff',
},
},
} )
).toEqual( variations[ 1 ] );
} );
it( 'should return the active variation based on the given isActive array (multiple values)', () => {
const variations = [
{
Expand Down
30 changes: 30 additions & 0 deletions packages/blocks/src/store/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,33 @@ export const getValueFromObjectPath = ( object, path, defaultValue ) => {
} );
return value ?? defaultValue;
};

function isObject( candidate ) {
return (
typeof candidate === 'object' &&
candidate.constructor === Object &&
candidate !== null
);
}

/**
* Determine whether a set of object properties matches a given object.
*
* Given an object of block attributes and an object of variation attributes,
* this function checks recursively whether all the variation attributes are
* present in the block attributes object.
*
* @param {Object} blockAttributes The object to inspect.
* @param {Object} variationAttributes The object of property values to match.
* @return {boolean} Whether the block attributes match the variation attributes.
*/
export function matchesAttributes( blockAttributes, variationAttributes ) {
if ( isObject( blockAttributes ) && isObject( variationAttributes ) ) {
return Object.entries( variationAttributes ).every(
( [ key, value ] ) =>
matchesAttributes( blockAttributes?.[ key ], value )
);
}

return blockAttributes === variationAttributes;
}

0 comments on commit 2f2677a

Please sign in to comment.