diff --git a/lib/compat/wordpress-6.3/site-editor.php b/lib/compat/wordpress-6.3/site-editor.php new file mode 100644 index 0000000000000..2cfde082554c4 --- /dev/null +++ b/lib/compat/wordpress-6.3/site-editor.php @@ -0,0 +1,27 @@ + 'none' ), + admin_url( 'site-editor.php' ) + ); + } + + return $location; +} + +add_filter( 'wp_redirect', 'gutenberg_prevent_site_editor_redirection', 1 ); diff --git a/lib/load.php b/lib/load.php index dc253408a9289..38839d047a455 100644 --- a/lib/load.php +++ b/lib/load.php @@ -101,6 +101,9 @@ function gutenberg_is_experiment_enabled( $name ) { require __DIR__ . '/compat/wordpress-6.2/html-api/class-wp-html-tag-processor.php'; } +// WordPress 6.3 compat. +require __DIR__ . '/compat/wordpress-6.3/site-editor.php'; + // Experimental features. remove_action( 'plugins_loaded', '_wp_theme_json_webfonts_handler' ); // Turns off WP 6.0's stopgap handler for Webfonts API. require __DIR__ . '/experimental/block-editor-settings-mobile.php'; diff --git a/packages/e2e-test-utils-playwright/src/index.ts b/packages/e2e-test-utils-playwright/src/index.ts index eb87d3b798751..9f283017f1016 100644 --- a/packages/e2e-test-utils-playwright/src/index.ts +++ b/packages/e2e-test-utils-playwright/src/index.ts @@ -3,5 +3,4 @@ export { Admin } from './admin'; export { Editor } from './editor'; export { PageUtils } from './page-utils'; export { RequestUtils } from './request-utils'; -export { SiteEditor } from './site-editor'; export { test, expect } from './test'; diff --git a/packages/e2e-test-utils-playwright/src/site-editor/index.ts b/packages/e2e-test-utils-playwright/src/site-editor/index.ts deleted file mode 100644 index 740dc56d31496..0000000000000 --- a/packages/e2e-test-utils-playwright/src/site-editor/index.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * External dependencies - */ -import type { Page } from '@playwright/test'; - -/** - * Internal dependencies - */ -import { enterEditMode } from './toggle-canvas-mode'; - -type AdminConstructorProps = { - page: Page; -}; - -export class SiteEditor { - page: Page; - - constructor( { page }: AdminConstructorProps ) { - this.page = page; - } - - enterEditMode = enterEditMode.bind( this ); -} diff --git a/packages/e2e-test-utils-playwright/src/site-editor/toggle-canvas-mode.js b/packages/e2e-test-utils-playwright/src/site-editor/toggle-canvas-mode.js deleted file mode 100644 index 01340eb085d88..0000000000000 --- a/packages/e2e-test-utils-playwright/src/site-editor/toggle-canvas-mode.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Enters the site editor edit mode. - * - * @this {import('.').SiteEditor} - */ -export async function enterEditMode() { - await this.page.click( '.edit-site-site-hub__edit-button' ); -} diff --git a/packages/e2e-test-utils-playwright/src/test.ts b/packages/e2e-test-utils-playwright/src/test.ts index 5e62c2d3e172c..b26117ff077ff 100644 --- a/packages/e2e-test-utils-playwright/src/test.ts +++ b/packages/e2e-test-utils-playwright/src/test.ts @@ -8,7 +8,7 @@ import type { ConsoleMessage } from '@playwright/test'; /** * Internal dependencies */ -import { Admin, Editor, PageUtils, RequestUtils, SiteEditor } from './index'; +import { Admin, Editor, PageUtils, RequestUtils } from './index'; const STORAGE_STATE_PATH = process.env.STORAGE_STATE_PATH || @@ -102,7 +102,6 @@ const test = base.extend< editor: Editor; pageUtils: PageUtils; snapshotConfig: void; - siteEditor: SiteEditor; }, { requestUtils: RequestUtils; @@ -114,9 +113,6 @@ const test = base.extend< editor: async ( { page }, use ) => { await use( new Editor( { page } ) ); }, - siteEditor: async ( { page }, use ) => { - await use( new SiteEditor( { page } ) ); - }, page: async ( { page }, use ) => { page.on( 'console', observeConsoleLogging ); diff --git a/packages/e2e-test-utils/src/site-editor.js b/packages/e2e-test-utils/src/site-editor.js index 425931ccf9cbc..bccbb51057912 100644 --- a/packages/e2e-test-utils/src/site-editor.js +++ b/packages/e2e-test-utils/src/site-editor.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { visitAdminPage } from '@wordpress/e2e-test-utils'; +import { canvas, visitAdminPage } from '@wordpress/e2e-test-utils'; import { addQueryArgs } from '@wordpress/url'; /** @@ -166,11 +166,13 @@ export async function openPreviousGlobalStylesPanel() { * Enters edit mode. */ export async function enterEditMode() { - const editSiteToggle = await page.$( '.edit-site-site-hub__edit-button' ); + const isViewMode = await page.$( + '.edit-site-visual-editor__editor-canvas[role="button"]' + ); // This check is necessary for the performance tests in old branches // where the site editor toggle was not implemented yet. - if ( ! editSiteToggle ) { + if ( ! isViewMode ) { return; } - await page.click( '.edit-site-site-hub__edit-button' ); + await canvas().click( 'body' ); } diff --git a/packages/e2e-tests/specs/performance/site-editor.test.js b/packages/e2e-tests/specs/performance/site-editor.test.js index cd071f0be5a85..2e4002bf0bb1c 100644 --- a/packages/e2e-tests/specs/performance/site-editor.test.js +++ b/packages/e2e-tests/specs/performance/site-editor.test.js @@ -87,7 +87,11 @@ describe( 'Site Editor Performance', () => { } ); beforeEach( async () => { - await visitSiteEditor( { postId: id, postType: 'page' } ); + await visitSiteEditor( { + postId: id, + postType: 'page', + path: '/navigation/single', + } ); } ); it( 'Loading', async () => { diff --git a/packages/e2e-tests/specs/site-editor/multi-entity-saving.test.js b/packages/e2e-tests/specs/site-editor/multi-entity-saving.test.js index fa039fb10fd2e..30b634712119a 100644 --- a/packages/e2e-tests/specs/site-editor/multi-entity-saving.test.js +++ b/packages/e2e-tests/specs/site-editor/multi-entity-saving.test.js @@ -265,6 +265,7 @@ describe( 'Multi-entity save flow', () => { await visitSiteEditor( { postId: 'emptytheme//index', postType: 'wp_template', + path: '/templates/single', } ); await enterEditMode(); @@ -304,6 +305,7 @@ describe( 'Multi-entity save flow', () => { await visitSiteEditor( { postId: 'emptytheme//index', postType: 'wp_template', + path: '/templates/single', } ); await enterEditMode(); diff --git a/packages/e2e-tests/specs/site-editor/settings-sidebar.test.js b/packages/e2e-tests/specs/site-editor/settings-sidebar.test.js index 3428ddd654023..c1d4a755a7c57 100644 --- a/packages/e2e-tests/specs/site-editor/settings-sidebar.test.js +++ b/packages/e2e-tests/specs/site-editor/settings-sidebar.test.js @@ -69,6 +69,7 @@ describe( 'Settings sidebar', () => { await visitSiteEditor( { postId: 'emptytheme//singular', postType: 'wp_template', + path: '/templates/single', } ); await enterEditMode(); const templateCardAfterNavigation = await getTemplateCard(); diff --git a/packages/edit-site/src/components/layout/index.js b/packages/edit-site/src/components/layout/index.js index 7c30496abd7c4..5beb5259d5b16 100644 --- a/packages/edit-site/src/components/layout/index.js +++ b/packages/edit-site/src/components/layout/index.js @@ -19,7 +19,7 @@ import { useResizeObserver, } from '@wordpress/compose'; import { __ } from '@wordpress/i18n'; -import { useState, useEffect, useRef } from '@wordpress/element'; +import { useState, useRef } from '@wordpress/element'; import { NavigableRegion } from '@wordpress/interface'; import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; @@ -87,21 +87,20 @@ export default function Layout() { } ); const disableMotion = useReducedMotion(); const isMobileViewport = useViewportMatch( 'medium', '<' ); - const [ isMobileCanvasVisible, setIsMobileCanvasVisible ] = - useState( false ); const canvasPadding = isMobileViewport ? 0 : 24; const showSidebar = - ( isMobileViewport && ! isMobileCanvasVisible ) || + ( isMobileViewport && ! isListPage ) || ( ! isMobileViewport && ( canvasMode === 'view' || ! isEditorPage ) ); const showCanvas = - ( isMobileViewport && isMobileCanvasVisible ) || ! isMobileViewport; + ( isMobileViewport && isEditorPage && canvasMode === 'edit' ) || + ! isMobileViewport || + ! isEditorPage; const showFrame = - ! isEditorPage || ( canvasMode === 'view' && ! isMobileViewport ); - + ( ! isEditorPage && ! isMobileViewport ) || + ( ! isMobileViewport && isEditorPage && canvasMode === 'view' ); const isFullCanvas = - ( isEditorPage && canvasMode === 'edit' && ! isMobileViewport ) || - isMobileCanvasVisible; - // Ideally this effect could be removed if we move the "isMobileCanvasVisible" into the store. + ( isMobileViewport && isListPage ) || + ( isEditorPage && canvasMode === 'edit' ); const [ canvasResizer, canvasSize ] = useResizeObserver(); const [ fullResizer, fullSize ] = useResizeObserver(); const [ forcedWidth, setForcedWidth ] = useState( null ); @@ -112,15 +111,6 @@ export default function Layout() { if ( showFrame && ! isResizing ) { canvasWidth = canvasSize.width - canvasPadding; } - useEffect( () => { - if ( canvasMode === 'view' && isMobileViewport ) { - setIsMobileCanvasVisible( false ); - } - - if ( canvasMode === 'edit' && isMobileViewport ) { - setIsMobileCanvasVisible( true ); - } - }, [ canvasMode, isMobileViewport ] ); // Synchronizing the URL with the store value of canvasMode happens in an effect // This condition ensures the component is only rendered after the synchronization happens @@ -153,37 +143,34 @@ export default function Layout() { ? forcedWidth - 48 : undefined, } } - isMobileCanvasVisible={ isMobileCanvasVisible } - setIsMobileCanvasVisible={ setIsMobileCanvasVisible } /> - { isEditorPage && - ( canvasMode === 'edit' || isMobileCanvasVisible ) && ( - - { canvasMode === 'edit' &&
} - - ) } + { isEditorPage && canvasMode === 'edit' && ( + + { canvasMode === 'edit' &&
} + + ) }
diff --git a/packages/edit-site/src/components/layout/style.scss b/packages/edit-site/src/components/layout/style.scss index 0957bfa20bafb..f9837e5fa3c84 100644 --- a/packages/edit-site/src/components/layout/style.scss +++ b/packages/edit-site/src/components/layout/style.scss @@ -1,5 +1,3 @@ -$hub-height: $grid-unit-20 * 2 + $button-size; - .edit-site-layout { height: 100%; background: $gray-900; @@ -10,24 +8,23 @@ $hub-height: $grid-unit-20 * 2 + $button-size; .edit-site-layout__hub { position: fixed; - top: $canvas-padding; - left: $canvas-padding; + top: 0; + left: 0; width: calc(100vw - #{$canvas-padding * 2}); - height: $hub-height; + height: $header-height; z-index: z-index(".edit-site-layout__hub"); - background: $black; - padding: $grid-unit-20; - padding-left: 0; - border-radius: $radius-block-ui * 4; - box-shadow: $shadow-modal; + .edit-site-layout.is-full-canvas.is-edit-mode & { + width: auto; + padding-right: 0; + } + + @include break-medium { + width: calc(#{$nav-sidebar-width} - #{$canvas-padding * 2}); + } .edit-site-layout.is-full-canvas & { - top: 0; - left: 0; - padding: 0; padding-right: $grid-unit-20; - height: $header-height; border-radius: 0; width: 100vw; box-shadow: none; @@ -37,15 +34,6 @@ $hub-height: $grid-unit-20 * 2 + $button-size; padding-right: 0; } } - - .edit-site-layout.is-full-canvas.is-edit-mode & { - width: auto; - padding-right: 0; - } - - @include break-medium { - width: calc(#{$nav-sidebar-width} - #{$canvas-padding * 2}); - } } .edit-site-layout__header { diff --git a/packages/edit-site/src/components/list/table.js b/packages/edit-site/src/components/list/table.js index d6f2405d76b42..8d561b7e9b818 100644 --- a/packages/edit-site/src/components/list/table.js +++ b/packages/edit-site/src/components/list/table.js @@ -84,6 +84,10 @@ export default function Table( { templateType } ) { diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-template/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-template/index.js new file mode 100644 index 0000000000000..043cee5dd3f2a --- /dev/null +++ b/packages/edit-site/src/components/sidebar-navigation-screen-template/index.js @@ -0,0 +1,52 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { useDispatch } from '@wordpress/data'; +import { Button } from '@wordpress/components'; + +/** + * Internal dependencies + */ +import SidebarNavigationScreen from '../sidebar-navigation-screen'; +import useEditedEntityRecord from '../use-edited-entity-record'; +import { unlock } from '../../private-apis'; +import { store as editSiteStore } from '../../store'; + +const config = { + wp_template: { + path: '/templates/single', + }, + wp_template_part: { + path: '/template-parts/single', + }, +}; + +export default function SidebarNavigationScreenTemplate( { + postType = 'wp_template', +} ) { + const { setCanvasMode } = unlock( useDispatch( editSiteStore ) ); + const { getDescription, getTitle, record } = useEditedEntityRecord(); + let description = getDescription(); + if ( ! description && record.is_custom ) { + description = __( + 'This is a custom template that can be applied manually to any Post or Page.' + ); + } + + return ( + setCanvasMode( 'edit' ) } + > + { __( 'Edit' ) } + + } + content={ description ?

{ description }

: undefined } + /> + ); +} diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-templates-browse/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-templates-browse/index.js new file mode 100644 index 0000000000000..b843a9f7d3b6a --- /dev/null +++ b/packages/edit-site/src/components/sidebar-navigation-screen-templates-browse/index.js @@ -0,0 +1,31 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import SidebarNavigationScreen from '../sidebar-navigation-screen'; + +const config = { + wp_template: { + path: '/templates/all', + title: __( 'All templates' ), + }, + wp_template_part: { + path: '/template-parts/all', + title: __( 'All template parts' ), + }, +}; + +export default function SidebarNavigationScreenTemplatesBrowse( { + postType = 'wp_template', +} ) { + return ( + + ); +} diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-templates/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-templates/index.js index 138cb8ed7bc38..5bba2467c0b73 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-templates/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-templates/index.js @@ -3,7 +3,6 @@ */ import { __experimentalItemGroup as ItemGroup } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; -import { useSelect } from '@wordpress/data'; import { useEntityRecords } from '@wordpress/core-data'; import { decodeEntities } from '@wordpress/html-entities'; import { useViewportMatch } from '@wordpress/compose'; @@ -14,8 +13,6 @@ import { useViewportMatch } from '@wordpress/compose'; import SidebarNavigationScreen from '../sidebar-navigation-screen'; import { useLink } from '../routes/link'; import SidebarNavigationItem from '../sidebar-navigation-item'; -import { useLocation } from '../routes'; -import { store as editSiteStore } from '../../store'; import AddNewTemplate from '../add-new-template'; function omit( object, keys ) { @@ -24,14 +21,6 @@ function omit( object, keys ) { ); } -const Item = ( { item } ) => { - const linkInfo = useLink( item.params ); - const props = item.params - ? { ...omit( item, 'params' ), ...linkInfo } - : item; - return ; -}; - const config = { wp_template: { path: '/templates', @@ -53,23 +42,22 @@ const config = { }, }; +const Item = ( { item } ) => { + const linkInfo = useLink( { + ...item.params, + path: config[ item.params.postType ].path + '/single', + } ); + const props = item.params + ? { ...omit( item, 'params' ), ...linkInfo } + : item; + return ; +}; + export default function SidebarNavigationScreenTemplates( { postType = 'wp_template', } ) { - const { params } = useLocation(); const isMobileViewport = useViewportMatch( 'medium', '<' ); - // Ideally the URL params would be enough. - // Loading the editor should ideally redirect to the home page - // instead of fetching the edited entity here. - const { editedPostId, editedPostType } = useSelect( ( select ) => { - const { getEditedPostType, getEditedPostId } = select( editSiteStore ); - return { - editedPostId: getEditedPostId(), - editedPostType: getEditedPostType(), - }; - }, [] ); - const { records: templates, isResolving: isLoading } = useEntityRecords( 'postType', postType, @@ -100,22 +88,18 @@ export default function SidebarNavigationScreenTemplates( { children: decodeEntities( template.title?.rendered || template.slug ), - 'aria-current': - ( params.postType === postType && - params.postId === template.id ) || - // This is a special case for the home page. - ( editedPostId === template.id && - editedPostType === postType && - !! params.postId ) - ? 'page' - : undefined, } ) ); } + const browseAllLink = useLink( { + postType, + postId: undefined, + path: config[ postType ].path + '/all', + } ); + return ( ) ) } - + { ! isMobileViewport && ( + + ) } } diff --git a/packages/edit-site/src/components/sidebar-navigation-screen/index.js b/packages/edit-site/src/components/sidebar-navigation-screen/index.js index 3f61a7c8208e8..6e34c9f10e4f9 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen/index.js @@ -7,12 +7,11 @@ import { __experimentalNavigatorToParentButton as NavigatorToParentButton, __experimentalNavigatorScreen as NavigatorScreen, } from '@wordpress/components'; -import { isRTL, __, sprintf } from '@wordpress/i18n'; +import { isRTL, __ } from '@wordpress/i18n'; import { chevronRight, chevronLeft } from '@wordpress/icons'; export default function SidebarNavigationScreen( { path, - parentTitle, title, actions, content, @@ -28,15 +27,11 @@ export default function SidebarNavigationScreen( { justify="flex-start" className="edit-site-sidebar-navigation-screen__title-icon" > - { parentTitle ? ( + { path !== '/' ? ( ) : (
diff --git a/packages/edit-site/src/components/sidebar-navigation-screen/style.scss b/packages/edit-site/src/components/sidebar-navigation-screen/style.scss index 1cfb27def5476..efef42e625d27 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen/style.scss +++ b/packages/edit-site/src/components/sidebar-navigation-screen/style.scss @@ -7,13 +7,14 @@ .edit-site-sidebar-navigation-screen__content { margin: 0 $grid-unit-20 $grid-unit-20 $button-size; + color: $gray-600; } .edit-site-sidebar-navigation-screen__title-icon { position: sticky; top: 0; background: $gray-900; - padding-top: $grid-unit-60 + $hub-height + $canvas-padding * 2; + padding-top: $grid-unit-60 + $header-height; box-shadow: 0 $grid-unit-10 $grid-unit-20 $gray-900; margin-bottom: $grid-unit-10; padding-bottom: $grid-unit-10; diff --git a/packages/edit-site/src/components/sidebar/index.js b/packages/edit-site/src/components/sidebar/index.js index 5fa49717b6908..796ac6b0d6567 100644 --- a/packages/edit-site/src/components/sidebar/index.js +++ b/packages/edit-site/src/components/sidebar/index.js @@ -11,12 +11,14 @@ import { store as coreStore } from '@wordpress/core-data'; */ import SidebarNavigationScreenMain from '../sidebar-navigation-screen-main'; import SidebarNavigationScreenTemplates from '../sidebar-navigation-screen-templates'; -import useSyncSidebarPathWithURL from '../sync-state-with-url/use-sync-sidebar-path-with-url'; +import SidebarNavigationScreenTemplate from '../sidebar-navigation-screen-template'; +import useSyncPathWithURL from '../sync-state-with-url/use-sync-path-with-url'; import SidebarNavigationScreenNavigationMenus from '../sidebar-navigation-screen-navigation-menus'; +import SidebarNavigationScreenTemplatesBrowse from '../sidebar-navigation-screen-templates-browse'; import SaveButton from '../save-button'; function SidebarScreens() { - useSyncSidebarPathWithURL(); + useSyncPathWithURL(); return ( <> @@ -24,6 +26,10 @@ function SidebarScreens() { + + + + ); } diff --git a/packages/edit-site/src/components/site-hub/index.js b/packages/edit-site/src/components/site-hub/index.js index 461352af8a926..dc7ef20bd51c0 100644 --- a/packages/edit-site/src/components/site-hub/index.js +++ b/packages/edit-site/src/components/site-hub/index.js @@ -11,9 +11,8 @@ import { Button, __unstableMotion as motion, __experimentalHStack as HStack, - __experimentalVStack as VStack, } from '@wordpress/components'; -import { useReducedMotion, useViewportMatch } from '@wordpress/compose'; +import { useReducedMotion } from '@wordpress/compose'; import { __ } from '@wordpress/i18n'; import { store as blockEditorStore } from '@wordpress/block-editor'; import { store as coreStore } from '@wordpress/core-data'; @@ -23,137 +22,83 @@ import { forwardRef } from '@wordpress/element'; * Internal dependencies */ import { store as editSiteStore } from '../../store'; -import { useLocation } from '../routes'; -import getIsListPage from '../../utils/get-is-list-page'; import SiteIcon from '../site-icon'; -import useEditedEntityRecord from '../use-edited-entity-record'; import { unlock } from '../../private-apis'; const HUB_ANIMATION_DURATION = 0.3; -const SiteHub = forwardRef( - ( { isMobileCanvasVisible, setIsMobileCanvasVisible, ...props }, ref ) => { - const { params } = useLocation(); - const isListPage = getIsListPage( params ); - const isEditorPage = ! isListPage; - const { canvasMode, dashboardLink, entityConfig } = useSelect( - ( select ) => { - select( editSiteStore ).getEditedPostType(); - const { getCanvasMode, getSettings, getEditedPostType } = - unlock( select( editSiteStore ) ); - return { - canvasMode: getCanvasMode(), - dashboardLink: getSettings().__experimentalDashboardLink, - entityConfig: select( coreStore ).getEntityConfig( - 'postType', - getEditedPostType() - ), - }; - }, - [] +const SiteHub = forwardRef( ( props, ref ) => { + const { canvasMode, dashboardLink } = useSelect( ( select ) => { + select( editSiteStore ).getEditedPostType(); + const { getCanvasMode, getSettings } = unlock( + select( editSiteStore ) ); - const disableMotion = useReducedMotion(); - const isMobileViewport = useViewportMatch( 'medium', '<' ); - const { setCanvasMode } = unlock( useDispatch( editSiteStore ) ); - const { clearSelectedBlock } = useDispatch( blockEditorStore ); - const showEditButton = - ( isEditorPage && canvasMode === 'view' && ! isMobileViewport ) || - ( isMobileViewport && - canvasMode === 'view' && - isMobileCanvasVisible ); - const isBackToDashboardButton = - ( ! isMobileViewport && canvasMode === 'view' ) || - ( isMobileViewport && ! isMobileCanvasVisible ); - const showLabels = canvasMode !== 'edit'; - const siteIconButtonProps = isBackToDashboardButton - ? { - href: dashboardLink || 'index.php', - 'aria-label': __( 'Go back to the dashboard' ), - } - : { - label: __( 'Open Navigation Sidebar' ), - onClick: () => { - clearSelectedBlock(); - setIsMobileCanvasVisible( false ); - setCanvasMode( 'view' ); - }, - }; - const { getTitle } = useEditedEntityRecord(); + return { + canvasMode: getCanvasMode(), + dashboardLink: getSettings().__experimentalDashboardLink, + }; + }, [] ); + const disableMotion = useReducedMotion(); + const { setCanvasMode } = unlock( useDispatch( editSiteStore ) ); + const { clearSelectedBlock } = useDispatch( blockEditorStore ); + const isBackToDashboardButton = canvasMode === 'view'; + const showLabels = canvasMode !== 'edit'; + const siteIconButtonProps = isBackToDashboardButton + ? { + href: dashboardLink || 'index.php', + 'aria-label': __( 'Go back to the dashboard' ), + } + : { + label: __( 'Open Navigation Sidebar' ), + onClick: () => { + clearSelectedBlock(); + setCanvasMode( 'view' ); + }, + }; + const siteTitle = useSelect( + ( select ) => + select( coreStore ).getEntityRecord( 'root', 'site' )?.title, + [] + ); - return ( - + - - - - - - { showLabels && ( - -
- { getTitle() } -
-
- { entityConfig?.label } -
-
- ) } -
- - { showEditButton && ( - ) } +
- { isMobileViewport && ! isMobileCanvasVisible && ( - - ) } - - ); - } -); + { showLabels &&
{ siteTitle }
} + + + ); +} ); export default SiteHub; diff --git a/packages/edit-site/src/components/site-hub/style.scss b/packages/edit-site/src/components/site-hub/style.scss index dc7d121e7caf1..004bf19251422 100644 --- a/packages/edit-site/src/components/site-hub/style.scss +++ b/packages/edit-site/src/components/site-hub/style.scss @@ -5,11 +5,6 @@ gap: $grid-unit-10; } -.edit-site-site-hub__edit-button { - height: $grid-unit-40; - color: $white; -} - .edit-site-site-hub__post-type { opacity: 0.6; } @@ -18,12 +13,7 @@ height: $header-height; width: $header-height + 4px; flex-shrink: 0; -} - -.edit-site-layout.is-edit-mode { - .edit-site-site-hub__view-mode-toggle-container { - width: $header-height; - } + background: $gray-900; } .edit-site-site-hub__text-content { diff --git a/packages/edit-site/src/components/sync-state-with-url/use-init-edited-entity-from-url.js b/packages/edit-site/src/components/sync-state-with-url/use-init-edited-entity-from-url.js index a6061d205ffc0..52fae37e0d51d 100644 --- a/packages/edit-site/src/components/sync-state-with-url/use-init-edited-entity-from-url.js +++ b/packages/edit-site/src/components/sync-state-with-url/use-init-edited-entity-from-url.js @@ -2,7 +2,8 @@ * WordPress dependencies */ import { useEffect } from '@wordpress/element'; -import { useDispatch } from '@wordpress/data'; +import { useSelect, useDispatch } from '@wordpress/data'; +import { store as coreDataStore } from '@wordpress/core-data'; /** * Internal dependencies @@ -11,22 +12,55 @@ import { useLocation } from '../routes'; import { store as editSiteStore } from '../../store'; export default function useInitEditedEntityFromURL() { + const { params: { postId, postType, path = '/' } = {} } = useLocation(); + const { isRequestingSite, homepageId } = useSelect( ( select ) => { + const { getSite } = select( coreDataStore ); + const siteData = getSite(); + + return { + isRequestingSite: ! siteData, + homepageId: + siteData?.show_on_front === 'page' + ? siteData.page_on_front + : null, + }; + }, [] ); + const { setTemplate, setTemplatePart, setPage } = useDispatch( editSiteStore ); - const { - params: { postId, postType }, - } = useLocation(); - // Set correct entity on page navigation. useEffect( () => { - // This URL scheme mean we can't open a template part with the context of a given post. - // Potentially posts and pages could be moved to a "context" query string instead. - if ( 'page' === postType || 'post' === postType ) { - setPage( { context: { postType, postId } } ); // Resolves correct template based on ID. - } else if ( 'wp_template' === postType ) { - setTemplate( postId ); - } else if ( 'wp_template_part' === postType ) { - setTemplatePart( postId ); + switch ( path ) { + case '/templates/single': + setTemplate( postId ); + break; + case '/template-parts/single': + setTemplatePart( postId ); + break; + case '/navigation/single': + setPage( { + context: { postType, postId }, + } ); + break; + default: { + if ( homepageId ) { + setPage( { + context: { postType: 'page', postId: homepageId }, + } ); + } else if ( ! isRequestingSite ) { + setPage( { + path: '/', + } ); + } + } } - }, [ postId, postType ] ); + }, [ + path, + postId, + homepageId, + isRequestingSite, + setPage, + setTemplate, + setTemplatePart, + ] ); } diff --git a/packages/edit-site/src/components/sync-state-with-url/use-sync-sidebar-path-with-url.js b/packages/edit-site/src/components/sync-state-with-url/use-sync-path-with-url.js similarity index 62% rename from packages/edit-site/src/components/sync-state-with-url/use-sync-sidebar-path-with-url.js rename to packages/edit-site/src/components/sync-state-with-url/use-sync-path-with-url.js index 885772cdf4046..f7376fcffbe19 100644 --- a/packages/edit-site/src/components/sync-state-with-url/use-sync-sidebar-path-with-url.js +++ b/packages/edit-site/src/components/sync-state-with-url/use-sync-path-with-url.js @@ -9,28 +9,28 @@ import { useEffect, useRef } from '@wordpress/element'; */ import { useLocation, useHistory } from '../routes'; -export default function useSyncSidebarPathWithURL() { +export default function useSyncPathWithURL() { const history = useHistory(); const { params } = useLocation(); - const { sidebar = '/' } = params; + const { path = '/' } = params; const { location, goTo } = useNavigator(); - const currentSidebar = useRef( sidebar ); + const currentPath = useRef( path ); const currentNavigatorLocation = useRef( location.path ); useEffect( () => { - currentSidebar.current = sidebar; - if ( sidebar !== currentNavigatorLocation.current ) { - goTo( sidebar ); + currentPath.current = path; + if ( path !== currentNavigatorLocation.current ) { + goTo( path ); } - }, [ sidebar ] ); + }, [ path ] ); useEffect( () => { currentNavigatorLocation.current = location.path; - if ( location.path !== currentSidebar.current ) { + if ( location.path !== currentPath.current ) { history.push( { ...params, - sidebar: location.path, + path: location.path, } ); } }, [ location.path, history ] ); - return sidebar; + return path; } diff --git a/packages/edit-site/src/components/use-edited-entity-record/index.js b/packages/edit-site/src/components/use-edited-entity-record/index.js index 5d72877095782..66377e0d80976 100644 --- a/packages/edit-site/src/components/use-edited-entity-record/index.js +++ b/packages/edit-site/src/components/use-edited-entity-record/index.js @@ -12,7 +12,7 @@ import { decodeEntities } from '@wordpress/html-entities'; import { store as editSiteStore } from '../../store'; export default function useEditedEntityRecord() { - const { record, title, isLoaded } = useSelect( ( select ) => { + const { record, title, description, isLoaded } = useSelect( ( select ) => { const { getEditedPostType, getEditedPostId } = select( editSiteStore ); const { getEditedEntityRecord } = select( coreStore ); const { __experimentalGetTemplateInfo: getTemplateInfo } = @@ -21,10 +21,12 @@ export default function useEditedEntityRecord() { const postId = getEditedPostId(); const _record = getEditedEntityRecord( 'postType', postType, postId ); const _isLoaded = !! postId; + const templateInfo = getTemplateInfo( _record ); return { record: _record, - title: getTemplateInfo( _record ).title, + title: templateInfo.title, + description: templateInfo.description, isLoaded: _isLoaded, }; }, [] ); @@ -33,5 +35,7 @@ export default function useEditedEntityRecord() { isLoaded, record, getTitle: () => ( title ? decodeEntities( title ) : null ), + getDescription: () => + description ? decodeEntities( description ) : null, }; } diff --git a/packages/edit-site/src/utils/get-is-list-page.js b/packages/edit-site/src/utils/get-is-list-page.js index ef08058d00e82..58a4ebe0bfdf4 100644 --- a/packages/edit-site/src/utils/get-is-list-page.js +++ b/packages/edit-site/src/utils/get-is-list-page.js @@ -1,11 +1,11 @@ /** * Returns if the params match the list page route. * - * @param {Object} params The search params. - * @param {string} params.postId The post ID. - * @param {string} params.postType The post type. + * @param {Object} params The url params. + * @param {string} params.path The current path. + * * @return {boolean} Is list page or not. */ -export default function getIsListPage( { postId, postType } ) { - return !! ( ! postId && postType ); +export default function getIsListPage( { path } ) { + return path === '/templates/all' || path === '/template-parts/all'; } diff --git a/test/e2e/specs/site-editor/block-list-panel-preference.spec.js b/test/e2e/specs/site-editor/block-list-panel-preference.spec.js index 8745a75f3ad46..ede473816235e 100644 --- a/test/e2e/specs/site-editor/block-list-panel-preference.spec.js +++ b/test/e2e/specs/site-editor/block-list-panel-preference.spec.js @@ -12,13 +12,14 @@ test.describe( 'Block list view', () => { await requestUtils.activateTheme( 'twentytwentyone' ); } ); - test( 'Should open by default', async ( { admin, page, siteEditor } ) => { + test( 'Should open by default', async ( { admin, page, editor } ) => { await admin.visitSiteEditor( { postId: 'emptytheme//index', postType: 'wp_template', + path: '/templates/single', } ); - await siteEditor.enterEditMode(); + await editor.canvas.click( 'body' ); // Should display the Preview button. await expect( diff --git a/test/e2e/specs/site-editor/iframe-rendering.spec.js b/test/e2e/specs/site-editor/iframe-rendering.spec.js index 02389cc936f06..a4c39cdbbbfad 100644 --- a/test/e2e/specs/site-editor/iframe-rendering.spec.js +++ b/test/e2e/specs/site-editor/iframe-rendering.spec.js @@ -19,6 +19,7 @@ test.describe( 'Site editor iframe rendering mode', () => { await admin.visitSiteEditor( { postId: 'emptytheme//index', postType: 'wp_template', + path: '/templates/single', } ); const compatMode = await page diff --git a/test/e2e/specs/site-editor/push-to-global-styles.spec.js b/test/e2e/specs/site-editor/push-to-global-styles.spec.js index d51ce41dc3ea8..06c634a1f5498 100644 --- a/test/e2e/specs/site-editor/push-to-global-styles.spec.js +++ b/test/e2e/specs/site-editor/push-to-global-styles.spec.js @@ -16,9 +16,9 @@ test.describe( 'Push to Global Styles button', () => { await requestUtils.activateTheme( 'twentytwentyone' ); } ); - test.beforeEach( async ( { admin, siteEditor } ) => { + test.beforeEach( async ( { admin, editor } ) => { await admin.visitSiteEditor(); - await siteEditor.enterEditMode(); + await editor.canvas.click( 'body' ); } ); test( 'should apply Heading block styles to all Heading blocks', async ( { diff --git a/test/e2e/specs/site-editor/site-editor-inserter.spec.js b/test/e2e/specs/site-editor/site-editor-inserter.spec.js index d3dfedc9bde71..f8ab0a534d858 100644 --- a/test/e2e/specs/site-editor/site-editor-inserter.spec.js +++ b/test/e2e/specs/site-editor/site-editor-inserter.spec.js @@ -16,9 +16,9 @@ test.describe( 'Site Editor Inserter', () => { await requestUtils.activateTheme( 'twentytwentyone' ); } ); - test.beforeEach( async ( { admin, siteEditor } ) => { + test.beforeEach( async ( { admin, editor } ) => { await admin.visitSiteEditor(); - await siteEditor.enterEditMode(); + await editor.canvas.click( 'body' ); } ); test( 'inserter toggle button should toggle global inserter', async ( { diff --git a/test/e2e/specs/site-editor/style-book.spec.js b/test/e2e/specs/site-editor/style-book.spec.js index ca58ff154a481..cf7fa8dfa67c9 100644 --- a/test/e2e/specs/site-editor/style-book.spec.js +++ b/test/e2e/specs/site-editor/style-book.spec.js @@ -18,9 +18,9 @@ test.describe( 'Style Book', () => { await requestUtils.activateTheme( 'twentytwentyone' ); } ); - test.beforeEach( async ( { admin, siteEditor, styleBook, page } ) => { + test.beforeEach( async ( { admin, editor, styleBook, page } ) => { await admin.visitSiteEditor(); - await siteEditor.enterEditMode(); + await editor.canvas.click( 'body' ); await styleBook.open(); await expect( page.locator( 'role=region[name="Style Book"i]' ) diff --git a/test/e2e/specs/site-editor/style-variations.spec.js b/test/e2e/specs/site-editor/style-variations.spec.js index 925be3780c8fd..d662238535745 100644 --- a/test/e2e/specs/site-editor/style-variations.spec.js +++ b/test/e2e/specs/site-editor/style-variations.spec.js @@ -33,13 +33,14 @@ test.describe( 'Global styles variations', () => { admin, page, siteEditorStyleVariations, - siteEditor, + editor, } ) => { await admin.visitSiteEditor( { postId: 'gutenberg-test-themes/style-variations//index', postType: 'wp_template', + path: '/templates/single', } ); - await siteEditor.enterEditMode(); + await editor.canvas.click( 'body' ); await siteEditorStyleVariations.browseStyles(); @@ -70,13 +71,14 @@ test.describe( 'Global styles variations', () => { admin, page, siteEditorStyleVariations, - siteEditor, + editor, } ) => { await admin.visitSiteEditor( { postId: 'gutenberg-test-themes/style-variations//index', postType: 'wp_template', + path: '/templates/single', } ); - await siteEditor.enterEditMode(); + await editor.canvas.click( 'body' ); await siteEditorStyleVariations.browseStyles(); await page.click( 'role=button[name="pink"i]' ); await page.click( @@ -111,13 +113,14 @@ test.describe( 'Global styles variations', () => { admin, page, siteEditorStyleVariations, - siteEditor, + editor, } ) => { await admin.visitSiteEditor( { postId: 'gutenberg-test-themes/style-variations//index', postType: 'wp_template', + path: '/templates/single', } ); - await siteEditor.enterEditMode(); + await editor.canvas.click( 'body' ); await siteEditorStyleVariations.browseStyles(); await page.click( 'role=button[name="yellow"i]' ); await page.click( @@ -158,13 +161,14 @@ test.describe( 'Global styles variations', () => { admin, page, siteEditorStyleVariations, - siteEditor, + editor, } ) => { await admin.visitSiteEditor( { postId: 'gutenberg-test-themes/style-variations//index', postType: 'wp_template', + path: '/templates/single', } ); - await siteEditor.enterEditMode(); + await editor.canvas.click( 'body' ); await siteEditorStyleVariations.browseStyles(); await page.click( 'role=button[name="pink"i]' ); await page.click( @@ -190,13 +194,14 @@ test.describe( 'Global styles variations', () => { admin, page, siteEditorStyleVariations, - siteEditor, + editor, } ) => { await admin.visitSiteEditor( { postId: 'gutenberg-test-themes/style-variations//index', postType: 'wp_template', + path: '/templates/single', } ); - await siteEditor.enterEditMode(); + await editor.canvas.click( 'body' ); await siteEditorStyleVariations.browseStyles(); await page.click( 'role=button[name="yellow"i]' ); diff --git a/test/e2e/specs/site-editor/template-part.spec.js b/test/e2e/specs/site-editor/template-part.spec.js index 8002fafad107c..c33da19290855 100644 --- a/test/e2e/specs/site-editor/template-part.spec.js +++ b/test/e2e/specs/site-editor/template-part.spec.js @@ -23,13 +23,13 @@ test.describe( 'Template Part', () => { admin, editor, page, - siteEditor, } ) => { await admin.visitSiteEditor( { postId: 'emptytheme//header', postType: 'wp_template_part', + path: '/template-parts/single', } ); - await siteEditor.enterEditMode(); + await editor.canvas.click( 'body' ); // Insert a new template block and 'start blank'. await editor.insertBlock( { name: 'core/template-part' } ); @@ -54,11 +54,10 @@ test.describe( 'Template Part', () => { admin, editor, page, - siteEditor, } ) => { // Visit the index. await admin.visitSiteEditor(); - await siteEditor.enterEditMode(); + await editor.canvas.click( 'body' ); const headerTemplateParts = editor.canvas.locator( '[data-type="core/template-part"]' ); @@ -81,12 +80,11 @@ test.describe( 'Template Part', () => { admin, editor, page, - siteEditor, } ) => { const paragraphText = 'Test 2'; await admin.visitSiteEditor(); - await siteEditor.enterEditMode(); + await editor.canvas.click( 'body' ); // Add a block and select it. await editor.insertBlock( { name: 'core/paragraph', @@ -121,13 +119,12 @@ test.describe( 'Template Part', () => { admin, editor, page, - siteEditor, } ) => { const paragraphText1 = 'Test 3'; const paragraphText2 = 'Test 4'; await admin.visitSiteEditor(); - await siteEditor.enterEditMode(); + await editor.canvas.click( 'body' ); // Add a block and select it. await editor.insertBlock( { name: 'core/paragraph', @@ -181,7 +178,6 @@ test.describe( 'Template Part', () => { test( 'can detach blocks from a template part', async ( { admin, editor, - siteEditor, } ) => { const paragraphText = 'Test 3'; @@ -189,8 +185,9 @@ test.describe( 'Template Part', () => { await admin.visitSiteEditor( { postId: 'emptytheme//header', postType: 'wp_template_part', + path: '/template-parts/single', } ); - await siteEditor.enterEditMode(); + await editor.canvas.click( 'body' ); await editor.insertBlock( { name: 'core/paragraph', attributes: { @@ -201,7 +198,7 @@ test.describe( 'Template Part', () => { // Visit the index. await admin.visitSiteEditor(); - await siteEditor.enterEditMode(); + await editor.canvas.click( 'body' ); // Check that the header contains the paragraph added earlier. const paragraph = editor.canvas.locator( `p >> text="${ paragraphText }"` @@ -226,15 +223,15 @@ test.describe( 'Template Part', () => { test( 'shows changes in a template when a template part it contains is modified', async ( { admin, editor, - siteEditor, } ) => { const paragraphText = 'Test 1'; await admin.visitSiteEditor( { postId: 'emptytheme//header', postType: 'wp_template_part', + path: '/template-parts/single', } ); - await siteEditor.enterEditMode(); + await editor.canvas.click( 'body' ); // Edit the header. await editor.insertBlock( { name: 'core/paragraph', @@ -247,7 +244,7 @@ test.describe( 'Template Part', () => { // Visit the index. await admin.visitSiteEditor(); - await siteEditor.enterEditMode(); + await editor.canvas.click( 'body' ); const paragraph = editor.canvas.locator( `p >> text="${ paragraphText }"` ); @@ -260,15 +257,15 @@ test.describe( 'Template Part', () => { admin, editor, page, - siteEditor, } ) => { const paragraphText = 'Test 4'; await admin.visitSiteEditor( { postId: 'emptytheme//header', postType: 'wp_template_part', + path: '/template-parts/single', } ); - await siteEditor.enterEditMode(); + await editor.canvas.click( 'body' ); await editor.insertBlock( { name: 'core/paragraph', attributes: { @@ -302,12 +299,11 @@ test.describe( 'Template Part', () => { test( 'can import a widget area into an empty template part', async ( { admin, - siteEditor, editor, page, } ) => { await admin.visitSiteEditor(); - await siteEditor.enterEditMode(); + await editor.canvas.click( 'body' ); // Add a block and select it. await editor.insertBlock( { @@ -344,12 +340,11 @@ test.describe( 'Template Part', () => { test( 'can not import a widget area into a non-empty template part', async ( { admin, - siteEditor, editor, page, } ) => { await admin.visitSiteEditor(); - await siteEditor.enterEditMode(); + await editor.canvas.click( 'body' ); // Select existing header template part. await editor.selectBlocks( diff --git a/test/e2e/specs/site-editor/template-revert.spec.js b/test/e2e/specs/site-editor/template-revert.spec.js index dcf8ebedb8b78..f1f6b3eb5d014 100644 --- a/test/e2e/specs/site-editor/template-revert.spec.js +++ b/test/e2e/specs/site-editor/template-revert.spec.js @@ -20,10 +20,10 @@ test.describe( 'Template Revert', () => { await requestUtils.deleteAllTemplates( 'wp_template_part' ); await requestUtils.activateTheme( 'twentytwentyone' ); } ); - test.beforeEach( async ( { admin, requestUtils, siteEditor } ) => { + test.beforeEach( async ( { admin, requestUtils, editor } ) => { await requestUtils.deleteAllTemplates( 'wp_template' ); await admin.visitSiteEditor(); - await siteEditor.enterEditMode(); + await editor.canvas.click( 'body' ); } ); test( 'should delete the template after saving the reverted template', async ( { @@ -248,7 +248,6 @@ test.describe( 'Template Revert', () => { editor, page, templateRevertUtils, - siteEditor, } ) => { await editor.insertBlock( { name: 'core/paragraph', @@ -267,7 +266,7 @@ test.describe( 'Template Revert', () => { await editor.saveSiteEditorEntities(); await admin.visitSiteEditor(); - await siteEditor.enterEditMode(); + await editor.canvas.click( 'body' ); const contentAfter = await templateRevertUtils.getCurrentSiteEditorContent(); expect( contentAfter ).toEqual( contentBefore ); diff --git a/test/e2e/specs/site-editor/title.spec.js b/test/e2e/specs/site-editor/title.spec.js index 9b1ad4a5098d1..d8059531aabc5 100644 --- a/test/e2e/specs/site-editor/title.spec.js +++ b/test/e2e/specs/site-editor/title.spec.js @@ -15,14 +15,15 @@ test.describe( 'Site editor title', () => { test( 'displays the selected template name in the title for the index template', async ( { admin, page, - siteEditor, + editor, } ) => { // Navigate to a template. await admin.visitSiteEditor( { postId: 'emptytheme//index', postType: 'wp_template', + path: '/templates/single', } ); - await siteEditor.enterEditMode(); + await editor.canvas.click( 'body' ); const title = await page.locator( 'role=region[name="Editor top bar"i] >> role=heading[level=1]' ); @@ -33,14 +34,15 @@ test.describe( 'Site editor title', () => { test( 'displays the selected template name in the title for the header template', async ( { admin, page, - siteEditor, + editor, } ) => { // Navigate to a template part. await admin.visitSiteEditor( { postId: 'emptytheme//header', postType: 'wp_template_part', + path: '/template-parts/single', } ); - await siteEditor.enterEditMode(); + await editor.canvas.click( 'body' ); const title = await page.locator( 'role=region[name="Editor top bar"i] >> role=heading[level=1]' ); @@ -51,13 +53,14 @@ test.describe( 'Site editor title', () => { test( "displays the selected template part's name in the secondary title when a template part is selected from List View", async ( { admin, page, - siteEditor, + editor, } ) => { await admin.visitSiteEditor( { postId: 'emptytheme//index', postType: 'wp_template', + path: '/templates/single', } ); - await siteEditor.enterEditMode(); + await editor.canvas.click( 'body' ); // Select the header template part via list view. await page.click( 'role=button[name="List View"i]' ); const listView = await page.locator( diff --git a/test/e2e/specs/site-editor/writing-flow.spec.js b/test/e2e/specs/site-editor/writing-flow.spec.js index f8fa78b4fd812..0f8d4773b84c5 100644 --- a/test/e2e/specs/site-editor/writing-flow.spec.js +++ b/test/e2e/specs/site-editor/writing-flow.spec.js @@ -18,14 +18,14 @@ test.describe( 'Site editor writing flow', () => { editor, page, pageUtils, - siteEditor, } ) => { // Navigate to a template part with only a couple of blocks. await admin.visitSiteEditor( { postId: 'emptytheme//header', postType: 'wp_template_part', + path: '/template-parts/single', } ); - await siteEditor.enterEditMode(); + await editor.canvas.click( 'body' ); // Select the first site title block. const siteTitleBlock = editor.canvas.locator( 'role=document[name="Block: Site Title"i]' @@ -47,14 +47,14 @@ test.describe( 'Site editor writing flow', () => { editor, page, pageUtils, - siteEditor, } ) => { // Navigate to a template part with only a couple of blocks. await admin.visitSiteEditor( { postId: 'emptytheme//header', postType: 'wp_template_part', + path: '/template-parts/single', } ); - await siteEditor.enterEditMode(); + await editor.canvas.click( 'body' ); // Make sure the sidebar is open. await editor.openDocumentSettingsSidebar();