Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve block inserter keyboard navigation #26938

Merged
merged 21 commits into from
Mar 12, 2021
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
0df4171
Try new block inserter keyboard navigation
diegohaz Nov 12, 2020
4f906dd
Merge branch 'master' into try/block-inserter-keyboard-navigation
diegohaz Nov 12, 2020
13d209d
Refactor code
diegohaz Nov 19, 2020
5c71072
Merge branch 'master' into try/block-inserter-keyboard-navigation
diegohaz Nov 19, 2020
543ef7c
Pass aria-orientation to listbox
diegohaz Nov 19, 2020
ce34a54
Merge branch 'master' into try/block-inserter-keyboard-navigation
diegohaz Dec 3, 2020
eca11f9
Update package.json
diegohaz Dec 3, 2020
fcd6573
Address review comments
diegohaz Dec 4, 2020
bcc8d85
Merge branch 'master' into try/block-inserter-keyboard-navigation
diegohaz Dec 10, 2020
3155cee
Add announcement
diegohaz Jan 7, 2021
dc52e50
Merge branch 'master' into try/block-inserter-keyboard-navigation
diegohaz Jan 7, 2021
eddebc4
Merge branch 'master' into try/block-inserter-keyboard-navigation
diegohaz Jan 9, 2021
8518ec3
Merge branch 'master' into try/block-inserter-keyboard-navigation
diegohaz Jan 16, 2021
40a0043
Import composite from @wordpress/components
diegohaz Jan 16, 2021
2f7889a
Fix import names
diegohaz Jan 16, 2021
1b1d02f
Merge branch 'master' into try/block-inserter-keyboard-navigation
diegohaz Jan 21, 2021
cb7b4d7
Fix keyboard navigation on search results
diegohaz Jan 21, 2021
454a69f
Fix e2e test
diegohaz Jan 21, 2021
c0d52d1
Announce instructions after block and group name
diegohaz Jan 21, 2021
5f3bc81
Merge branch 'trunk' into try/block-inserter-keyboard-navigation
diegohaz Mar 11, 2021
cfbf51b
Merge branch 'trunk' into try/block-inserter-keyboard-navigation
diegohaz Mar 12, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 28 additions & 28 deletions packages/block-editor/src/components/block-types-list/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,20 @@
* WordPress dependencies
*/
import { getBlockMenuDefaultClassName } from '@wordpress/blocks';
import {
__unstableComposite as Composite,
__unstableUseCompositeState as useCompositeState,
} from '@wordpress/components';

/**
* Internal dependencies
*/
import InserterListItem from '../inserter-list-item';
import { InserterListboxGroup, InserterListboxRow } from '../inserter-listbox';

function chunk( array, size ) {
const chunks = [];
for ( let i = 0, j = array.length; i < j; i += size ) {
chunks.push( array.slice( i, i + size ) );
}
return chunks;
}

function BlockTypesList( {
items = [],
Expand All @@ -20,35 +25,30 @@ function BlockTypesList( {
label,
isDraggable = true,
} ) {
const composite = useCompositeState();
return (
/*
* Disable reason: The `list` ARIA role is redundant but
* Safari+VoiceOver won't announce the list otherwise.
*/
/* eslint-disable jsx-a11y/no-redundant-roles */
<Composite
{ ...composite }
role="listbox"
<InserterListboxGroup
className="block-editor-block-types-list"
aria-label={ label }
>
{ items.map( ( item ) => {
return (
<InserterListItem
key={ item.id }
item={ item }
className={ getBlockMenuDefaultClassName( item.id ) }
onSelect={ onSelect }
onHover={ onHover }
composite={ composite }
isDraggable={ isDraggable }
/>
);
} ) }
{ chunk( items, 3 ).map( ( row, i ) => (
<InserterListboxRow key={ i }>
{ row.map( ( item, j ) => (
<InserterListItem
key={ item.id }
item={ item }
className={ getBlockMenuDefaultClassName(
item.id
) }
onSelect={ onSelect }
onHover={ onHover }
isDraggable={ isDraggable }
isFirst={ i === 0 && j === 0 }
/>
) ) }
</InserterListboxRow>
) ) }
{ children }
</Composite>
/* eslint-enable jsx-a11y/no-redundant-roles */
</InserterListboxGroup>
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
.block-editor-block-types-list {
list-style: none;
.block-editor-block-types-list > [role="presentation"] {
padding: 4px;
margin-left: -4px;
margin-right: -4px;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@ import classnames from 'classnames';
* WordPress dependencies
*/
import { useMemo, useRef, memo } from '@wordpress/element';
import {
Button,
__unstableCompositeItem as CompositeItem,
} from '@wordpress/components';
import {
createBlock,
createBlocksFromInnerBlocksTemplate,
Expand All @@ -20,11 +16,12 @@ import {
* Internal dependencies
*/
import BlockIcon from '../block-icon';
import { InserterListboxItem } from '../inserter-listbox';
import InserterDraggableBlocks from '../inserter-draggable-blocks';

function InserterListItem( {
className,
composite,
isFirst,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

InserterListItem isn't exposed as part of the public API so it's fine to change 👍🏻

item,
onSelect,
onHover,
Expand Down Expand Up @@ -72,10 +69,8 @@ function InserterListItem( {
}
} }
>
<CompositeItem
role="option"
as={ Button }
{ ...composite }
<InserterListboxItem
isFirst={ isFirst }
className={ classnames(
'block-editor-block-types-list__item',
className
Expand All @@ -100,10 +95,6 @@ function InserterListItem( {
} }
onMouseLeave={ () => onHover( null ) }
onBlur={ () => onHover( null ) }
// Use the CompositeItem `focusable` prop over Button's
// isFocusable. The latter was shown to cause an issue
// with tab order in the inserter list.
focusable
{ ...props }
>
<span
Expand All @@ -115,7 +106,7 @@ function InserterListItem( {
<span className="block-editor-block-types-list__item-title">
{ item.title }
</span>
</CompositeItem>
</InserterListboxItem>
</div>
) }
</InserterDraggableBlocks>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* WordPress dependencies
*/
import { createContext } from '@wordpress/element';

const InserterListboxContext = createContext();

export default InserterListboxContext;
40 changes: 40 additions & 0 deletions packages/block-editor/src/components/inserter-listbox/group.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* WordPress dependencies
*/
import { forwardRef, useEffect, useState } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { speak } from '@wordpress/a11y';

function InserterListboxGroup( props, ref ) {
const [ shouldSpeak, setShouldSpeak ] = useState( false );

useEffect( () => {
if ( shouldSpeak ) {
speak(
__( 'Use left and right arrow keys to move through blocks' )
);
}
}, [ shouldSpeak ] );

return (
<div
ref={ ref }
role="listbox"
aria-orientation="horizontal"
onFocus={ () => {
setShouldSpeak( true );
} }
onBlur={ ( event ) => {
const focusingOutsideGroup = ! event.currentTarget.contains(
event.relatedTarget
);
if ( focusingOutsideGroup ) {
setShouldSpeak( false );
}
} }
{ ...props }
/>
);
}

export default forwardRef( InserterListboxGroup );
27 changes: 27 additions & 0 deletions packages/block-editor/src/components/inserter-listbox/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* WordPress dependencies
*/
import { __unstableUseCompositeState as useCompositeState } from '@wordpress/components';

/**
* Internal dependencies
*/
import InserterListboxContext from './context';

export { default as InserterListboxGroup } from './group';
export { default as InserterListboxRow } from './row';
export { default as InserterListboxItem } from './item';

function InserterListbox( { children } ) {
const compositeState = useCompositeState( {
shift: true,
wrap: 'horizontal',
} );
return (
<InserterListboxContext.Provider value={ compositeState }>
{ children }
</InserterListboxContext.Provider>
);
}

export default InserterListbox;
52 changes: 52 additions & 0 deletions packages/block-editor/src/components/inserter-listbox/item.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* WordPress dependencies
*/
import {
Button,
__unstableCompositeItem as CompositeItem,
} from '@wordpress/components';
import { forwardRef, useContext } from '@wordpress/element';

/**
* Internal dependencies
*/
import InserterListboxContext from './context';

function InserterListboxItem(
{ isFirst, as: Component, children, ...props },
ref
) {
const state = useContext( InserterListboxContext );
return (
<CompositeItem
ref={ ref }
state={ state }
role="option"
// Use the CompositeItem `focusable` prop over Button's
// isFocusable. The latter was shown to cause an issue
// with tab order in the inserter list.
focusable
{ ...props }
>
{ ( htmlProps ) => {
const propsWithTabIndex = {
...htmlProps,
tabIndex: isFirst ? 0 : htmlProps.tabIndex,
};
if ( Component ) {
return (
<Component { ...propsWithTabIndex }>
{ children }
</Component>
);
}
if ( typeof children === 'function' ) {
return children( propsWithTabIndex );
}
return <Button { ...propsWithTabIndex }>{ children }</Button>;
} }
</CompositeItem>
);
}

export default forwardRef( InserterListboxItem );
24 changes: 24 additions & 0 deletions packages/block-editor/src/components/inserter-listbox/row.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* WordPress dependencies
*/
import { forwardRef, useContext } from '@wordpress/element';
import { __unstableCompositeGroup as CompositeGroup } from '@wordpress/components';

/**
* Internal dependencies
*/
import InserterListboxContext from './context';

function InserterListboxRow( props, ref ) {
const state = useContext( InserterListboxContext );
return (
<CompositeGroup
state={ state }
role="presentation"
ref={ ref }
{ ...props }
/>
);
}

export default forwardRef( InserterListboxRow );
Loading