Skip to content

Commit

Permalink
Remove scope from Query patterns and introduce blockTypes (#30471)
Browse files Browse the repository at this point in the history
* remove scope from query patterns

* clone blocks on insertion

* properly handle query update and make previews show 1 post per page

* fix typos
  • Loading branch information
ntsekouras authored Apr 5, 2021
1 parent aca801a commit 98a51d7
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 83 deletions.
50 changes: 29 additions & 21 deletions lib/block-patterns.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,60 +5,68 @@
* @package gutenberg
*/

register_block_pattern_category( 'Query', array( 'label' => __( 'Query', 'gutenberg' ) ) );

// Initial Query block patterns.
register_block_pattern(
'query/large-posts',
array(
'title' => __( 'Large', 'gutenberg' ),
'scope' => array(
'inserter' => false,
'block' => array( 'core/query' ),
),
'content' => '<!-- wp:post-title {"isLink":true} /-->
'title' => __( 'Large', 'gutenberg' ),
'blockTypes' => array( 'core/query' ),
'categories' => array( 'Query' ),
'content' => '<!-- wp:query {"query":{"perPage":1,"pages":0,"offset":0,"postType":"post","categoryIds":[],"tagIds":[],"order":"desc","orderBy":"date","author":"","search":"","exclude":[],"sticky":"","inherit":true}} -->
<!-- wp:query-loop -->
<!-- wp:post-title {"isLink":true} /-->
<!-- wp:post-featured-image {"isLink":true,"align":"wide"} /-->
<!-- wp:post-excerpt /-->
<!-- wp:separator -->
<hr class="wp-block-separator"/>
<!-- /wp:separator -->
<!-- wp:post-date /-->',
<!-- wp:post-date /-->
<!-- /wp:query-loop -->
<!-- /wp:query -->',
)
);

register_block_pattern(
'query/medium-posts',
array(
'title' => __( 'Medium', 'gutenberg' ),
'scope' => array(
'inserter' => false,
'block' => array( 'core/query' ),
),
'content' => '<!-- wp:columns {"align":"wide"} -->
'title' => __( 'Medium', 'gutenberg' ),
'blockTypes' => array( 'core/query' ),
'categories' => array( 'Query' ),
'content' => '<!-- wp:query {"query":{"perPage":1,"pages":0,"offset":0,"postType":"post","categoryIds":[],"tagIds":[],"order":"desc","orderBy":"date","author":"","search":"","exclude":[],"sticky":"","inherit":true}} -->
<!-- wp:query-loop -->
<!-- wp:columns {"align":"wide"} -->
<div class="wp-block-columns alignwide"><!-- wp:column {"width":"66.66%"} -->
<div class="wp-block-column" style="flex-basis:66.66%"><!-- wp:post-featured-image {"isLink":true} /--></div>
<!-- /wp:column -->
<!-- wp:column {"width":"33.33%"} -->
<div class="wp-block-column" style="flex-basis:33.33%"><!-- wp:post-title {"isLink":true} /-->
<!-- wp:post-excerpt /--></div>
<!-- /wp:column --></div>
<!-- /wp:columns -->',
<!-- /wp:columns -->
<!-- /wp:query-loop -->
<!-- /wp:query -->',
)
);

register_block_pattern(
'query/small-posts',
array(
'title' => __( 'Small', 'gutenberg' ),
'scope' => array(
'inserter' => false,
'block' => array( 'core/query' ),
),
'content' => '<!-- wp:columns {"verticalAlignment":"center"} -->
'title' => __( 'Small', 'gutenberg' ),
'blockTypes' => array( 'core/query' ),
'categories' => array( 'Query' ),
'content' => '<!-- wp:query {"query":{"perPage":1,"pages":0,"offset":0,"postType":"post","categoryIds":[],"tagIds":[],"order":"desc","orderBy":"date","author":"","search":"","exclude":[],"sticky":"","inherit":true}} -->
<!-- wp:query-loop -->
<!-- wp:columns {"verticalAlignment":"center"} -->
<div class="wp-block-columns are-vertically-aligned-center"><!-- wp:column {"verticalAlignment":"center","width":"25%"} -->
<div class="wp-block-column is-vertically-aligned-center" style="flex-basis:25%"><!-- wp:post-featured-image {"isLink":true} /--></div>
<!-- /wp:column -->
<!-- wp:column {"verticalAlignment":"center","width":"75%"} -->
<div class="wp-block-column is-vertically-aligned-center" style="flex-basis:75%"><!-- wp:post-title {"isLink":true} /--></div>
<!-- /wp:column --></div>
<!-- /wp:columns -->',
<!-- /wp:columns -->
<!-- /wp:query-loop -->
<!-- /wp:query -->',
)
);
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,8 @@ const usePatternsState = ( onInsert, rootClientId ) => {
const { __experimentalGetAllowedPatterns, getSettings } = select(
blockEditorStore
);
const inserterPatterns = __experimentalGetAllowedPatterns(
rootClientId
).filter(
( pattern ) => ! pattern.scope || pattern.scope.inserter
);
return {
patterns: inserterPatterns,
patterns: __experimentalGetAllowedPatterns( rootClientId ),
patternCategories: getSettings()
.__experimentalBlockPatternCategories,
};
Expand Down
43 changes: 27 additions & 16 deletions packages/block-editor/src/store/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -1838,29 +1838,40 @@ export const __experimentalGetAllowedPatterns = createSelector(
);

/**
* Returns the list of patterns based on specific `scope` and
* a block's name.
* `inserter` scope should be handled differently, probably in
* combination with `__experimentalGetAllowedPatterns`.
* For now `__experimentalGetScopedBlockPatterns` handles properly
* all other scopes.
* Since both APIs are experimental we should revisit this.
* Returns the list of patterns based on their declared `blockTypes`
* and a block's name.
* Patterns can use `blockTypes` to integrate in work flows like
* suggesting appropriate patterns in a Placeholder state(during insertion)
* or blocks transformations.
*
* @param {Object} state Editor state.
* @param {string} scope Block pattern scope.
* @param {string} blockName Block's name.
* @param {string|string[]} blockNames Block's name or array of block names to find matching pattens.
* @param {?string} rootClientId Optional target root client ID.
*
* @return {Array} The list of matched block patterns based on provided scope and block name.
* @return {Array} The list of matched block patterns based on declared `blockTypes` and block name.
*/
export const __experimentalGetScopedBlockPatterns = createSelector(
( state, scope, blockName ) => {
if ( ! scope && ! blockName ) return EMPTY_ARRAY;
const patterns = state.settings.__experimentalBlockPatterns;
export const __experimentalGetPatternsByBlockTypes = createSelector(
( state, blockNames, rootClientId = null ) => {
if ( ! blockNames ) return EMPTY_ARRAY;
const patterns = __experimentalGetAllowedPatterns(
state,
rootClientId
);
const normalizedBlockNames = Array.isArray( blockNames )
? blockNames
: [ blockNames ];
return patterns.filter( ( pattern ) =>
pattern.scope?.[ scope ]?.includes?.( blockName )
pattern?.blockTypes?.some?.( ( blockName ) =>
normalizedBlockNames.includes( blockName )
)
);
},
( state ) => [ state.settings.__experimentalBlockPatterns ]
( state, rootClientId ) => [
...__experimentalGetAllowedPatterns.getDependants(
state,
rootClientId
),
]
);

/**
Expand Down
61 changes: 43 additions & 18 deletions packages/block-editor/src/store/test/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ const {
__experimentalGetActiveBlockIdByBlockNames: getActiveBlockIdByBlockNames,
__experimentalGetParsedReusableBlock,
__experimentalGetAllowedPatterns,
__experimentalGetScopedBlockPatterns,
__experimentalGetPatternsByBlockTypes,
__unstableGetClientIdWithClientIdsTree,
__unstableGetClientIdsTree,
} = selectors;
Expand Down Expand Up @@ -3407,52 +3407,77 @@ describe( 'selectors', () => {
).toHaveLength( 0 );
} );
} );
describe( '__experimentalGetScopedBlockPatterns', () => {
describe( '__experimentalGetPatternsByBlockTypes', () => {
const state = {
blocks: {},
blocks: {
byClientId: {
block1: { name: 'core/test-block-a' },
},
},
blockListSettings: {
block1: {
allowedBlocks: [ 'core/test-block-b' ],
},
},
settings: {
__experimentalBlockPatterns: [
{
name: 'pattern-a',
blockTypes: [ 'test/block-a' ],
title: 'pattern a',
scope: { block: [ 'test/block-a' ] },
content:
'<!-- wp:test-block-a --><!-- /wp:test-block-a -->',
},
{
name: 'pattern-b',
blockTypes: [ 'test/block-b' ],
title: 'pattern b',
scope: { block: [ 'test/block-b' ] },
content:
'<!-- wp:test-block-b --><!-- /wp:test-block-b -->',
},
{
title: 'pattern c',
blockTypes: [ 'test/block-a' ],
content:
'<!-- wp:test-block-b --><!-- /wp:test-block-b -->',
},
],
},
};
it( 'should return empty array if no scope and block name is provided', () => {
expect( __experimentalGetScopedBlockPatterns( state ) ).toEqual(
it( 'should return empty array if no block name is provided', () => {
expect( __experimentalGetPatternsByBlockTypes( state ) ).toEqual(
[]
);
expect(
__experimentalGetScopedBlockPatterns( state, 'block' )
).toEqual( [] );
} );
it( 'shoud return empty array if no match is found', () => {
const patterns = __experimentalGetScopedBlockPatterns(
const patterns = __experimentalGetPatternsByBlockTypes(
state,
'block',
'test/block-not-exists'
);
expect( patterns ).toEqual( [] );
} );
it( 'should return proper results when there are matched block patterns', () => {
const patterns = __experimentalGetScopedBlockPatterns(
const patterns = __experimentalGetPatternsByBlockTypes(
state,
'block',
'test/block-a'
);
expect( patterns ).toHaveLength( 2 );
expect( patterns ).toEqual(
expect.arrayContaining( [
expect.objectContaining( { title: 'pattern a' } ),
expect.objectContaining( { title: 'pattern c' } ),
] )
);
} );
it( 'should return proper result with matched patterns and allowed blocks from rootClientId', () => {
const patterns = __experimentalGetPatternsByBlockTypes(
state,
'test/block-a',
'block1'
);
expect( patterns ).toHaveLength( 1 );
expect( patterns[ 0 ] ).toEqual(
expect.objectContaining( {
title: 'pattern a',
scope: { block: [ 'test/block-a' ] },
} )
expect.objectContaining( { title: 'pattern c' } )
);
} );
} );
Expand Down
14 changes: 5 additions & 9 deletions packages/block-library/src/query/edit/block-setup/layout-step.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
* WordPress dependencies
*/
import { useSelect } from '@wordpress/data';
import { useState, useMemo } from '@wordpress/element';
import { parse, store as blocksStore } from '@wordpress/blocks';
import { useState } from '@wordpress/element';
import { store as blocksStore } from '@wordpress/blocks';
import { useInstanceId } from '@wordpress/compose';
import {
BlockPreview,
Expand Down Expand Up @@ -36,14 +36,11 @@ const LayoutSetupStep = ( {
const { getBlockVariations, getDefaultBlockVariation } = select(
blocksStore
);
const { __experimentalGetScopedBlockPatterns } = select(
const { __experimentalGetPatternsByBlockTypes } = select(
blockEditorStore
);
const { name } = blockType;
const _patterns = __experimentalGetScopedBlockPatterns(
'block',
name
);
const _patterns = __experimentalGetPatternsByBlockTypes( name );
const _blockVariations = getBlockVariations( name, 'block' );
return {
defaultVariation: getDefaultBlockVariation( name, 'block' ),
Expand Down Expand Up @@ -126,8 +123,7 @@ const LayoutSetupStep = ( {
};

function BlockPattern( { pattern, onSelect, composite } ) {
const { content, viewportWidth } = pattern;
const blocks = useMemo( () => parse( content ), [ content ] );
const { viewportWidth, blocks } = pattern;
const descriptionId = useInstanceId(
BlockPattern,
'block-setup-block-layout-list__item-description'
Expand Down
59 changes: 46 additions & 13 deletions packages/block-library/src/query/edit/query-block-setup.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
/**
* External dependencies
*/
import { cloneDeep } from 'lodash';
/**
* WordPress dependencies
*/
import { useDispatch } from '@wordpress/data';
import { __, _x } from '@wordpress/i18n';
import { SelectControl, ToggleControl } from '@wordpress/components';
import { createBlocksFromInnerBlocksTemplate } from '@wordpress/blocks';
import {
cloneBlock,
createBlocksFromInnerBlocksTemplate,
} from '@wordpress/blocks';
import { store as blockEditorStore } from '@wordpress/block-editor';

/**
Expand All @@ -20,29 +27,55 @@ const QueryBlockSetup = ( {
name: blockName,
} ) => {
const { postType, inherit } = query;
const { replaceInnerBlocks } = useDispatch( blockEditorStore );
const { replaceBlocks, replaceInnerBlocks } = useDispatch(
blockEditorStore
);
const { postTypesSelectOptions } = usePostTypes();
const updateQuery = ( newQuery ) =>
setAttributes( { query: { ...query, ...newQuery } } );
const onFinish = ( innerBlocks ) => {
if ( innerBlocks ) {
replaceInnerBlocks(
clientId,
createBlocksFromInnerBlocksTemplate( innerBlocks ),
false
);
}
};
const onVariationSelect = ( nextVariation ) => {
if ( nextVariation.attributes ) {
setAttributes( nextVariation.attributes );
}
if ( nextVariation.innerBlocks ) {
onFinish( nextVariation.innerBlocks );
replaceInnerBlocks(
clientId,
createBlocksFromInnerBlocksTemplate(
nextVariation.innerBlocks
),
false
);
}
};
const onBlockPatternSelect = ( blocks ) => {
onFinish( [ [ 'core/query-loop', {}, blocks ] ] );
const clonedBlocks = blocks.map( ( block ) => {
const clone = cloneBlock( block );
/**
* TODO: this check will be revised with the ongoing work on block patterns.
* For now we keep the value of posts per page (`query.perPage`) from Query patterns
* so as to preview the pattern as intended, without possible big previews.
* During insertion, we need to override the Query's attributes that can be set in
* the Placeholder and we unset the `perPage` value to be set appropriately by Query block.
*/
if ( block.name === 'core/query' ) {
/**
* We need to `cloneDeep` the Query's attributes, as `cloneBlock` does a swallow
* copy of the block.
*/
const queryAttributes = cloneDeep( clone.attributes );
Object.assign( queryAttributes.query, {
inherit: query.inherit,
postType: query.postType,
perPage: null,
} );
return {
...clone,
attributes: queryAttributes,
};
}
return clone;
} );
replaceBlocks( clientId, clonedBlocks );
};
const inheritToggleHelp = !! inherit
? _x(
Expand Down

0 comments on commit 98a51d7

Please sign in to comment.