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

Improve create new UI feedback in Nav block #39219

Merged
merged 16 commits into from
Mar 9, 2022
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
73 changes: 64 additions & 9 deletions packages/block-library/src/navigation/edit/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ import useConvertClassicToBlockMenu, {
CLASSIC_MENU_CONVERSION_PENDING,
CLASSIC_MENU_CONVERSION_SUCCESS,
} from './use-convert-classic-menu-to-block-menu';
import useCreateNavigationMenu, {
CREATE_NAVIGATION_MENU_ERROR,
CREATE_NAVIGATION_MENU_PENDING,
CREATE_NAVIGATION_MENU_SUCCESS,
} from './use-create-navigation-menu';

const EMPTY_ARRAY = [];

Expand Down Expand Up @@ -162,6 +167,51 @@ function Navigation( {
// the Select Menu dropdown.
useNavigationEntities();

const [
showNavigationMenuCreateNotice,
hideNavigationMenuCreateNotice,
] = useNavigationNotice( {
name: 'block-library/core/navigation/create',
} );

const {
create: createNavigationMenu,
status: createNavigationMenuStatus,
error: createNavigationMenuError,
value: createNavigationMenuPost,
} = useCreateNavigationMenu( clientId );

const isCreatingNavigationMenu =
createNavigationMenuStatus === CREATE_NAVIGATION_MENU_PENDING;

useEffect( () => {
hideNavigationMenuCreateNotice();

if ( createNavigationMenuStatus === CREATE_NAVIGATION_MENU_PENDING ) {
speak( __( `Creating Navigation Menu.` ) );
}

if ( createNavigationMenuStatus === CREATE_NAVIGATION_MENU_SUCCESS ) {
setRef( createNavigationMenuPost.id );
selectBlock( clientId );

showNavigationMenuCreateNotice(
__( `Navigation Menu successfully created.` )
);
}

if ( createNavigationMenuStatus === CREATE_NAVIGATION_MENU_ERROR ) {
showNavigationMenuCreateNotice(
__( 'Failed to create Navigation Menu.' )
);
}
}, [
createNavigationMenu,
createNavigationMenuStatus,
createNavigationMenuError,
createNavigationMenuPost,
] );

const {
hasUncontrolledInnerBlocks,
uncontrolledInnerBlocks,
Expand Down Expand Up @@ -251,24 +301,28 @@ function Navigation( {
const TagName = 'nav';

// "placeholder" shown if:
// - we don't have a ref attribute pointing to a Navigation Post.
// - we are not running a menu conversion process.
// - we don't have uncontrolled blocks.
// - (legacy) we have a Navigation Area without a ref attribute pointing to a Navigation Post.
// - there is no ref attribute pointing to a Navigation Post.
// - there is no classic menu conversion process in progress.
// - there is no menu creation process in progress.
// - there are no uncontrolled blocks.
// - (legacy) there is a Navigation Area without a ref attribute pointing to a Navigation Post.
const isPlaceholder =
! ref &&
! isCreatingNavigationMenu &&
! isConvertingClassicMenu &&
( ! hasUncontrolledInnerBlocks || isWithinUnassignedArea );

const isEntityAvailable =
! isNavigationMenuMissing && isNavigationMenuResolved;

// "loading" state:
// - we are running the Classic Menu conversion process.
// - there is a menu creation process in progress.
// - there is a classic menu conversion process in progress.
// OR
// - there is a ref attribute pointing to a Navigation Post
// - the Navigation Post isn't available (hasn't resolved) yet.
const isLoading =
isCreatingNavigationMenu ||
isConvertingClassicMenu ||
!! ( ref && ! isEntityAvailable && ! isConvertingClassicMenu );

Expand Down Expand Up @@ -466,7 +520,7 @@ function Navigation( {
[ convert, handleUpdateMenu ]
);

const startWithEmptyMenu = useCallback( () => {
const resetToEmptyBlock = useCallback( () => {
registry.batch( () => {
if ( navigationArea ) {
setAreaMenu( 0 );
Expand Down Expand Up @@ -528,7 +582,7 @@ function Navigation( {
{ __(
'Navigation menu has been deleted or is unavailable. '
) }
<Button onClick={ startWithEmptyMenu } variant="link">
<Button onClick={ resetToEmptyBlock } variant="link">
{ __( 'Create a new menu?' ) }
</Button>
</Warning>
Expand Down Expand Up @@ -569,6 +623,7 @@ function Navigation( {
isResolvingCanUserCreateNavigationMenu
}
onFinish={ handleSelectNavigation }
onCreateEmpty={ () => createNavigationMenu( '', [] ) }
/>
</TagName>
);
Expand All @@ -584,7 +639,7 @@ function Navigation( {
currentMenuId={ ref }
clientId={ clientId }
onSelect={ handleSelectNavigation }
onCreateNew={ startWithEmptyMenu }
onCreateNew={ resetToEmptyBlock }
/* translators: %s: The name of a menu. */
actionLabel={ __( "Switch to '%s'" ) }
showManageActions
Expand Down Expand Up @@ -739,7 +794,7 @@ function Navigation( {
{ hasResolvedCanUserDeleteNavigationMenu &&
canUserDeleteNavigationMenu && (
<NavigationMenuDeleteControl
onDelete={ startWithEmptyMenu }
onDelete={ resetToEmptyBlock }
/>
) }
</InspectorControls>
Expand Down
21 changes: 2 additions & 19 deletions packages/block-library/src/navigation/edit/placeholder/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import { useEffect } from '@wordpress/element';
*/
import useNavigationEntities from '../../use-navigation-entities';
import PlaceholderPreview from './placeholder-preview';
import useCreateNavigationMenu from '../use-create-navigation-menu';
import NavigationMenuSelector from '../navigation-menu-selector';

export default function NavigationPlaceholder( {
Expand All @@ -22,26 +21,10 @@ export default function NavigationPlaceholder( {
canUserCreateNavigationMenu = false,
isResolvingCanUserCreateNavigationMenu,
onFinish,
onCreateEmpty,
} ) {
const createNavigationMenu = useCreateNavigationMenu( clientId );

const onFinishMenuCreation = async (
blocks,
navigationMenuTitle = null
) => {
const navigationMenu = await createNavigationMenu(
navigationMenuTitle,
blocks
);
onFinish( navigationMenu, blocks );
};

const { isResolvingMenus, hasResolvedMenus } = useNavigationEntities();

const onCreateEmptyMenu = () => {
onFinishMenuCreation( [] );
};

useEffect( () => {
if ( ! isSelected ) {
return;
Expand Down Expand Up @@ -98,7 +81,7 @@ export default function NavigationPlaceholder( {
{ canUserCreateNavigationMenu && (
<Button
variant="tertiary"
onClick={ onCreateEmptyMenu }
onClick={ onCreateEmpty }
>
{ __( 'Start empty' ) }
</Button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,9 @@ export default function UnsavedInnerBlocks( {

const { hasResolvedNavigationMenus, navigationMenus } = useNavigationMenu();

const createNavigationMenu = useCreateNavigationMenu( clientId );
const { create: createNavigationMenu } = useCreateNavigationMenu(
clientId
);

// Automatically save the uncontrolled blocks.
useEffect( async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ export const CLASSIC_MENU_CONVERSION_PENDING = 'pending';
export const CLASSIC_MENU_CONVERSION_IDLE = 'idle';

function useConvertClassicToBlockMenu( clientId ) {
const createNavigationMenu = useCreateNavigationMenu( clientId );
const { create: createNavigationMenu } = useCreateNavigationMenu(
clientId
);
const registry = useRegistry();

const [ status, setStatus ] = useState( CLASSIC_MENU_CONVERSION_IDLE );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,87 @@
import { serialize } from '@wordpress/blocks';
import { store as coreStore } from '@wordpress/core-data';
import { useDispatch } from '@wordpress/data';
import { useCallback } from '@wordpress/element';
import { useState, useCallback } from '@wordpress/element';

/**
* Internal dependencies
*/
import useGenerateDefaultNavigationTitle from './use-generate-default-navigation-title';

export const CREATE_NAVIGATION_MENU_SUCCESS = 'success';
export const CREATE_NAVIGATION_MENU_ERROR = 'error';
export const CREATE_NAVIGATION_MENU_PENDING = 'pending';
export const CREATE_NAVIGATION_MENU_IDLE = 'idle';

export default function useCreateNavigationMenu( clientId ) {
const [ status, setStatus ] = useState( CREATE_NAVIGATION_MENU_IDLE );
const [ value, setValue ] = useState( null );
const [ error, setError ] = useState( null );

const { saveEntityRecord } = useDispatch( coreStore );
const generateDefaultTitle = useGenerateDefaultNavigationTitle( clientId );

// This callback uses data from the two placeholder steps and only creates
// a new navigation menu when the user completes the final step.
return useCallback(
const create = useCallback(
async ( title = null, blocks = [] ) => {
// Guard against creating Navigations without a title.
// Note you can pass no title, but if one is passed it must be
// a string otherwise the title may end up being empty.
if ( title && typeof title !== 'string' ) {
setError(
'Invalid title supplied when creating Navigation Menu.'
);
setStatus( CREATE_NAVIGATION_MENU_ERROR );
throw new Error(
`Value of supplied title argument was not a string.`
);
}

setStatus( CREATE_NAVIGATION_MENU_PENDING );
setValue( null );
setError( null );

if ( ! title ) {
title = await generateDefaultTitle();
title = await generateDefaultTitle().catch( ( err ) => {
setError( err?.message );
setStatus( CREATE_NAVIGATION_MENU_ERROR );
throw new Error(
'Failed to create title when saving new Navigation Menu.',
{
cause: err,
}
);
} );
}
const record = {
title,
content: serialize( blocks ),
status: 'publish',
};

return await saveEntityRecord(
'postType',
'wp_navigation',
record
);
// Return affords ability to await on this function directly
return saveEntityRecord( 'postType', 'wp_navigation', record )
.then( ( response ) => {
setValue( response );
setStatus( CREATE_NAVIGATION_MENU_SUCCESS );
return response;
} )
.catch( ( err ) => {
setError( err?.message );
setStatus( CREATE_NAVIGATION_MENU_ERROR );
throw new Error( 'Unable to save new Navigation Menu', {
cause: err,
} );
} );
},
[ serialize, saveEntityRecord ]
);

return {
create,
status,
value,
error,
};
}
Loading