Skip to content

Commit

Permalink
Focus inserter toggle when closing the inserter sidebar (#61467)
Browse files Browse the repository at this point in the history
* Focus inserter toggle when closing the inserter sidebar

* pass the onClose down

* fix linter

* restore code from bad refactor

* Fix widgets editor inserter toggle

* Fix linting

---------

Co-authored-by: Ben Dwyer <[email protected]>
Co-authored-by: jeryj <[email protected]>
Co-authored-by: scruffian <[email protected]>
Co-authored-by: mikachan <[email protected]>
Co-authored-by: draganescu <[email protected]>
Co-authored-by: ntsekouras <[email protected]>
Co-authored-by: kevin940726 <[email protected]>
Co-authored-by: afercia <[email protected]>
Co-authored-by: alexstine <[email protected]>
  • Loading branch information
10 people authored May 10, 2024
1 parent 9b77e39 commit 550cb6b
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 92 deletions.
80 changes: 30 additions & 50 deletions packages/edit-widgets/src/components/header/document-tools/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,90 +4,70 @@
import { useSelect, useDispatch } from '@wordpress/data';
import { __, _x } from '@wordpress/i18n';
import { Button, ToolbarItem } from '@wordpress/components';
import {
NavigableToolbar,
store as blockEditorStore,
} from '@wordpress/block-editor';
import { NavigableToolbar } from '@wordpress/block-editor';
import { listView, plus } from '@wordpress/icons';
import { useCallback, useRef } from '@wordpress/element';
import { useCallback } from '@wordpress/element';
import { useViewportMatch } from '@wordpress/compose';

/**
* Internal dependencies
*/
import UndoButton from '../undo-redo/undo';
import RedoButton from '../undo-redo/redo';
import useLastSelectedWidgetArea from '../../../hooks/use-last-selected-widget-area';
import { store as editWidgetsStore } from '../../../store';
import { unlock } from '../../../lock-unlock';

function DocumentTools() {
const isMediumViewport = useViewportMatch( 'medium' );
const inserterButton = useRef();
const widgetAreaClientId = useLastSelectedWidgetArea();
const isLastSelectedWidgetAreaOpen = useSelect(
( select ) =>
select( editWidgetsStore ).getIsWidgetAreaOpen(
widgetAreaClientId
),
[ widgetAreaClientId ]
);
const { isInserterOpen, isListViewOpen, listViewToggleRef } = useSelect(
( select ) => {
const { isInserterOpened, isListViewOpened, getListViewToggleRef } =
unlock( select( editWidgetsStore ) );
return {
isInserterOpen: isInserterOpened(),
isListViewOpen: isListViewOpened(),
listViewToggleRef: getListViewToggleRef(),
};
},
[]
);
const { setIsWidgetAreaOpen, setIsInserterOpened, setIsListViewOpened } =

const {
isInserterOpen,
isListViewOpen,
inserterSidebarToggleRef,
listViewToggleRef,
} = useSelect( ( select ) => {
const {
isInserterOpened,
getInserterSidebarToggleRef,
isListViewOpened,
getListViewToggleRef,
} = unlock( select( editWidgetsStore ) );
return {
isInserterOpen: isInserterOpened(),
isListViewOpen: isListViewOpened(),
inserterSidebarToggleRef: getInserterSidebarToggleRef(),
listViewToggleRef: getListViewToggleRef(),
};
}, [] );
const { setIsInserterOpened, setIsListViewOpened } =
useDispatch( editWidgetsStore );
const { selectBlock } = useDispatch( blockEditorStore );
const handleClick = () => {
if ( isInserterOpen ) {
// Focusing the inserter button closes the inserter popover.
setIsInserterOpened( false );
} else {
if ( ! isLastSelectedWidgetAreaOpen ) {
// Select the last selected block if hasn't already.
selectBlock( widgetAreaClientId );
// Open the last selected widget area when opening the inserter.
setIsWidgetAreaOpen( widgetAreaClientId, true );
}
// The DOM updates resulting from selectBlock() and setIsInserterOpened() calls are applied the
// same tick and pretty much in a random order. The inserter is closed if any other part of the
// app receives focus. If selectBlock() happens to take effect after setIsInserterOpened() then
// the inserter is visible for a brief moment and then gets auto-closed due to focus moving to
// the selected block.
window.requestAnimationFrame( () => setIsInserterOpened( true ) );
}
};

const toggleListView = useCallback(
() => setIsListViewOpened( ! isListViewOpen ),
[ setIsListViewOpened, isListViewOpen ]
);

const toggleInserterSidebar = useCallback(
() => setIsInserterOpened( ! isInserterOpen ),
[ setIsInserterOpened, isInserterOpen ]
);

return (
<NavigableToolbar
className="edit-widgets-header-toolbar"
aria-label={ __( 'Document tools' ) }
variant="unstyled"
>
<ToolbarItem
ref={ inserterButton }
ref={ inserterSidebarToggleRef }
as={ Button }
className="edit-widgets-header-toolbar__inserter-toggle"
variant="primary"
isPressed={ isInserterOpen }
onMouseDown={ ( event ) => {
event.preventDefault();
} }
onClick={ handleClick }
onClick={ toggleInserterSidebar }
icon={ plus }
/* translators: button label text should, if possible, be under 16
characters. */
Expand Down
4 changes: 4 additions & 0 deletions packages/edit-widgets/src/store/private-selectors.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
export function getListViewToggleRef( state ) {
return state.listViewToggleRef;
}

export function getInserterSidebarToggleRef( state ) {
return state.inserterSidebarToggleRef;
}
13 changes: 13 additions & 0 deletions packages/edit-widgets/src/store/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,21 @@ export function listViewToggleRef( state = { current: null } ) {
return state;
}

/**
* This reducer does nothing aside initializing a ref to the inserter sidebar toggle.
* We will have a unique ref per "editor" instance.
*
* @param {Object} state
* @return {Object} Reference to the inserter sidebar toggle button.
*/
export function inserterSidebarToggleRef( state = { current: null } ) {
return state;
}

export default combineReducers( {
blockInserterPanel,
inserterSidebarToggleRef,
listViewPanel,
listViewToggleRef,
widgetAreasOpenState,
} );
30 changes: 14 additions & 16 deletions packages/editor/src/components/document-tools/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
} from '@wordpress/block-editor';
import { Button, ToolbarItem } from '@wordpress/components';
import { listView, plus } from '@wordpress/icons';
import { useRef, useCallback } from '@wordpress/element';
import { useCallback } from '@wordpress/element';
import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts';
import { store as preferencesStore } from '@wordpress/preferences';

Expand All @@ -38,22 +38,26 @@ function DocumentTools( {
// This is a temporary prop until the list view is fully unified between post and site editors.
listViewLabel = __( 'Document Overview' ),
} ) {
const inserterButton = useRef();
const { setIsInserterOpened, setIsListViewOpened } =
useDispatch( editorStore );
const {
isDistractionFree,
isInserterOpened,
isListViewOpen,
listViewShortcut,
inserterSidebarToggleRef,
listViewToggleRef,
hasFixedToolbar,
showIconLabels,
} = useSelect( ( select ) => {
const { getSettings } = select( blockEditorStore );
const { get } = select( preferencesStore );
const { isListViewOpened, getListViewToggleRef, getEditorMode } =
unlock( select( editorStore ) );
const {
isListViewOpened,
getEditorMode,
getInserterSidebarToggleRef,
getListViewToggleRef,
} = unlock( select( editorStore ) );
const { getShortcutRepresentation } = select( keyboardShortcutsStore );
const { __unstableGetEditorMode } = select( blockEditorStore );

Expand All @@ -63,6 +67,7 @@ function DocumentTools( {
listViewShortcut: getShortcutRepresentation(
'core/editor/toggle-list-view'
),
inserterSidebarToggleRef: getInserterSidebarToggleRef(),
listViewToggleRef: getListViewToggleRef(),
hasFixedToolbar: getSettings().hasFixedToolbar,
showIconLabels: get( 'core', 'showIconLabels' ),
Expand All @@ -83,17 +88,10 @@ function DocumentTools( {
[ setIsListViewOpened, isListViewOpen ]
);

const toggleInserter = useCallback( () => {
if ( isInserterOpened ) {
// Focusing the inserter button should close the inserter popover.
// However, there are some cases it won't close when the focus is lost.
// See https://github.com/WordPress/gutenberg/issues/43090 for more details.
inserterButton.current.focus();
setIsInserterOpened( false );
} else {
setIsInserterOpened( true );
}
}, [ isInserterOpened, setIsInserterOpened ] );
const toggleInserter = useCallback(
() => setIsInserterOpened( ! isInserterOpened ),
[ isInserterOpened, setIsInserterOpened ]
);

/* translators: button label text should, if possible, be under 16 characters. */
const longLabel = _x(
Expand All @@ -119,7 +117,7 @@ function DocumentTools( {
<div className="editor-document-tools__left">
{ ! isDistractionFree && (
<ToolbarItem
ref={ inserterButton }
ref={ inserterSidebarToggleRef }
as={ Button }
className="editor-document-tools__inserter-toggle"
variant="primary"
Expand Down
74 changes: 48 additions & 26 deletions packages/editor/src/components/inserter-sidebar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ import {
store as blockEditorStore,
} from '@wordpress/block-editor';
import { useViewportMatch } from '@wordpress/compose';
import { useRef } from '@wordpress/element';
import { useCallback, useRef } from '@wordpress/element';
import { store as preferencesStore } from '@wordpress/preferences';
import { ESCAPE } from '@wordpress/keycodes';

/**
* Internal dependencies
Expand All @@ -20,37 +21,58 @@ export default function InserterSidebar( {
closeGeneralSidebar,
isRightSidebarOpen,
} ) {
const { insertionPoint, showMostUsedBlocks, blockSectionRootClientId } =
useSelect( ( select ) => {
const { getInsertionPoint } = unlock( select( editorStore ) );
const {
getBlockRootClientId,
__unstableGetEditorMode,
getSettings,
} = select( blockEditorStore );
const { get } = select( preferencesStore );
const getBlockSectionRootClientId = () => {
if ( __unstableGetEditorMode() === 'zoom-out' ) {
const { sectionRootClientId } = unlock( getSettings() );
if ( sectionRootClientId ) {
return sectionRootClientId;
}
const {
blockSectionRootClientId,
inserterSidebarToggleRef,
insertionPoint,
showMostUsedBlocks,
} = useSelect( ( select ) => {
const { getInserterSidebarToggleRef, getInsertionPoint } = unlock(
select( editorStore )
);
const { getBlockRootClientId, __unstableGetEditorMode, getSettings } =
select( blockEditorStore );
const { get } = select( preferencesStore );
const getBlockSectionRootClientId = () => {
if ( __unstableGetEditorMode() === 'zoom-out' ) {
const { sectionRootClientId } = unlock( getSettings() );
if ( sectionRootClientId ) {
return sectionRootClientId;
}
return getBlockRootClientId();
};
return {
insertionPoint: getInsertionPoint(),
showMostUsedBlocks: get( 'core', 'mostUsedBlocks' ),
blockSectionRootClientId: getBlockSectionRootClientId(),
};
}, [] );
}
return getBlockRootClientId();
};
return {
inserterSidebarToggleRef: getInserterSidebarToggleRef(),
insertionPoint: getInsertionPoint(),
showMostUsedBlocks: get( 'core', 'mostUsedBlocks' ),
blockSectionRootClientId: getBlockSectionRootClientId(),
};
}, [] );
const { setIsInserterOpened } = useDispatch( editorStore );

const isMobileViewport = useViewportMatch( 'medium', '<' );
const libraryRef = useRef();

// When closing the inserter, focus should return to the toggle button.
const closeInserterSidebar = useCallback( () => {
setIsInserterOpened( false );
inserterSidebarToggleRef.current?.focus();
}, [ inserterSidebarToggleRef, setIsInserterOpened ] );

const closeOnEscape = useCallback(
( event ) => {
if ( event.keyCode === ESCAPE && ! event.defaultPrevented ) {
event.preventDefault();
closeInserterSidebar();
}
},
[ closeInserterSidebar ]
);

return (
<div className="editor-inserter-sidebar">
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
<div onKeyDown={ closeOnEscape } className="editor-inserter-sidebar">
<div className="editor-inserter-sidebar__content">
<Library
showMostUsedBlocks={ showMostUsedBlocks }
Expand All @@ -69,7 +91,7 @@ export default function InserterSidebar( {
isRightSidebarOpen ? closeGeneralSidebar : undefined
}
ref={ libraryRef }
onClose={ () => setIsInserterOpened( false ) }
onClose={ closeInserterSidebar }
/>
</div>
</div>
Expand Down
3 changes: 3 additions & 0 deletions packages/editor/src/store/private-selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ export const getInsertionPoint = createRegistrySelector( ( select ) =>
export function getListViewToggleRef( state ) {
return state.listViewToggleRef;
}
export function getInserterSidebarToggleRef( state ) {
return state.inserterSidebarToggleRef;
}
const CARD_ICONS = {
wp_block: symbol,
wp_navigation: navigation,
Expand Down
12 changes: 12 additions & 0 deletions packages/editor/src/store/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,17 @@ export function listViewToggleRef( state = { current: null } ) {
return state;
}

/**
* This reducer does nothing aside initializing a ref to the inserter sidebar toggle.
* We will have a unique ref per "editor" instance.
*
* @param {Object} state
* @return {Object} Reference to the inserter sidebar toggle button.
*/
export function inserterSidebarToggleRef( state = { current: null } ) {
return state;
}

export function publishSidebarActive( state = false, action ) {
switch ( action.type ) {
case 'OPEN_PUBLISH_SIDEBAR':
Expand Down Expand Up @@ -387,6 +398,7 @@ export default combineReducers( {
deviceType,
removedPanels,
blockInserterPanel,
inserterSidebarToggleRef,
listViewPanel,
listViewToggleRef,
publishSidebarActive,
Expand Down

0 comments on commit 550cb6b

Please sign in to comment.