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

Creation of menus on the edit navigation screen #22309

Merged
merged 36 commits into from
May 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
ffc21f0
Use hasFinishedResolution to correctly determine loading state
talldan May 13, 2020
c1cd1ec
First draft of a form to create a new menu
talldan May 13, 2020
a05b38d
Add conditional logic for UI
talldan May 13, 2020
26de8a8
Use saveMenu action to create menu
talldan May 13, 2020
69e98e1
Handle returning to initial state when deleting last menu
talldan May 13, 2020
82351d8
Add create first menu message
talldan May 13, 2020
13d4e6c
Markup CreateMenuForm component as a proper form
talldan May 13, 2020
c1072a1
Add button for creating new menus when an existing menu is selected
talldan May 13, 2020
570e1a4
Improve responsive styles
talldan May 13, 2020
b0be954
Fix header text displaying at wrong time
talldan May 13, 2020
00a8d89
Add an option for cancelling the menu creation process
talldan May 14, 2020
b0a18d4
Tidy up styling of cancel button
talldan May 14, 2020
c413d56
Tidy up rendering logic
talldan May 14, 2020
395d664
Use notices for errors
talldan May 14, 2020
3d11160
Add busy indicator that is not really shown
talldan May 14, 2020
9d1e051
Use getIsResolving for more succinct code
talldan May 14, 2020
c0e37e4
Focus input field when creating a menu
talldan May 14, 2020
00c6585
Try withFocusReturn
talldan May 14, 2020
c7e57a8
Fix returning to first menu when selecting cancel
talldan May 14, 2020
83192e1
Try a snackbar
talldan May 14, 2020
366ed6f
Remove dead code
talldan May 14, 2020
7d784e2
Remove unused logic for hiding contents of panels
talldan May 15, 2020
762e721
Show create panel at same time as other panels
talldan May 15, 2020
037fbbb
Try setting the created menu after menu creation
talldan May 15, 2020
c8fd34b
Try only showing loading spinner on first load
talldan May 15, 2020
357dfda
Fix menu creation notifications
talldan May 15, 2020
7431733
Only hide panels on first load
talldan May 15, 2020
eb794a6
Fix uncontrolled select
talldan May 15, 2020
27ea6f8
Fix button busy appearance
talldan May 15, 2020
16270a4
Use pixel value for margin
talldan May 15, 2020
9ef96d6
Ensure items do not take up full width
talldan May 15, 2020
3b0316d
Ensure loading indicator when creating menus isn't shown when validat…
talldan May 18, 2020
63cbfd5
Use a `finally` clause to mark when a save finishes
talldan May 18, 2020
d74569b
Fix menu save error message
talldan May 18, 2020
0d7c57b
Remove selector dependency
talldan May 18, 2020
e9f8129
Center items along horizontal axis when using row layout
talldan May 18, 2020
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
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@ export default function NavigationStructurePanel( { blocks, initialOpen } ) {
[]
);
const { selectBlock } = useDispatch( 'core/block-editor' );
const showNavigationStructure = !! blocks.length;

return (
<Panel className="edit-navigation-menu-editor__navigation-structure-panel">
<PanelBody
title={ __( 'Navigation structure' ) }
initialOpen={ initialOpen }
>
{ !! blocks.length && (
{ showNavigationStructure && (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above - should this component just return null when showNavigationStructure === false?

Copy link
Contributor Author

@talldan talldan May 15, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed this back to how it was before for the same reason as above, the panel will show at the same time as the create panel.

<__experimentalBlockNavigationTree
blocks={ blocks }
selectedBlockClientId={ selectedBlockClientIds[ 0 ] }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
}

.edit-navigation-menu-editor__navigation-structure-panel {
// IE11 requires the column to be explicity declared.
// IE11 requires the column to be explicitly declared.
grid-column: 1;

// Make panels collapsible in IE. The IE analogue of align-items: self-start;.
Expand All @@ -59,7 +59,7 @@

.edit-navigation-menu-editor__block-editor-panel {
@include break-medium {
// IE11 requires the column to be explicity declared.
// IE11 requires the column to be explicitly declared.
// Only shift this into the second column on desktop.
grid-column: 2;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/**
* External dependencies
*/
import { some } from 'lodash';

/**
* WordPress dependencies
*/
import {
Button,
Panel,
PanelBody,
TextControl,
withFocusReturn,
} from '@wordpress/components';
import { useDispatch, useSelect } from '@wordpress/data';
import { useCallback, useEffect, useState } from '@wordpress/element';
import { __, sprintf } from '@wordpress/i18n';
const { DOMParser } = window;

const noticeId = 'edit-navigation-create-menu-error';

const menuNameMatches = ( menuName ) => ( menu ) =>
menu.name.toLowerCase() === menuName.toLowerCase();

export function CreateMenuForm( { onCancel, onCreateMenu, menus } ) {
const [ menuName, setMenuName ] = useState( '' );
const [ isCreatingMenu, setIsCreatingMenu ] = useState( false );
const menuSaveError = useSelect( ( select ) =>
select( 'core' ).getLastEntitySaveError( 'root', 'menu' )
);
const { saveMenu } = useDispatch( 'core' );
const { createInfoNotice, createErrorNotice, removeNotice } = useDispatch(
'core/notices'
);

// Handle REST API Error messages.
useEffect( () => {
if ( menuSaveError ) {
// Error messages from the REST API often contain HTML.
// createErrorNotice does not support HTML in error text, so first
// strip HTML out using DOMParser.
const document = new DOMParser().parseFromString(
menuSaveError.message,
'text/html'
);
const errorText = document.body.textContent || '';
createErrorNotice( errorText, { id: noticeId } );
}
}, [ menuSaveError ] );

const createMenu = useCallback(
async ( event ) => {
// Prevent form submission.
event.preventDefault();

// Remove existing notices.
removeNotice( noticeId );

if ( menuName.length === 0 ) {
// Button is aria-disabled, do nothing.
return;
}

// Validate the menu name doesn't match an existing menu.
if ( some( menus, menuNameMatches( menuName ) ) ) {
const message = sprintf(
// translators: %s: the name of a menu.
__(
'The menu name %s conflicts with another menu name. Please try another.'
),
menuName
);
createErrorNotice( message, { id: noticeId } );
return;
}

setIsCreatingMenu( true );

const menu = await saveMenu( { name: menuName } );
if ( menu ) {
createInfoNotice( __( 'Menu created' ), {
type: 'snackbar',
isDismissible: true,
} );
onCreateMenu( menu.id );
}
talldan marked this conversation as resolved.
Show resolved Hide resolved

setIsCreatingMenu( false );
},
[ menuName, menus ]
);

return (
<Panel className="edit-navigation-menus-editor__create-menu-panel">
<PanelBody title={ __( 'Create navigation menu' ) }>
<form onSubmit={ createMenu }>
<TextControl
// Disable reason - autoFocus is legitimate in this usage,
// The first focusable on the form should be focused,
// which is this element.
// eslint-disable-next-line jsx-a11y/no-autofocus
autoFocus
label={ __( 'Menu name' ) }
value={ menuName }
onChange={ setMenuName }
placeholder={ __( 'Main Navigation' ) }
/>
<Button
type="submit"
isBusy={ isCreatingMenu }
onClick={ createMenu }
aria-disabled={ menuName.length === 0 }
isPrimary
>
{ __( 'Create menu' ) }
</Button>
{ onCancel && (
<Button
className="edit-navigation-menus-editor__cancel-create-menu-button"
isSecondary
onClick={ onCancel }
>
{ __( 'Cancel' ) }
</Button>
) }
</form>
</PanelBody>
</Panel>
);
}

export default withFocusReturn( CreateMenuForm );
talldan marked this conversation as resolved.
Show resolved Hide resolved
106 changes: 86 additions & 20 deletions packages/edit-navigation/src/components/menus-editor/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,49 +3,113 @@
*/
import { useSelect } from '@wordpress/data';
import { useState, useEffect } from '@wordpress/element';
import { Card, CardBody, Spinner, SelectControl } from '@wordpress/components';
import {
Button,
Card,
CardBody,
Spinner,
SelectControl,
} from '@wordpress/components';
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import CreateMenuPanel from './create-menu-panel';
import MenuEditor from '../menu-editor';

export default function MenusEditor( { blockEditorSettings } ) {
const menus = useSelect( ( select ) => select( 'core' ).getMenus() );
const { menus, hasLoadedMenus } = useSelect( ( select ) => {
const { getMenus, hasFinishedResolution } = select( 'core' );
return {
menus: getMenus(),
hasLoadedMenus: hasFinishedResolution( 'getMenus' ),
};
}, [] );

const [ menuId, setMenuId ] = useState( 0 );
const [ stateMenus, setStateMenus ] = useState( null );
const [ hasCompletedFirstLoad, setHasCompletedFirstLoad ] = useState(
false
);

useEffect( () => {
if ( ! hasCompletedFirstLoad && hasLoadedMenus ) {
setHasCompletedFirstLoad( true );
}
}, [ hasLoadedMenus ] );

const [ menuId, setMenuId ] = useState();
const [ stateMenus, setStateMenus ] = useState();
const [ showCreateMenuPanel, setShowCreateMenuPanel ] = useState( false );

useEffect( () => {
if ( menus?.length ) {
setStateMenus( menus );
setMenuId( menus[ 0 ].id );

// Only set menuId if it's currently unset.
if ( ! menuId ) {
setMenuId( menus[ 0 ].id );
}
}
}, [ menus ] );
}, [ menus, menuId ] );

if ( ! stateMenus ) {
if ( ! hasCompletedFirstLoad ) {
return <Spinner />;
}

const hasMenus = !! stateMenus?.length;
const isCreateMenuPanelVisible =
hasCompletedFirstLoad && ( ! hasMenus || showCreateMenuPanel );

return (
<>
<Card className="edit-navigation-menus-editor__menu-selection-card">
<CardBody>
<SelectControl
className="edit-navigation-menus-editor__menu-select-control"
label={ __( 'Select navigation to edit:' ) }
options={ stateMenus.map( ( menu ) => ( {
value: menu.id,
label: menu.name,
} ) ) }
onChange={ ( selectedMenuId ) =>
setMenuId( selectedMenuId )
}
/>
<CardBody className="edit-navigation-menus-editor__menu-selection-card-body">
{ hasCompletedFirstLoad && ! hasMenus && (
<p className="edit-navigation-menus-editor__menu-selection-card-instructional-text">
{ __( 'Create your first menu below.' ) }
</p>
) }
{ hasMenus && (
<>
<SelectControl
className="edit-navigation-menus-editor__menu-select-control"
label={ __( 'Select navigation to edit:' ) }
options={ stateMenus?.map( ( menu ) => ( {
value: menu.id,
label: menu.name,
} ) ) }
onChange={ ( selectedMenuId ) =>
setMenuId( selectedMenuId )
}
value={ menuId }
/>
<Button
isLink
onClick={ () => setShowCreateMenuPanel( true ) }
>
{ __( 'Create a new menu' ) }
</Button>
</>
) }
</CardBody>
</Card>
{ !! menuId && (
{ isCreateMenuPanelVisible && (
<CreateMenuPanel
menus={ stateMenus }
onCancel={
// User can only cancel out of menu creation if there
// are other menus to fall back to showing.
hasMenus
? () => setShowCreateMenuPanel( false )
: undefined
}
onCreateMenu={ ( newMenuId ) => {
setMenuId( newMenuId );
setShowCreateMenuPanel( false );
} }
/>
) }
{ hasMenus && (
<MenuEditor
menuId={ menuId }
blockEditorSettings={ blockEditorSettings }
Expand All @@ -56,6 +120,8 @@ export default function MenusEditor( { blockEditorSettings } ) {
setStateMenus( newStateMenus );
if ( newStateMenus.length ) {
setMenuId( newStateMenus[ 0 ].id );
} else {
setMenuId();
talldan marked this conversation as resolved.
Show resolved Hide resolved
}
} }
/>
Expand Down
39 changes: 39 additions & 0 deletions packages/edit-navigation/src/components/menus-editor/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,33 @@
margin-bottom: 10px;
}

.edit-navigation-menus-editor__menu-selection-card-body {
display: flex;
flex-direction: column;
align-items: flex-start;

.edit-navigation-menus-editor__menu-select-control {
margin-right: 0;
margin-bottom: 10px;
align-self: normal;
}

@include break-large {
flex-direction: row;
align-items: center;

.edit-navigation-menus-editor__menu-select-control {
align-self: center;
margin-right: 1ch;
margin-bottom: 0;
}
}
}

.edit-navigation-menus-editor__menu-select-control {
flex: 1;
margin-right: 1ch;

@include break-small {
.components-base-control__field {
display: flex;
Expand All @@ -15,3 +41,16 @@
}
}
}

.edit-navigation-menus-editor__menu-selection-card-instructional-text {
margin: 0;
}

.edit-navigation-menus-editor__create-menu-panel {
margin-bottom: 10px;
}

.edit-navigation-menus-editor__cancel-create-menu-button {
margin-left: 8px;
}