diff --git a/packages/editor/src/components/global-keyboard-shortcuts/index.js b/packages/editor/src/components/global-keyboard-shortcuts/index.js index a46d4b55a7bfd..18d96d822f8e0 100644 --- a/packages/editor/src/components/global-keyboard-shortcuts/index.js +++ b/packages/editor/src/components/global-keyboard-shortcuts/index.js @@ -91,9 +91,15 @@ export default function EditorKeyboardShortcuts() { savePost(); } ); - // Only opens the list view. Other functionality for this shortcut happens in the rendered sidebar. + // Only opens the list view. Other functionality for this shortcut happens + // in the rendered sidebar. When the `showListViewByDefault` preference is + // enabled, the sidebar is rendered by default. As such, we need to prevent + // the callback from running twice by using an additional check for + // `event.defaultPrevented` otherwise the shortcut: + // 1. It will first be invoked in the sidebar, thus closing it. + // 2. It will then run again here, reopening the sidebar unexpectedly. useShortcut( 'core/editor/toggle-list-view', ( event ) => { - if ( ! isListViewOpened() ) { + if ( ! isListViewOpened() && ! event.defaultPrevented ) { event.preventDefault(); setIsListViewOpened( true ); } diff --git a/packages/editor/src/components/list-view-sidebar/index.js b/packages/editor/src/components/list-view-sidebar/index.js index 50c9e7e3c54c7..c67faf6fa2bff 100644 --- a/packages/editor/src/components/list-view-sidebar/index.js +++ b/packages/editor/src/components/list-view-sidebar/index.js @@ -57,6 +57,9 @@ export default function ListViewSidebar() { const closeOnEscape = useCallback( ( event ) => { if ( event.keyCode === ESCAPE && ! event.defaultPrevented ) { + // Always use `event.preventDefault` before calling `closeListView`. + // This is important to prevent the `core/editor/toggle-list-view` + // shortcut callback from being twice. event.preventDefault(); closeListView(); } @@ -148,23 +151,36 @@ export default function ListViewSidebar() { } } - const handleToggleListViewShortcut = useCallback( () => { - // If the sidebar has focus, it is safe to close. - if ( - sidebarRef.current.contains( - sidebarRef.current.ownerDocument.activeElement - ) - ) { - closeListView(); - } else { - // If the list view or outline does not have focus, focus should be moved to it. - handleSidebarFocus( tab ); - } - }, [ closeListView, tab ] ); + const handleToggleListViewShortcut = useCallback( + ( event ) => { + // If the sidebar has focus, it is safe to close. + if ( + sidebarRef.current.contains( + sidebarRef.current.ownerDocument.activeElement + ) + ) { + // Always use `event.preventDefault` before calling `closeListView`. + // This is important to prevent the `core/editor/toggle-list-view` + // shortcut callback from running twice. + event.preventDefault(); + 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/editor/toggle-list-view', handleToggleListViewShortcut ); + // It is the same shortcut to open the sidebar but that is defined as a global + // shortcut. However, when the `showListViewByDefault` preference is enabled, + // the sidebar is open by default and the shortcut callback would be invoked + // twice (here and in the global shortcut). To prevent that, we pass the event + // for some additional logic in the global shortcut based on `event.defaultPrevented`. + useShortcut( 'core/editor/toggle-list-view', ( event ) => { + handleToggleListViewShortcut( event ); + } ); return ( // eslint-disable-next-line jsx-a11y/no-static-element-interactions