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

[Inserter]: Add keyboard navigation in Patterns #28520

Merged
merged 5 commits into from
Feb 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
19 changes: 19 additions & 0 deletions packages/block-editor/src/components/block-patterns-list/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,25 @@ The performed event after a click on a block pattern. In most cases, the pattern
- Type: `Function`
- Required: Yes

#### isDraggable
Enables drag and drop functionality to the available block patterns.

- Type: `boolean`
- Required: No

#### orientation
The orientation value determines which arrow keys can be used to move focus. Available options are (`vertical`|`horizontal`). If not provided all arrow keys work.

- Type: `string`
- Required: No

#### label
The aria label for the block patterns list.

- Type: `string`
- Required: No
- Default: `Block Patterns`

## Related components

Block Editor components are components that can be used to compose the UI of your block editor. Thus, they can only be used under a [`BlockEditorProvider`](https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/provider/README.md) in the components tree.
95 changes: 55 additions & 40 deletions packages/block-editor/src/components/block-patterns-list/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,22 @@
*/
import { useMemo } from '@wordpress/element';
import { parse } from '@wordpress/blocks';
import { ENTER, SPACE } from '@wordpress/keycodes';
import { VisuallyHidden } from '@wordpress/components';
import {
VisuallyHidden,
__unstableComposite as Composite,
__unstableUseCompositeState as useCompositeState,
__unstableCompositeItem as CompositeItem,
} from '@wordpress/components';
import { useInstanceId } from '@wordpress/compose';
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import BlockPreview from '../block-preview';
import InserterDraggableBlocks from '../inserter-draggable-blocks';

function BlockPattern( { isDraggable, pattern, onClick } ) {
function BlockPattern( { isDraggable, pattern, onClick, composite } ) {
const { content, viewportWidth } = pattern;
const blocks = useMemo( () => parse( content ), [ content ] );
const instanceId = useInstanceId( BlockPattern );
Expand All @@ -23,18 +28,7 @@ function BlockPattern( { isDraggable, pattern, onClick } ) {
<InserterDraggableBlocks isEnabled={ isDraggable } blocks={ blocks }>
{ ( { draggable, onDragStart, onDragEnd } ) => (
<div
className="block-editor-block-patterns-list__item"
role="button"
onClick={ () => onClick( pattern, blocks ) }
onKeyDown={ ( event ) => {
if (
ENTER === event.keyCode ||
SPACE === event.keyCode
) {
onClick( pattern, blocks );
}
} }
tabIndex={ 0 }
className="block-editor-block-patterns-list__list-item"
aria-label={ pattern.title }
aria-describedby={
pattern.description ? descriptionId : undefined
Expand All @@ -43,18 +37,26 @@ function BlockPattern( { isDraggable, pattern, onClick } ) {
onDragStart={ onDragStart }
onDragEnd={ onDragEnd }
>
<BlockPreview
blocks={ blocks }
viewportWidth={ viewportWidth }
/>
<div className="block-editor-block-patterns-list__item-title">
{ pattern.title }
</div>
{ !! pattern.description && (
<VisuallyHidden id={ descriptionId }>
{ pattern.description }
</VisuallyHidden>
) }
<CompositeItem
role="option"
as="div"
{ ...composite }
className="block-editor-block-patterns-list__item"
onClick={ () => onClick( pattern, blocks ) }
>
<BlockPreview
blocks={ blocks }
viewportWidth={ viewportWidth }
/>
<div className="block-editor-block-patterns-list__item-title">
{ pattern.title }
</div>
{ !! pattern.description && (
<VisuallyHidden id={ descriptionId }>
{ pattern.description }
</VisuallyHidden>
) }
</CompositeItem>
</div>
) }
</InserterDraggableBlocks>
Expand All @@ -72,20 +74,33 @@ function BlockPatternList( {
blockPatterns,
shownPatterns,
onClickPattern,
orientation,
label = __( 'Block Patterns' ),
} ) {
return blockPatterns.map( ( pattern ) => {
const isShown = shownPatterns.includes( pattern );
return isShown ? (
<BlockPattern
key={ pattern.name }
pattern={ pattern }
onClick={ onClickPattern }
isDraggable={ isDraggable }
/>
) : (
<BlockPatternPlaceholder key={ pattern.name } />
);
} );
const composite = useCompositeState( { orientation } );
return (
<Composite
{ ...composite }
role="listbox"
className="block-editor-block-patterns-list"
aria-label={ label }
>
{ blockPatterns.map( ( pattern ) => {
const isShown = shownPatterns.includes( pattern );
return isShown ? (
<BlockPattern
key={ pattern.name }
pattern={ pattern }
onClick={ onClickPattern }
isDraggable={ isDraggable }
composite={ composite }
/>
) : (
<BlockPatternPlaceholder key={ pattern.name } />
);
} ) }
</Composite>
);
}

export default BlockPatternList;
25 changes: 14 additions & 11 deletions packages/block-editor/src/components/block-patterns-list/style.scss
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
.block-editor-block-patterns-list__item {

border-radius: $radius-block-ui;
.block-editor-block-patterns-list__list-item {
cursor: pointer;
margin-top: $grid-unit-20;

&.is-placeholder {
min-height: 100px;
}

&[draggable="true"] .block-editor-block-preview__container {
cursor: grab;
}
}

.block-editor-block-patterns-list__item {
height: 100%;
border-radius: $radius-block-ui;
transition: all 0.05s ease-in-out;
position: relative;
border: $border-width solid transparent;
Expand All @@ -17,14 +28,6 @@
// Windows High Contrast mode will show this outline, but not the box-shadow.
outline: 2px solid transparent;
}

&.is-placeholder {
min-height: 100px;
}

&[draggable="true"] .block-editor-block-preview__container {
cursor: grab;
}
}

.block-editor-block-patterns-list__item-title {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ function BlockPatternsCategory( {
<>
{ !! currentCategoryPatterns.length && (
<PatternInserterPanel
title={ patternCategory.title }
selectedCategory={ patternCategory }
patternCategories={ populatedCategories }
onClickCategory={ onClickCategory }
Expand All @@ -112,6 +111,8 @@ function BlockPatternsCategory( {
shownPatterns={ currentShownPatterns }
blockPatterns={ currentCategoryPatterns }
onClickPattern={ onClick }
label={ patternCategory.label }
orientation="vertical"
isDraggable
/>
</PatternInserterPanel>
Expand Down
8 changes: 5 additions & 3 deletions packages/block-editor/src/components/inserter/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -333,9 +333,11 @@ $block-inserter-tabs-height: 44px;
}

.block-editor-inserter__quick-inserter-patterns {
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: $grid-unit-10;
.block-editor-block-patterns-list {
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: $grid-unit-10;
}
}

.block-editor-inserter__quick-inserter-separator {
Expand Down
2 changes: 1 addition & 1 deletion packages/e2e-test-utils/src/inserter.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ export async function insertBlock( searchTerm ) {
export async function insertPattern( searchTerm ) {
await searchForPattern( searchTerm );
const insertButton = await page.waitForXPath(
`//div[@role = 'button']//div[contains(text(), '${ searchTerm }')]`
`//div[@role = 'option']//div[contains(text(), '${ searchTerm }')]`
);
await insertButton.click();
// We should wait until the inserter closes and the focus moves to the content.
Expand Down