-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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 block: Create classic menus empty #43190
Changes from all commits
ee08868
c2b2ce2
95967f6
6d36b44
4c76a0d
a30497c
bb6ee93
100566d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,13 +6,7 @@ import classnames from 'classnames'; | |
/** | ||
* WordPress dependencies | ||
*/ | ||
import { | ||
useState, | ||
useEffect, | ||
useRef, | ||
useCallback, | ||
Platform, | ||
} from '@wordpress/element'; | ||
import { useState, useEffect, useRef, Platform } from '@wordpress/element'; | ||
import { | ||
InspectorControls, | ||
BlockControls, | ||
|
@@ -29,7 +23,7 @@ import { | |
} from '@wordpress/block-editor'; | ||
import { EntityProvider } from '@wordpress/core-data'; | ||
|
||
import { useDispatch, useRegistry } from '@wordpress/data'; | ||
import { useDispatch } from '@wordpress/data'; | ||
import { | ||
PanelBody, | ||
ToggleControl, | ||
|
@@ -41,6 +35,7 @@ import { | |
} from '@wordpress/components'; | ||
import { __, sprintf } from '@wordpress/i18n'; | ||
import { speak } from '@wordpress/a11y'; | ||
import { createBlock } from '@wordpress/blocks'; | ||
|
||
/** | ||
* Internal dependencies | ||
|
@@ -100,7 +95,6 @@ function Navigation( { | |
|
||
const ref = attributes.ref; | ||
|
||
const registry = useRegistry(); | ||
const setRef = ( postId ) => { | ||
setAttributes( { ref: postId } ); | ||
}; | ||
|
@@ -210,19 +204,37 @@ function Navigation( { | |
const navMenuResolvedButMissing = | ||
hasResolvedNavigationMenus && isNavigationMenuMissing; | ||
|
||
// Attempt to retrieve and prioritize any existing navigation menu unless | ||
// a specific ref is allocated or the user is explicitly creating a new menu. The aim is | ||
// for the block to "just work" from a user perspective using existing data. | ||
// 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. | ||
// - there are no menus to choose from. | ||
// This attempts to pick the first menu if there is a single Navigation Post. If more | ||
// than 1 exists then use the most recent. | ||
// The aim is for the block to "just work" from a user perspective using existing data. | ||
useEffect( () => { | ||
if ( | ||
hasUncontrolledInnerBlocks || | ||
isCreatingNavigationMenu || | ||
ref || | ||
! navigationMenus?.length || | ||
navigationMenus?.length > 1 | ||
! navigationMenus?.length | ||
) { | ||
return; | ||
} | ||
|
||
navigationMenus.sort( ( menuA, menuB ) => { | ||
const menuADate = new Date( menuA.date ); | ||
const menuBDate = new Date( menuB.date ); | ||
return menuADate.getTime() < menuBDate.getTime(); | ||
} ); | ||
|
||
/** | ||
* This fallback displays (both in editor and on front) | ||
* a list of pages only if no menu (user assigned or | ||
* automatically picked) is available. | ||
* The fallback should not request a save (entity dirty state) | ||
* nor to be undoable, hence why it is marked as non persistent | ||
*/ | ||
__unstableMarkNextChangeAsNotPersistent(); | ||
setRef( navigationMenus[ 0 ].id ); | ||
}, [ navigationMenus ] ); | ||
|
||
|
@@ -235,6 +247,7 @@ function Navigation( { | |
status: classicMenuConversionStatus, | ||
error: classicMenuConversionError, | ||
value: classicMenuConversionResult, | ||
classicInnerBlocks: classicMenuConversionInnerBlocks, | ||
} = useConvertClassicToBlockMenu( clientId ); | ||
|
||
const isConvertingClassicMenu = | ||
|
@@ -255,13 +268,24 @@ function Navigation( { | |
hasResolvedNavigationMenus && | ||
! hasUncontrolledInnerBlocks; | ||
|
||
if ( isPlaceholder && ! ref ) { | ||
/** | ||
* this fallback only displays (both in editor and on front) | ||
* the list of pages block if not menu is available | ||
* 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' ) ] ); | ||
} | ||
|
||
const isEntityAvailable = | ||
! isNavigationMenuMissing && isNavigationMenuResolved; | ||
|
||
// "loading" state: | ||
// - there is a menu creation process in progress. | ||
// - there is a classic menu conversion process in progress. | ||
// OR | ||
// OR: | ||
// - there is a ref attribute pointing to a Navigation Post | ||
// - the Navigation Post isn't available (hasn't resolved) yet. | ||
const isLoading = | ||
|
@@ -336,6 +360,8 @@ function Navigation( { | |
showClassicMenuConversionNotice( | ||
__( 'Classic menu imported successfully.' ) | ||
); | ||
|
||
replaceInnerBlocks( clientId, classicMenuConversionInnerBlocks ); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This does work, but other changes get triggered which means they get wiped. |
||
} | ||
|
||
if ( classicMenuConversionStatus === CLASSIC_MENU_CONVERSION_ERROR ) { | ||
|
@@ -347,6 +373,7 @@ function Navigation( { | |
classicMenuConversionStatus, | ||
classicMenuConversionResult, | ||
classicMenuConversionError, | ||
classicMenuConversionInnerBlocks, | ||
] ); | ||
|
||
// Spacer block needs orientation from context. This is a patch until | ||
|
@@ -441,17 +468,6 @@ function Navigation( { | |
shouldFocusNavigationSelector, | ||
] ); | ||
|
||
const resetToEmptyBlock = useCallback( () => { | ||
registry.batch( () => { | ||
setAttributes( { | ||
ref: undefined, | ||
} ); | ||
if ( ! ref ) { | ||
replaceInnerBlocks( clientId, [] ); | ||
} | ||
} ); | ||
}, [ clientId, ref ] ); | ||
|
||
const isResponsive = 'never' !== overlayMenu; | ||
|
||
const overlayMenuPreviewClasses = classnames( | ||
|
@@ -600,6 +616,27 @@ function Navigation( { | |
if ( hasUnsavedBlocks ) { | ||
return ( | ||
<TagName { ...blockProps }> | ||
<BlockControls> | ||
<ToolbarGroup className="wp-block-navigation__toolbar-menu-selector"> | ||
<NavigationMenuSelector | ||
ref={ null } | ||
currentMenuId={ null } | ||
clientId={ clientId } | ||
onSelectNavigationMenu={ ( menuId ) => { | ||
handleUpdateMenu( menuId ); | ||
setShouldFocusNavigationSelector( true ); | ||
} } | ||
onSelectClassicMenu={ ( classicMenu ) => { | ||
convert( classicMenu.id, classicMenu.name ); | ||
setShouldFocusNavigationSelector( true ); | ||
} } | ||
onCreateNew={ () => createNavigationMenu( '', [] ) } | ||
/* translators: %s: The name of a menu. */ | ||
actionLabel={ __( "Switch to '%s'" ) } | ||
showManageActions | ||
/> | ||
</ToolbarGroup> | ||
</BlockControls> | ||
{ stylingInspectorControls } | ||
<ResponsiveWrapper | ||
id={ clientId } | ||
|
@@ -639,16 +676,40 @@ function Navigation( { | |
// TODO - the user should be able to select a new one? | ||
if ( ref && isNavigationMenuMissing ) { | ||
return ( | ||
<div { ...blockProps }> | ||
<TagName { ...blockProps }> | ||
<BlockControls> | ||
<ToolbarGroup className="wp-block-navigation__toolbar-menu-selector"> | ||
<NavigationMenuSelector | ||
ref={ navigationSelectorRef } | ||
currentMenuId={ ref } | ||
clientId={ clientId } | ||
onSelectNavigationMenu={ ( menuId ) => { | ||
handleUpdateMenu( menuId ); | ||
setShouldFocusNavigationSelector( true ); | ||
} } | ||
onSelectClassicMenu={ ( classicMenu ) => { | ||
convert( classicMenu.id, classicMenu.name ); | ||
setShouldFocusNavigationSelector( true ); | ||
} } | ||
onCreateNew={ () => createNavigationMenu( '', [] ) } | ||
/* translators: %s: The name of a menu. */ | ||
actionLabel={ __( "Switch to '%s'" ) } | ||
showManageActions | ||
/> | ||
</ToolbarGroup> | ||
</BlockControls> | ||
<Warning> | ||
{ __( | ||
'Navigation menu has been deleted or is unavailable. ' | ||
) } | ||
<Button onClick={ resetToEmptyBlock } variant="link"> | ||
<Button | ||
onClick={ () => createNavigationMenu( '', [] ) } | ||
variant="link" | ||
> | ||
{ __( 'Create a new menu?' ) } | ||
</Button> | ||
</Warning> | ||
</div> | ||
</TagName> | ||
); | ||
} | ||
|
||
|
@@ -666,7 +727,17 @@ function Navigation( { | |
? CustomPlaceholder | ||
: Placeholder; | ||
|
||
if ( isPlaceholder ) { | ||
/** | ||
* Historically the navigation block has supported custom placeholders. | ||
* Even though the current UX tries as hard as possible not to | ||
* end up in a placeholder state, the block continues to support | ||
* this extensibility point, via a CustomPlaceholder. | ||
* When CustomPlaceholder is present it becomes the default fallback | ||
* for an empty navigation block, instead of the default fallbacks. | ||
* | ||
*/ | ||
|
||
if ( isPlaceholder && CustomPlaceholder ) { | ||
return ( | ||
<TagName { ...blockProps }> | ||
<PlaceholderComponent | ||
|
@@ -709,7 +780,9 @@ function Navigation( { | |
convert( classicMenu.id, classicMenu.name ); | ||
setShouldFocusNavigationSelector( true ); | ||
} } | ||
onCreateNew={ resetToEmptyBlock } | ||
onCreateNew={ () => | ||
createNavigationMenu( '', [] ) | ||
} | ||
/* translators: %s: The name of a menu. */ | ||
actionLabel={ __( "Switch to '%s'" ) } | ||
showManageActions | ||
|
@@ -728,7 +801,7 @@ function Navigation( { | |
canUserDeleteNavigationMenu && ( | ||
<NavigationMenuDeleteControl | ||
onDelete={ ( deletedMenuTitle = '' ) => { | ||
resetToEmptyBlock(); | ||
replaceInnerBlocks( clientId, [] ); | ||
showNavigationMenuStatusNotice( | ||
sprintf( | ||
// translators: %s: the name of a menu (e.g. Header navigation). | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,6 +25,7 @@ function useConvertClassicToBlockMenu( clientId ) { | |
const [ status, setStatus ] = useState( CLASSIC_MENU_CONVERSION_IDLE ); | ||
const [ value, setValue ] = useState( null ); | ||
const [ error, setError ] = useState( null ); | ||
const [ classicInnerBlocks, setClassicInnerBlocks ] = useState( null ); | ||
|
||
async function convertClassicMenuToBlockMenu( menuId, menuName ) { | ||
let navigationMenu; | ||
|
@@ -65,13 +66,11 @@ function useConvertClassicToBlockMenu( clientId ) { | |
|
||
// 2. Convert the classic items into blocks. | ||
const { innerBlocks } = menuItemsToBlocks( classicMenuItems ); | ||
setClassicInnerBlocks( innerBlocks ); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So that they can be passed from the hook back to the component, |
||
|
||
// 3. Create the `wp_navigation` Post with the blocks. | ||
try { | ||
navigationMenu = await createNavigationMenu( | ||
menuName, | ||
innerBlocks | ||
); | ||
navigationMenu = await createNavigationMenu( menuName, [] ); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't need this change. |
||
} catch ( err ) { | ||
throw new Error( | ||
sprintf( | ||
|
@@ -100,7 +99,7 @@ function useConvertClassicToBlockMenu( clientId ) { | |
setValue( null ); | ||
setError( null ); | ||
|
||
convertClassicMenuToBlockMenu( menuId, menuName ) | ||
return convertClassicMenuToBlockMenu( menuId, menuName ) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't need this change. |
||
.then( ( navMenu ) => { | ||
setValue( navMenu ); | ||
setStatus( CLASSIC_MENU_CONVERSION_SUCCESS ); | ||
|
@@ -130,6 +129,7 @@ function useConvertClassicToBlockMenu( clientId ) { | |
status, | ||
value, | ||
error, | ||
classicInnerBlocks, | ||
}; | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is used to pass the innerBlocks to the block from the hook.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This changed a bit with #43081 and will change more in #42956.
But I think its ok.
convert
can return thewp_navigation
and theinnerBlocks
: