From bd06b1852015f1a711dc92789299f4b1c84a0e33 Mon Sep 17 00:00:00 2001 From: David Szabo Date: Tue, 30 Mar 2021 08:10:33 +0200 Subject: [PATCH 01/20] Check blocks recursively --- packages/block-editor/src/store/selectors.js | 89 ++++++++++++++------ 1 file changed, 64 insertions(+), 25 deletions(-) diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index 564f6bd1a89709..1ad61032f9657b 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -1260,6 +1260,22 @@ export function getTemplateLock( state, rootClientId ) { return blockListSettings.templateLock; } +const checkAllowList = ( list, item, defaultResult = null ) => { + if ( isBoolean( list ) ) { + return list; + } + if ( isArray( list ) ) { + // TODO: when there is a canonical way to detect that we are editing a post + // the following check should be changed to something like: + // if ( list.includes( 'core/post-content' ) && getEditorMode() === 'post-content' && item === null ) + if ( list.includes( 'core/post-content' ) && item === null ) { + return true; + } + return list.includes( item ); + } + return defaultResult; +}; + /** * Determines if the given block type is allowed to be inserted into the block list. * This function is not exported and not memoized because using a memoized selector @@ -1278,22 +1294,6 @@ const canInsertBlockTypeUnmemoized = ( blockName, rootClientId = null ) => { - const checkAllowList = ( list, item, defaultResult = null ) => { - if ( isBoolean( list ) ) { - return list; - } - if ( isArray( list ) ) { - // TODO: when there is a canonical way to detect that we are editing a post - // the following check should be changed to something like: - // if ( list.includes( 'core/post-content' ) && getEditorMode() === 'post-content' && item === null ) - if ( list.includes( 'core/post-content' ) && item === null ) { - return true; - } - return list.includes( item ); - } - return defaultResult; - }; - let blockType; if ( blockName && 'object' === typeof blockName ) { blockType = blockName; @@ -1791,14 +1791,53 @@ export const __experimentalGetParsedPattern = createSelector( if ( ! pattern ) { return null; } + + const { allowedBlockTypes } = getSettings( state ); + + const blocks = parse( pattern.content ); + const isAllowed = ( () => { + const blocksQueue = [ ...blocks ]; + while ( blocksQueue.length > 0 ) { + const block = blocksQueue.shift(); + const isBlockAllowedInEditor = checkAllowList( + allowedBlockTypes, + block.name, + true + ); + if ( ! isBlockAllowedInEditor ) { + return false; + } + block.blocks?.forEach( ( innerBlock ) => { + blocksQueue.push( innerBlock ); + } ); + } + return true; + } )(); + return { ...pattern, - blocks: parse( pattern.content ), + blocks, + isAllowed, }; }, ( state ) => [ state.settings.__experimentalBlockPatterns ] ); +export const __experimentalGetAvailableParsedPatterns = createSelector( + ( state ) => { + const patterns = state.settings.__experimentalBlockPatterns; + const parsedPatterns = patterns.map( ( { name } ) => + __experimentalGetParsedPattern( state, name ) + ); + return filter( parsedPatterns, 'isAllowed' ); + }, + ( state ) => [ + state.settings.__experimentalBlockPatterns, + state.settings.allowedBlockTypes, + state.settings.templateLock, + ] +); + /** * Returns the list of allowed patterns for inner blocks children * @@ -1809,16 +1848,16 @@ export const __experimentalGetParsedPattern = createSelector( */ export const __experimentalGetAllowedPatterns = createSelector( ( state, rootClientId = null ) => { - const patterns = state.settings.__experimentalBlockPatterns; - const parsedPatterns = patterns.map( ( { name } ) => - __experimentalGetParsedPattern( state, name ) + const availableParsedPatterns = __experimentalGetAvailableParsedPatterns( + state ); - const patternsAllowed = filter( parsedPatterns, ( { blocks } ) => - blocks.every( ( { name } ) => - canInsertBlockType( state, name, rootClientId ) - ) + const patternsAllowed = filter( + availableParsedPatterns, + ( { blocks } ) => + blocks.every( ( { name } ) => + canInsertBlockType( state, name, rootClientId ) + ) ); - return patternsAllowed; }, ( state, rootClientId ) => [ From 1d2a7077ba71a918b895b1ee53da00a39a6be854 Mon Sep 17 00:00:00 2001 From: David Szabo Date: Tue, 30 Mar 2021 08:13:19 +0200 Subject: [PATCH 02/20] Return early in case when all blocks are allowed/disallowed --- packages/block-editor/src/store/selectors.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index 1ad61032f9657b..42bc5549b1f1ea 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -1796,6 +1796,10 @@ export const __experimentalGetParsedPattern = createSelector( const blocks = parse( pattern.content ); const isAllowed = ( () => { + if ( isBoolean( allowedBlockTypes ) ) { + return allowedBlockTypes; + } + const blocksQueue = [ ...blocks ]; while ( blocksQueue.length > 0 ) { const block = blocksQueue.shift(); From 109c98ba6d53e06e2292f93d708b787f10373ac4 Mon Sep 17 00:00:00 2001 From: David Szabo Date: Tue, 30 Mar 2021 08:15:05 +0200 Subject: [PATCH 03/20] Update dependencies --- packages/block-editor/src/store/selectors.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index 42bc5549b1f1ea..0047597b002428 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -1824,7 +1824,11 @@ export const __experimentalGetParsedPattern = createSelector( isAllowed, }; }, - ( state ) => [ state.settings.__experimentalBlockPatterns ] + ( state ) => [ + state.settings.__experimentalBlockPatterns, + state.settings.allowedBlockTypes, + state.settings.templateLock, + ] ); export const __experimentalGetAvailableParsedPatterns = createSelector( From b0e1c35370e061ff80574b33bac948bb1a585330 Mon Sep 17 00:00:00 2001 From: David Szabo Date: Tue, 30 Mar 2021 17:27:39 +0200 Subject: [PATCH 04/20] Remove unused dependency --- packages/block-editor/src/store/selectors.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index 0047597b002428..de0795d7135327 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -1827,7 +1827,6 @@ export const __experimentalGetParsedPattern = createSelector( ( state ) => [ state.settings.__experimentalBlockPatterns, state.settings.allowedBlockTypes, - state.settings.templateLock, ] ); @@ -1842,7 +1841,6 @@ export const __experimentalGetAvailableParsedPatterns = createSelector( ( state ) => [ state.settings.__experimentalBlockPatterns, state.settings.allowedBlockTypes, - state.settings.templateLock, ] ); From 5038470b9347bab8734cc63b3229fdbd6914596a Mon Sep 17 00:00:00 2001 From: David Szabo Date: Tue, 30 Mar 2021 17:27:58 +0200 Subject: [PATCH 05/20] Use correct innerBlocks property --- packages/block-editor/src/store/selectors.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index de0795d7135327..3646bbd5cd9cd8 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -1811,7 +1811,7 @@ export const __experimentalGetParsedPattern = createSelector( if ( ! isBlockAllowedInEditor ) { return false; } - block.blocks?.forEach( ( innerBlock ) => { + block.innerBlocks?.forEach( ( innerBlock ) => { blocksQueue.push( innerBlock ); } ); } From 4cd5b4ddc4cbfc688c6f442d28c686a6ec315b9a Mon Sep 17 00:00:00 2001 From: David Szabo Date: Tue, 30 Mar 2021 17:41:50 +0200 Subject: [PATCH 06/20] Remove IIFE --- packages/block-editor/src/store/selectors.js | 32 +++++++------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index 3646bbd5cd9cd8..0ecd703b705b8c 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -1792,31 +1792,21 @@ export const __experimentalGetParsedPattern = createSelector( return null; } - const { allowedBlockTypes } = getSettings( state ); - const blocks = parse( pattern.content ); - const isAllowed = ( () => { - if ( isBoolean( allowedBlockTypes ) ) { - return allowedBlockTypes; - } - - const blocksQueue = [ ...blocks ]; - while ( blocksQueue.length > 0 ) { - const block = blocksQueue.shift(); - const isBlockAllowedInEditor = checkAllowList( - allowedBlockTypes, - block.name, - true - ); - if ( ! isBlockAllowedInEditor ) { - return false; - } + const { allowedBlockTypes } = getSettings( state ); + let isAllowed = isBoolean( allowedBlockTypes ) + ? allowedBlockTypes + : true; + const blocksQueue = [ ...blocks ]; + while ( isAllowed && blocksQueue.length > 0 ) { + const block = blocksQueue.shift(); + isAllowed = checkAllowList( allowedBlockTypes, block.name, true ); + if ( isAllowed ) { block.innerBlocks?.forEach( ( innerBlock ) => { blocksQueue.push( innerBlock ); } ); } - return true; - } )(); + } return { ...pattern, @@ -1836,7 +1826,7 @@ export const __experimentalGetAvailableParsedPatterns = createSelector( const parsedPatterns = patterns.map( ( { name } ) => __experimentalGetParsedPattern( state, name ) ); - return filter( parsedPatterns, 'isAllowed' ); + return parsedPatterns.filter( ( { isAllowed } ) => isAllowed ); }, ( state ) => [ state.settings.__experimentalBlockPatterns, From 0c362221c83635346271c76a8e523a512a726f48 Mon Sep 17 00:00:00 2001 From: David Szabo Date: Wed, 31 Mar 2021 20:49:03 +0200 Subject: [PATCH 07/20] Extract function --- packages/block-editor/src/store/selectors.js | 36 +++++++++++++------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index 0ecd703b705b8c..4d82376465e63c 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -1784,6 +1784,28 @@ export const __experimentalGetAllowedBlocks = createSelector( ] ); +const checkAllowListRecursive = ( blocks, allowedBlockTypes ) => { + if ( isBoolean( allowedBlockTypes ) ) { + return allowedBlockTypes; + } + + const blocksQueue = [ ...blocks ]; + while ( blocksQueue.length > 0 ) { + const block = blocksQueue.shift(); + + const isAllowed = checkAllowList( allowedBlockTypes, block.name, true ); + if ( ! isAllowed ) { + return false; + } + + block.innerBlocks?.forEach( ( innerBlock ) => { + blocksQueue.push( innerBlock ); + } ); + } + + return true; +}; + export const __experimentalGetParsedPattern = createSelector( ( state, patternName ) => { const patterns = state.settings.__experimentalBlockPatterns; @@ -1794,19 +1816,7 @@ export const __experimentalGetParsedPattern = createSelector( const blocks = parse( pattern.content ); const { allowedBlockTypes } = getSettings( state ); - let isAllowed = isBoolean( allowedBlockTypes ) - ? allowedBlockTypes - : true; - const blocksQueue = [ ...blocks ]; - while ( isAllowed && blocksQueue.length > 0 ) { - const block = blocksQueue.shift(); - isAllowed = checkAllowList( allowedBlockTypes, block.name, true ); - if ( isAllowed ) { - block.innerBlocks?.forEach( ( innerBlock ) => { - blocksQueue.push( innerBlock ); - } ); - } - } + const isAllowed = checkAllowListRecursive( blocks, allowedBlockTypes ); return { ...pattern, From 6380fd0f92efcb236cab421f4b2a798d641ffd39 Mon Sep 17 00:00:00 2001 From: David Szabo Date: Fri, 2 Apr 2021 20:55:38 +0200 Subject: [PATCH 08/20] Add e2e tests --- .../allowed-patterns-disable-blocks.php | 24 +++++++ .../e2e-tests/plugins/allowed-patterns.php | 41 ++++++++++++ .../editor/various/allowed-patterns.test.js | 67 +++++++++++++++++++ 3 files changed, 132 insertions(+) create mode 100644 packages/e2e-tests/plugins/allowed-patterns-disable-blocks.php create mode 100644 packages/e2e-tests/plugins/allowed-patterns.php create mode 100644 packages/e2e-tests/specs/editor/various/allowed-patterns.test.js diff --git a/packages/e2e-tests/plugins/allowed-patterns-disable-blocks.php b/packages/e2e-tests/plugins/allowed-patterns-disable-blocks.php new file mode 100644 index 00000000000000..16e0d83ceff407 --- /dev/null +++ b/packages/e2e-tests/plugins/allowed-patterns-disable-blocks.php @@ -0,0 +1,24 @@ +post_type ) { + return $allowed_block_types; + } + return array( 'core/heading', 'core/columns', 'core/column', 'core/image', 'core/spacer' ); +} + +add_filter( 'allowed_block_types', 'my_plugin_allowed_block_types', 10, 2 ); diff --git a/packages/e2e-tests/plugins/allowed-patterns.php b/packages/e2e-tests/plugins/allowed-patterns.php new file mode 100644 index 00000000000000..a0ccd0f00bacc7 --- /dev/null +++ b/packages/e2e-tests/plugins/allowed-patterns.php @@ -0,0 +1,41 @@ + 'Test: Single heading', + 'scope' => array( + 'inserter' => true, + ), + 'content' => '

Hello!

', + ) +); + +register_block_pattern( + 'test-allowed-patterns/lone-paragraph', + array( + 'title' => 'Test: Single paragraph', + 'scope' => array( + 'inserter' => true, + ), + 'content' => '

Hello!

', + ) +); + +register_block_pattern( + 'test-allowed-patterns/paragraph-inside-group', + array( + 'title' => 'Test: Paragraph inside group', + 'scope' => array( + 'inserter' => true, + ), + 'content' => '

Hello!

', + ) +); diff --git a/packages/e2e-tests/specs/editor/various/allowed-patterns.test.js b/packages/e2e-tests/specs/editor/various/allowed-patterns.test.js new file mode 100644 index 00000000000000..6eb9090e4a4933 --- /dev/null +++ b/packages/e2e-tests/specs/editor/various/allowed-patterns.test.js @@ -0,0 +1,67 @@ +/** + * WordPress dependencies + */ +import { + activatePlugin, + createNewPost, + deactivatePlugin, + searchForPattern, + toggleGlobalBlockInserter, +} from '@wordpress/e2e-test-utils'; + +const isPatternAvailable = async ( name ) => { + await searchForPattern( name ); + const elements = await page.$x( + `//div[@role = 'option']//div[contains(text(), '${ name }')]` + ); + const patternExists = elements.length > 0; + await toggleGlobalBlockInserter(); + return patternExists; +}; + +const TEST_PATTERNS = [ + [ 'Test: Single heading', false ], + [ 'Test: Single paragraph', true ], + [ 'Test: Paragraph inside group', true ], +]; + +describe( 'Allowed Patterns', () => { + beforeAll( async () => { + await activatePlugin( 'gutenberg-test-allowed-patterns' ); + } ); + afterAll( async () => { + await deactivatePlugin( 'gutenberg-test-allowed-patterns' ); + } ); + beforeEach( async () => { + await createNewPost(); + } ); + + describe( 'Disable blocks plugin disabled', () => { + it( 'should show test patterns', async () => { + for ( const [ patternName ] of TEST_PATTERNS ) { + expect( await isPatternAvailable( patternName ) ).toBe( true ); + } + } ); + } ); + + describe( 'Disable blocks plugin enabled', () => { + beforeAll( async () => { + await activatePlugin( + 'gutenberg-test-allowed-patterns-disable-blocks' + ); + } ); + afterAll( async () => { + await deactivatePlugin( + 'gutenberg-test-allowed-patterns-disable-blocks' + ); + } ); + + it( 'should not show test patterns', async () => { + for ( const [ patternName, shouldBeAvailable ] of TEST_PATTERNS ) { + expect( await isPatternAvailable( patternName ) ).toBe( + shouldBeAvailable + ); + } + } ); + } ); +} ); From d9340142b3e73175aabc1dacdfa6c0365faacd8d Mon Sep 17 00:00:00 2001 From: David Szabo Date: Tue, 6 Apr 2021 13:16:03 +0200 Subject: [PATCH 09/20] Rework tests --- .../editor/various/allowed-patterns.test.js | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/packages/e2e-tests/specs/editor/various/allowed-patterns.test.js b/packages/e2e-tests/specs/editor/various/allowed-patterns.test.js index 6eb9090e4a4933..ea2fd3a982c443 100644 --- a/packages/e2e-tests/specs/editor/various/allowed-patterns.test.js +++ b/packages/e2e-tests/specs/editor/various/allowed-patterns.test.js @@ -20,9 +20,9 @@ const isPatternAvailable = async ( name ) => { }; const TEST_PATTERNS = [ - [ 'Test: Single heading', false ], - [ 'Test: Single paragraph', true ], - [ 'Test: Paragraph inside group', true ], + [ 'Test: Single heading', true ], + [ 'Test: Single paragraph', false ], + [ 'Test: Paragraph inside group', false ], ]; describe( 'Allowed Patterns', () => { @@ -37,11 +37,11 @@ describe( 'Allowed Patterns', () => { } ); describe( 'Disable blocks plugin disabled', () => { - it( 'should show test patterns', async () => { - for ( const [ patternName ] of TEST_PATTERNS ) { + for ( const [ patternName ] of TEST_PATTERNS ) { + it( `should show test pattern "${ patternName }"`, async () => { expect( await isPatternAvailable( patternName ) ).toBe( true ); - } - } ); + } ); + } } ); describe( 'Disable blocks plugin enabled', () => { @@ -56,12 +56,14 @@ describe( 'Allowed Patterns', () => { ); } ); - it( 'should not show test patterns', async () => { - for ( const [ patternName, shouldBeAvailable ] of TEST_PATTERNS ) { + for ( const [ patternName, shouldBeAvailable ] of TEST_PATTERNS ) { + it( `should${ + shouldBeAvailable ? '' : ' not' + } show test "pattern ${ patternName }"`, async () => { expect( await isPatternAvailable( patternName ) ).toBe( shouldBeAvailable ); - } - } ); + } ); + } } ); } ); From cf1eaa6d966457e4040c4e3a715681e0d1d13f4c Mon Sep 17 00:00:00 2001 From: David Szabo Date: Tue, 6 Apr 2021 14:00:15 +0200 Subject: [PATCH 10/20] Remove scope --- packages/e2e-tests/plugins/allowed-patterns.php | 9 --------- 1 file changed, 9 deletions(-) diff --git a/packages/e2e-tests/plugins/allowed-patterns.php b/packages/e2e-tests/plugins/allowed-patterns.php index a0ccd0f00bacc7..b598cb6c0b82e7 100644 --- a/packages/e2e-tests/plugins/allowed-patterns.php +++ b/packages/e2e-tests/plugins/allowed-patterns.php @@ -11,9 +11,6 @@ 'test-allowed-patterns/lone-heading', array( 'title' => 'Test: Single heading', - 'scope' => array( - 'inserter' => true, - ), 'content' => '

Hello!

', ) ); @@ -22,9 +19,6 @@ 'test-allowed-patterns/lone-paragraph', array( 'title' => 'Test: Single paragraph', - 'scope' => array( - 'inserter' => true, - ), 'content' => '

Hello!

', ) ); @@ -33,9 +27,6 @@ 'test-allowed-patterns/paragraph-inside-group', array( 'title' => 'Test: Paragraph inside group', - 'scope' => array( - 'inserter' => true, - ), 'content' => '

Hello!

', ) ); From c953c6f256c7dff1b610128b3095b9aaffcac1b6 Mon Sep 17 00:00:00 2001 From: David Szabo Date: Mon, 10 May 2021 11:31:05 +0200 Subject: [PATCH 11/20] Make tests faster and more consistent --- .../editor/various/allowed-patterns.test.js | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/packages/e2e-tests/specs/editor/various/allowed-patterns.test.js b/packages/e2e-tests/specs/editor/various/allowed-patterns.test.js index ea2fd3a982c443..9c0fd19c6728a8 100644 --- a/packages/e2e-tests/specs/editor/various/allowed-patterns.test.js +++ b/packages/e2e-tests/specs/editor/various/allowed-patterns.test.js @@ -9,12 +9,13 @@ import { toggleGlobalBlockInserter, } from '@wordpress/e2e-test-utils'; -const isPatternAvailable = async ( name ) => { +const checkPatternExistance = async ( name, available = true ) => { await searchForPattern( name ); - const elements = await page.$x( - `//div[@role = 'option']//div[contains(text(), '${ name }')]` + const patternElement = await page.waitForXPath( + `//div[@role = 'option']//div[contains(text(), '${ name }')]`, + { timeout: 5000, visible: available, hidden: ! available } ); - const patternExists = elements.length > 0; + const patternExists = !! patternElement; await toggleGlobalBlockInserter(); return patternExists; }; @@ -28,18 +29,18 @@ const TEST_PATTERNS = [ describe( 'Allowed Patterns', () => { beforeAll( async () => { await activatePlugin( 'gutenberg-test-allowed-patterns' ); + await createNewPost(); } ); afterAll( async () => { await deactivatePlugin( 'gutenberg-test-allowed-patterns' ); } ); - beforeEach( async () => { - await createNewPost(); - } ); describe( 'Disable blocks plugin disabled', () => { for ( const [ patternName ] of TEST_PATTERNS ) { it( `should show test pattern "${ patternName }"`, async () => { - expect( await isPatternAvailable( patternName ) ).toBe( true ); + expect( await checkPatternExistance( patternName, true ) ).toBe( + true + ); } ); } } ); @@ -49,6 +50,7 @@ describe( 'Allowed Patterns', () => { await activatePlugin( 'gutenberg-test-allowed-patterns-disable-blocks' ); + await createNewPost(); } ); afterAll( async () => { await deactivatePlugin( @@ -60,9 +62,12 @@ describe( 'Allowed Patterns', () => { it( `should${ shouldBeAvailable ? '' : ' not' } show test "pattern ${ patternName }"`, async () => { - expect( await isPatternAvailable( patternName ) ).toBe( - shouldBeAvailable - ); + expect( + await checkPatternExistance( + patternName, + shouldBeAvailable + ) + ).toBe( shouldBeAvailable ); } ); } } ); From eec6c1daac89c27abb7930db406ae625fa811e16 Mon Sep 17 00:00:00 2001 From: David Szabo Date: Mon, 10 May 2021 11:35:12 +0200 Subject: [PATCH 12/20] Use stage one parsing only --- package-lock.json | 1 + packages/block-editor/package.json | 1 + packages/block-editor/src/store/selectors.js | 50 +++++++++++--------- 3 files changed, 30 insertions(+), 22 deletions(-) diff --git a/package-lock.json b/package-lock.json index f4c169e392bef9..ffe15c5322cda4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13205,6 +13205,7 @@ "@babel/runtime": "^7.13.10", "@wordpress/a11y": "file:packages/a11y", "@wordpress/blob": "file:packages/blob", + "@wordpress/block-serialization-default-parser": "file:packages/block-serialization-default-parser", "@wordpress/blocks": "file:packages/blocks", "@wordpress/components": "file:packages/components", "@wordpress/compose": "file:packages/compose", diff --git a/packages/block-editor/package.json b/packages/block-editor/package.json index 301e354f27d329..8352c9e00683ed 100644 --- a/packages/block-editor/package.json +++ b/packages/block-editor/package.json @@ -34,6 +34,7 @@ "@babel/runtime": "^7.13.10", "@wordpress/a11y": "file:../a11y", "@wordpress/blob": "file:../blob", + "@wordpress/block-serialization-default-parser": "file:../block-serialization-default-parser", "@wordpress/blocks": "file:../blocks", "@wordpress/components": "file:../components", "@wordpress/compose": "file:../compose", diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index 4d82376465e63c..222171f0331187 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -30,6 +30,7 @@ import { } from '@wordpress/blocks'; import { SVG, Rect, G, Path } from '@wordpress/components'; import { Platform } from '@wordpress/element'; +import { parse as stageOneParse } from '@wordpress/block-serialization-default-parser'; /** * A block selection object. @@ -1793,7 +1794,11 @@ const checkAllowListRecursive = ( blocks, allowedBlockTypes ) => { while ( blocksQueue.length > 0 ) { const block = blocksQueue.shift(); - const isAllowed = checkAllowList( allowedBlockTypes, block.name, true ); + const isAllowed = checkAllowList( + allowedBlockTypes, + block.name || block.blockName, + true + ); if ( ! isAllowed ) { return false; } @@ -1813,30 +1818,31 @@ export const __experimentalGetParsedPattern = createSelector( if ( ! pattern ) { return null; } - - const blocks = parse( pattern.content ); - const { allowedBlockTypes } = getSettings( state ); - const isAllowed = checkAllowListRecursive( blocks, allowedBlockTypes ); - return { ...pattern, - blocks, - isAllowed, + blocks: parse( pattern.content ), }; }, - ( state ) => [ - state.settings.__experimentalBlockPatterns, - state.settings.allowedBlockTypes, - ] + ( state ) => [ state.settings.__experimentalBlockPatterns ] ); -export const __experimentalGetAvailableParsedPatterns = createSelector( +export const __experimentalGetAvailableStageOneParsedPatterns = createSelector( ( state ) => { const patterns = state.settings.__experimentalBlockPatterns; - const parsedPatterns = patterns.map( ( { name } ) => - __experimentalGetParsedPattern( state, name ) - ); - return parsedPatterns.filter( ( { isAllowed } ) => isAllowed ); + const { allowedBlockTypes } = getSettings( state ); + return patterns + .map( ( pattern ) => ( { + ...pattern, + stageOneBlocks: stageOneParse( pattern.content ), + } ) ) + .filter( ( { stageOneBlocks } ) => { + const isAllowed = checkAllowListRecursive( + stageOneBlocks, + allowedBlockTypes + ); + + return isAllowed; + } ); }, ( state ) => [ state.settings.__experimentalBlockPatterns, @@ -1850,18 +1856,18 @@ export const __experimentalGetAvailableParsedPatterns = createSelector( * @param {Object} state Editor state. * @param {?string} rootClientId Optional target root client ID. * - * @return {Array?} The list of allowed block types. + * @return {Array?} The list of allowed patterns. */ export const __experimentalGetAllowedPatterns = createSelector( ( state, rootClientId = null ) => { - const availableParsedPatterns = __experimentalGetAvailableParsedPatterns( + const availableParsedPatterns = __experimentalGetAvailableStageOneParsedPatterns( state ); const patternsAllowed = filter( availableParsedPatterns, - ( { blocks } ) => - blocks.every( ( { name } ) => - canInsertBlockType( state, name, rootClientId ) + ( { stageOneBlocks } ) => + stageOneBlocks.every( ( { blockName } ) => + canInsertBlockType( state, blockName, rootClientId ) ) ); return patternsAllowed; From 341b3eaccc3df5f6012f0cb9cf395dd0462d74e6 Mon Sep 17 00:00:00 2001 From: David Szabo Date: Tue, 11 May 2021 13:28:09 +0200 Subject: [PATCH 13/20] Rename function and don't export it --- packages/block-editor/src/store/selectors.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index 222171f0331187..4c25492703cb53 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -1826,7 +1826,7 @@ export const __experimentalGetParsedPattern = createSelector( ( state ) => [ state.settings.__experimentalBlockPatterns ] ); -export const __experimentalGetAvailableStageOneParsedPatterns = createSelector( +const __experimentalGetAllAllowedPatterns = createSelector( ( state ) => { const patterns = state.settings.__experimentalBlockPatterns; const { allowedBlockTypes } = getSettings( state ); @@ -1860,7 +1860,7 @@ export const __experimentalGetAvailableStageOneParsedPatterns = createSelector( */ export const __experimentalGetAllowedPatterns = createSelector( ( state, rootClientId = null ) => { - const availableParsedPatterns = __experimentalGetAvailableStageOneParsedPatterns( + const availableParsedPatterns = __experimentalGetAllAllowedPatterns( state ); const patternsAllowed = filter( From f83e4302420192a6ab63b4419219b0ea35cde142 Mon Sep 17 00:00:00 2001 From: David Szabo Date: Tue, 11 May 2021 13:31:30 +0200 Subject: [PATCH 14/20] Add comment --- packages/block-editor/src/store/selectors.js | 24 +++++++++----------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index 4c25492703cb53..5b388bab054250 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -1830,19 +1830,17 @@ const __experimentalGetAllAllowedPatterns = createSelector( ( state ) => { const patterns = state.settings.__experimentalBlockPatterns; const { allowedBlockTypes } = getSettings( state ); - return patterns - .map( ( pattern ) => ( { - ...pattern, - stageOneBlocks: stageOneParse( pattern.content ), - } ) ) - .filter( ( { stageOneBlocks } ) => { - const isAllowed = checkAllowListRecursive( - stageOneBlocks, - allowedBlockTypes - ); - - return isAllowed; - } ); + const parsedPatterns = patterns.map( ( pattern ) => ( { + ...pattern, + // We use the Stage I parser here for performance reasons. + // Since we only need the structure of the parsed content, + // Stage I parsing is enough here. + stageOneBlocks: stageOneParse( pattern.content ), + } ) ); + const allowedPatterns = parsedPatterns.filter( ( { stageOneBlocks } ) => + checkAllowListRecursive( stageOneBlocks, allowedBlockTypes ) + ); + return allowedPatterns; }, ( state ) => [ state.settings.__experimentalBlockPatterns, From 6513185697e4486f89326317e233a395c8a707c8 Mon Sep 17 00:00:00 2001 From: David Szabo Date: Wed, 12 May 2021 12:03:31 +0200 Subject: [PATCH 15/20] Rename properties and update comment Thanks @mcsf ! --- packages/block-editor/src/store/selectors.js | 28 ++++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index 5b388bab054250..bf30192921355c 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -30,7 +30,7 @@ import { } from '@wordpress/blocks'; import { SVG, Rect, G, Path } from '@wordpress/components'; import { Platform } from '@wordpress/element'; -import { parse as stageOneParse } from '@wordpress/block-serialization-default-parser'; +import { parse as parseBlocks } from '@wordpress/block-serialization-default-parser'; /** * A block selection object. @@ -1826,19 +1826,21 @@ export const __experimentalGetParsedPattern = createSelector( ( state ) => [ state.settings.__experimentalBlockPatterns ] ); -const __experimentalGetAllAllowedPatterns = createSelector( +const getAllAllowedPatterns = createSelector( ( state ) => { const patterns = state.settings.__experimentalBlockPatterns; const { allowedBlockTypes } = getSettings( state ); const parsedPatterns = patterns.map( ( pattern ) => ( { ...pattern, - // We use the Stage I parser here for performance reasons. - // Since we only need the structure of the parsed content, - // Stage I parsing is enough here. - stageOneBlocks: stageOneParse( pattern.content ), + // We only need the overall block structure of the pattern. So, for + // performance reasons, we can parse the pattern's content using + // the raw blocks parser, also known as the "stage I" block parser. + // This is about 250x faster than the full parse that the Block API + // offers. + blockNodes: parseBlocks( pattern.content ), } ) ); - const allowedPatterns = parsedPatterns.filter( ( { stageOneBlocks } ) => - checkAllowListRecursive( stageOneBlocks, allowedBlockTypes ) + const allowedPatterns = parsedPatterns.filter( ( { blockNodes } ) => + checkAllowListRecursive( blockNodes, allowedBlockTypes ) ); return allowedPatterns; }, @@ -1849,7 +1851,7 @@ const __experimentalGetAllAllowedPatterns = createSelector( ); /** - * Returns the list of allowed patterns for inner blocks children + * Returns the list of allowed patterns for inner blocks children. * * @param {Object} state Editor state. * @param {?string} rootClientId Optional target root client ID. @@ -1858,13 +1860,11 @@ const __experimentalGetAllAllowedPatterns = createSelector( */ export const __experimentalGetAllowedPatterns = createSelector( ( state, rootClientId = null ) => { - const availableParsedPatterns = __experimentalGetAllAllowedPatterns( - state - ); + const availableParsedPatterns = getAllAllowedPatterns( state ); const patternsAllowed = filter( availableParsedPatterns, - ( { stageOneBlocks } ) => - stageOneBlocks.every( ( { blockName } ) => + ( { blockNodes } ) => + blockNodes.every( ( { blockName } ) => canInsertBlockType( state, blockName, rootClientId ) ) ); From 854d9e9f138962842d1802b92997d38da8860bc0 Mon Sep 17 00:00:00 2001 From: David Szabo Date: Wed, 12 May 2021 19:41:03 +0200 Subject: [PATCH 16/20] Fix typo --- .../e2e-tests/specs/editor/various/allowed-patterns.test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/e2e-tests/specs/editor/various/allowed-patterns.test.js b/packages/e2e-tests/specs/editor/various/allowed-patterns.test.js index 9c0fd19c6728a8..449306f06d18d2 100644 --- a/packages/e2e-tests/specs/editor/various/allowed-patterns.test.js +++ b/packages/e2e-tests/specs/editor/various/allowed-patterns.test.js @@ -9,7 +9,7 @@ import { toggleGlobalBlockInserter, } from '@wordpress/e2e-test-utils'; -const checkPatternExistance = async ( name, available = true ) => { +const checkPatternExistence = async ( name, available = true ) => { await searchForPattern( name ); const patternElement = await page.waitForXPath( `//div[@role = 'option']//div[contains(text(), '${ name }')]`, @@ -38,7 +38,7 @@ describe( 'Allowed Patterns', () => { describe( 'Disable blocks plugin disabled', () => { for ( const [ patternName ] of TEST_PATTERNS ) { it( `should show test pattern "${ patternName }"`, async () => { - expect( await checkPatternExistance( patternName, true ) ).toBe( + expect( await checkPatternExistence( patternName, true ) ).toBe( true ); } ); @@ -63,7 +63,7 @@ describe( 'Allowed Patterns', () => { shouldBeAvailable ? '' : ' not' } show test "pattern ${ patternName }"`, async () => { expect( - await checkPatternExistance( + await checkPatternExistence( patternName, shouldBeAvailable ) From 3109b0d32a30b81d51a48be6422e61a857fd70dd Mon Sep 17 00:00:00 2001 From: David Szabo Date: Thu, 13 May 2021 13:11:42 +0200 Subject: [PATCH 17/20] Update usages --- .../components/block-pattern-setup/index.js | 23 +++++++++++++++---- .../use-transformed-patterns.js | 16 +++++++++++-- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/packages/block-editor/src/components/block-pattern-setup/index.js b/packages/block-editor/src/components/block-pattern-setup/index.js index 2fa25326a5c9c9..15b8189cae1957 100644 --- a/packages/block-editor/src/components/block-pattern-setup/index.js +++ b/packages/block-editor/src/components/block-pattern-setup/index.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { useDispatch } from '@wordpress/data'; +import { select, useDispatch, useSelect } from '@wordpress/data'; import { cloneBlock } from '@wordpress/blocks'; import { VisuallyHidden, @@ -72,7 +72,12 @@ const SetupContent = ( { function BlockPattern( { pattern, onSelect, composite } ) { const baseClassName = 'block-editor-block-pattern-setup-list'; - const { blocks, title, description, viewportWidth = 700 } = pattern; + const { name, title, description, viewportWidth = 700 } = pattern; + const { blocks } = useSelect( + ( _select ) => + _select( blockEditorStore ).__experimentalGetParsedPattern( name ), + [ name ] + ); const descriptionId = useInstanceId( BlockPattern, `${ baseClassName }__item-description` @@ -108,7 +113,12 @@ function BlockPattern( { pattern, onSelect, composite } ) { } function BlockPatternSlide( { className, pattern } ) { - const { blocks, title, description } = pattern; + const { name, title, description } = pattern; + const { blocks } = useSelect( + ( _select ) => + _select( blockEditorStore ).__experimentalGetParsedPattern( name ), + [ name ] + ); const descriptionId = useInstanceId( BlockPatternSlide, 'block-editor-block-pattern-setup-list__item-description' @@ -168,7 +178,12 @@ const BlockPatternSetup = ( { setActiveSlide( ( active ) => active - 1 ); } } onBlockPatternSelect={ () => { - onPatternSelectCallback( patterns[ activeSlide ].blocks ); + const { blocks } = select( + blockEditorStore + ).__experimentalGetAllowedPatterns( + patterns[ activeSlide ].name + ); + onPatternSelectCallback( blocks ); } } onStartBlank={ () => { setShowBlank( true ); diff --git a/packages/block-editor/src/components/block-switcher/use-transformed-patterns.js b/packages/block-editor/src/components/block-switcher/use-transformed-patterns.js index 7f8c956ea69883..0ce374fbbc8cbc 100644 --- a/packages/block-editor/src/components/block-switcher/use-transformed-patterns.js +++ b/packages/block-editor/src/components/block-switcher/use-transformed-patterns.js @@ -3,11 +3,13 @@ */ import { useMemo } from '@wordpress/element'; import { cloneBlock } from '@wordpress/blocks'; +import { useSelect } from '@wordpress/data'; /** * Internal dependencies */ import { getMatchingBlockByName, getRetainedBlockAttributes } from './utils'; +import { store as blockEditorStore } from '../../store'; /** * Mutate the matched block's attributes by getting @@ -94,9 +96,19 @@ export const getPatternTransformedBlocks = ( */ // TODO tests const useTransformedPatterns = ( patterns, selectedBlocks ) => { + const parsedPatterns = useSelect( + ( select ) => + patterns.map( ( { name } ) => + select( blockEditorStore ).__experimentalGetParsedPattern( + name + ) + ), + [ patterns ] + ); + return useMemo( () => - patterns.reduce( ( accumulator, _pattern ) => { + parsedPatterns.reduce( ( accumulator, _pattern ) => { const transformedBlocks = getPatternTransformedBlocks( selectedBlocks, _pattern.blocks @@ -109,7 +121,7 @@ const useTransformedPatterns = ( patterns, selectedBlocks ) => { } return accumulator; }, [] ), - [ patterns, selectedBlocks ] + [ parsedPatterns, selectedBlocks ] ); }; From 7e7d9c3f9813f7f223bab7b572ad738606d818f9 Mon Sep 17 00:00:00 2001 From: David Szabo Date: Thu, 13 May 2021 15:21:28 +0200 Subject: [PATCH 18/20] Fix wrong selector called --- .../block-editor/src/components/block-pattern-setup/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/block-pattern-setup/index.js b/packages/block-editor/src/components/block-pattern-setup/index.js index 15b8189cae1957..78d05be69d3a63 100644 --- a/packages/block-editor/src/components/block-pattern-setup/index.js +++ b/packages/block-editor/src/components/block-pattern-setup/index.js @@ -180,7 +180,7 @@ const BlockPatternSetup = ( { onBlockPatternSelect={ () => { const { blocks } = select( blockEditorStore - ).__experimentalGetAllowedPatterns( + ).__experimentalGetParsedPattern( patterns[ activeSlide ].name ); onPatternSelectCallback( blocks ); From fb6af97bbb16c917338fbdecab3ad29842c85f7b Mon Sep 17 00:00:00 2001 From: David Szabo Date: Fri, 14 May 2021 12:52:19 +0200 Subject: [PATCH 19/20] Move selector call to usePatternsSetup --- .../src/components/block-pattern-setup/index.js | 16 +++------------- .../block-pattern-setup/use-patterns-setup.js | 16 +++++++++++----- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/packages/block-editor/src/components/block-pattern-setup/index.js b/packages/block-editor/src/components/block-pattern-setup/index.js index 78d05be69d3a63..03988544d2fae5 100644 --- a/packages/block-editor/src/components/block-pattern-setup/index.js +++ b/packages/block-editor/src/components/block-pattern-setup/index.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { select, useDispatch, useSelect } from '@wordpress/data'; +import { useDispatch, useSelect } from '@wordpress/data'; import { cloneBlock } from '@wordpress/blocks'; import { VisuallyHidden, @@ -72,12 +72,7 @@ const SetupContent = ( { function BlockPattern( { pattern, onSelect, composite } ) { const baseClassName = 'block-editor-block-pattern-setup-list'; - const { name, title, description, viewportWidth = 700 } = pattern; - const { blocks } = useSelect( - ( _select ) => - _select( blockEditorStore ).__experimentalGetParsedPattern( name ), - [ name ] - ); + const { blocks, title, description, viewportWidth = 700 } = pattern; const descriptionId = useInstanceId( BlockPattern, `${ baseClassName }__item-description` @@ -178,12 +173,7 @@ const BlockPatternSetup = ( { setActiveSlide( ( active ) => active - 1 ); } } onBlockPatternSelect={ () => { - const { blocks } = select( - blockEditorStore - ).__experimentalGetParsedPattern( - patterns[ activeSlide ].name - ); - onPatternSelectCallback( blocks ); + onPatternSelectCallback( patterns[ activeSlide ].blocks ); } } onStartBlank={ () => { setShowBlank( true ); diff --git a/packages/block-editor/src/components/block-pattern-setup/use-patterns-setup.js b/packages/block-editor/src/components/block-pattern-setup/use-patterns-setup.js index 1b6a90b87457a0..709e127ded6428 100644 --- a/packages/block-editor/src/components/block-pattern-setup/use-patterns-setup.js +++ b/packages/block-editor/src/components/block-pattern-setup/use-patterns-setup.js @@ -14,17 +14,23 @@ function usePatternsSetup( clientId, blockName, filterPatternsFn ) { const { getBlockRootClientId, __experimentalGetPatternsByBlockTypes, + __experimentalGetParsedPattern, __experimentalGetAllowedPatterns, } = select( blockEditorStore ); const rootClientId = getBlockRootClientId( clientId ); + let patterns = []; if ( filterPatternsFn ) { - return __experimentalGetAllowedPatterns( rootClientId ).filter( - filterPatternsFn + patterns = __experimentalGetAllowedPatterns( + rootClientId + ).filter( filterPatternsFn ); + } else { + patterns = __experimentalGetPatternsByBlockTypes( + blockName, + rootClientId ); } - return __experimentalGetPatternsByBlockTypes( - blockName, - rootClientId + return patterns.map( ( { name } ) => + __experimentalGetParsedPattern( name ) ); }, [ clientId, blockName, filterPatternsFn ] From 70d80c248cfc7a244fdb1de6c6df8e838236a30f Mon Sep 17 00:00:00 2001 From: David Szabo Date: Fri, 14 May 2021 13:39:59 +0200 Subject: [PATCH 20/20] Remove selector call --- .../src/components/block-pattern-setup/index.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/block-editor/src/components/block-pattern-setup/index.js b/packages/block-editor/src/components/block-pattern-setup/index.js index 03988544d2fae5..2fa25326a5c9c9 100644 --- a/packages/block-editor/src/components/block-pattern-setup/index.js +++ b/packages/block-editor/src/components/block-pattern-setup/index.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { useDispatch, useSelect } from '@wordpress/data'; +import { useDispatch } from '@wordpress/data'; import { cloneBlock } from '@wordpress/blocks'; import { VisuallyHidden, @@ -108,12 +108,7 @@ function BlockPattern( { pattern, onSelect, composite } ) { } function BlockPatternSlide( { className, pattern } ) { - const { name, title, description } = pattern; - const { blocks } = useSelect( - ( _select ) => - _select( blockEditorStore ).__experimentalGetParsedPattern( name ), - [ name ] - ); + const { blocks, title, description } = pattern; const descriptionId = useInstanceId( BlockPatternSlide, 'block-editor-block-pattern-setup-list__item-description'