From b3ade86080e97eb6a36574c7f635c99a6585e354 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Fri, 12 Jun 2020 13:48:16 +0800 Subject: [PATCH 1/4] Extract and refactor placeholder from navigation block edit function --- packages/block-library/src/navigation/edit.js | 391 +----------------- .../src/navigation/placeholder.js | 334 +++++++++++++++ 2 files changed, 349 insertions(+), 376 deletions(-) create mode 100644 packages/block-library/src/navigation/placeholder.js diff --git a/packages/block-library/src/navigation/edit.js b/packages/block-library/src/navigation/edit.js index 9019aec823f98..c52ba5b9581cf 100644 --- a/packages/block-library/src/navigation/edit.js +++ b/packages/block-library/src/navigation/edit.js @@ -1,13 +1,13 @@ /** * External dependencies */ -import { escape, upperFirst } from 'lodash'; +import { upperFirst } from 'lodash'; import classnames from 'classnames'; /** * WordPress dependencies */ -import { useMemo, useState, useRef, useCallback } from '@wordpress/element'; +import { useRef } from '@wordpress/element'; import { InnerBlocks, InspectorControls, @@ -17,8 +17,6 @@ import { __experimentalUseColors, __experimentalBlock as Block, } from '@wordpress/block-editor'; - -import { createBlock } from '@wordpress/blocks'; import { useSelect, useDispatch, @@ -26,18 +24,13 @@ import { withDispatch, } from '@wordpress/data'; import { - Button, PanelBody, - Placeholder, - Spinner, ToggleControl, Toolbar, ToolbarGroup, - CustomSelectControl, } from '@wordpress/components'; import { compose } from '@wordpress/compose'; import { __ } from '@wordpress/i18n'; -import { navigation as icon } from '@wordpress/icons'; /** * Internal dependencies @@ -45,20 +38,7 @@ import { navigation as icon } from '@wordpress/icons'; import useBlockNavigator from './use-block-navigator'; import BlockColorsStyleSelector from './block-colors-selector'; import * as navIcons from './icons'; -import createDataTree from './create-data-tree'; - -// Constants -const CREATE_EMPTY_OPTION_VALUE = '__CREATE_EMPTY__'; -const CREATE_FROM_PAGES_OPTION_VALUE = '__CREATE_FROM_PAGES__'; -const CREATE_PLACEHOLDER_VALUE = '__CREATE_PLACEHOLDER__'; - -function LoadingSpinner() { - return ( - <> - { __( 'Loading…' ) } - - ); -} +import NavigationPlaceholder from './placeholder'; function Navigation( { selectedBlockHasDescendants, @@ -66,29 +46,18 @@ function Navigation( { clientId, fontSize, hasExistingNavItems, - hasResolvedPages, isImmediateParentOfSelectedBlock, - isRequestingPages, - getHasResolvedMenuItems, - hasResolvedMenus, - isRequestingMenus, isSelected, - pages, - menus, - getMenuItems, setAttributes, setFontSize, - updateNavItemBlocks, + updateInnerBlocks, className, } ) { // // HOOKS // const ref = useRef(); - const [ - selectedCreateActionOption, - setSelectedCreateActionOption, - ] = useState( null ); + const { selectBlock } = useDispatch( 'core/block-editor' ); const { TextColor, BackgroundColor, ColorPanel } = __experimentalUseColors( [ @@ -120,69 +89,6 @@ function Navigation( { clientId ); - const isRequestingEntities = isRequestingPages || isRequestingMenus; - const selectedCreateActionOptionKey = selectedCreateActionOption?.key; - - // Builds navigation links from default Pages. - const buildNavLinkBlocksFromPages = useMemo( () => { - if ( ! pages ) { - return null; - } - - return pages.map( ( { title, type, link: url, id } ) => - createBlock( 'core/navigation-link', { - type, - id, - url, - label: ! title.rendered - ? __( '(no title)' ) - : escape( title.rendered ), - opensInNewTab: false, - } ) - ); - }, [ pages ] ); - - const menuItems = getMenuItems( selectedCreateActionOptionKey ); - - // Builds navigation links from selected Menu's items. - const buildNavLinkBlocksFromMenuItems = useMemo( () => { - if ( ! menuItems ) { - return null; - } - - function initialiseBlocks( nodes ) { - return nodes.map( ( { title, type, link: url, id, children } ) => { - const innerBlocks = - children && children.length - ? initialiseBlocks( children ) - : []; - - return createBlock( - 'core/navigation-link', - { - type, - id, - url, - label: ! title.rendered - ? __( '(no title)' ) - : escape( title.rendered ), - opensInNewTab: false, - }, - innerBlocks - ); - } ); - } - - const menuTree = createDataTree( menuItems ); - - const menuBlocksTree = initialiseBlocks( menuTree ); - - return menuBlocksTree; - }, [ menuItems ] ); - - const hasPages = !! ( hasResolvedPages && pages?.length ); - const hasMenus = !! ( hasResolvedMenus && menus?.length ); - // // HANDLERS // @@ -196,215 +102,19 @@ function Navigation( { }; } - function handleCreateEmpty() { - const emptyNavLinkBlock = createBlock( 'core/navigation-link' ); - updateNavItemBlocks( [ emptyNavLinkBlock ] ); - } - - function handleCreateFromExistingPages() { - updateNavItemBlocks( buildNavLinkBlocksFromPages ); - selectBlock( clientId ); - } - - function handleCreateFromExistingMenu() { - updateNavItemBlocks( buildNavLinkBlocksFromMenuItems ); - selectBlock( clientId ); - } - - function handleCreate() { - const { key } = selectedCreateActionOption; - - // Explicity request to create empty. - if ( key === CREATE_EMPTY_OPTION_VALUE ) { - return handleCreateEmpty(); - } - - // Create from Pages. - if ( hasPages && key === CREATE_FROM_PAGES_OPTION_VALUE ) { - return handleCreateFromExistingPages(); - } - - // Create from WP Menu (if exists and not empty). - if ( - hasMenus && - selectedCreateActionOption && - buildNavLinkBlocksFromMenuItems?.length - ) { - return handleCreateFromExistingMenu(); - } - - // Default to empty menu - return handleCreateEmpty(); - } - - const buildPlaceholderInstructionText = useCallback( () => { - if ( isRequestingEntities ) { - return ''; - } - - if ( hasMenus && hasPages ) { - return __( - 'Create a navigation from all existing pages, or choose a menu.' - ); - } - - if ( ! hasMenus && ! hasPages ) { - return __( 'Create an empty navigation.' ); - } - - if ( hasMenus && ! hasPages ) { - return __( 'Create a navigation from a menu or create empty.' ); - } - - if ( ! hasMenus && hasPages ) { - return __( - 'Create a navigation from all existing pages, or create empty.' - ); - } - }, [ isRequestingEntities, hasMenus, hasPages ] ); - - const createActionOptions = useMemo( - () => [ - { - id: CREATE_PLACEHOLDER_VALUE, - name: __( 'Select where to start from…' ), - }, - ...( hasMenus ? menus : [] ), - { - id: CREATE_EMPTY_OPTION_VALUE, - name: __( 'Create empty menu' ), - className: 'is-create-empty-option', - }, - ...( hasPages - ? [ - { - id: CREATE_FROM_PAGES_OPTION_VALUE, - name: __( 'New from all top-level pages' ), - }, - ] - : [] ), - ], - [ - CREATE_PLACEHOLDER_VALUE, - CREATE_EMPTY_OPTION_VALUE, - CREATE_FROM_PAGES_OPTION_VALUE, - hasMenus, - menus, - hasPages, - ] - ); - - const shouldDisableCreateButton = useCallback( () => { - // If there is no key at all then disable. - if ( ! selectedCreateActionOptionKey ) { - return true; - } - - // Always disable if the default "placeholder" option is selected. - if ( selectedCreateActionOptionKey === CREATE_PLACEHOLDER_VALUE ) { - return true; - } - - // Always enable if Create Empty is selected. - if ( selectedCreateActionOptionKey === CREATE_EMPTY_OPTION_VALUE ) { - return false; - } - - // Enable if Pages option selected and we have Pages available. - if ( - selectedCreateActionOptionKey === CREATE_FROM_PAGES_OPTION_VALUE && - hasResolvedPages - ) { - return false; - } - - // Only "menu" options use an integer based key. - const selectedOptionIsMenu = Number.isInteger( - selectedCreateActionOptionKey - ); - - const menuItemsResolved = - selectedOptionIsMenu && - getHasResolvedMenuItems( selectedCreateActionOptionKey ); - - return ! menuItemsResolved; - }, [ - selectedCreateActionOptionKey, - hasResolvedPages, - CREATE_PLACEHOLDER_VALUE, - CREATE_EMPTY_OPTION_VALUE, - CREATE_FROM_PAGES_OPTION_VALUE, - ] ); - // If we don't have existing items then show the Placeholder if ( ! hasExistingNavItems ) { return ( - - { isRequestingEntities ? ( -
- -
- ) : ( -
- <> - { - if ( - selectedItem?.key === - selectedCreateActionOptionKey - ) { - return; - } - setSelectedCreateActionOption( - selectedItem - ); - } } - options={ createActionOptions.map( - ( option ) => { - return { - ...option, - key: option.id, - }; - } - ) } - /> - - -
- ) } -
+ { + updateInnerBlocks( blocks ); + if ( selectNavigationBlock ) { + selectBlock( clientId ); + } + } } + />
); } @@ -494,9 +204,6 @@ function Navigation( { className={ blockClassNames } style={ blockInlineStyles } > - { ! hasExistingNavItems && isRequestingEntities && ( - - ) } { - if ( ! menuId ) { - return false; - } - - // If the option is a placeholder or doesn't have a valid - // id then reject - if ( ! Number.isInteger( menuId ) ) { - return false; - } - - return select( 'core' ).getMenuItems( { - menus: menuId, - per_page: -1, - } ); - }, - getIsRequestingMenuItems: ( menuId ) => { - return select( 'core' ).isResolving( 'getMenuItems', [ - { - menus: menuId, - per_page: -1, - }, - ] ); - }, - getHasResolvedMenuItems: ( menuId ) => { - return select( 'core' ).hasFinishedResolution( 'getMenuItems', [ - { - menus: menuId, - per_page: -1, - }, - ] ); - }, - - isRequestingPages: select( 'core/data' ).isResolving( - ...pagesSelect - ), - - hasResolvedPages: select( 'core/data' ).hasFinishedResolution( - ...pagesSelect - ), }; } ), withDispatch( ( dispatch, { clientId } ) => { return { - updateNavItemBlocks( blocks ) { + updateInnerBlocks( blocks ) { if ( blocks?.length === 0 ) { return false; } diff --git a/packages/block-library/src/navigation/placeholder.js b/packages/block-library/src/navigation/placeholder.js new file mode 100644 index 0000000000000..eacf04f09fb0b --- /dev/null +++ b/packages/block-library/src/navigation/placeholder.js @@ -0,0 +1,334 @@ +/** + * External dependencies + */ + +import { escape } from 'lodash'; +import classnames from 'classnames'; + +/** + * WordPress dependencies + */ +import { createBlock } from '@wordpress/blocks'; +import { + Button, + CustomSelectControl, + Spinner, + Placeholder, +} from '@wordpress/components'; +import { useSelect } from '@wordpress/data'; +import { forwardRef, useCallback, useMemo, useState } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { navigation as icon } from '@wordpress/icons'; + +/** + * Internal dependencies + */ +import createDataTree from './create-data-tree'; + +const CREATE_EMPTY_OPTION_VALUE = '__CREATE_EMPTY__'; +const CREATE_FROM_PAGES_OPTION_VALUE = '__CREATE_FROM_PAGES__'; +const CREATE_PLACEHOLDER_VALUE = '__CREATE_PLACEHOLDER__'; + +function getPlaceholderInstructions( hasMenus, hasPages ) { + if ( hasMenus && hasPages ) { + return __( + 'Create a navigation from all existing pages, or choose a menu.' + ); + } else if ( hasMenus && ! hasPages ) { + return __( 'Create a navigation from a menu or create empty.' ); + } else if ( ! hasMenus && hasPages ) { + return __( + 'Create a navigation from all existing pages, or create empty.' + ); + } + + return __( 'Create an empty navigation.' ); +} + +function getSelectedMenu( selectedCreateOption ) { + const optionKey = selectedCreateOption?.key; + return optionKey !== undefined && Number.isInteger( optionKey ) + ? optionKey + : undefined; +} + +function initialiseBlocks( nodes ) { + return nodes.map( ( { title, type, link: url, id, children } ) => { + const innerBlocks = + children && children.length ? initialiseBlocks( children ) : []; + + return createBlock( + 'core/navigation-link', + { + type, + id, + url, + label: ! title.rendered + ? __( '(no title)' ) + : escape( title.rendered ), + opensInNewTab: false, + }, + innerBlocks + ); + } ); +} + +// Builds navigation links from selected Menu's items. +function buildNavLinkBlocksFromMenuItems( menuItems ) { + if ( ! menuItems ) { + return null; + } + + const menuTree = createDataTree( menuItems ); + return initialiseBlocks( menuTree ); +} + +// Builds navigation links from default Pages. +function buildNavLinkBlocksFromPages( pages ) { + if ( ! pages ) { + return null; + } + + return pages.map( ( { title, type, link: url, id } ) => + createBlock( 'core/navigation-link', { + type, + id, + url, + label: ! title.rendered + ? __( '(no title)' ) + : escape( title.rendered ), + opensInNewTab: false, + } ) + ); +} + +function shouldDisableCreateButton( + selectedCreateOption, + hasResolvedPages, + hasResolvedMenuItems +) { + // If there is no key at all then disable. + if ( ! selectedCreateOption ) { + return true; + } + + const optionKey = selectedCreateOption?.key; + + // Always disable if the default "placeholder" option is selected. + if ( optionKey === CREATE_PLACEHOLDER_VALUE ) { + return true; + } + + // Always enable if Create Empty is selected. + if ( optionKey === CREATE_EMPTY_OPTION_VALUE ) { + return false; + } + + // Enable if Pages option selected and we have Pages available. + if ( optionKey === CREATE_FROM_PAGES_OPTION_VALUE && hasResolvedPages ) { + return false; + } + + // Enable if a menu is selected and menu items have loaded. + const selectedMenu = getSelectedMenu( selectedCreateOption ); + return selectedMenu === undefined || ! hasResolvedMenuItems; +} + +function NavigationPlaceholder( { onCreate }, ref ) { + const [ selectedCreateOption, setSelectedCreateOption ] = useState(); + + const { + pages, + isResolvingPages, + hasResolvedPages, + menus, + isResolvingMenus, + hasResolvedMenus, + menuItems, + hasResolvedMenuItems, + } = useSelect( + ( select ) => { + const { + getEntityRecords, + getMenus, + getMenuItems, + isResolving, + hasFinishedResolution, + } = select( 'core' ); + const pagesParameters = [ + 'postType', + 'page', + { + parent: 0, + order: 'asc', + orderby: 'id', + }, + ]; + const menusParameters = [ { per_page: -1 } ]; + const selectedMenu = getSelectedMenu( selectedCreateOption ); + const hasSelectedMenu = selectedMenu !== undefined; + const menuItemsParameters = hasSelectedMenu + ? [ + { + menus: selectedMenu, + per_page: -1, + }, + ] + : undefined; + + return { + pages: getEntityRecords( ...pagesParameters ), + isResolvingPages: isResolving( + 'getEntityRecords', + pagesParameters + ), + hasResolvedPages: hasFinishedResolution( + 'getEntityRecords', + pagesParameters + ), + menus: getMenus( ...menusParameters ), + isResolvingMenus: isResolving( 'getMenus', menusParameters ), + hasResolvedMenus: hasFinishedResolution( + 'getMenus', + menusParameters + ), + menuItems: hasSelectedMenu + ? getMenuItems( ...menuItemsParameters ) + : undefined, + hasResolvedMenuItems: hasSelectedMenu + ? hasFinishedResolution( + 'getMenuItems', + menuItemsParameters + ) + : false, + }; + }, + [ selectedCreateOption ] + ); + + const hasPages = !! ( hasResolvedPages && pages?.length ); + const hasMenus = !! ( hasResolvedMenus && menus?.length ); + const isLoading = isResolvingPages || isResolvingMenus; + + const createOptions = useMemo( + () => [ + { + id: CREATE_PLACEHOLDER_VALUE, + name: __( 'Select where to start from…' ), + }, + ...( hasMenus ? menus : [] ), + { + id: CREATE_EMPTY_OPTION_VALUE, + name: __( 'Create empty menu' ), + className: 'is-create-empty-option', + }, + ...( hasPages + ? [ + { + id: CREATE_FROM_PAGES_OPTION_VALUE, + name: __( 'New from all top-level pages' ), + }, + ] + : [] ), + ], + [ menus, hasMenus, hasPages ] + ); + + const onCreateButtonClick = useCallback( () => { + if ( ! selectedCreateOption ) { + return; + } + + const { key } = selectedCreateOption; + + if ( key === CREATE_EMPTY_OPTION_VALUE ) { + const blocks = [ createBlock( 'core/navigation-link' ) ]; + onCreate( blocks ); + } + + if ( hasPages && key === CREATE_FROM_PAGES_OPTION_VALUE ) { + const blocks = buildNavLinkBlocksFromPages( pages ); + const selectNavigationBlock = true; + onCreate( blocks, selectNavigationBlock ); + return; + } + + // Infer that the user selected a menu to create from. + // If either there's no selected menu or menu items are undefined + // this is undefined behavior, do nothing. + const selectedMenu = getSelectedMenu( selectedCreateOption ); + if ( selectedMenu === undefined || menuItems === undefined ) { + return; + } + + const blocks = buildNavLinkBlocksFromMenuItems( menuItems ); + const selectNavigationBlock = true; + onCreate( blocks, selectNavigationBlock ); + } ); + + return ( + + { isLoading && ( +
+ { __( 'Loading…' ) } +
+ ) } + { ! isLoading && ( +
+ <> + { + if ( + selectedItem?.key === selectedCreateOption + ) { + return; + } + setSelectedCreateOption( selectedItem ); + } } + options={ createOptions.map( ( option ) => { + return { + ...option, + key: option.id, + }; + } ) } + /> + + +
+ ) } +
+ ); +} + +export default forwardRef( NavigationPlaceholder ); From 4bf7ce82fc71268a0b09f408a86239aa9474d7b3 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Fri, 12 Jun 2020 15:22:46 +0800 Subject: [PATCH 2/4] Rename function --- packages/block-library/src/navigation/placeholder.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/block-library/src/navigation/placeholder.js b/packages/block-library/src/navigation/placeholder.js index eacf04f09fb0b..772f712a69b28 100644 --- a/packages/block-library/src/navigation/placeholder.js +++ b/packages/block-library/src/navigation/placeholder.js @@ -102,7 +102,7 @@ function buildNavLinkBlocksFromPages( pages ) { ); } -function shouldDisableCreateButton( +function getIsCreateButtonDisabled( selectedCreateOption, hasResolvedPages, hasResolvedMenuItems @@ -316,7 +316,7 @@ function NavigationPlaceholder( { onCreate }, ref ) { isSecondary className="wp-block-navigation-placeholder__button" onClick={ onCreateButtonClick } - disabled={ shouldDisableCreateButton( + disabled={ getIsCreateButtonDisabled( selectedCreateOption, hasResolvedPages, hasResolvedMenuItems From 7783dadedbffba13c2580e103719455a5b04f049 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Fri, 12 Jun 2020 15:35:29 +0800 Subject: [PATCH 3/4] Add JSDocs --- .../src/navigation/placeholder.js | 63 ++++++++++++++++--- 1 file changed, 54 insertions(+), 9 deletions(-) diff --git a/packages/block-library/src/navigation/placeholder.js b/packages/block-library/src/navigation/placeholder.js index 772f712a69b28..5ad51b07c4354 100644 --- a/packages/block-library/src/navigation/placeholder.js +++ b/packages/block-library/src/navigation/placeholder.js @@ -29,6 +29,14 @@ const CREATE_EMPTY_OPTION_VALUE = '__CREATE_EMPTY__'; const CREATE_FROM_PAGES_OPTION_VALUE = '__CREATE_FROM_PAGES__'; const CREATE_PLACEHOLDER_VALUE = '__CREATE_PLACEHOLDER__'; +/** + * Get instruction text for the Placeholder component. + * + * @param {boolean} hasMenus Flag that indicates if there are menus. + * @param {boolean} hasPages Flag that indicates if there are pages. + * + * @return {string} Text to display as the placeholder instructions. + */ function getPlaceholderInstructions( hasMenus, hasPages ) { if ( hasMenus && hasPages ) { return __( @@ -45,6 +53,14 @@ function getPlaceholderInstructions( hasMenus, hasPages ) { return __( 'Create an empty navigation.' ); } +/** + * Return the menu id if the user has one selected. + * + * @param {Object} selectedCreateOption An object containing details of + * the selected create option. + * + * @return {number|undefined} The menu id. + */ function getSelectedMenu( selectedCreateOption ) { const optionKey = selectedCreateOption?.key; return optionKey !== undefined && Number.isInteger( optionKey ) @@ -52,10 +68,17 @@ function getSelectedMenu( selectedCreateOption ) { : undefined; } -function initialiseBlocks( nodes ) { +/** + * A recursive function that maps menu item nodes to blocks. + * + * @param {Object[]} nodes An array of menu items. + * + * @return {WPBlock[]} An array of blocks. + */ +function mapMenuItemsToBlocks( nodes ) { return nodes.map( ( { title, type, link: url, id, children } ) => { const innerBlocks = - children && children.length ? initialiseBlocks( children ) : []; + children && children.length ? mapMenuItemsToBlocks( children ) : []; return createBlock( 'core/navigation-link', @@ -73,18 +96,30 @@ function initialiseBlocks( nodes ) { } ); } -// Builds navigation links from selected Menu's items. -function buildNavLinkBlocksFromMenuItems( menuItems ) { +/** + * Convert a flat menu item structure to a nested blocks structure. + * + * @param {Object[]} menuItems An array of menu items. + * + * @return {WPBlock[]} An array of blocks. + */ +function convertMenuItemsToBlocks( menuItems ) { if ( ! menuItems ) { return null; } const menuTree = createDataTree( menuItems ); - return initialiseBlocks( menuTree ); + return mapMenuItemsToBlocks( menuTree ); } -// Builds navigation links from default Pages. -function buildNavLinkBlocksFromPages( pages ) { +/** + * Convert pages to blocks. + * + * @param {Object[]} pages An array of pages. + * + * @return {WPBlock[]} An array of blocks. + */ +function convertPagesToBlocks( pages ) { if ( ! pages ) { return null; } @@ -102,6 +137,16 @@ function buildNavLinkBlocksFromPages( pages ) { ); } +/** + * Returns a value that indicates whether the create button should be disabled. + * + * @param {Object} selectedCreateOption An object containing details of + * the selected create option. + * @param {boolean} hasResolvedPages Indicates whether pages have loaded. + * @param {boolean} hasResolvedMenuItems Indicates whether menu items have loaded. + * + * @return {boolean} A value that indicates whether the create button is disabled. + */ function getIsCreateButtonDisabled( selectedCreateOption, hasResolvedPages, @@ -247,7 +292,7 @@ function NavigationPlaceholder( { onCreate }, ref ) { } if ( hasPages && key === CREATE_FROM_PAGES_OPTION_VALUE ) { - const blocks = buildNavLinkBlocksFromPages( pages ); + const blocks = convertPagesToBlocks( pages ); const selectNavigationBlock = true; onCreate( blocks, selectNavigationBlock ); return; @@ -261,7 +306,7 @@ function NavigationPlaceholder( { onCreate }, ref ) { return; } - const blocks = buildNavLinkBlocksFromMenuItems( menuItems ); + const blocks = convertMenuItemsToBlocks( menuItems ); const selectNavigationBlock = true; onCreate( blocks, selectNavigationBlock ); } ); From 26fa6642bbc2a2623934297358ba86f1b3b26b5c Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Fri, 12 Jun 2020 17:34:37 +0800 Subject: [PATCH 4/4] Create navigation block with at least one child block when selected menu is empty --- packages/block-library/src/navigation/placeholder.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/block-library/src/navigation/placeholder.js b/packages/block-library/src/navigation/placeholder.js index 5ad51b07c4354..5dbc53f7e04bf 100644 --- a/packages/block-library/src/navigation/placeholder.js +++ b/packages/block-library/src/navigation/placeholder.js @@ -286,18 +286,18 @@ function NavigationPlaceholder( { onCreate }, ref ) { const { key } = selectedCreateOption; - if ( key === CREATE_EMPTY_OPTION_VALUE ) { - const blocks = [ createBlock( 'core/navigation-link' ) ]; - onCreate( blocks ); - } - - if ( hasPages && key === CREATE_FROM_PAGES_OPTION_VALUE ) { + if ( key === CREATE_FROM_PAGES_OPTION_VALUE && hasPages ) { const blocks = convertPagesToBlocks( pages ); const selectNavigationBlock = true; onCreate( blocks, selectNavigationBlock ); return; } + if ( key === CREATE_EMPTY_OPTION_VALUE || ! menuItems?.length ) { + const blocks = [ createBlock( 'core/navigation-link' ) ]; + onCreate( blocks ); + } + // Infer that the user selected a menu to create from. // If either there's no selected menu or menu items are undefined // this is undefined behavior, do nothing.