-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Template Sidebar: Fix list of current template part areas to show nes…
…ted template parts (#47232) * Try: Update list of current template part areas to recursively check for template parts * Simplify logic, add memoized call * Move logic to a single function to reduce multiple loops, move to utils, add a test * Add test to cover memoization, only export memoized function
- Loading branch information
1 parent
c160a7f
commit a065bb5
Showing
5 changed files
with
258 additions
and
26 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
/** | ||
* Internal dependencies | ||
*/ | ||
import { getFilteredTemplatePartBlocks } from '../utils'; | ||
|
||
const NESTED_BLOCKS = [ | ||
{ | ||
clientId: '1', | ||
name: 'core/group', | ||
innerBlocks: [ | ||
{ | ||
clientId: '2', | ||
name: 'core/template-part', | ||
attributes: { | ||
slug: 'header', | ||
theme: 'my-theme', | ||
}, | ||
innerBlocks: [ | ||
{ | ||
clientId: '3', | ||
name: 'core/group', | ||
innerBlocks: [], | ||
}, | ||
], | ||
}, | ||
{ | ||
clientId: '4', | ||
name: 'core/template-part', | ||
attributes: { | ||
slug: 'aside', | ||
theme: 'my-theme', | ||
}, | ||
innerBlocks: [], | ||
}, | ||
], | ||
}, | ||
{ | ||
clientId: '5', | ||
name: 'core/paragraph', | ||
innerBlocks: [], | ||
}, | ||
{ | ||
clientId: '6', | ||
name: 'core/template-part', | ||
attributes: { | ||
slug: 'footer', | ||
theme: 'my-theme', | ||
}, | ||
innerBlocks: [], | ||
}, | ||
]; | ||
|
||
const FLATTENED_BLOCKS = [ | ||
{ | ||
block: { | ||
clientId: '2', | ||
name: 'core/template-part', | ||
attributes: { | ||
slug: 'header', | ||
theme: 'my-theme', | ||
}, | ||
}, | ||
templatePart: { | ||
id: 'my-theme//header', | ||
slug: 'header', | ||
theme: 'my-theme', | ||
}, | ||
}, | ||
{ | ||
block: { | ||
clientId: '4', | ||
name: 'core/template-part', | ||
attributes: { | ||
slug: 'aside', | ||
theme: 'my-theme', | ||
}, | ||
}, | ||
templatePart: { | ||
id: 'my-theme//aside', | ||
slug: 'aside', | ||
theme: 'my-theme', | ||
}, | ||
}, | ||
{ | ||
block: { | ||
clientId: '6', | ||
name: 'core/template-part', | ||
attributes: { | ||
slug: 'footer', | ||
theme: 'my-theme', | ||
}, | ||
}, | ||
templatePart: { | ||
id: 'my-theme//footer', | ||
slug: 'footer', | ||
theme: 'my-theme', | ||
}, | ||
}, | ||
]; | ||
|
||
const SINGLE_TEMPLATE_PART_BLOCK = { | ||
clientId: '1', | ||
name: 'core/template-part', | ||
innerBlocks: [], | ||
attributes: { | ||
slug: 'aside', | ||
theme: 'my-theme', | ||
}, | ||
}; | ||
|
||
const TEMPLATE_PARTS = [ | ||
{ | ||
id: 'my-theme//header', | ||
slug: 'header', | ||
theme: 'my-theme', | ||
}, | ||
{ | ||
id: 'my-theme//aside', | ||
slug: 'aside', | ||
theme: 'my-theme', | ||
}, | ||
{ | ||
id: 'my-theme//footer', | ||
slug: 'footer', | ||
theme: 'my-theme', | ||
}, | ||
]; | ||
|
||
describe( 'utils', () => { | ||
describe( 'getFilteredTemplatePartBlocks', () => { | ||
it( 'returns a flattened list of filtered template parts preserving a depth-first order', () => { | ||
const flattenedFilteredTemplateParts = | ||
getFilteredTemplatePartBlocks( NESTED_BLOCKS, TEMPLATE_PARTS ); | ||
expect( flattenedFilteredTemplateParts ).toEqual( | ||
FLATTENED_BLOCKS | ||
); | ||
} ); | ||
|
||
it( 'returns a cached result when passed the same params', () => { | ||
// Clear the cache and call the function twice. | ||
getFilteredTemplatePartBlocks.clear(); | ||
getFilteredTemplatePartBlocks( NESTED_BLOCKS, TEMPLATE_PARTS ); | ||
expect( | ||
getFilteredTemplatePartBlocks( NESTED_BLOCKS, TEMPLATE_PARTS ) | ||
).toEqual( FLATTENED_BLOCKS ); | ||
|
||
// The function has been called twice with the same params, so the cache size should be 1. | ||
const [ , , originalSize ] = | ||
getFilteredTemplatePartBlocks.getCache(); | ||
expect( originalSize ).toBe( 1 ); | ||
|
||
// Call the function again, with different params. | ||
expect( | ||
getFilteredTemplatePartBlocks( | ||
[ SINGLE_TEMPLATE_PART_BLOCK ], | ||
TEMPLATE_PARTS | ||
) | ||
).toEqual( [ | ||
{ | ||
block: { | ||
clientId: '1', | ||
name: 'core/template-part', | ||
attributes: { | ||
slug: 'aside', | ||
theme: 'my-theme', | ||
}, | ||
}, | ||
templatePart: { | ||
id: 'my-theme//aside', | ||
slug: 'aside', | ||
theme: 'my-theme', | ||
}, | ||
}, | ||
] ); | ||
|
||
// The function has been called with different params, so the cache size should now be 2. | ||
const [ , , finalSize ] = getFilteredTemplatePartBlocks.getCache(); | ||
expect( finalSize ).toBe( 2 ); | ||
} ); | ||
} ); | ||
} ); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import memoize from 'memize'; | ||
|
||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { isTemplatePart } from '@wordpress/blocks'; | ||
|
||
const EMPTY_ARRAY = []; | ||
|
||
/** | ||
* Get a flattened and filtered list of template parts and the matching block for that template part. | ||
* | ||
* Takes a list of blocks defined within a template, and a list of template parts, and returns a | ||
* flattened list of template parts and the matching block for that template part. | ||
* | ||
* @param {Array} blocks Blocks to flatten. | ||
* @param {?Array} templateParts Available template parts. | ||
* @return {Array} An array of template parts and their blocks. | ||
*/ | ||
function getFilteredTemplatePartBlocks( blocks = EMPTY_ARRAY, templateParts ) { | ||
const templatePartsById = templateParts | ||
? // Key template parts by their ID. | ||
templateParts.reduce( | ||
( newTemplateParts, part ) => ( { | ||
...newTemplateParts, | ||
[ part.id ]: part, | ||
} ), | ||
{} | ||
) | ||
: {}; | ||
|
||
const result = []; | ||
|
||
// Iterate over all blocks, recursing into inner blocks. | ||
// Output will be based on a depth-first traversal. | ||
const stack = [ ...blocks ]; | ||
while ( stack.length ) { | ||
const { innerBlocks, ...block } = stack.shift(); | ||
// Place inner blocks at the beginning of the stack to preserve order. | ||
stack.unshift( ...innerBlocks ); | ||
|
||
if ( isTemplatePart( block ) ) { | ||
const { | ||
attributes: { theme, slug }, | ||
} = block; | ||
const templatePartId = `${ theme }//${ slug }`; | ||
const templatePart = templatePartsById[ templatePartId ]; | ||
|
||
// Only add to output if the found template part block is in the list of available template parts. | ||
if ( templatePart ) { | ||
result.push( { | ||
templatePart, | ||
block, | ||
} ); | ||
} | ||
} | ||
} | ||
|
||
return result; | ||
} | ||
|
||
const memoizedGetFilteredTemplatePartBlocks = memoize( | ||
getFilteredTemplatePartBlocks | ||
); | ||
|
||
export { memoizedGetFilteredTemplatePartBlocks as getFilteredTemplatePartBlocks }; |