diff --git a/packages/blocks/src/store/selectors.js b/packages/blocks/src/store/selectors.js index 5f18ecc50697c0..e97048e92b0c07 100644 --- a/packages/blocks/src/store/selectors.js +++ b/packages/blocks/src/store/selectors.js @@ -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 */ @@ -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; diff --git a/packages/blocks/src/store/test/selectors.js b/packages/blocks/src/store/test/selectors.js index 88e5828b2fd0e4..1f95809c9674b0 100644 --- a/packages/blocks/src/store/test/selectors.js +++ b/packages/blocks/src/store/test/selectors.js @@ -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 = [ { diff --git a/packages/blocks/src/store/utils.js b/packages/blocks/src/store/utils.js index 64974bd8000b27..ce9b9f9ab3be94 100644 --- a/packages/blocks/src/store/utils.js +++ b/packages/blocks/src/store/utils.js @@ -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; +}