diff --git a/packages/edit-site/src/components/add-new-page/index.js b/packages/edit-site/src/components/add-new-page/index.js
new file mode 100644
index 00000000000000..5b3f6426251117
--- /dev/null
+++ b/packages/edit-site/src/components/add-new-page/index.js
@@ -0,0 +1,105 @@
+/**
+ * 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';
+
+export default function AddNewPageModal( { onSave, onClose } ) {
+ const [ isCreatingPage, setIsCreatingPage ] = useState( false );
+ const [ title, setTitle ] = useState( '' );
+
+ 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 || __( 'No title' ) ),
+ },
+ { throwOnError: true }
+ );
+
+ onSave( newPage );
+
+ 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 (
+
+
+
+ );
+}
diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-pages/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-pages/index.js
index 0ad7df02020d15..25979fcd4a036b 100644
--- a/packages/edit-site/src/components/sidebar-navigation-screen-pages/index.js
+++ b/packages/edit-site/src/components/sidebar-navigation-screen-pages/index.js
@@ -7,10 +7,12 @@ import {
__experimentalTruncate as Truncate,
__experimentalVStack as VStack,
} from '@wordpress/components';
+import { useState } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { useEntityRecords, store as coreStore } from '@wordpress/core-data';
import { decodeEntities } from '@wordpress/html-entities';
-import { layout, page, home, loop } from '@wordpress/icons';
+import { privateApis as routerPrivateApis } from '@wordpress/router';
+import { layout, page, home, loop, plus } from '@wordpress/icons';
import { useSelect } from '@wordpress/data';
/**
@@ -19,6 +21,11 @@ import { useSelect } from '@wordpress/data';
import SidebarNavigationScreen from '../sidebar-navigation-screen';
import { useLink } from '../routes/link';
import SidebarNavigationItem from '../sidebar-navigation-item';
+import SidebarButton from '../sidebar-button';
+import AddNewPageModal from '../add-new-page';
+import { unlock } from '../../private-apis';
+
+const { useHistory } = unlock( routerPrivateApis );
const PageItem = ( { postType = 'page', postId, ...props } ) => {
const linkInfo = useLink( {
@@ -85,98 +92,127 @@ export default function SidebarNavigationScreenPages() {
reorderedPages.splice( 1, 0, ...blogPage );
}
+ const [ showAddPage, setShowAddPage ] = useState( false );
+
+ const history = useHistory();
+
+ const handleNewPage = ( { type, id } ) => {
+ // Navigate to the created template editor.
+ history.push( {
+ postId: id,
+ postType: type,
+ canvas: 'edit',
+ } );
+ setShowAddPage( false );
+ };
+
return (
-
- { ( isLoadingPages || isLoadingTemplates ) && (
-
- - { __( 'Loading pages' ) }
-
- ) }
- { ! ( isLoadingPages || isLoadingTemplates ) && (
-
- { ! pagesAndTemplates?.length && (
- - { __( 'No page found' ) }
- ) }
- { isHomePageBlog && homeTemplate && (
-
-
- { decodeEntities(
- homeTemplate.title?.rendered ||
- __( '(no title)' )
- ) }
-
-
- ) }
- { reorderedPages?.map( ( item ) => {
- let itemIcon;
- switch ( item.id ) {
- case frontPage:
- itemIcon = home;
- break;
- case postsPage:
- itemIcon = loop;
- break;
- default:
- itemIcon = page;
- }
- return (
-
-
- { decodeEntities(
- item?.title?.rendered ||
- __( '(no title)' )
- ) }
-
-
- );
- } ) }
-
- { dynamicPageTemplates?.map( ( item ) => (
+ <>
+ { showAddPage && (
+ setShowAddPage( false ) }
+ />
+ ) }
+ setShowAddPage( true ) }
+ />
+ }
+ content={
+ <>
+ { ( isLoadingPages || isLoadingTemplates ) && (
+
+ - { __( 'Loading pages' ) }
+
+ ) }
+ { ! ( isLoadingPages || isLoadingTemplates ) && (
+
+ { ! pagesAndTemplates?.length && (
+ - { __( 'No page found' ) }
+ ) }
+ { isHomePageBlog && homeTemplate && (
{ decodeEntities(
- item.title?.rendered ||
+ homeTemplate.title?.rendered ||
__( '(no title)' )
) }
- ) ) }
- {
- document.location =
- 'edit.php?post_type=page';
- } }
- >
- { __( 'Manage all pages' ) }
-
-
-
- ) }
- >
- }
- />
+ ) }
+ { reorderedPages?.map( ( item ) => {
+ let itemIcon;
+ switch ( item.id ) {
+ case frontPage:
+ itemIcon = home;
+ break;
+ case postsPage:
+ itemIcon = loop;
+ break;
+ default:
+ itemIcon = page;
+ }
+ return (
+
+
+ { decodeEntities(
+ item?.title?.rendered ||
+ __( '(no title)' )
+ ) }
+
+
+ );
+ } ) }
+
+ { dynamicPageTemplates?.map( ( item ) => (
+
+
+ { decodeEntities(
+ item.title?.rendered ||
+ __( '(no title)' )
+ ) }
+
+
+ ) ) }
+ {
+ document.location =
+ 'edit.php?post_type=page';
+ } }
+ >
+ { __( 'Manage all pages' ) }
+
+
+
+ ) }
+ >
+ }
+ />
+ >
);
}
diff --git a/test/e2e/specs/site-editor/pages.spec.js b/test/e2e/specs/site-editor/pages.spec.js
new file mode 100644
index 00000000000000..ba4f1986050300
--- /dev/null
+++ b/test/e2e/specs/site-editor/pages.spec.js
@@ -0,0 +1,30 @@
+/**
+ * WordPress dependencies
+ */
+const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' );
+
+test.describe( 'Pages', () => {
+ test.beforeAll( async ( { requestUtils } ) => {
+ await requestUtils.activateTheme( 'emptytheme' );
+ } );
+ test( 'Create a new page', async ( { admin, page } ) => {
+ const pageName = 'demo';
+ await admin.visitSiteEditor();
+ await page.getByRole( 'button', { name: 'Pages' } ).click();
+ await page.getByRole( 'button', { name: 'Draft a new page' } ).click();
+ // Fill the page title and submit.
+ const newPageDialog = page.locator(
+ 'role=dialog[name="Draft a new page"i]'
+ );
+ const pageTitleInput = newPageDialog.locator(
+ 'role=textbox[name="Page title"i]'
+ );
+ await pageTitleInput.fill( pageName );
+ await page.keyboard.press( 'Enter' );
+ await expect(
+ page.locator(
+ `role=button[name="Dismiss this notice"i] >> text="${ pageName }" successfully created.`
+ )
+ ).toBeVisible();
+ } );
+} );