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

Navigation: Always create a fallback menu #47684

Merged
merged 10 commits into from
Feb 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 70 additions & 33 deletions packages/block-library/src/navigation/edit/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';

/**
Expand Down Expand Up @@ -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.
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we / Should we - also hide the snackbar about the importing of the default classic menu? If I have a classic menu and it is used as the basis for the default block navigation I get a snackbar saying classic menu imported ..

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I thought about that, but I'm not sure. It's a different case and I thought it might be useful to know what's happened in that case.

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.
Expand Down Expand Up @@ -244,10 +278,10 @@ function Navigation( {

useEffect( () => {
if (
ref ||
! hasResolvedNavigationMenus ||
isConvertingClassicMenu ||
fallbackNavigationMenus?.length > 0 ||
! classicMenus?.length
fallbackNavigationMenus?.length > 0
) {
return;
}
Expand All @@ -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'
);
}
Expand All @@ -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;

Expand Down
61 changes: 42 additions & 19 deletions packages/block-library/src/navigation/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -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' ) ? '<!-- wp:page-list /-->' : '';

draganescu marked this conversation as resolved.
Show resolved Hide resolved
// 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.
Expand All @@ -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
Expand All @@ -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.
*
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
createClassicMenu,
createNavigationMenu,
deleteAllMenus,
getNavigationMenus,
} from './menus';
import { deleteAllPages, createPage } from './pages';
import { resetPreferences } from './preferences';
Expand Down Expand Up @@ -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 );
Expand Down
16 changes: 16 additions & 0 deletions packages/e2e-test-utils-playwright/src/request-utils/menus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
15 changes: 9 additions & 6 deletions test/e2e/specs/editor/blocks/navigation.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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' } );
Expand All @@ -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(
`<!-- wp:navigation -->
<!-- wp:page-list /-->
<!-- /wp:navigation -->`
);
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 ( {
Expand Down