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() {
>