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 0000000000000..5b3f642625111 --- /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 0ad7df02020d1..25979fcd4a036 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 0000000000000..ba4f198605030 --- /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(); + } ); +} );