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 Editor: Improve experience for user creating their first menu #28767

Merged
merged 22 commits into from Feb 15, 2021
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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>
);
}