Skip to content

Commit

Permalink
Navigation Editor: Improve experience for user creating their first m…
Browse files Browse the repository at this point in the history
…enu (#28767)

* Ensure a navigation post cannot be returned with a null menu id

* Show empty state when there are no menus

* Add loading spinner to empty state

* Add some styling

* Finesse loading states

* Tidy up variables

* Refactor AddMenuForm to general component

* Add the menu creation form to the empty state

* Update contents of add menu form to match design

* Style the add menu form

* Hide header items when creating first menu

* Remove autofocus

* Reduce forehead

* Move heading element inside form and remove wrapper div

* Hide block inspector when creating first menu

* Add autofocus back

* Move the loading spinner into layout and style

* Update e2e test

* Fix linting issue caused by git

* Wrap boolean logic

Co-authored-by: Kai Hao <kevin830726@gmail.com>

* Avoid calling selector instead of returning early from selector

* Coerce to boolean

Co-authored-by: Kai Hao <kevin830726@gmail.com>
  • Loading branch information
talldan and kevin940726 committed Feb 15, 2021
1 parent f31c35c commit 47bb513
Show file tree
Hide file tree
Showing 11 changed files with 213 additions and 122 deletions.
11 changes: 1 addition & 10 deletions packages/e2e-tests/specs/experiments/navigation-editor.test.js
Expand Up @@ -126,7 +126,7 @@ describe( 'Navigation editor', () => {
await visitNavigationEditor();

// Wait for the header to show that no menus are available.
await page.waitForXPath( '//h2[contains(., "No menus available")]', {
await page.waitForXPath( '//h3[.="Create your first menu"]', {
visible: true,
} );

Expand All @@ -139,21 +139,12 @@ describe( 'Navigation editor', () => {
...getMenuItemMocks( { GET: [] } ),
] );

// Add a new menu.
const [ addNewButton ] = await page.$x(
'//button[contains(., "Add new")]'
);
await addNewButton.click();

await page.keyboard.type( 'Main Menu' );
const createMenuButton = await page.waitForXPath(
'//button[contains(., "Create menu")]'
);
await createMenuButton.click();

// Close the dropdown.
await page.keyboard.press( 'Escape' );

// A snackbar will appear when menu creation has completed.
await page.waitForXPath( '//div[contains(., "Menu created")]' );

Expand Down
Expand Up @@ -2,7 +2,7 @@
* External dependencies
*/
import { some } from 'lodash';

import classnames from 'classnames';
/**
* WordPress dependencies
*/
Expand All @@ -15,13 +15,10 @@ import { store as noticesStore } from '@wordpress/notices';
const menuNameMatches = ( menuName ) => ( menu ) =>
menu.name.toLowerCase() === menuName.toLowerCase();

export default function AddMenuForm( { menus, onCreate } ) {
export default function AddMenu( { className, menus, onCreate } ) {
const [ menuName, setMenuName ] = useState( '' );

const { createErrorNotice, createInfoNotice } = useDispatch( noticesStore );

const [ isCreatingMenu, setIsCreatingMenu ] = useState( false );

const { saveMenu } = useDispatch( 'core' );

const createMenu = async ( event ) => {
Expand Down Expand Up @@ -51,29 +48,43 @@ export default function AddMenuForm( { menus, onCreate } ) {
type: 'snackbar',
isDismissible: true,
} );
onCreate( menu.id );
if ( onCreate ) {
onCreate( menu.id );
}
}

setIsCreatingMenu( false );
};

const hasMenus = menus?.length;

const titleText = hasMenus
? __( 'Create a new menu' )
: __( 'Create your first menu' );

const helpText = hasMenus
? __( 'A short descriptive name for your menu.' )
: __( 'A short descriptive name for your first menu.' );

return (
<form
className={ classnames( 'edit-navigation-add-menu', className ) }
onSubmit={ createMenu }
className="edit-navigation-header__add-menu-form"
>
<h3 className="edit-navigation-add-menu__title">{ titleText }</h3>
<TextControl
// Disable reason: The name field should receive focus when
// component mounts.
// Disable reason: it should focus.
//
// eslint-disable-next-line jsx-a11y/no-autofocus
autoFocus
label={ __( 'Menu name' ) }
value={ menuName }
onChange={ setMenuName }
help={ helpText }
/>

<Button
className="edit-navigation-header__create-menu-button"
className="edit-navigation-add-menu__create-menu-button"
type="submit"
isPrimary
disabled={ ! menuName.length }
Expand Down
12 changes: 12 additions & 0 deletions packages/edit-navigation/src/components/add-menu/style.scss
@@ -0,0 +1,12 @@
.edit-navigation-add-menu {
display: flex;
flex-direction: column;
}

.edit-navigation-add-menu__title {
margin-top: 0;
}

.edit-navigation-add-menu__create-menu-button {
align-self: flex-end;
}
2 changes: 1 addition & 1 deletion packages/edit-navigation/src/components/editor/style.scss
Expand Up @@ -2,7 +2,7 @@
background: $white;
border: $border-width solid $gray-900;
border-radius: $radius-block-ui;
max-width: 420px;
max-width: $navigation-editor-width;
margin: auto;

.components-spinner {
Expand Down
133 changes: 69 additions & 64 deletions packages/edit-navigation/src/components/header/index.js
Expand Up @@ -21,7 +21,7 @@ import {
*/
import SaveButton from './save-button';
import ManageLocations from './manage-locations';
import AddMenuForm from './add-menu-form';
import AddMenu from '../add-menu';

export default function Header( {
menus,
Expand All @@ -47,82 +47,87 @@ export default function Header( {
actionHeaderText = __( 'No menus available' );
}

const hasMenus = !! menus?.length;

return (
<div className="edit-navigation-header">
<div className="edit-navigation-header__title-subtitle">
<h1 className="edit-navigation-header__title">
{ __( 'Navigation' ) }
</h1>
<h2 className="edit-navigation-header__subtitle">
{ actionHeaderText }
{ hasMenus && actionHeaderText }
</h2>
</div>
<div className="edit-navigation-header__actions">
<DropdownMenu
icon={ null }
toggleProps={ {
showTooltip: false,
children: __( 'Select menu' ),
isTertiary: true,
disabled: ! menus?.length,
__experimentalIsFocusable: true,
} }
popoverProps={ {
position: 'bottom left',
} }
>
{ () => (
<MenuGroup>
<MenuItemsChoice
value={ selectedMenuId }
onSelect={ onSelectMenu }
choices={ menus.map( ( menu ) => ( {
value: menu.id,
label: menu.name,
} ) ) }
/>
</MenuGroup>
) }
</DropdownMenu>
{ hasMenus && (
<div className="edit-navigation-header__actions">
<DropdownMenu
icon={ null }
toggleProps={ {
showTooltip: false,
children: __( 'Select menu' ),
isTertiary: true,
disabled: ! menus?.length,
__experimentalIsFocusable: true,
} }
popoverProps={ {
position: 'bottom left',
} }
>
{ () => (
<MenuGroup>
<MenuItemsChoice
value={ selectedMenuId }
onSelect={ onSelectMenu }
choices={ menus.map( ( menu ) => ( {
value: menu.id,
label: menu.name,
} ) ) }
/>
</MenuGroup>
) }
</DropdownMenu>

<Dropdown
position="bottom left"
renderToggle={ ( { isOpen, onToggle } ) => (
<Button
isTertiary
aria-expanded={ isOpen }
onClick={ onToggle }
>
{ __( 'Add new' ) }
</Button>
) }
renderContent={ () => (
<AddMenuForm
menus={ menus }
onCreate={ onSelectMenu }
/>
) }
/>
<Dropdown
position="bottom left"
renderToggle={ ( { isOpen, onToggle } ) => (
<Button
isTertiary
aria-expanded={ isOpen }
onClick={ onToggle }
>
{ __( 'Add new' ) }
</Button>
) }
renderContent={ () => (
<AddMenu
className="edit-navigation-header__add-menu"
menus={ menus }
onCreate={ onSelectMenu }
/>
) }
/>

<Dropdown
contentClassName="edit-navigation-header__manage-locations"
position="bottom left"
renderToggle={ ( { isOpen, onToggle } ) => (
<Button
isTertiary
aria-expanded={ isOpen }
onClick={ onToggle }
>
{ __( 'Manage locations' ) }
</Button>
) }
renderContent={ () => <ManageLocations /> }
/>
<Dropdown
contentClassName="edit-navigation-header__manage-locations"
position="bottom left"
renderToggle={ ( { isOpen, onToggle } ) => (
<Button
isTertiary
aria-expanded={ isOpen }
onClick={ onToggle }
>
{ __( 'Manage locations' ) }
</Button>
) }
renderContent={ () => <ManageLocations /> }
/>

<SaveButton navigationPost={ navigationPost } />
<SaveButton navigationPost={ navigationPost } />

<Popover.Slot name="block-toolbar" />
</div>
<Popover.Slot name="block-toolbar" />
</div>
) }
</div>
);
}
2 changes: 1 addition & 1 deletion packages/edit-navigation/src/components/header/style.scss
Expand Up @@ -25,6 +25,6 @@
display: flex;
}

.edit-navigation-header__add-menu-form {
.edit-navigation-header__add-menu {
min-width: 220px;
}
19 changes: 19 additions & 0 deletions packages/edit-navigation/src/components/layout/empty-state.js
@@ -0,0 +1,19 @@
/**
* WordPress dependencies
*/
import { Card, CardBody } from '@wordpress/components';

/**
* Internal dependencies
*/
import AddMenu from '../add-menu';

export default function EmptyState() {
return (
<Card className="edit-navigation-empty-state">
<CardBody>
<AddMenu />
</CardBody>
</Card>
);
}

0 comments on commit 47bb513

Please sign in to comment.