Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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
104 changes: 104 additions & 0 deletions packages/edit-site/src/components/add-new-page/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/**
* External dependencies
*/
import { kebabCase } from 'lodash';

/**
* WordPress dependencies
*/
import {
Button,
Modal,
__experimentalHStack as HStack,
__experimentalVStack as VStack,
TextControl,
} from '@wordpress/components';
import { __, sprintf } from '@wordpress/i18n';
import { useDispatch } from '@wordpress/data';
import { useState } from '@wordpress/element';
import { store as coreStore } from '@wordpress/core-data';
import { store as noticesStore } from '@wordpress/notices';

const DEFAULT_TITLE = __( 'Untitled page' );

export default function AddNewPageModal( { onSave, onCancel } ) {
const [ isCreatingPage, setIsCreatingPage ] = useState( false );
const [ title, setTitle ] = useState( DEFAULT_TITLE );

const { saveEntityRecord } = useDispatch( coreStore );
const { createErrorNotice, createSuccessNotice } =
useDispatch( noticesStore );

async function createPage( event ) {
event.preventDefault();

if ( isCreatingPage ) {
return;
}
setIsCreatingPage( true );
try {
const newPage = await saveEntityRecord(
'postType',
'page',
{
status: 'draft',
title,
slug: kebabCase( title || DEFAULT_TITLE ),
},
{ throwOnError: true }
);

onSave( newPage );

Copy link
Contributor

Choose a reason for hiding this comment

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

After the page is created, where should focus get placed? Right now it's being dropped at the hidden Back button in the closed sidebar. I agree with @carolinan that the current mode of being moved to the site editor template for this page is an odd experience. I'd be fine with it if the sidebar stayed open, in which case, handling the focus here isn't an issue since it's being placed in a sensible spot.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

the current mode of being moved to the site editor template for this page is an odd experience

This will make more sense once the content editing PR is merged. There is also some follow up work around improving the add page experience, including selecting starter patterns.

createSuccessNotice(
sprintf(
// translators: %s: Title of the created template e.g: "Category".
__( '"%s" successfully created.' ),
newPage.title?.rendered || title
),
{
type: 'snackbar',
}
);
} catch ( error ) {
const errorMessage =
error.message && error.code !== 'unknown_error'
? error.message
: __( 'An error occurred while creating the page.' );

createErrorNotice( errorMessage, {
type: 'snackbar',
} );
} finally {
setIsCreatingPage( false );
}
}

return (
<Modal title="Draft a new page" onRequestClose={ onCancel }>
<form onSubmit={ createPage }>
<VStack spacing={ 3 }>
<TextControl
help="You can always change this later"
label="Page title"
onChange={ setTitle }
value={ title }
/>
<HStack spacing={ 2 } justify="end">
<Button variant="tertiary" onClick={ onCancel }>
{ __( 'Cancel' ) }
</Button>
<Button
variant="primary"
type="submit"
isBusy={ isCreatingPage }
aria-disabled={ isCreatingPage }
>
{ __( 'Create draft' ) }
</Button>
</HStack>
</VStack>
</form>
</Modal>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,27 @@ import {
__experimentalItemGroup as ItemGroup,
__experimentalItem as Item,
} from '@wordpress/components';
import { useState } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { useEntityRecords } from '@wordpress/core-data';
import { decodeEntities } from '@wordpress/html-entities';
import { plus } from '@wordpress/icons';
import { useDispatch } from '@wordpress/data';
import { privateApis as routerPrivateApis } from '@wordpress/router';

/**
* Internal dependencies
*/
import { store as editSiteStore } from '../../store';
import SidebarNavigationScreen from '../sidebar-navigation-screen';
import { useLink } from '../routes/link';
import SidebarNavigationItem from '../sidebar-navigation-item';
import SidebarNavigationSubtitle from '../sidebar-navigation-subtitle';
import SidebarButton from '../sidebar-button';
import AddNewPageModal from '../add-new-page';
import { unlock } from '../../private-apis';

const { useHistory } = unlock( routerPrivateApis );

const PageItem = ( { postId, ...props } ) => {
const linkInfo = useLink( {
Expand All @@ -31,52 +41,85 @@ export default function SidebarNavigationScreenPages() {
'page'
);

const [ showAddPage, setShowAddPage ] = useState( false );

const history = useHistory();
const { setPage } = unlock( useDispatch( editSiteStore ) );

const handleNewPage = ( { type, id } ) => {
setPage( {
context: { postType: type, postId: id },
} );
// Navigate to the created template editor.
history.push( {
postId: id,
postType: type,
canvas: 'edit',
} );
setShowAddPage( false );
};

return (
<SidebarNavigationScreen
title={ __( 'Pages' ) }
description={ __( 'Browse and edit pages on your site.' ) }
content={
<>
{ isLoading && (
<ItemGroup>
<Item>{ __( 'Loading pages' ) }</Item>
</ItemGroup>
) }
{ ! isLoading && (
<>
<SidebarNavigationSubtitle>
{ __( 'Recent' ) }
</SidebarNavigationSubtitle>
<>
{ showAddPage && (
<AddNewPageModal
onSave={ handleNewPage }
onCancel={ () => setShowAddPage( false ) }
/>
) }
<SidebarNavigationScreen
title={ __( 'Pages' ) }
description={ __( 'Browse and edit pages on your site.' ) }
actions={
<SidebarButton
icon={ plus }
label={ __( 'Draft a new page' ) }
onClick={ () => setShowAddPage( true ) }
/>
}
content={
<>
{ isLoading && (
<ItemGroup>
{ ! pages?.length && (
<Item>{ __( 'No page found' ) }</Item>
) }
{ pages?.map( ( page ) => (
<PageItem
postId={ page.id }
key={ page.id }
withChevron
>
{ decodeEntities(
page.title?.rendered
) ?? __( '(no title)' ) }
</PageItem>
) ) }
<SidebarNavigationItem
className="edit-site-sidebar-navigation-screen-pages__see-all"
href="edit.php?post_type=page"
onClick={ () => {
document.location =
'edit.php?post_type=page';
} }
>
{ __( 'Manage all pages' ) }
</SidebarNavigationItem>
<Item>{ __( 'Loading pages' ) }</Item>
</ItemGroup>
</>
) }
</>
}
/>
) }
{ ! isLoading && (
<>
<SidebarNavigationSubtitle>
{ __( 'Recent' ) }
</SidebarNavigationSubtitle>
<ItemGroup>
{ ! pages?.length && (
<Item>{ __( 'No page found' ) }</Item>
) }
{ pages?.map( ( page ) => (
<PageItem
postId={ page.id }
key={ page.id }
withChevron
>
{ decodeEntities(
page.title?.rendered
) ?? __( '(no title)' ) }
Copy link
Contributor

Choose a reason for hiding this comment

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

If the page title is an empty string this will return an empty string rather than (no title).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@scruffian I think this is something we can address in this issue as its more directly related to the pages list

</PageItem>
) ) }
<SidebarNavigationItem
className="edit-site-sidebar-navigation-screen-pages__see-all"
href="edit.php?post_type=page"
onClick={ () => {
document.location =
'edit.php?post_type=page';
} }
>
{ __( 'Manage all pages' ) }
</SidebarNavigationItem>
</ItemGroup>
</>
) }
</>
}
/>
</>
);
}