From c43a0c9944b33ad01fa2805a02a171b42b791282 Mon Sep 17 00:00:00 2001 From: Tyler Bailey Date: Tue, 19 Nov 2024 10:33:59 -0500 Subject: [PATCH] Feature: Set editor rendering mode by post type (#62304) Co-authored-by: TylerB24890 Co-authored-by: Sidsector9 Co-authored-by: fabiankaegy Co-authored-by: youknowriad Co-authored-by: dcalhoun Co-authored-by: ramonjd Co-authored-by: mcsf Co-authored-by: jasmussen Co-authored-by: annezazu Co-authored-by: jameskoster Co-authored-by: dinhtungdu --- backport-changelog/6.8/7129.md | 3 + ...tenberg-rest-post-types-controller-6-8.php | 61 ++++++++++++++ lib/compat/wordpress-6.8/post.php | 57 +++++++++++++ lib/compat/wordpress-6.8/rest-api.php | 22 +++++ lib/load.php | 3 + .../block-editor/use-site-editor-settings.js | 11 +-- .../edit-site/src/components/editor/index.js | 4 +- .../editor/src/components/provider/index.js | 83 ++++++++++++------- packages/editor/src/store/reducer.native.js | 2 + .../initialize-editor.js | 17 ++++ test/performance/fixtures/perf-utils.ts | 20 +++++ test/performance/specs/site-editor.spec.js | 2 + 12 files changed, 243 insertions(+), 42 deletions(-) create mode 100644 backport-changelog/6.8/7129.md create mode 100644 lib/compat/wordpress-6.8/class-gutenberg-rest-post-types-controller-6-8.php create mode 100644 lib/compat/wordpress-6.8/post.php create mode 100644 lib/compat/wordpress-6.8/rest-api.php diff --git a/backport-changelog/6.8/7129.md b/backport-changelog/6.8/7129.md new file mode 100644 index 0000000000000..90c9168cdc6f8 --- /dev/null +++ b/backport-changelog/6.8/7129.md @@ -0,0 +1,3 @@ +https://github.com/WordPress/wordpress-develop/pull/7129 + +* https://github.com/WordPress/gutenberg/pull/62304 diff --git a/lib/compat/wordpress-6.8/class-gutenberg-rest-post-types-controller-6-8.php b/lib/compat/wordpress-6.8/class-gutenberg-rest-post-types-controller-6-8.php new file mode 100644 index 0000000000000..da0489210e21f --- /dev/null +++ b/lib/compat/wordpress-6.8/class-gutenberg-rest-post-types-controller-6-8.php @@ -0,0 +1,61 @@ +default_rendering_mode, $item ); + + /** + * Filters the block editor rendering mode for a specific post type. + * Applied after the generic `post_type_default_rendering_mode` filter. + * + * The dynamic portion of the hook name, `$item->name`, refers to the post type slug. + * + * @since 6.8.0 + * @param string $default_rendering_mode Default rendering mode for the post type. + * @param WP_Post_Type $post_type Post type object. + * @return string Default rendering mode for the post type. + */ + $rendering_mode = apply_filters( "post_type_{$item->name}_default_rendering_mode", $rendering_mode, $item ); + + // Validate the filtered rendering mode. + if ( ! in_array( $rendering_mode, gutenberg_post_type_rendering_modes(), true ) ) { + $rendering_mode = 'post-only'; + } + + $response->data['default_rendering_mode'] = $rendering_mode; + } + + return rest_ensure_response( $response ); + } +} diff --git a/lib/compat/wordpress-6.8/post.php b/lib/compat/wordpress-6.8/post.php new file mode 100644 index 0000000000000..26e6c3adc07a3 --- /dev/null +++ b/lib/compat/wordpress-6.8/post.php @@ -0,0 +1,57 @@ +register_routes(); + } +} +add_action( 'rest_api_init', 'gutenberg_add_post_type_rendering_mode' ); diff --git a/lib/load.php b/lib/load.php index d7e4a33cd02c9..100160176f391 100644 --- a/lib/load.php +++ b/lib/load.php @@ -49,6 +49,8 @@ function gutenberg_is_experiment_enabled( $name ) { // WordPress 6.8 compat. require __DIR__ . '/compat/wordpress-6.8/block-comments.php'; require __DIR__ . '/compat/wordpress-6.8/class-gutenberg-rest-comment-controller-6-8.php'; + require __DIR__ . '/compat/wordpress-6.8/class-gutenberg-rest-post-types-controller-6-8.php'; + require __DIR__ . '/compat/wordpress-6.8/rest-api.php'; // Plugin specific code. require_once __DIR__ . '/class-wp-rest-global-styles-controller-gutenberg.php'; @@ -120,6 +122,7 @@ function gutenberg_is_experiment_enabled( $name ) { require __DIR__ . '/compat/wordpress-6.8/preload.php'; require __DIR__ . '/compat/wordpress-6.8/blocks.php'; require __DIR__ . '/compat/wordpress-6.8/functions.php'; +require __DIR__ . '/compat/wordpress-6.8/post.php'; // Experimental features. require __DIR__ . '/experimental/block-editor-settings-mobile.php'; diff --git a/packages/edit-site/src/components/block-editor/use-site-editor-settings.js b/packages/edit-site/src/components/block-editor/use-site-editor-settings.js index 5c75de6d81e72..186f4aacf7923 100644 --- a/packages/edit-site/src/components/block-editor/use-site-editor-settings.js +++ b/packages/edit-site/src/components/block-editor/use-site-editor-settings.js @@ -36,9 +36,7 @@ function useNavigateToPreviousEntityRecord() { return goBack; } -export function useSpecificEditorSettings( - shouldUseTemplateAsDefaultRenderingMode -) { +export function useSpecificEditorSettings() { const { params } = useLocation(); const { canvas = 'view' } = params; const onNavigateToEntityRecord = useNavigateToEntityRecord(); @@ -49,11 +47,6 @@ export function useSpecificEditorSettings( }; }, [] ); - // TODO: The `shouldUseTemplateAsDefaultRenderingMode` check should be removed when the default rendering mode per post type is merged. - // @see https://github.com/WordPress/gutenberg/pull/62304/ - const defaultRenderingMode = shouldUseTemplateAsDefaultRenderingMode - ? 'template-locked' - : 'post-only'; const onNavigateToPreviousEntityRecord = useNavigateToPreviousEntityRecord(); const defaultEditorSettings = useMemo( () => { @@ -63,7 +56,6 @@ export function useSpecificEditorSettings( richEditingEnabled: true, supportsTemplateMode: true, focusMode: canvas !== 'view', - defaultRenderingMode, onNavigateToEntityRecord, onNavigateToPreviousEntityRecord, isPreviewMode: canvas === 'view', @@ -71,7 +63,6 @@ export function useSpecificEditorSettings( }, [ settings, canvas, - defaultRenderingMode, onNavigateToEntityRecord, onNavigateToPreviousEntityRecord, ] ); diff --git a/packages/edit-site/src/components/editor/index.js b/packages/edit-site/src/components/editor/index.js index 51d734f25c6ad..1d115dca7518d 100644 --- a/packages/edit-site/src/components/editor/index.js +++ b/packages/edit-site/src/components/editor/index.js @@ -131,9 +131,7 @@ export default function EditSiteEditor( { isPostsList = false } ) { 'edit-site-editor__loading-progress' ); - const settings = useSpecificEditorSettings( - !! context?.postId && context?.postType !== 'post' - ); + const settings = useSpecificEditorSettings(); const styles = useMemo( () => [ ...settings.styles, diff --git a/packages/editor/src/components/provider/index.js b/packages/editor/src/components/provider/index.js index 50d02610062c0..6c05e5b58235b 100644 --- a/packages/editor/src/components/provider/index.js +++ b/packages/editor/src/components/provider/index.js @@ -72,8 +72,7 @@ const NON_CONTEXTUAL_POST_TYPES = [ * @return {Array} Block editor props. */ function useBlockEditorProps( post, template, mode ) { - const rootLevelPost = - mode === 'post-only' || ! template ? 'post' : 'template'; + const rootLevelPost = mode === 'template-locked' ? 'template' : 'post'; const [ postBlocks, onInput, onChange ] = useEntityBlockEditor( 'postType', post.type, @@ -164,30 +163,48 @@ export const ExperimentalEditorProvider = withRegistryProvider( BlockEditorProviderComponent = ExperimentalBlockEditorProvider, __unstableTemplate: template, } ) => { - const { editorSettings, selection, isReady, mode, postTypeEntities } = - useSelect( - ( select ) => { - const { - getEditorSettings, - getEditorSelection, - getRenderingMode, - __unstableIsEditorReady, - } = select( editorStore ); - const { getEntitiesConfig } = select( coreStore ); + const { + editorSettings, + selection, + isReady, + mode, + defaultMode, + postTypeEntities, + hasLoadedPostObject, + } = useSelect( + ( select ) => { + const { + getEditorSettings, + getEditorSelection, + getRenderingMode, + __unstableIsEditorReady, + } = select( editorStore ); + const { getEntitiesConfig } = select( coreStore ); + + const postTypeObject = select( coreStore ).getPostType( + post.type + ); + + const _hasLoadedPostObject = select( + coreStore + ).hasFinishedResolution( 'getPostType', [ post.type ] ); - return { - editorSettings: getEditorSettings(), - isReady: __unstableIsEditorReady(), - mode: getRenderingMode(), - selection: getEditorSelection(), - postTypeEntities: - post.type === 'wp_template' - ? getEntitiesConfig( 'postType' ) - : null, - }; - }, - [ post.type ] - ); + return { + hasLoadedPostObject: _hasLoadedPostObject, + editorSettings: getEditorSettings(), + isReady: __unstableIsEditorReady(), + mode: getRenderingMode(), + defaultMode: + postTypeObject?.default_rendering_mode ?? 'post-only', + selection: getEditorSelection(), + postTypeEntities: + post.type === 'wp_template' + ? getEntitiesConfig( 'postType' ) + : null, + }; + }, + [ post.type ] + ); const shouldRenderTemplate = !! template && mode !== 'post-only'; const rootLevelPost = shouldRenderTemplate ? template : post; const defaultBlockContext = useMemo( () => { @@ -282,7 +299,15 @@ export const ExperimentalEditorProvider = withRegistryProvider( } ); } - }, [] ); + }, [ + createWarningNotice, + initialEdits, + settings, + post, + recovery, + setupEditor, + updatePostLock, + ] ); // Synchronizes the active post with the state useEffect( () => { @@ -301,15 +326,15 @@ export const ExperimentalEditorProvider = withRegistryProvider( // Sets the right rendering mode when loading the editor. useEffect( () => { - setRenderingMode( settings.defaultRenderingMode ?? 'post-only' ); - }, [ settings.defaultRenderingMode, setRenderingMode ] ); + setRenderingMode( defaultMode ); + }, [ defaultMode, setRenderingMode ] ); useHideBlocksFromInserter( post.type, mode ); // Register the editor commands. useCommands(); - if ( ! isReady ) { + if ( ! isReady || ! mode || ! hasLoadedPostObject ) { return null; } diff --git a/packages/editor/src/store/reducer.native.js b/packages/editor/src/store/reducer.native.js index 7566dfc5dfd03..fbf6c968f57d0 100644 --- a/packages/editor/src/store/reducer.native.js +++ b/packages/editor/src/store/reducer.native.js @@ -9,6 +9,7 @@ import { combineReducers } from '@wordpress/data'; import { postId, postType, + renderingMode, saving, postLock, postSavingLock, @@ -82,6 +83,7 @@ export default combineReducers( { postId, postType, postTitle, + renderingMode, saving, postLock, postSavingLock, diff --git a/test/native/integration-test-helpers/initialize-editor.js b/test/native/integration-test-helpers/initialize-editor.js index 511f0223e1135..3b89da979aee3 100644 --- a/test/native/integration-test-helpers/initialize-editor.js +++ b/test/native/integration-test-helpers/initialize-editor.js @@ -10,6 +10,8 @@ import { v4 as uuid } from 'uuid'; import { createElement, cloneElement } from '@wordpress/element'; // eslint-disable-next-line no-restricted-imports import { initializeEditor as internalInitializeEditor } from '@wordpress/edit-post'; +import { store as coreStore } from '@wordpress/core-data'; +import { select } from '@wordpress/data'; /** * Internal dependencies @@ -28,6 +30,21 @@ import { getGlobalStyles } from './get-global-styles'; * @return {import('@testing-library/react-native').RenderAPI} A Testing Library screen. */ export async function initializeEditor( props, { component } = {} ) { + const resolutionSpy = jest.spyOn( + select( coreStore ), + 'hasFinishedResolution' + ); + const actualResolution = resolutionSpy.getMockImplementation(); + resolutionSpy.mockImplementation( ( selectorName, args ) => { + // The mobile editor only supports the `post-only` rendering mode, so we + // presume a resolved `getPostType` selector to unblock editor rendering. + if ( 'getPostType' === selectorName ) { + return true; + } + + return actualResolution( selectorName, args ); + } ); + const uniqueId = uuid(); const postId = `post-id-${ uniqueId }`; const postType = 'post'; diff --git a/test/performance/fixtures/perf-utils.ts b/test/performance/fixtures/perf-utils.ts index 592e8194852e3..8d23d91ff91bf 100644 --- a/test/performance/fixtures/perf-utils.ts +++ b/test/performance/fixtures/perf-utils.ts @@ -97,6 +97,26 @@ export class PerfUtils { return canvas; } + /** + * Change the rendering mode of the editor. + * + * Setting the rendering mode to something other than the default is sometimes + * needed when for example we want to update the contents of the editor from a + * HTML file. Calling the resetBlocks method of the core/block-editor store will + * replace the contents of the template if the rendering mode is not post-only. + * So this should always be called before the resetBlocks method is used. + * + * @param newRenderingMode Rendering mode to set + * + * @return Promise + */ + async setRenderingMode( newRenderingMode: string ) { + await this.page.evaluate( ( _newRenderingMode ) => { + const { dispatch } = window.wp.data; + dispatch( 'core/editor' ).setRenderingMode( _newRenderingMode ); + }, newRenderingMode ); + } + /** * Loads blocks from the small post with containers fixture into the editor * canvas. diff --git a/test/performance/specs/site-editor.spec.js b/test/performance/specs/site-editor.spec.js index 9c9d8aec71da4..e72d83fa8b3aa 100644 --- a/test/performance/specs/site-editor.spec.js +++ b/test/performance/specs/site-editor.spec.js @@ -64,6 +64,7 @@ test.describe( 'Site Editor Performance', () => { test( 'Setup the test page', async ( { admin, perfUtils } ) => { await admin.createNewPost( { postType: 'page' } ); + await perfUtils.setRenderingMode( 'post-only' ); await perfUtils.loadBlocksForLargePost(); draftId = await perfUtils.saveDraft(); @@ -122,6 +123,7 @@ test.describe( 'Site Editor Performance', () => { test( 'Setup the test post', async ( { admin, editor, perfUtils } ) => { await admin.createNewPost( { postType: 'page' } ); + await perfUtils.setRenderingMode( 'post-only' ); await perfUtils.loadBlocksForLargePost(); await editor.insertBlock( { name: 'core/paragraph' } );