From a59ecdcca0f62f40bff069d40d35ac07b76c854a Mon Sep 17 00:00:00 2001 From: Michael Day Date: Thu, 23 Mar 2023 09:15:56 -0500 Subject: [PATCH 01/57] Register new meta; hydrate editor with patternCategories --- wp-modules/editor/editor.php | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/wp-modules/editor/editor.php b/wp-modules/editor/editor.php index 18821f97..57c34112 100644 --- a/wp-modules/editor/editor.php +++ b/wp-modules/editor/editor.php @@ -174,6 +174,24 @@ function register_pattern_post_type() { 'default' => get_pattern_defaults()['keywords'], ) ); + + register_post_meta( + $post_type_key, + 'customCategories', + array( + 'show_in_rest' => array( + 'schema' => array( + 'type' => 'array', + 'items' => array( + 'type' => 'string', + ), + ), + ), + 'single' => true, + 'type' => 'array', + 'default' => [], + ) + ); } add_action( 'init', __NAMESPACE__ . '\register_pattern_post_type' ); @@ -263,13 +281,14 @@ function enqueue_meta_fields_in_editor() { 'pattern_manager_post_meta', 'patternManager', [ - 'activeTheme' => basename( get_stylesheet_directory() ), - 'apiEndpoints' => array( + 'activeTheme' => basename( get_stylesheet_directory() ), + 'apiEndpoints' => array( 'getPatternNamesEndpoint' => get_rest_url( false, 'pattern-manager/v1/get-pattern-names/' ), ), - 'apiNonce' => wp_create_nonce( 'wp_rest' ), - 'patternNames' => \PatternManager\PatternDataHandlers\get_pattern_names(), - 'siteUrl' => get_bloginfo( 'url' ), + 'apiNonce' => wp_create_nonce( 'wp_rest' ), + 'patternNames' => \PatternManager\PatternDataHandlers\get_pattern_names(), + 'siteUrl' => get_bloginfo( 'url' ), + 'patternCategories' => \WP_Block_Pattern_Categories_Registry::get_instance()->get_all_registered(), ] ); From 867881ffc35ff7609d495726cbfdf79274fb2546 Mon Sep 17 00:00:00 2001 From: Michael Day Date: Thu, 23 Mar 2023 09:19:10 -0500 Subject: [PATCH 02/57] Adjust types for new customCategories, global --- wp-modules/editor/js/src/types.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/wp-modules/editor/js/src/types.ts b/wp-modules/editor/js/src/types.ts index 9e25266a..317c5c2b 100644 --- a/wp-modules/editor/js/src/types.ts +++ b/wp-modules/editor/js/src/types.ts @@ -3,6 +3,7 @@ export type PostMeta = { blockTypes: string[]; categories: string[]; + customCategories: string[]; description: string; inserter: boolean; keywords: string[]; @@ -16,7 +17,11 @@ export type PatternPostData = PostMeta & { }; export type SelectQuery = ( dataStore: string ) => { - getBlockPatternCategories: () => { name: string; label: string }[]; + getBlockPatternCategories: () => { + name: string; + label: string; + pm_meta?: string; + }[]; getBlockTypes: () => { name: string; transforms?: unknown }[]; getCurrentPostAttribute: ( attributeName: 'meta' ) => PostMeta; getEditedPostAttribute: ( postAttribute: string ) => unknown; @@ -34,6 +39,7 @@ export type SelectQuery = ( dataStore: string ) => { export type Pattern = { blockTypes: string[]; categories: string[]; + customCategories: string[]; content: string; description: string; inserter: boolean; @@ -55,6 +61,12 @@ export type InitialPatternManager = { apiEndpoints: { getPatternNamesEndpoint: string; }; + patternCategories: { + name: string; + label: string; + description?: string; + pm_meta?: string; + }[]; patternNames: Array< Pattern[ 'slug' ] >; siteUrl: string; }; From 477d3f33b5dcad53cf04176308b48c06cfcd6f10 Mon Sep 17 00:00:00 2001 From: Michael Day Date: Thu, 23 Mar 2023 09:20:02 -0500 Subject: [PATCH 03/57] Update usePatternData to use global, create combinedCategories --- .../editor/js/src/hooks/usePatternData.ts | 52 ++++++++++++++----- 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/wp-modules/editor/js/src/hooks/usePatternData.ts b/wp-modules/editor/js/src/hooks/usePatternData.ts index 9c76e9bb..818600e0 100644 --- a/wp-modules/editor/js/src/hooks/usePatternData.ts +++ b/wp-modules/editor/js/src/hooks/usePatternData.ts @@ -3,6 +3,8 @@ import sortAlphabetically from '../utils/sortAlphabetically'; import { useSelect, useDispatch } from '@wordpress/data'; import { useEffect } from '@wordpress/element'; import { PostMeta, SelectQuery } from '../types'; +import { patternManager } from '../globals'; +import convertToSlug from '../utils/convertToSlug'; export default function usePatternData( postMeta: PostMeta ) { const { editPost } = useDispatch( 'core/editor' ); @@ -57,18 +59,44 @@ export default function usePatternData( postMeta: PostMeta ) { /** * Alphabetized block pattern categories for the site editor, mapped for react-select. + * + * Using categories from app hydration instead of select( 'core' ).getBlockPatternCategories(). + * Otherwise, custom fields (such as `pm_meta`) are not included in the result. */ - const categories = useSelect( ( select: SelectQuery ) => { - return sortAlphabetically( - select( 'core' ) - .getBlockPatternCategories() - .map( ( category ) => ( { - label: category.label, - value: category.name, - } ) ), - 'label' - ); - }, [] ); + const categories = sortAlphabetically( + patternManager.patternCategories.map( ( category ) => ( { + label: category.label, + value: category.name, + ...( category.pm_meta && { + pm_meta: category.pm_meta, + } ), + } ) ), + 'label' + ); + + /** + * Registered and newly added custom categories, combined. + * Needed for including new categories before the post is saved. + */ + const combinedCategories: typeof categories = [ + ...postMeta.customCategories.reduce( ( acc, categoryLabel ) => { + const missingCategory = ! categories.some( + ( queriedCategory ) => queriedCategory.label === categoryLabel + ); + + return missingCategory + ? [ + ...acc, + { + label: categoryLabel, + value: convertToSlug( categoryLabel ), + pm_meta: 'pm_custom_category', + }, + ] + : acc; + }, [] ), + ...categories, + ]; /** * The alphabetized list of transformable block types, mapped for react-select. @@ -195,7 +223,7 @@ export default function usePatternData( postMeta: PostMeta ) { return { queriedBlockTypes: blockTypes, - queriedCategories: categories, + queriedCategories: combinedCategories, queriedPostTypes: postTypes, updatePostMeta, updatePostMetaMulti, From a1e7af1bfe4cbb1ead54755285140ab78d5edb5b Mon Sep 17 00:00:00 2001 From: Michael Day Date: Thu, 23 Mar 2023 09:21:34 -0500 Subject: [PATCH 04/57] Add customCategories to pattern-data-handlers, parse in header, add registration calls to file --- .../pattern-data-handlers.php | 53 +++++++++++-------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/wp-modules/pattern-data-handlers/pattern-data-handlers.php b/wp-modules/pattern-data-handlers/pattern-data-handlers.php index c4388860..f209a510 100644 --- a/wp-modules/pattern-data-handlers/pattern-data-handlers.php +++ b/wp-modules/pattern-data-handlers/pattern-data-handlers.php @@ -69,7 +69,7 @@ function format_pattern_data( $pattern_data, $file ) { } // For properties of type array, parse data as comma-separated. - foreach ( array( 'categories', 'keywords', 'blockTypes', 'postTypes' ) as $property ) { + foreach ( array( 'categories', 'keywords', 'blockTypes', 'postTypes', 'customCategories' ) as $property ) { if ( ! empty( $pattern_data[ $property ] ) ) { $pattern_data[ $property ] = array_map( // Trim whitespace at start and end of each element. @@ -213,15 +213,16 @@ function get_pattern_file_paths() { */ function get_pattern_by_path( $path ) { $default_headers = array( - 'title' => 'Title', - 'slug' => 'Slug', - 'description' => 'Description', - 'viewportWidth' => 'Viewport Width', - 'categories' => 'Categories', - 'keywords' => 'Keywords', - 'blockTypes' => 'Block Types', - 'postTypes' => 'Post Types', - 'inserter' => 'Inserter', + 'title' => 'Title', + 'slug' => 'Slug', + 'description' => 'Description', + 'viewportWidth' => 'Viewport Width', + 'categories' => 'Categories', + 'keywords' => 'Keywords', + 'blockTypes' => 'Block Types', + 'postTypes' => 'Post Types', + 'inserter' => 'Inserter', + 'customCategories' => 'Custom Categories', ); $pattern_data = format_pattern_data( get_file_data( $path, $default_headers ), $path ); @@ -239,16 +240,17 @@ function get_pattern_by_path( $path ) { */ function get_pattern_defaults() { return [ - 'name' => '', - 'title' => '', - 'description' => '', - 'content' => '', - 'viewportWidth' => 1280, - 'categories' => [], - 'keywords' => [], - 'blockTypes' => [], - 'postTypes' => [], - 'inserter' => true, + 'name' => '', + 'title' => '', + 'description' => '', + 'content' => '', + 'viewportWidth' => 1280, + 'categories' => [], + 'keywords' => [], + 'blockTypes' => [], + 'postTypes' => [], + 'inserter' => true, + 'customCategories' => [], ]; } @@ -356,6 +358,14 @@ function construct_pattern_php_file_contents( $pattern_data ) { $pattern['content'] = remove_theme_name_from_template_parts( $pattern['content'] ); $pattern['content'] = move_block_images_to_theme( $pattern['content'] ); + $custom_category_registrations = array_map( + function ( $category_label ) { + $category_name = strtolower( str_replace( ' ', '-', $category_label ) ); + return "register_block_pattern_category( '$category_name', array( 'label' => '$category_label', 'pm_meta' => 'pm_custom_category' ) );"; + }, + $pattern['customCategories'], + ); + // phpcs:ignore $file_contents = " ' . trim( $pattern['content'] ) . ' '; From ebaa2c61c128ea1a3e7f02253f279b6acda4fa60 Mon Sep 17 00:00:00 2001 From: Michael Day Date: Thu, 23 Mar 2023 09:22:41 -0500 Subject: [PATCH 05/57] Use customCategories in CategoriesPanel --- .../PatternManagerMetaControls/index.tsx | 1 + .../SidebarPanels/CategoriesPanel.tsx | 68 +++++++++++++++---- 2 files changed, 57 insertions(+), 12 deletions(-) diff --git a/wp-modules/editor/js/src/components/PatternManagerMetaControls/index.tsx b/wp-modules/editor/js/src/components/PatternManagerMetaControls/index.tsx index 9052b73c..fc16fa1c 100644 --- a/wp-modules/editor/js/src/components/PatternManagerMetaControls/index.tsx +++ b/wp-modules/editor/js/src/components/PatternManagerMetaControls/index.tsx @@ -49,6 +49,7 @@ export default function PatternManagerMetaControls() { & +}: BaseSidebarProps< 'categories' | 'customCategories' > & AdditionalSidebarProps< 'categoryOptions' > ) { + const combinedSelectedCategories = [ + ...customCategories.filter( ( categoryTitle ) => + categories.includes( convertToSlug( categoryTitle ) ) + ), + ...categories, + ]; + return ( { categoryOptions ? ( -