Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reusable blocks: Rename to 'Patterns' and add option to also add a non-synced Pattern #51144

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
3475777
Add the option to create a non-synced pattern by adding content to ex…
glendaviesnz Jun 1, 2023
25c492a
Switch to using post meta to track sync status
glendaviesnz Jun 1, 2023
1314226
Fix linting
glendaviesnz Jun 1, 2023
24db1a3
More linting fixes
glendaviesnz Jun 1, 2023
4d5f40f
Lint lint lint
glendaviesnz Jun 1, 2023
1a6d5e9
Add categories
glendaviesnz Jun 1, 2023
6f59ffd
Switch to categories
glendaviesnz Jun 1, 2023
185f214
Finally fixed my locally linter - hopefully the CI Linting gods will …
glendaviesnz Jun 1, 2023
cb60756
Add translator comments
glendaviesnz Jun 1, 2023
6df91d6
Switch the categories to an array to allow multiple category selectio…
glendaviesnz Jun 1, 2023
eeadcc9
Stop the flash of the reusable block placeholder when adding a pattern
glendaviesnz Jun 1, 2023
3291eb2
prevent console errors if record not defined
glendaviesnz Jun 2, 2023
d8da55d
Add a post meta field for slug to allow overriding of theme patterns
glendaviesnz Jun 2, 2023
1e9d2d5
merge the meta fields into one object
glendaviesnz Jun 2, 2023
1109fff
Force the selection of a category rather than having a default
glendaviesnz Jun 5, 2023
cf30182
Switch to using a WP taxonomy to add pattern categories
glendaviesnz Jun 6, 2023
89d71f7
Fix linting errors
glendaviesnz Jun 6, 2023
e2a3fa6
Fix the path to sync status meta
glendaviesnz Jun 6, 2023
0be9f89
Fix typo
glendaviesnz Jun 6, 2023
00dc146
Add pattern categories to new wp_patterns taxonomy
glendaviesnz Jun 6, 2023
90443b0
Linting fixes
glendaviesnz Jun 6, 2023
11cfcf0
Linting fixes
glendaviesnz Jun 6, 2023
011ef3b
Only run insert term if it doesn't exist
glendaviesnz Jun 6, 2023
2f1af91
Merge reusable blocks with unsynced patterns
glendaviesnz Jun 7, 2023
77d2a28
Add sync status to pattern edit
glendaviesnz Jun 7, 2023
a70b271
Some fixes
glendaviesnz Jun 8, 2023
ff58829
Make sure existing reusable blocks show as synced
glendaviesnz Jun 8, 2023
68e42d3
Make category optional
glendaviesnz Jun 8, 2023
a85e7fc
Fix snackbar bug
glendaviesnz Jun 8, 2023
362777e
Comment updates from review feedback
glendaviesnz Jun 8, 2023
693a716
Fix untranslated string
glendaviesnz Jun 8, 2023
8651478
Change inserter tab order back
glendaviesnz Jun 8, 2023
a23bf9d
Fix for duplicate 'Posts' category heading
glendaviesnz Jun 8, 2023
6fd9b6c
Fix bug with sync status
glendaviesnz Jun 9, 2023
dbd734c
copy changes create pattern modal
SaxonF Jun 9, 2023
8ea45d4
default to unsynced pattern create
SaxonF Jun 9, 2023
571511b
Tidy ups from review
glendaviesnz Jun 9, 2023
af989d0
reorder additions
glendaviesnz Jun 9, 2023
7326da2
Remove redundant changes
glendaviesnz Jun 9, 2023
96a053e
Only add post meta for unsynced sync status
glendaviesnz Jun 14, 2023
278bc62
Make new taxonomy private for now
glendaviesnz Jun 14, 2023
c679696
Revert "Make new taxonomy private for now"
glendaviesnz Jun 14, 2023
a12c0ff
remove post meta if sync status is not unsynced
glendaviesnz Jun 14, 2023
ce26090
remove the slug postmeta for now as won't be used on 6.3
glendaviesnz Jun 14, 2023
f75223c
Change naming of reusable blocks tab
glendaviesnz Jun 15, 2023
2d575ee
Remove the patterns taxonomy for now
glendaviesnz Jun 15, 2023
a197050
Fix e2e tests
glendaviesnz Jun 15, 2023
764e82a
Remove whitespace
glendaviesnz Jun 15, 2023
370ef36
Fix native tests
glendaviesnz Jun 15, 2023
3311e3b
Another mobile tests fix
glendaviesnz Jun 16, 2023
120091d
another test fix
glendaviesnz Jun 16, 2023
7ea3f60
another test fix
glendaviesnz Jun 16, 2023
6e3ac59
And another!
glendaviesnz Jun 16, 2023
c21b9d0
simplify the sync_status postmeta
glendaviesnz Jun 16, 2023
542272a
Fingers crossed the last e2e test fix for the week
glendaviesnz Jun 16, 2023
e0e8882
Remove unsynced patterns from synced patterns inserter tab
glendaviesnz Jun 16, 2023
e96852c
More e2e test fixes
glendaviesnz Jun 18, 2023
302f5f9
Unit test fix
glendaviesnz Jun 18, 2023
c04f927
Put all user patterns in the old reusable blocks tab for now
glendaviesnz Jun 19, 2023
6d7fd7e
More test fixes
glendaviesnz Jun 19, 2023
ec7056b
Reorder pattern tabs, with synced only in the old reusable blocks tab
glendaviesnz Jun 19, 2023
f1975c6
Moving parsing of blocks to patterns panel to avoid parsing reusable …
glendaviesnz Jun 19, 2023
c5845c3
Update copy
talldan Jun 19, 2023
1bbe290
Changes from review
glendaviesnz Jun 19, 2023
ee75c26
Move the sync status filter to the selector
glendaviesnz Jun 19, 2023
88f48f3
Put full stop back
glendaviesnz Jun 19, 2023
d477199
Change labels
glendaviesnz Jun 20, 2023
a309562
Remove the synced to unsynced conversion call
glendaviesnz Jun 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/reference-guides/core-blocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Add a user’s avatar. ([Source](https://github.com/WordPress/gutenberg/tree/tru
- **Supports:** align, color (~~background~~, ~~text~~), spacing (margin, padding), ~~alignWide~~, ~~html~~
- **Attributes:** isLink, linkTarget, size, userId

## Reusable block
## Pattern
glendaviesnz marked this conversation as resolved.
Show resolved Hide resolved

Create and save content to reuse across your site. Update the block, and the changes apply everywhere it’s used. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/block))

Expand Down Expand Up @@ -473,7 +473,7 @@ Start with the basic building block of all narrative. ([Source](https://github.c
- **Supports:** __unstablePasteTextInline, anchor, color (background, gradients, link, text), spacing (margin, padding), typography (fontSize, lineHeight), ~~className~~
- **Attributes:** align, content, direction, dropCap, placeholder

## Pattern
## Pattern placeholder

Show a block pattern. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/pattern))

Expand Down
1 change: 1 addition & 0 deletions docs/reference-guides/data/data-core-block-editor.md
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,7 @@ _Parameters_

- _state_ `Object`: Editor state.
- _rootClientId_ `?string`: Optional root client ID of block list.
- _syncStatus_ `?string`: Optional sync status to filter pattern blocks by.

_Returns_

Expand Down
110 changes: 110 additions & 0 deletions lib/compat/wordpress-6.3/blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,113 @@ function gutenberg_add_selectors_property_to_block_type_settings( $settings, $me
return $settings;
}
add_filter( 'block_type_metadata_settings', 'gutenberg_add_selectors_property_to_block_type_settings', 10, 2 );

/**
* Renames Reusable block CPT to Pattern.
*
* Note: This should be removed when the minimum required WP version is >= 6.3.
*
* @see https://github.com/WordPress/gutenberg/pull/51144
*
* @param array $args Register post type args.
* @param string $post_type The post type string.
*
* @return array Register post type args.
*/
function gutenberg_rename_reusable_block_cpt_to_pattern( $args, $post_type ) {
if ( 'wp_block' === $post_type ) {
$args['labels']['name'] = _x( 'Patterns', 'post type general name' );
$args['labels']['singular_name'] = _x( 'Pattern', 'post type singular name' );
$args['labels']['add_new_item'] = __( 'Add new Pattern' );
$args['labels']['new_item'] = __( 'New Pattern' );
$args['labels']['edit_item'] = __( 'Edit Pattern' );
$args['labels']['view_item'] = __( 'View Pattern' );
$args['labels']['all_items'] = __( 'All Patterns' );
$args['labels']['search_items'] = __( 'Search Patterns' );
$args['labels']['not_found'] = __( 'No Patterns found.' );
$args['labels']['not_found_in_trash'] = __( 'No Patterns found in Trash.' );
$args['labels']['filter_items_list'] = __( 'Filter Patterns list' );
$args['labels']['items_list_navigation'] = __( 'Patterns list navigation' );
$args['labels']['items_list'] = __( 'Patterns list' );
$args['labels']['item_published'] = __( 'Pattern published.' );
$args['labels']['item_published_privately'] = __( 'Pattern published privately.' );
$args['labels']['item_reverted_to_draft'] = __( 'Pattern reverted to draft.' );
$args['labels']['item_scheduled'] = __( 'Pattern scheduled.' );
$args['labels']['item_updated'] = __( 'Pattern updated.' );
}

return $args;
}

add_filter( 'register_post_type_args', 'gutenberg_rename_reusable_block_cpt_to_pattern', 10, 2 );

/**
* Adds custom fields support to the wp_block post type so an unsynced option can be added.
*
* Note: This should be removed when the minimum required WP version is >= 6.3.
*
* @see https://github.com/WordPress/gutenberg/pull/51144
*
* @param array $args Register post type args.
* @param string $post_type The post type string.
*
* @return array Register post type args.
*/
function gutenberg_add_custom_fields_to_wp_block( $args, $post_type ) {
if ( 'wp_block' === $post_type ) {
array_push( $args['supports'], 'custom-fields' );
glendaviesnz marked this conversation as resolved.
Show resolved Hide resolved
}

return $args;
}
add_filter( 'register_post_type_args', 'gutenberg_add_custom_fields_to_wp_block', 10, 2 );

/**
* Adds sync_status meta fields to the wp_block post type so an unsynced option can be added.
*
* Note: This should be removed when the minimum required WP version is >= 6.3.
*
* @see https://github.com/WordPress/gutenberg/pull/51144
*
* @return void
*/
function gutenberg_wp_block_register_post_meta() {
$post_type = 'wp_block';
register_post_meta(
$post_type,
'sync_status',
array(
'auth_callback' => function() {
return current_user_can( 'edit_posts' );

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this use the meta capability somehow?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The auth callback has been removed in the backport to core as the relevant checks for user permissions for the given post type are already done, I assume this includes the relevant postmeta permissions - have copied this question over to that port to check.

},
'sanitize_callback' => 'gutenberg_wp_block_sanitize_post_meta',
'single' => true,
'type' => 'string',
'show_in_rest' => array(
'schema' => array(
'type' => 'string',
'properties' => array(
'sync_status' => array(
'type' => 'string',
),
),
),
),
)
);
}
/**
* Sanitizes the array of wp_block post meta sync_status string.
*
* Note: This should be removed when the minimum required WP version is >= 6.3.
*
* @see https://github.com/WordPress/gutenberg/pull/51144
*
* @param array $meta_value String to sanitize.
*
* @return array Sanitized string.
*/
function gutenberg_wp_block_sanitize_post_meta( $meta_value ) {
return sanitize_text_field( $meta_value );
}
add_action( 'init', 'gutenberg_wp_block_register_post_meta' );
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
Button,
} from '@wordpress/components';
import { Icon, chevronRight, chevronLeft } from '@wordpress/icons';
import { parse } from '@wordpress/blocks';
import { focus } from '@wordpress/dom';

/**
Expand All @@ -27,6 +28,7 @@ import usePatternsState from './hooks/use-patterns-state';
import BlockPatternList from '../block-patterns-list';
import PatternsExplorerModal from './block-patterns-explorer/explorer';
import MobileTabNavigation from './mobile-tab-navigation';
import useBlockTypesState from './hooks/use-block-types-state';

const noop = () => {};

Expand All @@ -49,6 +51,18 @@ function usePatternsCategories( rootClientId ) {
rootClientId
);

const [ unsyncedPatterns ] = useBlockTypesState(
rootClientId,
undefined,
'unsynced'
);

const filteredUnsyncedPatterns = useMemo( () => {
return unsyncedPatterns.filter(
( { category: unsyncedPatternCategory } ) =>
unsyncedPatternCategory === 'reusable'
);
}, [ unsyncedPatterns ] );
Comment on lines +54 to +65
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This filtering could probably do with some optimization, as unsyncedPatterns seems to contain the inserter data for every block type, which then gets filtered down. There's probably a more direct way to do this that doesn't involved getting other non-pattern block types first.

Seems like a good task for a follow-up though.

const hasRegisteredCategory = useCallback(
( pattern ) => {
if ( ! pattern.categories || ! pattern.categories.length ) {
Expand Down Expand Up @@ -93,9 +107,20 @@ function usePatternsCategories( rootClientId ) {
label: _x( 'Uncategorized' ),
} );
}
if ( filteredUnsyncedPatterns.length > 0 ) {
categories.push( {
name: 'reusable',
label: _x( 'Custom patterns' ),
} );
}

return categories;
}, [ allPatterns, allCategories ] );
}, [
allCategories,
allPatterns,
filteredUnsyncedPatterns.length,
hasRegisteredCategory,
] );

return populatedCategories;
}
Expand Down Expand Up @@ -144,6 +169,24 @@ export function BlockPatternsCategoryPanel( {
onInsert,
rootClientId
);
const [ unsyncedPatterns ] = useBlockTypesState(
rootClientId,
onInsert,
'unsynced'
);
const filteredUnsyncedPatterns = useMemo( () => {
return unsyncedPatterns
.filter(
( { category: unsyncedPatternCategory } ) =>
unsyncedPatternCategory === 'reusable'
)
.map( ( syncedPattern ) => ( {
...syncedPattern,
blocks: parse( syncedPattern.content, {
__unstableSkipMigrationLogs: true,
} ),
} ) );
}, [ unsyncedPatterns ] );

const availableCategories = usePatternsCategories( rootClientId );
const currentCategoryPatterns = useMemo(
Expand All @@ -167,13 +210,19 @@ export function BlockPatternsCategoryPanel( {
} ),
[ allPatterns, category ]
);

const currentShownPatterns = useAsyncList( currentCategoryPatterns );
const patterns =
category.name === 'reusable'
? filteredUnsyncedPatterns
: currentCategoryPatterns;
const currentShownPatterns = useAsyncList( patterns );

// Hide block pattern preview on unmount.
useEffect( () => () => onHover( null ), [] );

if ( ! currentCategoryPatterns.length ) {
if (
! currentCategoryPatterns.length &&
! filteredUnsyncedPatterns.length
) {
return null;
}

Expand All @@ -185,7 +234,7 @@ export function BlockPatternsCategoryPanel( {
<p>{ category.description }</p>
<BlockPatternList
shownPatterns={ currentShownPatterns }
blockPatterns={ currentCategoryPatterns }
blockPatterns={ patterns }
onClickPattern={ onClick }
onHover={ onHover }
label={ category.label }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ import { store as blockEditorStore } from '../../../store';
*
* @param {string=} rootClientId Insertion's root client ID.
* @param {Function} onInsert function called when inserter a list of blocks.
* @param {?string} syncStatus Optional sync status to filter pattern blocks by.
* @return {Array} Returns the block types state. (block types, categories, collections, onSelect handler)
*/
const useBlockTypesState = ( rootClientId, onInsert ) => {
const useBlockTypesState = ( rootClientId, onInsert, syncStatus ) => {
const { categories, collections, items } = useSelect(
( select ) => {
const { getInserterItems } = select( blockEditorStore );
Expand All @@ -30,10 +31,10 @@ const useBlockTypesState = ( rootClientId, onInsert ) => {
return {
categories: getCategories(),
collections: getCollections(),
items: getInserterItems( rootClientId ),
items: getInserterItems( rootClientId, syncStatus ),
};
},
[ rootClientId ]
[ rootClientId, syncStatus ]
);

const onSelectItem = useCallback(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ function ReusableBlocksList( { onHover, onInsert, rootClientId } ) {
}

return (
<InserterPanel title={ __( 'Reusable blocks' ) }>
<InserterPanel title={ __( 'Synced patterns' ) }>
<BlockTypesList
items={ filteredItems }
onSelect={ onSelectItem }
onHover={ onHover }
label={ __( 'Reusable blocks' ) }
label={ __( 'Synced patterns' ) }
/>
</InserterPanel>
);
Expand Down Expand Up @@ -67,7 +67,7 @@ export function ReusableBlocksTab( { rootClientId, onInsert, onHover } ) {
post_type: 'wp_block',
} ) }
>
{ __( 'Manage Reusable blocks' ) }
{ __( 'Manage custom patterns' ) }
</Button>
</div>
</>
Expand Down
6 changes: 3 additions & 3 deletions packages/block-editor/src/components/inserter/tabs.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ const blocksTab = {
};
const patternsTab = {
name: 'patterns',
/* translators: Patterns tab title in the block inserter. */
/* translators: Theme and Directory Patterns tab title in the block inserter. */
title: __( 'Patterns' ),
};
const reusableBlocksTab = {
name: 'reusable',
/* translators: Reusable blocks tab title in the block inserter. */
title: __( 'Reusable' ),
/* translators: Locally created Patterns tab title in the block inserter. */
title: __( 'Synced patterns' ),
icon: reusableBlockIcon,
};
const mediaTab = {
Expand Down
13 changes: 11 additions & 2 deletions packages/block-editor/src/store/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -1945,6 +1945,7 @@ const buildBlockTypeItem =
*
* @param {Object} state Editor state.
* @param {?string} rootClientId Optional root client ID of block list.
* @param {?string} syncStatus Optional sync status to filter pattern blocks by.
*
* @return {WPEditorInserterItem[]} Items that appear in inserter.
*
Expand All @@ -1961,7 +1962,7 @@ const buildBlockTypeItem =
* @property {number} frecency Heuristic that combines frequency and recency.
*/
export const getInserterItems = createSelector(
( state, rootClientId = null ) => {
( state, rootClientId = null, syncStatus ) => {
const buildBlockTypeInserterItem = buildBlockTypeItem( state, {
buildScope: 'inserter',
} );
Expand Down Expand Up @@ -2026,6 +2027,7 @@ export const getInserterItems = createSelector(
isDisabled: false,
utility: 1, // Deprecated.
frecency,
content: reusableBlock.content.raw,
};
};

Expand All @@ -2040,7 +2042,14 @@ export const getInserterItems = createSelector(
'core/block',
rootClientId
)
? getReusableBlocks( state ).map( buildReusableBlockInserterItem )
? getReusableBlocks( state )
.filter(
( reusableBlock ) =>
syncStatus === reusableBlock.meta?.sync_status ||
( ! syncStatus &&
reusableBlock.meta?.sync_status === '' )
)
.map( buildReusableBlockInserterItem )
: [];

const items = blockTypeInserterItems.reduce( ( accumulator, item ) => {
Expand Down