Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Extract function and add basic tests
  • Loading branch information
getdave committed Dec 6, 2022
commit a065d9adb9e8bf20fbab3e7664527dbcd8cd7471
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
export function areInnerBlocksDirty( originalBlocks, blocks ) {
return ! isDeepEqual( originalBlocks, blocks, ( prop, x ) => {
// Skip inner blocks of page list during comparison as they
// are **always** controlled and may be updated async due to
// syncing with enitiy records. Left unchecked this would
// inadvertently trigger the dirty state.
if ( x?.name === 'core/page-list' && prop === 'innerBlocks' ) {
return true;
}
} );
}

/**
* Conditionally compares two candidates for deep equality.
* Provides an option to skip a given property of an object during comparison.
*
* @param {*} x 1st candidate for comparison
* @param {*} y 2nd candidate for comparison
* @param {Function|undefined} shouldSkip a function which can be used to skip a given property of an object.
* @return {boolean} whether the two candidates are deeply equal.
*/
const isDeepEqual = ( x, y, shouldSkip ) => {
if ( x === y ) {
return true;
} else if (
typeof x === 'object' &&
x !== null &&
x !== undefined &&
typeof y === 'object' &&
y !== null &&
y !== undefined
) {
if ( Object.keys( x ).length !== Object.keys( y ).length ) return false;

for ( const prop in x ) {
if ( y.hasOwnProperty( prop ) ) {
// Afford skipping a given property of an object.
if ( shouldSkip && shouldSkip( prop, x ) ) {
return true;
}

if ( ! isDeepEqual( x[ prop ], y[ prop ], shouldSkip ) )
return false;
} else return false;
}

return true;
}

return false;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* Internal dependencies
*/
import { areInnerBlocksDirty } from '../are-inner-blocks-dirty';

describe( 'areInnerBlocksDirty', () => {
it( 'should be false if the inner blocks are the same', () => {
expect(
areInnerBlocksDirty(
[
{
name: 'core/group',
innerBlocks: [
{ name: 'core/paragraph', innerBlocks: [] },
],
},
],
[
{
name: 'core/group',
innerBlocks: [
{ name: 'core/paragraph', innerBlocks: [] },
],
},
]
)
).toBe( false );
} );

it( 'should be true if the inner blocks are dirty', () => {
expect(
areInnerBlocksDirty(
[
{
name: 'core/group',
innerBlocks: [
{ name: 'core/paragraph', innerBlocks: [] },
],
},
],
[
{
name: 'core/group',
innerBlocks: [
{ name: 'core/paragraph', innerBlocks: [] },
{ name: 'core/paragraph', innerBlocks: [] },
],
},
]
)
).toBe( true );
} );
} );
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { useContext, useEffect, useRef, useMemo } from '@wordpress/element';
* Internal dependencies
*/
import useNavigationMenu from '../use-navigation-menu';
import { areInnerBlocksDirty } from './are-inner-blocks-dirty';

const EMPTY_OBJECT = {};
const DRAFT_MENU_PARAMS = [
Expand All @@ -35,46 +36,6 @@ const ALLOWED_BLOCKS = [
'core/navigation-submenu',
];

/**
* Conditionally compares two candidates for deep equality.
* Provides an option to skip a given property of an object during comparison.
*
* @param {*} x 1st candidate for comparison
* @param {*} y 2nd candidate for comparison
* @param {Function|undefined} shouldSkip a function which can be used to skip a given property of an object.
* @return {boolean} whether the two candidates are deeply equal.
*/
const isDeepEqual = ( x, y, shouldSkip ) => {
if ( x === y ) {
return true;
} else if (
typeof x === 'object' &&
x !== null &&
x !== undefined &&
typeof y === 'object' &&
y !== null &&
y !== undefined
) {
if ( Object.keys( x ).length !== Object.keys( y ).length ) return false;

for ( const prop in x ) {
if ( y.hasOwnProperty( prop ) ) {
// Afford skipping a given property of an object.
if ( shouldSkip && shouldSkip( prop, x ) ) {
return true;
}

if ( ! isDeepEqual( x[ prop ], y[ prop ], shouldSkip ) )
return false;
} else return false;
}

return true;
}

return false;
};

export default function UnsavedInnerBlocks( {
blocks,
createNavigationMenu,
Expand All @@ -98,18 +59,9 @@ export default function UnsavedInnerBlocks( {
// of the page list are controlled and may be updated async due to syncing with
// entity records. As a result we need to perform a deep equality check skipping
// the page list's inner blocks.
const innerBlocksAreDirty = ! isDeepEqual(
originalBlocks.current,
blocks,
( prop, x ) => {
// Skip inner blocks of page list during comparison as they
// are **always** controlled and may be updated async due to
// syncing with enitiy records. Left unchecked this would
// inadvertently trigger the dirty state.
if ( x?.name === 'core/page-list' && prop === 'innerBlocks' ) {
return true;
}
}
const innerBlocksAreDirty = areInnerBlocksDirty(
originalBlocks?.current,
blocks
);

const shouldDirectInsert = useMemo(
Expand Down