diff --git a/packages/block-library/src/navigation/edit/index.js b/packages/block-library/src/navigation/edit/index.js index f3fe01508818b2..02780e26502e85 100644 --- a/packages/block-library/src/navigation/edit/index.js +++ b/packages/block-library/src/navigation/edit/index.js @@ -40,7 +40,7 @@ import { } from '@wordpress/components'; import { __, sprintf } from '@wordpress/i18n'; import { speak } from '@wordpress/a11y'; -import { createBlock } from '@wordpress/blocks'; +import { createBlock, getBlockType } from '@wordpress/blocks'; import { close, Icon } from '@wordpress/icons'; /** @@ -209,6 +209,40 @@ function Navigation( { [ navigationMenus ] ); + // This useEffect adds snackbar and speak status notices when menus are created. + // If there are no fallback navigation menus then we don't show these messages, + // because this means that we are creating the first, fallback navigation menu. + useEffect( () => { + hideNavigationMenuStatusNotice(); + + if ( fallbackNavigationMenus && isCreatingNavigationMenu ) { + speak( __( `Creating Navigation Menu.` ) ); + } + + if ( createNavigationMenuIsSuccess ) { + handleUpdateMenu( createNavigationMenuPost.id, { + focusNavigationBlock: true, + } ); + + if ( fallbackNavigationMenus ) { + showNavigationMenuStatusNotice( + __( `Navigation Menu successfully created.` ) + ); + } + } + + if ( createNavigationMenuIsError ) { + showNavigationMenuStatusNotice( + __( 'Failed to create Navigation Menu.' ) + ); + } + }, [ + createNavigationMenuStatus, + createNavigationMenuError, + createNavigationMenuPost, + fallbackNavigationMenus, + ] ); + // Attempt to retrieve and prioritize any existing navigation menu unless: // - the are uncontrolled inner blocks already present in the block. // - the user is creating a new menu. @@ -244,10 +278,10 @@ function Navigation( { useEffect( () => { if ( + ref || ! hasResolvedNavigationMenus || isConvertingClassicMenu || - fallbackNavigationMenus?.length > 0 || - ! classicMenus?.length + fallbackNavigationMenus?.length > 0 ) { return; } @@ -256,25 +290,41 @@ function Navigation( { // a classic menu with a `primary` location or slug, // then create a new navigation menu based on it. // Otherwise, use the most recently created classic menu. - const primaryMenus = classicMenus.filter( - ( classicMenu ) => - classicMenu.locations.includes( 'primary' ) || - classicMenu.slug === 'primary' - ); - - if ( primaryMenus.length ) { - convertClassicMenu( - primaryMenus[ 0 ].id, - primaryMenus[ 0 ].name, - 'publish' + if ( classicMenus?.length ) { + const primaryMenus = classicMenus.filter( + ( classicMenu ) => + classicMenu.locations.includes( 'primary' ) || + classicMenu.slug === 'primary' ); + + if ( primaryMenus.length ) { + convertClassicMenu( + primaryMenus[ 0 ].id, + primaryMenus[ 0 ].name, + 'publish' + ); + } else { + classicMenus.sort( ( a, b ) => { + return b.id - a.id; + } ); + convertClassicMenu( + classicMenus[ 0 ].id, + classicMenus[ 0 ].name, + 'publish' + ); + } } else { - classicMenus.sort( ( a, b ) => { - return b.id - a.id; - } ); - convertClassicMenu( - classicMenus[ 0 ].id, - classicMenus[ 0 ].name, + // If there are no fallback navigation menus and no classic menus, + // then create a new navigation menu. + + // Check that we have a page-list block type. + let defaultBlocks = []; + if ( getBlockType( 'core/page-list' ) ) { + defaultBlocks = [ createBlock( 'core/page-list' ) ]; + } + createNavigationMenu( + 'Navigation', // TODO - use the template slug in future + defaultBlocks, 'publish' ); } @@ -298,19 +348,6 @@ function Navigation( { classicMenus?.length === 0 && ! hasUncontrolledInnerBlocks; - useEffect( () => { - if ( isPlaceholder ) { - /** - * this fallback only displays (both in editor and on front) - * the list of pages block if no menu is available as a fallback. - * We don't want the fallback to request a save, - * nor to be undoable, hence we mark it non persistent. - */ - __unstableMarkNextChangeAsNotPersistent(); - replaceInnerBlocks( clientId, [ createBlock( 'core/page-list' ) ] ); - } - }, [ clientId, isPlaceholder, ref ] ); - const isEntityAvailable = ! isNavigationMenuMissing && isNavigationMenuResolved; diff --git a/packages/block-library/src/navigation/index.php b/packages/block-library/src/navigation/index.php index 03aaae31c5bc91..2a2812179e3fbf 100644 --- a/packages/block-library/src/navigation/index.php +++ b/packages/block-library/src/navigation/index.php @@ -431,6 +431,37 @@ function block_core_navigation_block_contains_core_navigation( $inner_blocks ) { return false; } +/** + * Create and returns a navigation menu containing a page-list as a fallback. + * + * @return array the newly created navigation menu. + */ +function block_core_navigation_get_default_pages_fallback() { + $registry = WP_Block_Type_Registry::get_instance(); + + // If `core/page-list` is not registered then use empty blocks. + $default_blocks = $registry->is_registered( 'core/page-list' ) ? '' : ''; + + // Create a new navigation menu from the fallback blocks. + $wp_insert_post_result = wp_insert_post( + array( + 'post_content' => $default_blocks, + 'post_title' => 'Navigation', // TODO - use the template slug in future. + 'post_name' => 'Navigation', // TODO - use the template slug in future. + 'post_status' => 'publish', + 'post_type' => 'wp_navigation', + ), + true // So that we can check whether the result is an error. + ); + + if ( is_wp_error( $wp_insert_post_result ) ) { + return; + } + + // Fetch the most recently published navigation which will be the default one created above. + return block_core_navigation_get_most_recently_published_navigation(); +} + /** * Retrieves the appropriate fallback to be used on the front of the * site when there is no menu assigned to the Nav block. @@ -441,19 +472,7 @@ function block_core_navigation_block_contains_core_navigation( $inner_blocks ) { * @return array the array of blocks to be used as a fallback. */ function block_core_navigation_get_fallback_blocks() { - $page_list_fallback = array( - array( - 'blockName' => 'core/page-list', - ), - ); - - $registry = WP_Block_Type_Registry::get_instance(); - - // If `core/page-list` is not registered then return empty blocks. - $fallback_blocks = $registry->is_registered( 'core/page-list' ) ? $page_list_fallback : array(); - - // Default to a list of Pages. - + // Get the most recently published Navigation post. $navigation_post = block_core_navigation_get_most_recently_published_navigation(); // If there are no navigation posts then try to find a classic menu @@ -462,16 +481,21 @@ function block_core_navigation_get_fallback_blocks() { $navigation_post = block_core_navigation_maybe_use_classic_menu_fallback(); } - // Use the first non-empty Navigation as fallback if available. + // If there are no navigation posts then default to a list of Pages. + if ( ! $navigation_post ) { + $navigation_post = block_core_navigation_get_default_pages_fallback(); + } + + // Use the first non-empty Navigation as fallback, there should always be one. if ( $navigation_post ) { $parsed_blocks = parse_blocks( $navigation_post->post_content ); $maybe_fallback = block_core_navigation_filter_out_empty_blocks( $parsed_blocks ); - - // Normalizing blocks may result in an empty array of blocks if they were all `null` blocks. - // In this case default to the (Page List) fallback. - $fallback_blocks = ! empty( $maybe_fallback ) ? $maybe_fallback : $fallback_blocks; } + // Normalizing blocks may result in an empty array of blocks if they were all `null` blocks. + // In this case default empty blocks. + $fallback_blocks = ! empty( $maybe_fallback ) ? $maybe_fallback : array(); + /** * Filters the fallback experience for the Navigation block. * @@ -531,7 +555,6 @@ function block_core_navigation_from_block_get_post_ids( $block ) { * @return string Returns the post content with the legacy widget added. */ function render_block_core_navigation( $attributes, $content, $block ) { - static $seen_menu_names = array(); // Flag used to indicate whether the rendered output is considered to be diff --git a/packages/e2e-test-utils-playwright/src/request-utils/index.ts b/packages/e2e-test-utils-playwright/src/request-utils/index.ts index 1d701822c55f65..53ff07886173ee 100644 --- a/packages/e2e-test-utils-playwright/src/request-utils/index.ts +++ b/packages/e2e-test-utils-playwright/src/request-utils/index.ts @@ -25,6 +25,7 @@ import { createClassicMenu, createNavigationMenu, deleteAllMenus, + getNavigationMenus, } from './menus'; import { deleteAllPages, createPage } from './pages'; import { resetPreferences } from './preferences'; @@ -134,6 +135,7 @@ class RequestUtils { createClassicMenu = createClassicMenu.bind( this ); createNavigationMenu = createNavigationMenu.bind( this ); deleteAllMenus = deleteAllMenus.bind( this ); + getNavigationMenus = getNavigationMenus.bind( this ); createComment = createComment.bind( this ); deleteAllComments = deleteAllComments.bind( this ); deleteAllWidgets = deleteAllWidgets.bind( this ); diff --git a/packages/e2e-test-utils-playwright/src/request-utils/menus.ts b/packages/e2e-test-utils-playwright/src/request-utils/menus.ts index 4058662a4d9ee7..b2493e824eb861 100644 --- a/packages/e2e-test-utils-playwright/src/request-utils/menus.ts +++ b/packages/e2e-test-utils-playwright/src/request-utils/menus.ts @@ -104,3 +104,19 @@ export async function deleteAllMenus( this: RequestUtils ) { ); } } + +/** + * Get latest navigation menus + * + * @return {string} Menu content. + */ +export async function getNavigationMenus( this: RequestUtils ) { + const navigationMenus = await this.rest< NavigationMenu[] >( { + method: 'GET', + path: `/wp/v2/navigation/`, + data: { + status: 'publish', + }, + } ); + return navigationMenus; +} diff --git a/test/e2e/specs/editor/blocks/navigation.spec.js b/test/e2e/specs/editor/blocks/navigation.spec.js index b6e0b832eab5a4..3ebf62c45ac827 100644 --- a/test/e2e/specs/editor/blocks/navigation.spec.js +++ b/test/e2e/specs/editor/blocks/navigation.spec.js @@ -29,6 +29,7 @@ test.describe( test( 'default to a list of pages if there are no menus', async ( { admin, editor, + requestUtils, } ) => { await admin.createNewPost(); await editor.insertBlock( { name: 'core/navigation' } ); @@ -47,12 +48,14 @@ test.describe( // Check the markup of the block is correct. await editor.publishPost(); - const content = await editor.getEditedPostContent(); - expect( content ).toBe( - ` - -` - ); + const navigationMenus = await requestUtils.getNavigationMenus(); + const latestNavigationMenu = navigationMenus[ 0 ]; + await expect.poll( editor.getBlocks ).toMatchObject( [ + { + name: 'core/navigation', + attributes: { ref: latestNavigationMenu.id }, + }, + ] ); } ); test( 'default to my only existing menu', async ( {