From 49b9cffca5447454abe429bff1958194bef264c7 Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Thu, 21 Sep 2023 09:48:00 +1000 Subject: [PATCH] List View: Try directing focus to the list view toggle button when closing the list view (#54175) * List View: Try directing focus to the list view toggle button when closing the list view * Only focus if no blocks are selected * Also use focus logic for when the list view is closed via the keyboard shortcut * Try implementing in the site editor, too * Always return focus to the toggle whenever the list view is closed, roll out to widgets editor * Update e2e tests --- .../components/header/header-toolbar/index.js | 3 +- .../edit-post/src/components/header/index.js | 9 ++- .../edit-post/src/components/layout/index.js | 11 +++- .../secondary-sidebar/list-view-sidebar.js | 54 ++++++++++-------- .../edit-site/src/components/editor/index.js | 8 ++- .../src/components/header-edit-mode/index.js | 3 +- .../edit-site/src/components/layout/index.js | 11 +++- .../secondary-sidebar/list-view-sidebar.js | 57 ++++++++++--------- .../src/components/header/index.js | 3 +- .../src/components/layout/interface.js | 17 +++++- .../src/components/secondary-sidebar/index.js | 6 +- .../secondary-sidebar/list-view-sidebar.js | 46 +++++++-------- .../specs/editor/various/list-view.spec.js | 7 +-- test/e2e/specs/site-editor/list-view.spec.js | 13 +++-- 14 files changed, 148 insertions(+), 100 deletions(-) diff --git a/packages/edit-post/src/components/header/header-toolbar/index.js b/packages/edit-post/src/components/header/header-toolbar/index.js index 840067e9fb9b3..e814f7a49072f 100644 --- a/packages/edit-post/src/components/header/header-toolbar/index.js +++ b/packages/edit-post/src/components/header/header-toolbar/index.js @@ -33,7 +33,7 @@ const preventDefault = ( event ) => { event.preventDefault(); }; -function HeaderToolbar() { +function HeaderToolbar( { setListViewToggleElement } ) { const inserterButton = useRef(); const { setIsInserterOpened, setIsListViewOpened } = useDispatch( editPostStore ); @@ -108,6 +108,7 @@ function HeaderToolbar() { showTooltip={ ! showIconLabels } variant={ showIconLabels ? 'tertiary' : undefined } aria-expanded={ isListViewOpen } + ref={ setListViewToggleElement } /> ); diff --git a/packages/edit-post/src/components/header/index.js b/packages/edit-post/src/components/header/index.js index 4d567fe37493d..2e0d470818fec 100644 --- a/packages/edit-post/src/components/header/index.js +++ b/packages/edit-post/src/components/header/index.js @@ -32,7 +32,10 @@ const slideX = { hover: { x: 0, transition: { type: 'tween', delay: 0.2 } }, }; -function Header( { setEntitiesSavedStatesCallback } ) { +function Header( { + setEntitiesSavedStatesCallback, + setListViewToggleElement, +} ) { const isLargeViewport = useViewportMatch( 'large' ); const { hasActiveMetaboxes, isPublishSidebarOpened, showIconLabels } = useSelect( @@ -61,7 +64,9 @@ function Header( { setEntitiesSavedStatesCallback } ) { transition={ { type: 'tween', delay: 0.8 } } className="edit-post-header__toolbar" > - +
diff --git a/packages/edit-post/src/components/layout/index.js b/packages/edit-post/src/components/layout/index.js index 878177c250f84..5265cac569239 100644 --- a/packages/edit-post/src/components/layout/index.js +++ b/packages/edit-post/src/components/layout/index.js @@ -210,6 +210,10 @@ function Layout() { // Note 'truthy' callback implies an open panel. const [ entitiesSavedStatesCallback, setEntitiesSavedStatesCallback ] = useState( false ); + + const [ listViewToggleElement, setListViewToggleElement ] = + useState( null ); + const closeEntitiesSavedStates = useCallback( ( arg ) => { if ( typeof entitiesSavedStatesCallback === 'function' ) { @@ -244,7 +248,11 @@ function Layout() { return ; } if ( mode === 'visual' && isListViewOpened ) { - return ; + return ( + + ); } return null; @@ -285,6 +293,7 @@ function Layout() { setEntitiesSavedStatesCallback={ setEntitiesSavedStatesCallback } + setListViewToggleElement={ setListViewToggleElement } /> } editorNotices={ } diff --git a/packages/edit-post/src/components/secondary-sidebar/list-view-sidebar.js b/packages/edit-post/src/components/secondary-sidebar/list-view-sidebar.js index 77a56617cb1c6..0d96606e9e043 100644 --- a/packages/edit-post/src/components/secondary-sidebar/list-view-sidebar.js +++ b/packages/edit-post/src/components/secondary-sidebar/list-view-sidebar.js @@ -3,14 +3,10 @@ */ import { __experimentalListView as ListView } from '@wordpress/block-editor'; import { Button, TabPanel } from '@wordpress/components'; -import { - useFocusOnMount, - useFocusReturn, - useMergeRefs, -} from '@wordpress/compose'; +import { useFocusOnMount, useMergeRefs } from '@wordpress/compose'; import { useDispatch } from '@wordpress/data'; import { focus } from '@wordpress/dom'; -import { useRef, useState } from '@wordpress/element'; +import { useCallback, useRef, useState } from '@wordpress/element'; import { __, _x } from '@wordpress/i18n'; import { closeSmall } from '@wordpress/icons'; import { useShortcut } from '@wordpress/keyboard-shortcuts'; @@ -22,21 +18,27 @@ import { ESCAPE } from '@wordpress/keycodes'; import { store as editPostStore } from '../../store'; import ListViewOutline from './list-view-outline'; -export default function ListViewSidebar() { +export default function ListViewSidebar( { listViewToggleElement } ) { const { setIsListViewOpened } = useDispatch( editPostStore ); // This hook handles focus when the sidebar first renders. const focusOnMountRef = useFocusOnMount( 'firstElement' ); - // The next 2 hooks handle focus for when the sidebar closes and returning focus to the element that had focus before sidebar opened. - const headerFocusReturnRef = useFocusReturn(); - const contentFocusReturnRef = useFocusReturn(); - function closeOnEscape( event ) { - if ( event.keyCode === ESCAPE && ! event.defaultPrevented ) { - event.preventDefault(); - setIsListViewOpened( false ); - } - } + // When closing the list view, focus should return to the toggle button. + const closeListView = useCallback( () => { + setIsListViewOpened( false ); + listViewToggleElement?.focus(); + }, [ listViewToggleElement, setIsListViewOpened ] ); + + const closeOnEscape = useCallback( + ( event ) => { + if ( event.keyCode === ESCAPE && ! event.defaultPrevented ) { + event.preventDefault(); + closeListView(); + } + }, + [ closeListView ] + ); // Use internal state instead of a ref to make sure that the component // re-renders when the dropZoneElement updates. @@ -53,7 +55,6 @@ export default function ListViewSidebar() { // Must merge the refs together so focus can be handled properly in the next function. const listViewContainerRef = useMergeRefs( [ - contentFocusReturnRef, focusOnMountRef, listViewRef, setDropZoneElement, @@ -87,20 +88,26 @@ export default function ListViewSidebar() { } } - // This only fires when the sidebar is open because of the conditional rendering. It is the same shortcut to open but that is defined as a global shortcut and only fires when the sidebar is closed. - useShortcut( 'core/edit-post/toggle-list-view', () => { + const handleToggleListViewShortcut = useCallback( () => { // If the sidebar has focus, it is safe to close. if ( sidebarRef.current.contains( sidebarRef.current.ownerDocument.activeElement ) ) { - setIsListViewOpened( false ); - // If the list view or outline does not have focus, focus should be moved to it. + closeListView(); } else { + // If the list view or outline does not have focus, focus should be moved to it. handleSidebarFocus( tab ); } - } ); + }, [ closeListView, tab ] ); + + // This only fires when the sidebar is open because of the conditional rendering. + // It is the same shortcut to open but that is defined as a global shortcut and only fires when the sidebar is closed. + useShortcut( + 'core/edit-post/toggle-list-view', + handleToggleListViewShortcut + ); /** * Render tab content for a given tab name. @@ -127,10 +134,9 @@ export default function ListViewSidebar() { >