diff --git a/packages/edit-site/src/components/app/index.js b/packages/edit-site/src/components/app/index.js index 7ee4e21e02a3ce..05e6dc8b829785 100644 --- a/packages/edit-site/src/components/app/index.js +++ b/packages/edit-site/src/components/app/index.js @@ -1,66 +1,56 @@ /** * WordPress dependencies */ -import { SlotFillProvider } from '@wordpress/components'; -import { UnsavedChangesWarning } from '@wordpress/editor'; -import { store as noticesStore } from '@wordpress/notices'; -import { useDispatch } from '@wordpress/data'; -import { __, sprintf } from '@wordpress/i18n'; -import { PluginArea } from '@wordpress/plugins'; +import { useSelect } from '@wordpress/data'; +import { store as interfaceStore } from '@wordpress/interface'; +import { store as coreStore } from '@wordpress/core-data'; /** * Internal dependencies */ -import { Routes } from '../routes'; +import { store as editSiteStore } from '../../store'; +import { useLocation } from '../routes'; import Editor from '../editor'; import List from '../list'; -import NavigationSidebar from '../navigation-sidebar'; import getIsListPage from '../../utils/get-is-list-page'; export default function EditSiteApp( { reboot } ) { - const { createErrorNotice } = useDispatch( noticesStore ); + const { params } = useLocation(); + const { + isInserterOpen, + isListViewOpen, + sidebarIsOpened, + postType, + } = useSelect( + ( select ) => { + const { isInserterOpened, isListViewOpened } = select( + editSiteStore + ); - function onPluginAreaError( name ) { - createErrorNotice( - sprintf( - /* translators: %s: plugin name */ - __( - 'The "%s" plugin has encountered an error and cannot be rendered.' - ), - name - ) - ); - } - - return ( - - + // The currently selected entity to display. Typically template or template part. + return { + isInserterOpen: isInserterOpened(), + isListViewOpen: isListViewOpened(), + sidebarIsOpened: !! select( + interfaceStore + ).getActiveComplementaryArea( editSiteStore.name ), + postType: select( coreStore ).getPostType( params.postType ), + }; + }, + [ params.postType ] + ); - - { ( { params } ) => { - const isListPage = getIsListPage( params ); + const isListPage = getIsListPage( params ); - return ( - <> - { isListPage ? ( - - ) : ( - - ) } - - { /* Keep the instance of the sidebar to ensure focus will not be lost - * when navigating to other pages. */ } - - - ); - } } - - - ); + return isListPage + ? List.renderLayout( { + postType, + activeTemplateType: params.postType, + } ) + : Editor.renderLayout( { + isInserterOpen, + isListViewOpen, + sidebarIsOpened, + reboot, + } ); } diff --git a/packages/edit-site/src/components/editor/actions.js b/packages/edit-site/src/components/editor/actions.js new file mode 100644 index 00000000000000..ad845986be61c4 --- /dev/null +++ b/packages/edit-site/src/components/editor/actions.js @@ -0,0 +1,44 @@ +/** + * WordPress dependencies + */ +import { useCallback } from '@wordpress/element'; +import { useSelect, useDispatch } from '@wordpress/data'; +import { Button } from '@wordpress/components'; +import { EntitiesSavedStates } from '@wordpress/editor'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import { store as editSiteStore } from '../../store'; + +export default function EditorActions() { + const isEntitiesSavedStatesOpen = useSelect( + ( select ) => select( editSiteStore ).getIsEntitiesSavedStatesOpen(), + [] + ); + const { setIsEntitiesSavedStatesOpen } = useDispatch( editSiteStore ); + const openEntitiesSavedStates = useCallback( + () => setIsEntitiesSavedStatesOpen( true ), + [ setIsEntitiesSavedStatesOpen ] + ); + const closeEntitiesSavedStates = useCallback( + () => setIsEntitiesSavedStatesOpen( false ), + [ setIsEntitiesSavedStatesOpen ] + ); + + return isEntitiesSavedStatesOpen ? ( + + ) : ( +
+ +
+ ); +} diff --git a/packages/edit-site/src/components/editor/index.js b/packages/edit-site/src/components/editor/index.js index a092754465fbd7..53ad9fde4535ed 100644 --- a/packages/edit-site/src/components/editor/index.js +++ b/packages/edit-site/src/components/editor/index.js @@ -1,75 +1,56 @@ /** * WordPress dependencies */ -import { useEffect, useState, useMemo, useCallback } from '@wordpress/element'; +import { useMemo, useEffect } from '@wordpress/element'; import { useSelect, useDispatch } from '@wordpress/data'; -import { Popover, Button, Notice } from '@wordpress/components'; +import { Notice } from '@wordpress/components'; import { EntityProvider, store as coreStore } from '@wordpress/core-data'; import { BlockContextProvider, BlockBreadcrumb } from '@wordpress/block-editor'; import { - InterfaceSkeleton, ComplementaryArea, store as interfaceStore, } from '@wordpress/interface'; -import { - EditorNotices, - EditorSnackbars, - EntitiesSavedStates, -} from '@wordpress/editor'; +import { EditorNotices } from '@wordpress/editor'; import { __ } from '@wordpress/i18n'; -import { - ShortcutProvider, - store as keyboardShortcutsStore, -} from '@wordpress/keyboard-shortcuts'; /** * Internal dependencies */ import Header from '../header'; import { SidebarComplementaryAreaFills } from '../sidebar'; -import NavigationSidebar from '../navigation-sidebar'; import BlockEditor from '../block-editor'; import CodeEditor from '../code-editor'; import KeyboardShortcuts from '../keyboard-shortcuts'; -import URLQueryController from '../url-query-controller'; +import useURLQueryController from './use-url-query-controller'; import InserterSidebar from '../secondary-sidebar/inserter-sidebar'; import ListViewSidebar from '../secondary-sidebar/list-view-sidebar'; import ErrorBoundary from '../error-boundary'; import WelcomeGuide from '../welcome-guide'; import { store as editSiteStore } from '../../store'; import { GlobalStylesRenderer } from './global-styles-renderer'; -import { GlobalStylesProvider } from '../global-styles/global-styles-provider'; import useTitle from '../routes/use-title'; +import Layout from '../layout'; +import EditorActions from './actions'; const interfaceLabels = { secondarySidebar: __( 'Block Library' ), drawer: __( 'Navigation Sidebar' ), }; - function Editor( { onError } ) { const { - isInserterOpen, - isListViewOpen, - sidebarIsOpened, settings, entityId, templateType, page, template, templateResolved, - isNavigationOpen, - previousShortcut, - nextShortcut, editorMode, } = useSelect( ( select ) => { const { - isInserterOpened, - isListViewOpened, getSettings, getEditedPostType, getEditedPostId, getPage, - isNavigationOpened, getEditorMode, } = select( editSiteStore ); const { hasFinishedResolution, getEntityRecord } = select( coreStore ); @@ -78,11 +59,6 @@ function Editor( { onError } ) { // The currently selected entity to display. Typically template or template part. return { - isInserterOpen: isInserterOpened(), - isListViewOpen: isListViewOpened(), - sidebarIsOpened: !! select( - interfaceStore - ).getActiveComplementaryArea( editSiteStore.name ), settings: getSettings(), templateType: postType, page: getPage(), @@ -97,31 +73,12 @@ function Editor( { onError } ) { ] ) : false, entityId: postId, - isNavigationOpen: isNavigationOpened(), - previousShortcut: select( - keyboardShortcutsStore - ).getAllShortcutKeyCombinations( 'core/edit-site/previous-region' ), - nextShortcut: select( - keyboardShortcutsStore - ).getAllShortcutKeyCombinations( 'core/edit-site/next-region' ), editorMode: getEditorMode(), }; }, [] ); const { setPage, setIsInserterOpened } = useDispatch( editSiteStore ); const { enableComplementaryArea } = useDispatch( interfaceStore ); - const [ - isEntitiesSavedStatesOpen, - setIsEntitiesSavedStatesOpen, - ] = useState( false ); - const openEntitiesSavedStates = useCallback( - () => setIsEntitiesSavedStatesOpen( true ), - [] - ); - const closeEntitiesSavedStates = useCallback( () => { - setIsEntitiesSavedStatesOpen( false ); - }, [] ); - const blockContext = useMemo( () => ( { ...page?.context, @@ -143,14 +100,6 @@ function Editor( { onError } ) { [ page?.context ] ); - useEffect( () => { - if ( isNavigationOpen ) { - document.body.classList.add( 'is-navigation-sidebar-open' ); - } else { - document.body.classList.remove( 'is-navigation-sidebar-open' ); - } - }, [ isNavigationOpen ] ); - useEffect( function openGlobalStylesOnLoad() { const searchParams = new URLSearchParams( window.location.search ); @@ -170,143 +119,86 @@ function Editor( { onError } ) { templateType !== undefined && entityId !== undefined; - const secondarySidebar = () => { - if ( isInserterOpen ) { - return ; - } - if ( isListViewOpen ) { - return ; - } - return null; - }; - // Only announce the title once the editor is ready to prevent "Replace" - // action in from double-announcing. + // action in useURlQueryController from double-announcing. useTitle( isReady && __( 'Editor (beta)' ) ); + useURLQueryController(); + + if ( ! isReady ) { + return null; + } + return ( - <> - - { isReady && ( - - - - - - - - - - - ) - } - drawer={ - - } - header={ -
- } - notices={ } - content={ - <> - - { editorMode === 'visual' && - template && ( - - ) } - { editorMode === 'text' && - template && ( - - ) } - { templateResolved && - ! template && - settings?.siteUrl && - entityId && ( - - { __( - "You attempted to edit an item that doesn't exist. Perhaps it was deleted?" - ) } - - ) } - - - } - actions={ - <> - { isEntitiesSavedStatesOpen ? ( - - ) : ( -
- -
- ) } - - } - footer={ - - } - shortcuts={ { - previous: previousShortcut, - next: nextShortcut, - } } - /> - - - - - - - - - ) } - + + + + + + + + + { editorMode === 'visual' && template && ( + + ) } + { editorMode === 'text' && template && } + { templateResolved && + ! template && + settings?.siteUrl && + entityId && ( + + { __( + "You attempted to edit an item that doesn't exist. Perhaps it was deleted?" + ) } + + ) } + + + + + ); } + +Editor.renderLayout = function renderEditorLayout( { + sidebarIsOpened, + isInserterOpen, + isListViewOpen, + reboot, +} ) { + let secondarySidebar = null; + if ( isInserterOpen ) { + secondarySidebar = ; + } else if ( isListViewOpen ) { + secondarySidebar = ; + } + + return ( + + ) + } + secondarySidebar={ secondarySidebar } + header={
} + content={ } + actions={ } + footer={ } + > + + + ); +}; + export default Editor; diff --git a/packages/edit-site/src/components/url-query-controller/index.js b/packages/edit-site/src/components/editor/use-url-query-controller.js similarity index 94% rename from packages/edit-site/src/components/url-query-controller/index.js rename to packages/edit-site/src/components/editor/use-url-query-controller.js index 8e4f8b17aef6f1..c513331d2ae9ba 100644 --- a/packages/edit-site/src/components/url-query-controller/index.js +++ b/packages/edit-site/src/components/editor/use-url-query-controller.js @@ -10,7 +10,7 @@ import { useDispatch } from '@wordpress/data'; import { useLocation } from '../routes'; import { store as editSiteStore } from '../../store'; -export default function URLQueryController() { +export default function useURLQueryController() { const { setTemplate, setTemplatePart, setPage } = useDispatch( editSiteStore ); diff --git a/packages/edit-site/src/components/header/index.js b/packages/edit-site/src/components/header/index.js index 0247f13c11fbca..44cc2eae864c68 100644 --- a/packages/edit-site/src/components/header/index.js +++ b/packages/edit-site/src/components/header/index.js @@ -31,10 +31,7 @@ const preventDefault = ( event ) => { event.preventDefault(); }; -export default function Header( { - openEntitiesSavedStates, - isEntitiesSavedStatesOpen, -} ) { +export default function Header() { const inserterButton = useRef(); const { deviceType, @@ -173,10 +170,7 @@ export default function Header( { setDeviceType={ setPreviewDeviceType } /> ) } - + diff --git a/packages/edit-site/src/components/keyboard-shortcuts/index.js b/packages/edit-site/src/components/keyboard-shortcuts/index.js index 9b6cb72906e641..4288f2255e2198 100644 --- a/packages/edit-site/src/components/keyboard-shortcuts/index.js +++ b/packages/edit-site/src/components/keyboard-shortcuts/index.js @@ -18,7 +18,7 @@ import { store as editSiteStore } from '../../store'; import { SIDEBAR_BLOCK } from '../sidebar/constants'; import { STORE_NAME } from '../../store/constants'; -function KeyboardShortcuts( { openEntitiesSavedStates } ) { +function KeyboardShortcuts() { const { __experimentalGetDirtyEntityRecords, isSavingEntityRecord, @@ -36,9 +36,11 @@ function KeyboardShortcuts( { openEntitiesSavedStates } ) { [] ); const { redo, undo } = useDispatch( coreStore ); - const { setIsListViewOpened, switchEditorMode } = useDispatch( - editSiteStore - ); + const { + setIsListViewOpened, + switchEditorMode, + setIsEntitiesSavesStateOpen, + } = useDispatch( editSiteStore ); const { enableComplementaryArea, disableComplementaryArea } = useDispatch( interfaceStore ); @@ -53,7 +55,7 @@ function KeyboardShortcuts( { openEntitiesSavedStates } ) { ); if ( ! isSaving && isDirty ) { - openEntitiesSavedStates(); + setIsEntitiesSavesStateOpen( true ); } } ); diff --git a/packages/edit-site/src/components/layout/index.js b/packages/edit-site/src/components/layout/index.js new file mode 100644 index 00000000000000..ccaedce296fc3e --- /dev/null +++ b/packages/edit-site/src/components/layout/index.js @@ -0,0 +1,103 @@ +/** + * WordPress dependencies + */ +import { useEffect } from '@wordpress/element'; +import { useSelect, useDispatch } from '@wordpress/data'; +import { SlotFillProvider, Popover } from '@wordpress/components'; +import { EntityProvider } from '@wordpress/core-data'; +import { InterfaceSkeleton } from '@wordpress/interface'; +import { EditorSnackbars, UnsavedChangesWarning } from '@wordpress/editor'; +import { + ShortcutProvider, + store as keyboardShortcutsStore, +} from '@wordpress/keyboard-shortcuts'; +import { __, sprintf } from '@wordpress/i18n'; +import { store as noticesStore } from '@wordpress/notices'; +import { PluginArea } from '@wordpress/plugins'; + +/** + * Internal dependencies + */ +import NavigationSidebar from '../navigation-sidebar'; +import { GlobalStylesProvider } from '../global-styles/global-styles-provider'; +import { store as editSiteStore } from '../../store'; + +function Layout( { + children, + isNavigationDefaultOpen, + activeTemplateType, + ...props +} ) { + const { isNavigationOpen, previousShortcut, nextShortcut } = useSelect( + ( select ) => { + const { isNavigationOpened } = select( editSiteStore ); + const { getAllShortcutKeyCombinations } = select( + keyboardShortcutsStore + ); + + // The currently selected entity to display. Typically template or template part. + return { + isNavigationOpen: isNavigationOpened(), + previousShortcut: getAllShortcutKeyCombinations( + 'core/edit-site/previous-region' + ), + nextShortcut: getAllShortcutKeyCombinations( + 'core/edit-site/next-region' + ), + }; + }, + [] + ); + const { createErrorNotice } = useDispatch( noticesStore ); + + useEffect( () => { + if ( isNavigationOpen ) { + document.body.classList.add( 'is-navigation-sidebar-open' ); + } else { + document.body.classList.remove( 'is-navigation-sidebar-open' ); + } + }, [ isNavigationOpen ] ); + + function onPluginAreaError( name ) { + createErrorNotice( + sprintf( + /* translators: %s: plugin name */ + __( + 'The "%s" plugin has encountered an error and cannot be rendered.' + ), + name + ) + ); + } + + return ( + + + + + + + } + notices={ } + shortcuts={ { + previous: previousShortcut, + next: nextShortcut, + } } + { ...props } + /> + + + { children } + + + + + ); +} + +export default Layout; diff --git a/packages/edit-site/src/components/list/header.js b/packages/edit-site/src/components/list/header.js index 4f98ecf7843d5b..bdbde06689c28f 100644 --- a/packages/edit-site/src/components/list/header.js +++ b/packages/edit-site/src/components/list/header.js @@ -9,8 +9,12 @@ import { __experimentalHeading as Heading } from '@wordpress/components'; * Internal dependencies */ import AddNewTemplate from '../add-new-template'; +import { useLocation } from '../routes'; -export default function Header( { templateType } ) { +export default function Header() { + const { + params: { postType: templateType }, + } = useLocation(); const postType = useSelect( ( select ) => select( coreStore ).getPostType( templateType ), [ templateType ] diff --git a/packages/edit-site/src/components/list/index.js b/packages/edit-site/src/components/list/index.js index 2b56930142ad4a..c758dc40b41c7f 100644 --- a/packages/edit-site/src/components/list/index.js +++ b/packages/edit-site/src/components/list/index.js @@ -1,60 +1,26 @@ -/** - * External dependencies - */ -import classnames from 'classnames'; - /** * WordPress dependencies */ -import { store as coreStore } from '@wordpress/core-data'; -import { useSelect } from '@wordpress/data'; -import { InterfaceSkeleton } from '@wordpress/interface'; import { __, sprintf } from '@wordpress/i18n'; -import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; -import { EditorSnackbars } from '@wordpress/editor'; /** * Internal dependencies */ -import useRegisterShortcuts from './use-register-shortcuts'; import Header from './header'; -import NavigationSidebar from '../navigation-sidebar'; import Table from './table'; -import { store as editSiteStore } from '../../store'; -import { useLocation } from '../routes'; -import useTitle from '../routes/use-title'; - -export default function List() { - const { - params: { postType: templateType }, - } = useLocation(); +import Layout from '../layout'; +import useRegisterShortcuts from './use-register-shortcuts'; +function List() { useRegisterShortcuts(); - const { previousShortcut, nextShortcut, isNavigationOpen } = useSelect( - ( select ) => { - return { - previousShortcut: select( - keyboardShortcutsStore - ).getAllShortcutKeyCombinations( - 'core/edit-site/previous-region' - ), - nextShortcut: select( - keyboardShortcutsStore - ).getAllShortcutKeyCombinations( 'core/edit-site/next-region' ), - isNavigationOpen: select( editSiteStore ).isNavigationOpened(), - }; - }, - [] - ); - - const postType = useSelect( - ( select ) => select( coreStore ).getPostType( templateType ), - [ templateType ] - ); - - useTitle( postType?.labels?.name ); + return ; +} +List.renderLayout = function renderListLayout( { + postType, + activeTemplateType, +} ) { // `postType` could load in asynchronously. Only provide the detailed region labels if // the postType has loaded, otherwise `InterfaceSkeleton` will fallback to the defaults. const itemsListLabel = postType?.labels?.items_list; @@ -74,22 +40,18 @@ export default function List() { : undefined; return ( - } - drawer={ } - notices={ } - content={
} - shortcuts={ { - previous: previousShortcut, - next: nextShortcut, - } } + header={
} + content={ } /> ); -} +}; + +export default List; diff --git a/packages/edit-site/src/components/list/style.scss b/packages/edit-site/src/components/list/style.scss index 0ad240e19f768a..c575322fc0c6a2 100644 --- a/packages/edit-site/src/components/list/style.scss +++ b/packages/edit-site/src/components/list/style.scss @@ -118,7 +118,7 @@ } } -.edit-site-list.is-navigation-open .components-snackbar-list { +body.is-navigation-sidebar-open .edit-site-list .components-snackbar-list { @include break-medium() { margin-left: $nav-sidebar-width; } diff --git a/packages/edit-site/src/components/list/table.js b/packages/edit-site/src/components/list/table.js index 47e7ca49d4aeba..f3ad18c29b71f6 100644 --- a/packages/edit-site/src/components/list/table.js +++ b/packages/edit-site/src/components/list/table.js @@ -13,11 +13,15 @@ import { decodeEntities } from '@wordpress/html-entities'; /** * Internal dependencies */ +import { useLocation } from '../routes'; import Link from '../routes/link'; import Actions from './actions'; import AddedBy from './added-by'; -export default function Table( { templateType } ) { +export default function Table() { + const { + params: { postType: templateType }, + } = useLocation(); const { templates, isLoading, postType } = useSelect( ( select ) => { const { diff --git a/packages/edit-site/src/components/navigation-sidebar/index.js b/packages/edit-site/src/components/navigation-sidebar/index.js index 9d03ec1ca7662a..aa91b5b609be88 100644 --- a/packages/edit-site/src/components/navigation-sidebar/index.js +++ b/packages/edit-site/src/components/navigation-sidebar/index.js @@ -18,11 +18,6 @@ export const { Slot: NavigationPanelPreviewSlot, } = createSlotFill( 'EditSiteNavigationPanelPreview' ); -const { - Fill: NavigationSidebarFill, - Slot: NavigationSidebarSlot, -} = createSlotFill( 'EditSiteNavigationSidebar' ); - function NavigationSidebar( { isDefaultOpen = false, activeTemplateType } ) { const isDesktopViewport = useViewportMatch( 'medium' ); const { setIsNavigationPanelOpened } = useDispatch( editSiteStore ); @@ -35,14 +30,12 @@ function NavigationSidebar( { isDefaultOpen = false, activeTemplateType } ) { ); return ( - + <> - + ); } -NavigationSidebar.Slot = NavigationSidebarSlot; - export default NavigationSidebar; diff --git a/packages/edit-site/src/components/navigation-sidebar/navigation-toggle/index.js b/packages/edit-site/src/components/navigation-sidebar/navigation-toggle/index.js index 2cdc68858e480c..df311c25bb25cb 100644 --- a/packages/edit-site/src/components/navigation-sidebar/navigation-toggle/index.js +++ b/packages/edit-site/src/components/navigation-sidebar/navigation-toggle/index.js @@ -6,8 +6,8 @@ import classnames from 'classnames'; /** * WordPress dependencies */ +import { useRef } from '@wordpress/element'; import { useSelect, useDispatch } from '@wordpress/data'; -import { useEffect, useRef } from '@wordpress/element'; import { Button, Icon, @@ -22,6 +22,7 @@ import { useReducedMotion } from '@wordpress/compose'; * Internal dependencies */ import { store as editSiteStore } from '../../../store'; +import useResetFocusOnRouteChange from '../../routes/use-reset-focus-on-route-change'; function NavigationToggle( { icon } ) { const { isNavigationOpen, isRequestingSiteIcon, siteIconUrl } = useSelect( @@ -46,16 +47,6 @@ function NavigationToggle( { icon } ) { const disableMotion = useReducedMotion(); - const navigationToggleRef = useRef(); - - useEffect( () => { - // TODO: Remove this effect when alternative solution is merged. - // See: https://github.com/WordPress/gutenberg/pull/37314 - if ( ! isNavigationOpen ) { - navigationToggleRef.current.focus(); - } - }, [ isNavigationOpen ] ); - const toggleNavigationPanel = () => setIsNavigationPanelOpened( ! isNavigationOpen ); @@ -88,6 +79,9 @@ function NavigationToggle( { icon } ) { 'has-icon': siteIconUrl, } ); + const buttonRef = useRef(); + useResetFocusOnRouteChange( buttonRef ); + return ( { buttonIcon } diff --git a/packages/edit-site/src/components/routes/index.js b/packages/edit-site/src/components/routes/index.js index b24fa069f1efd2..7ef19280d44433 100644 --- a/packages/edit-site/src/components/routes/index.js +++ b/packages/edit-site/src/components/routes/index.js @@ -46,7 +46,7 @@ export function Routes( { children } ) { return ( - { children( location ) } + { children } ); diff --git a/packages/edit-site/src/components/routes/use-reset-focus-on-route-change.js b/packages/edit-site/src/components/routes/use-reset-focus-on-route-change.js new file mode 100644 index 00000000000000..15a134bd4ba8d6 --- /dev/null +++ b/packages/edit-site/src/components/routes/use-reset-focus-on-route-change.js @@ -0,0 +1,45 @@ +/** + * External dependencies + */ +import { Action } from 'history'; + +/** + * WordPress dependencies + */ +import { useRef, useEffect } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import { useLocation, useHistory } from './index'; + +export default function useResetFocusOnRouteChange( targetRef ) { + const history = useHistory(); + const location = useLocation(); + const isInitialPageLoadRef = useRef( true ); + const expectRedirectionRef = useRef( false ); + + useEffect( () => { + // Don't focus for initial page load. + if ( isInitialPageLoadRef.current ) { + isInitialPageLoadRef.current = false; + expectRedirectionRef.current = true; + return; + } + // Don't focus for the initial page redirection. + // TODO: This can be removed once #36873 is resolved. + if ( expectRedirectionRef.current ) { + expectRedirectionRef.current = false; + if ( history.action === Action.Replace ) { + return; + } + } + + const activeElement = targetRef.current?.ownerDocument.activeElement; + + // Don't refocus if the activeElement is still on the page (like NavLink). + if ( ! activeElement || activeElement === document.body ) { + targetRef.current?.focus(); + } + }, [ location, targetRef, history ] ); +} diff --git a/packages/edit-site/src/components/save-button/index.js b/packages/edit-site/src/components/save-button/index.js index bd6743ee2dd14f..ac12ae13be3f56 100644 --- a/packages/edit-site/src/components/save-button/index.js +++ b/packages/edit-site/src/components/save-button/index.js @@ -6,28 +6,37 @@ import { some } from 'lodash'; /** * WordPress dependencies */ -import { useSelect } from '@wordpress/data'; +import { useSelect, useDispatch } from '@wordpress/data'; import { Button } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; import { store as coreStore } from '@wordpress/core-data'; -export default function SaveButton( { - openEntitiesSavedStates, - isEntitiesSavedStatesOpen, -} ) { - const { isDirty, isSaving } = useSelect( ( select ) => { - const { - __experimentalGetDirtyEntityRecords, - isSavingEntityRecord, - } = select( coreStore ); - const dirtyEntityRecords = __experimentalGetDirtyEntityRecords(); - return { - isDirty: dirtyEntityRecords.length > 0, - isSaving: some( dirtyEntityRecords, ( record ) => - isSavingEntityRecord( record.kind, record.name, record.key ) - ), - }; - }, [] ); +/** + * Internal dependencies + */ +import { store as editSiteStore } from '../../store'; + +export default function SaveButton() { + const { isDirty, isSaving, isEntitiesSavedStatesOpen } = useSelect( + ( select ) => { + const { + __experimentalGetDirtyEntityRecords, + isSavingEntityRecord, + } = select( coreStore ); + const dirtyEntityRecords = __experimentalGetDirtyEntityRecords(); + return { + isDirty: dirtyEntityRecords.length > 0, + isSaving: some( dirtyEntityRecords, ( record ) => + isSavingEntityRecord( record.kind, record.name, record.key ) + ), + isEntitiesSavedStatesOpen: select( + editSiteStore + ).getIsEntitiesSavedStatesOpen(), + }; + }, + [] + ); + const { setIsEntitiesSavedStatesOpen } = useDispatch( editSiteStore ); const disabled = ! isDirty || isSaving; @@ -40,7 +49,11 @@ export default function SaveButton( { aria-expanded={ isEntitiesSavedStatesOpen } disabled={ disabled } isBusy={ isSaving } - onClick={ disabled ? undefined : openEntitiesSavedStates } + onClick={ + disabled + ? undefined + : () => setIsEntitiesSavedStatesOpen( true ) + } > { __( 'Save' ) } diff --git a/packages/edit-site/src/index.js b/packages/edit-site/src/index.js index 7e51c857433d53..97046e1b8c06e2 100644 --- a/packages/edit-site/src/index.js +++ b/packages/edit-site/src/index.js @@ -22,6 +22,7 @@ import { getQueryArgs } from '@wordpress/url'; */ import './hooks'; import { store as editSiteStore } from './store'; +import { Routes } from './components/routes'; import EditSiteApp from './components/app'; import getIsListPage from './utils/get-is-list-page'; import redirectToHomepage from './components/routes/redirect-to-homepage'; @@ -87,7 +88,12 @@ export async function reinitializeEditor( target, settings ) { } } - render( , target ); + render( + + + , + target + ); } /** diff --git a/packages/edit-site/src/store/actions.js b/packages/edit-site/src/store/actions.js index 92eee1dd6778bc..17e42537445abf 100644 --- a/packages/edit-site/src/store/actions.js +++ b/packages/edit-site/src/store/actions.js @@ -463,3 +463,10 @@ export const switchEditorMode = ( mode ) => ( { dispatch, registry } ) => { speak( __( 'Mosaic view selected' ), 'assertive' ); } }; + +export function setIsEntitiesSavedStatesOpen( isOpen ) { + return { + type: 'SET_IS_ENTITIES_SAVED_STATES_OPEN', + isOpen, + }; +} diff --git a/packages/edit-site/src/store/reducer.js b/packages/edit-site/src/store/reducer.js index fa5ca383a4a5af..6d2ede65df925d 100644 --- a/packages/edit-site/src/store/reducer.js +++ b/packages/edit-site/src/store/reducer.js @@ -212,6 +212,15 @@ export function listViewPanel( state = false, action ) { return state; } +export function isEntitiesSavedStatesOpen( state = false, action ) { + switch ( action.type ) { + case 'SET_IS_ENTITIES_SAVED_STATES_OPEN': + return action.isOpen; + default: + return state; + } +} + export default combineReducers( { preferences, deviceType, @@ -221,4 +230,5 @@ export default combineReducers( { navigationPanel, blockInserterPanel, listViewPanel, + isEntitiesSavedStatesOpen, } ); diff --git a/packages/edit-site/src/store/selectors.js b/packages/edit-site/src/store/selectors.js index fc07bb7b77cd1d..c570d4c4886b41 100644 --- a/packages/edit-site/src/store/selectors.js +++ b/packages/edit-site/src/store/selectors.js @@ -341,3 +341,12 @@ export const getCurrentTemplateTemplateParts = createRegistrySelector( export function getEditorMode( state ) { return state.preferences.editorMode || 'visual'; } + +/** + * Returns if the entities saves states is open. + * + * @param {Object} state Global application state. + * @return {boolean} Is entities saved states open. + */ +export const getIsEntitiesSavedStatesOpen = ( state ) => + state.isEntitiesSavedStatesOpen;