@wordpress/interactivity
Advanced tools
Comparing version 6.10.0 to 6.11.0
@@ -247,2 +247,4 @@ /* wp:polyfill */ | ||
const deepMergeRecursive = (target, source, override = true) => { | ||
// If target is not a plain object and the source is, we don't need to merge | ||
// them because the source will be used as the new value of the target. | ||
if (!(isPlainObject(target) && isPlainObject(source))) { | ||
@@ -258,4 +260,8 @@ return; | ||
const propSignal = !!proxy && hasPropSignal(proxy, key) && getPropSignal(proxy, key); | ||
// Handle getters and setters | ||
if (typeof desc.get === 'function' || typeof desc.set === 'function') { | ||
if (override || isNew) { | ||
// Because we are setting a getter or setter, we need to use | ||
// Object.defineProperty to define the property on the target object. | ||
Object.defineProperty(target, key, { | ||
@@ -266,2 +272,3 @@ ...desc, | ||
}); | ||
// Update the getter in the property signal if it exists | ||
if (desc.get && propSignal) { | ||
@@ -271,16 +278,29 @@ propSignal.setGetter(desc.get); | ||
} | ||
// Handle nested objects | ||
} else if (isPlainObject(source[key])) { | ||
if (isNew || override && !isPlainObject(target[key])) { | ||
// Create a new object if the property is new or needs to be overridden | ||
target[key] = {}; | ||
if (propSignal) { | ||
propSignal.setValue(target[key]); | ||
// Create a new proxified state for the nested object | ||
const ns = getNamespaceFromProxy(proxy); | ||
propSignal.setValue(proxifyState(ns, target[key])); | ||
} | ||
} | ||
// Both target and source are plain objects, merge them recursively | ||
if (isPlainObject(target[key])) { | ||
deepMergeRecursive(target[key], source[key], override); | ||
} | ||
// Handle primitive values and non-plain objects | ||
} else if (override || isNew) { | ||
Object.defineProperty(target, key, desc); | ||
if (propSignal) { | ||
propSignal.setValue(desc.value); | ||
const { | ||
value | ||
} = desc; | ||
const ns = getNamespaceFromProxy(proxy); | ||
// Proxify the value if necessary before setting it in the signal | ||
propSignal.setValue(shouldProxy(value) ? proxifyState(ns, value) : value); | ||
} | ||
@@ -287,0 +307,0 @@ } |
@@ -256,2 +256,4 @@ /* wp:polyfill */ | ||
const deepMergeRecursive = (target, source, override = true) => { | ||
// If target is not a plain object and the source is, we don't need to merge | ||
// them because the source will be used as the new value of the target. | ||
if (!((0, _utils.isPlainObject)(target) && (0, _utils.isPlainObject)(source))) { | ||
@@ -267,4 +269,8 @@ return; | ||
const propSignal = !!proxy && hasPropSignal(proxy, key) && getPropSignal(proxy, key); | ||
// Handle getters and setters | ||
if (typeof desc.get === 'function' || typeof desc.set === 'function') { | ||
if (override || isNew) { | ||
// Because we are setting a getter or setter, we need to use | ||
// Object.defineProperty to define the property on the target object. | ||
Object.defineProperty(target, key, { | ||
@@ -275,2 +281,3 @@ ...desc, | ||
}); | ||
// Update the getter in the property signal if it exists | ||
if (desc.get && propSignal) { | ||
@@ -280,16 +287,29 @@ propSignal.setGetter(desc.get); | ||
} | ||
// Handle nested objects | ||
} else if ((0, _utils.isPlainObject)(source[key])) { | ||
if (isNew || override && !(0, _utils.isPlainObject)(target[key])) { | ||
// Create a new object if the property is new or needs to be overridden | ||
target[key] = {}; | ||
if (propSignal) { | ||
propSignal.setValue(target[key]); | ||
// Create a new proxified state for the nested object | ||
const ns = (0, _registry.getNamespaceFromProxy)(proxy); | ||
propSignal.setValue(proxifyState(ns, target[key])); | ||
} | ||
} | ||
// Both target and source are plain objects, merge them recursively | ||
if ((0, _utils.isPlainObject)(target[key])) { | ||
deepMergeRecursive(target[key], source[key], override); | ||
} | ||
// Handle primitive values and non-plain objects | ||
} else if (override || isNew) { | ||
Object.defineProperty(target, key, desc); | ||
if (propSignal) { | ||
propSignal.setValue(desc.value); | ||
const { | ||
value | ||
} = desc; | ||
const ns = (0, _registry.getNamespaceFromProxy)(proxy); | ||
// Proxify the value if necessary before setting it in the signal | ||
propSignal.setValue((0, _registry.shouldProxy)(value) ? proxifyState(ns, value) : value); | ||
} | ||
@@ -296,0 +316,0 @@ } |
@@ -5,2 +5,8 @@ <!-- Learn how to maintain this file at https://github.com/WordPress/gutenberg/tree/HEAD/packages#maintaining-changelogs. --> | ||
## 6.11.0 (2024-10-30) | ||
### Bug Fixes | ||
- Fix reactivity of undefined objects and arrays added with `deepMerge()` ([#66183](https://github.com/WordPress/gutenberg/pull/66183)). | ||
## 6.10.0 (2024-10-16) | ||
@@ -7,0 +13,0 @@ |
{ | ||
"name": "@wordpress/interactivity", | ||
"version": "6.10.0", | ||
"version": "6.11.0", | ||
"description": "Package that provides a standard and simple way to handle the frontend interactivity of Gutenberg blocks.", | ||
@@ -40,3 +40,3 @@ "author": "The WordPress Contributors", | ||
}, | ||
"gitHead": "ab34a7ac935fd1478eac63b596242d83270897ee" | ||
"gitHead": "dcf4613b33b0eda14e203ac30f700ed0db70347f" | ||
} |
@@ -303,2 +303,4 @@ /** | ||
) => { | ||
// If target is not a plain object and the source is, we don't need to merge | ||
// them because the source will be used as the new value of the target. | ||
if ( ! ( isPlainObject( target ) && isPlainObject( source ) ) ) { | ||
@@ -321,2 +323,3 @@ return; | ||
// Handle getters and setters | ||
if ( | ||
@@ -327,2 +330,4 @@ typeof desc.get === 'function' || | ||
if ( override || isNew ) { | ||
// Because we are setting a getter or setter, we need to use | ||
// Object.defineProperty to define the property on the target object. | ||
Object.defineProperty( target, key, { | ||
@@ -333,2 +338,3 @@ ...desc, | ||
} ); | ||
// Update the getter in the property signal if it exists | ||
if ( desc.get && propSignal ) { | ||
@@ -338,16 +344,31 @@ propSignal.setGetter( desc.get ); | ||
} | ||
// Handle nested objects | ||
} else if ( isPlainObject( source[ key ] ) ) { | ||
if ( isNew || ( override && ! isPlainObject( target[ key ] ) ) ) { | ||
// Create a new object if the property is new or needs to be overridden | ||
target[ key ] = {}; | ||
if ( propSignal ) { | ||
propSignal.setValue( target[ key ] ); | ||
// Create a new proxified state for the nested object | ||
const ns = getNamespaceFromProxy( proxy ); | ||
propSignal.setValue( | ||
proxifyState( ns, target[ key ] as Object ) | ||
); | ||
} | ||
} | ||
// Both target and source are plain objects, merge them recursively | ||
if ( isPlainObject( target[ key ] ) ) { | ||
deepMergeRecursive( target[ key ], source[ key ], override ); | ||
} | ||
// Handle primitive values and non-plain objects | ||
} else if ( override || isNew ) { | ||
Object.defineProperty( target, key, desc ); | ||
if ( propSignal ) { | ||
propSignal.setValue( desc.value ); | ||
const { value } = desc; | ||
const ns = getNamespaceFromProxy( proxy ); | ||
// Proxify the value if necessary before setting it in the signal | ||
propSignal.setValue( | ||
shouldProxy( value ) ? proxifyState( ns, value ) : value | ||
); | ||
} | ||
@@ -354,0 +375,0 @@ } |
@@ -254,20 +254,2 @@ /* eslint-disable eslint-comments/disable-enable-pair */ | ||
it( 'should handle arrays', () => { | ||
const target = { a: [ 1, 2 ] }; | ||
const source = { a: [ 3, 4 ] }; | ||
const result = {}; | ||
deepMerge( result, target ); | ||
deepMerge( result, source ); | ||
expect( result ).toEqual( { a: [ 3, 4 ] } ); | ||
} ); | ||
it( 'should handle arrays when overwrite is false', () => { | ||
const target = { a: [ 1, 2 ] }; | ||
const source = { a: [ 3, 4 ] }; | ||
const result = {}; | ||
deepMerge( result, target, false ); | ||
deepMerge( result, source, false ); | ||
expect( result ).toEqual( { a: [ 1, 2 ] } ); | ||
} ); | ||
it( 'should handle null values', () => { | ||
@@ -383,3 +365,6 @@ const target = { a: 1, b: null }; | ||
const spy = jest.fn( () => Object.keys( target ) ); | ||
let keys: any; | ||
const spy = jest.fn( () => { | ||
keys = Object.keys( target ); | ||
} ); | ||
effect( spy ); | ||
@@ -392,3 +377,3 @@ | ||
expect( spy ).toHaveBeenCalledTimes( 2 ); | ||
expect( spy ).toHaveLastReturnedWith( [ 'a', 'b', 'c' ] ); | ||
expect( keys ).toEqual( [ 'a', 'b', 'c' ] ); | ||
} ); | ||
@@ -418,2 +403,9 @@ | ||
expect( target.a.b.c.d ).toBe( 'test value' ); | ||
// Modify the nested value | ||
target.a.b.c.d = 'new test value'; | ||
// The effect should be called again | ||
expect( spy ).toHaveBeenCalledTimes( 3 ); | ||
expect( deepValue ).toBe( 'new test value' ); | ||
} ); | ||
@@ -469,3 +461,96 @@ | ||
} ); | ||
it( 'should keep reactivity of arrays that are initially undefined', () => { | ||
const target: any = proxifyState( 'test', {} ); | ||
let deepValue: any; | ||
const spy = jest.fn( () => { | ||
deepValue = target.array?.[ 0 ]; | ||
} ); | ||
effect( spy ); | ||
// Initial call, the deep value is undefined | ||
expect( spy ).toHaveBeenCalledTimes( 1 ); | ||
expect( deepValue ).toBeUndefined(); | ||
// Use deepMerge to add an array to the target | ||
deepMerge( target, { array: [ 'value 1' ] } ); | ||
// The effect should be called again | ||
expect( spy ).toHaveBeenCalledTimes( 2 ); | ||
expect( deepValue ).toBe( 'value 1' ); | ||
// Modify the array value | ||
target.array[ 0 ] = 'value 2'; | ||
// The effect should be called again | ||
expect( spy ).toHaveBeenCalledTimes( 3 ); | ||
expect( deepValue ).toBe( 'value 2' ); | ||
} ); | ||
describe( 'arrays', () => { | ||
it( 'should handle arrays', () => { | ||
const target = { a: [ 1, 2 ] }; | ||
const source = { a: [ 3, 4 ] }; | ||
const result = {}; | ||
deepMerge( result, target ); | ||
deepMerge( result, source ); | ||
expect( result ).toEqual( { a: [ 3, 4 ] } ); | ||
} ); | ||
it( 'should handle arrays when overwrite is false', () => { | ||
const target = { a: [ 1, 2 ] }; | ||
const source = { a: [ 3, 4 ] }; | ||
const result = {}; | ||
deepMerge( result, target, false ); | ||
deepMerge( result, source, false ); | ||
expect( result ).toEqual( { a: [ 1, 2 ] } ); | ||
} ); | ||
it( 'should add new array from source if not present in target', () => { | ||
const target = { a: 1 }; | ||
const source = { arr: [ 1, 2, 3 ] }; | ||
const result = {}; | ||
deepMerge( result, target ); | ||
deepMerge( result, source ); | ||
expect( result ).toEqual( { a: 1, arr: [ 1, 2, 3 ] } ); | ||
} ); | ||
it( 'should handle nested arrays', () => { | ||
const target = { nested: { arr: [ 1, 2 ] } }; | ||
const source = { nested: { arr: [ 3, 4, 5 ] } }; | ||
const result = {}; | ||
deepMerge( result, target ); | ||
deepMerge( result, source ); | ||
expect( result ).toEqual( { nested: { arr: [ 3, 4, 5 ] } } ); | ||
} ); | ||
it( 'should handle object with array as target and object with object as source', () => { | ||
const target = { arr: [ 1, 2, 3 ] }; | ||
const source = { arr: { 1: 'two', 3: 'four' } }; | ||
const result: any = {}; | ||
deepMerge( result, target ); | ||
deepMerge( result, source ); | ||
expect( result ).toEqual( { arr: { 1: 'two', 3: 'four' } } ); | ||
} ); | ||
it( 'should handle object with object as target and object with array as source', () => { | ||
const target = { arr: { 0: 'zero', 2: 'two' } }; | ||
const source = { arr: [ 'a', 'b', 'c' ] }; | ||
const result = {}; | ||
deepMerge( result, target ); | ||
deepMerge( result, source ); | ||
expect( result ).toEqual( { arr: [ 'a', 'b', 'c' ] } ); | ||
} ); | ||
it( 'should handle objects with arrays containing object elements', () => { | ||
const target = { arr: [ { a: 1 }, { b: 2 } ] }; | ||
const source = { arr: [ { a: 2 }, { c: 3 } ] }; | ||
const result: any = {}; | ||
deepMerge( result, target ); | ||
deepMerge( result, source ); | ||
expect( result ).toEqual( { arr: [ { a: 2 }, { c: 3 } ] } ); | ||
} ); | ||
} ); | ||
} ); | ||
} ); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
757030
10590