Skip to content

Commit

Permalink
Make accessible toolbar stable and deprecate old usage (#23316)
Browse files Browse the repository at this point in the history
* Deprecate legacy toolbar usage

* Update e2e tests

* Remove __experimental flag from Toolbar accessibilityLabel prop

* Use label instead of accessibilityLabel

* Deprecate <Toolbar> without label prop

* Make ToolbarItem component stable

* Deprecate ToolbarButton usage without a parent Toolbar

* Simplify jsdocs

* data-experimental-toolbar-item to data-toolbar-item

* Remove deprecation warning from ToolbarButton

This is actually an invalid warning as it may still be rendered inside <ToolbarGroup />

* Update unit test

* Update Toolbar README

* Update tests

* Remove ToolbarGroup test from Toolbar

* Replace Toolbar by ToolbarGroup on the Classic Editor

* Add Toolbar, ToolbarButton, ToolbarGroup and ToolbarItem docs

* Add links to depreaction notices and warnings

* Update ToolbarButton docs

* Improve documentation around BlockControls usage

* Update deprecation warning link on Navigable Toolbar

* Update ToolbarItem README

* Update ToolbarItem README
  • Loading branch information
diegohaz authored Aug 11, 2020
1 parent b518759 commit af768dc
Show file tree
Hide file tree
Showing 27 changed files with 255 additions and 148 deletions.
12 changes: 12 additions & 0 deletions docs/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -1199,6 +1199,18 @@
"markdown_source": "../packages/components/src/toolbar-button/README.md",
"parent": "components"
},
{
"title": "ToolbarGroup",
"slug": "toolbar-group",
"markdown_source": "../packages/components/src/toolbar-group/README.md",
"parent": "components"
},
{
"title": "ToolbarItem",
"slug": "toolbar-item",
"markdown_source": "../packages/components/src/toolbar-item/README.md",
"parent": "components"
},
{
"title": "Toolbar",
"slug": "toolbar",
Expand Down
5 changes: 1 addition & 4 deletions packages/block-editor/src/components/block-mover/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@ import classnames from 'classnames';
/**
* WordPress dependencies
*/
import {
ToolbarGroup,
__experimentalToolbarItem as ToolbarItem,
} from '@wordpress/components';
import { ToolbarGroup, ToolbarItem } from '@wordpress/components';
import { getBlockType } from '@wordpress/blocks';
import { Component } from '@wordpress/element';
import { withSelect } from '@wordpress/data';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
/**
* WordPress dependencies
*/
import {
ToolbarGroup,
__experimentalToolbarItem as ToolbarItem,
} from '@wordpress/components';
import { ToolbarGroup, ToolbarItem } from '@wordpress/components';

/**
* Internal dependencies
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
DropdownMenu,
ToolbarButton,
ToolbarGroup,
__experimentalToolbarItem as ToolbarItem,
ToolbarItem,
MenuGroup,
Popover,
} from '@wordpress/components';
Expand Down
15 changes: 12 additions & 3 deletions packages/block-editor/src/components/navigable-toolbar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
useEffect,
useCallback,
} from '@wordpress/element';
import deprecated from '@wordpress/deprecated';
import { focus } from '@wordpress/dom';
import { useShortcut } from '@wordpress/keyboard-shortcuts';

Expand All @@ -23,7 +24,7 @@ function useUpdateLayoutEffect( effect, deps ) {
}

function hasOnlyToolbarItem( elements ) {
const dataProp = 'experimentalToolbarItem';
const dataProp = 'toolbarItem';
return ! elements.some( ( element ) => ! ( dataProp in element.dataset ) );
}

Expand Down Expand Up @@ -59,7 +60,15 @@ function useIsAccessibleToolbar( ref ) {

const determineIsAccessibleToolbar = useCallback( () => {
const tabbables = focus.tabbable.find( ref.current );
setIsAccessibleToolbar( hasOnlyToolbarItem( tabbables ) );
const onlyToolbarItem = hasOnlyToolbarItem( tabbables );
if ( ! onlyToolbarItem ) {
deprecated( 'Using custom components as toolbar controls', {
alternative: 'ToolbarItem or ToolbarButton components',
link:
'https://developer.wordpress.org/block-editor/components/toolbar-button/#inside-blockcontrols',
} );
}
setIsAccessibleToolbar( onlyToolbarItem );
}, [] );

useLayoutEffect( determineIsAccessibleToolbar, [] );
Expand Down Expand Up @@ -106,7 +115,7 @@ function NavigableToolbar( { children, focusOnMount, ...props } ) {
if ( isAccessibleToolbar ) {
return (
<Toolbar
__experimentalAccessibilityLabel={ props[ 'aria-label' ] }
label={ props[ 'aria-label' ] }
ref={ wrapper }
{ ...props }
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { orderBy } from 'lodash';

import { __ } from '@wordpress/i18n';
import {
__experimentalToolbarItem as ToolbarItem,
ToolbarItem,
ToolbarGroup,
DropdownMenu,
Slot,
Expand Down
6 changes: 3 additions & 3 deletions packages/block-library/src/classic/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { debounce } from 'lodash';
* WordPress dependencies
*/
import { BlockControls } from '@wordpress/block-editor';
import { Toolbar } from '@wordpress/components';
import { ToolbarGroup } from '@wordpress/components';
import { Component } from '@wordpress/element';
import { __, _x } from '@wordpress/i18n';
import { BACKSPACE, DELETE, F10, isKeyboardEvent } from '@wordpress/keycodes';
Expand Down Expand Up @@ -248,9 +248,9 @@ export default class ClassicEdit extends Component {
return (
<>
<BlockControls>
<Toolbar>
<ToolbarGroup>
<ConvertToBlocksButton clientId={ clientId } />
</Toolbar>
</ToolbarGroup>
</BlockControls>
<div
key="toolbar"
Expand Down
4 changes: 1 addition & 3 deletions packages/block-library/src/heading/heading-level-dropdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,7 @@ export default function HeadingLevelDropdown( { selectedLevel, onChange } ) {
renderContent={ () => (
<Toolbar
className="block-library-heading-level-toolbar"
__experimentalAccessibilityLabel={ __(
'Change heading level'
) }
label={ __( 'Change heading level' ) }
>
<ToolbarGroup
isCollapsed={ false }
Expand Down
2 changes: 1 addition & 1 deletion packages/block-library/src/image/image-editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
import {
ToolbarGroup,
ToolbarButton,
__experimentalToolbarItem as ToolbarItem,
ToolbarItem,
Spinner,
RangeControl,
DropdownMenu,
Expand Down
2 changes: 1 addition & 1 deletion packages/block-library/src/table/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
TextControl,
ToggleControl,
ToolbarGroup,
__experimentalToolbarItem as ToolbarItem,
ToolbarItem,
} from '@wordpress/components';
import {
alignLeft,
Expand Down
2 changes: 1 addition & 1 deletion packages/components/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ export { default as Toolbar } from './toolbar';
export { default as ToolbarButton } from './toolbar-button';
export { default as __experimentalToolbarContext } from './toolbar-context';
export { default as ToolbarGroup } from './toolbar-group';
export { default as __experimentalToolbarItem } from './toolbar-item';
export { default as ToolbarItem } from './toolbar-item';
export { default as Tooltip } from './tooltip';
export {
default as __experimentalTreeGrid,
Expand Down
2 changes: 1 addition & 1 deletion packages/components/src/index.native.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export { default as Toolbar } from './toolbar';
export { default as ToolbarButton } from './toolbar-button';
export { default as __experimentalToolbarContext } from './toolbar-context';
export { default as ToolbarGroup } from './toolbar-group';
export { default as __experimentalToolbarItem } from './toolbar-item';
export { default as ToolbarItem } from './toolbar-item';
export { default as Icon } from './icon';
export { default as IconButton } from './button/deprecated';
export { default as Spinner } from './spinner';
Expand Down
53 changes: 43 additions & 10 deletions packages/components/src/toolbar-button/README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,52 @@
# ToolbarButton

A ToolbarButton can be used to add actions to your block control, usually inside a ToolbarGroup. It has similar features to the [Button](/packages/components/src/button/README.md) component. Using `ToolbarButton` will ensure the correct styling for a button in a toolbar, and also that keyboard interactions in a toolbar are consistent with the [WAI ARIA toolbar pattern](https://www.w3.org/TR/wai-aria-practices/#toolbar).
ToolbarButton can be used to add actions to a toolbar, usually inside a [Toolbar](/packages/components/src/toolbar/README.md) or [ToolbarGroup](/packages/components/src/toolbar-group/README.md) when used to create general interfaces. If you're using it to add controls to your custom block, you should consider using [BlockControls](/docs/designers-developers/developers/tutorials/block-tutorial/block-controls-toolbar-and-sidebar.md).

It has similar features to the [Button](/packages/components/src/button/README.md) component. Using `ToolbarButton` will ensure the correct styling for a button in a toolbar, and also that keyboard interactions in a toolbar are consistent with the [WAI-ARIA toolbar pattern](https://www.w3.org/TR/wai-aria-practices/#toolbar).

## Usage

To create general interfaces, you'll want to render ToolbarButton in a [Toolbar](/packages/components/src/toolbar/README.md) component.

```jsx
import { ToolbarButton } from "@wordpress/components";
import { edit } from "@wordpress/icons";

const MyToolbarButton = () => (
<MyToolbarButton
title="Edit"
icon={ edit }
onClick={ onEdit } />
);
import { Toolbar, ToolbarButton } from '@wordpress/components';
import { edit } from '@wordpress/icons';

function MyToolbar() {
return (
<Toolbar label="Options">
<ToolbarButton
icon={ edit }
label="Edit"
onClick={ () => alert( 'Editing' ) }
/>
</Toolbar>
);
}
```

### Inside BlockControls

If you're working on a custom block and you want to add controls to the block toolbar, you should use [BlockControls](/docs/designers-developers/developers/tutorials/block-tutorial/block-controls-toolbar-and-sidebar.md) instead. Optinally wrapping it with [ToolbarGroup](/packages/components/src/toolbar-group/README.md).

```jsx
import { BlockControls } from '@wordpress/block-editor';
import { ToolbarGroup, ToolbarButton } from '@wordpress/components';
import { edit } from '@wordpress/icons';

function Edit() {
return (
<BlockControls>
<ToolbarGroup>
<ToolbarButton
icon={ edit }
label="Edit"
onClick={ () => alert( 'Editing' ) }
/>
</ToolbarGroup>
</BlockControls>
);
}
```

## Props
Expand Down
4 changes: 1 addition & 3 deletions packages/components/src/toolbar-button/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ function ToolbarButton(
const accessibleToolbarState = useContext( ToolbarContext );

if ( ! accessibleToolbarState ) {
// This should be deprecated when <Toolbar __experimentalAccessibilityLabel="label">
// becomes stable.
return (
<ToolbarButtonContainer className={ containerClassName }>
<Button
Expand All @@ -52,7 +50,7 @@ function ToolbarButton(
) }
isPressed={ isActive }
disabled={ isDisabled }
data-experimental-toolbar-item
data-toolbar-item
{ ...extraProps }
{ ...props }
>
Expand Down
2 changes: 1 addition & 1 deletion packages/components/src/toolbar-button/stories/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const _default = () => {
const icon = text( 'Icon', 'wordpress' );

return (
<Toolbar __experimentalAccessibilityLabel="Example Toolbar">
<Toolbar label="Example Toolbar">
<ToolbarButton icon={ icon } label={ label } />
</Toolbar>
);
Expand Down
33 changes: 33 additions & 0 deletions packages/components/src/toolbar-group/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# ToolbarGroup

A ToolbarGroup can be used to create subgroups of controls inside a [Toolbar](/packages/components/src/toolbar/README.md).

## Usage

```jsx
import { Toolbar, ToolbarGroup, ToolbarButton } from '@wordpress/components';
import { paragraph, formatBold, formatItalic, link } from '@wordpress/icons';

function MyToolbar() {
return (
<Toolbar label="Options">
<ToolbarGroup>
<ToolbarButton icon={ paragraph } label="Paragraph" />
</ToolbarGroup>
<ToolbarGroup>
<ToolbarButton icon={ formatBold } label="Bold" />
<ToolbarButton icon={ formatItalic } label="Italic" />
<ToolbarButton icon={ link } label="Link" />
</ToolbarGroup>
</Toolbar>
);
}
```

### Props

ToolbarGroup will pass all HTML props to the underlying element.

## Related components

* ToolbarGroup may contain [ToolbarButton](/packages/components/src/toolbar-button/README.md) and [ToolbarItem](/packages/components/src/toolbar-Item/README.md) as children.
2 changes: 1 addition & 1 deletion packages/components/src/toolbar-group/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ function ToolbarGroup( {
...props
} ) {
// It'll contain state if `ToolbarGroup` is being used within
// `<Toolbar accessibilityLabel="label" />`
// `<Toolbar label="label" />`
const accessibleToolbarState = useContext( ToolbarContext );

if ( ( ! controls || ! controls.length ) && ! children ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ import ToolbarItem from '../toolbar-item';

function ToolbarGroupCollapsed( { controls = [], ...props } ) {
// It'll contain state if `ToolbarGroup` is being used within
// `<Toolbar __experimentalAccessibilityLabel="label" />`
// `<Toolbar label="label" />`
const accessibleToolbarState = useContext( ToolbarContext );

const renderDropdownMenu = ( toggleProps ) => (
<DropdownMenu
controls={ controls }
toggleProps={ {
...toggleProps,
'data-experimental-toolbar-item': true,
'data-toolbar-item': true,
} }
{ ...props }
/>
Expand Down
72 changes: 72 additions & 0 deletions packages/components/src/toolbar-item/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# ToolbarItem

A ToolbarItem is a generic headless component that can be used to make any custom component a [Toolbar](/packages/components/src/toolbar/README.md) item. It should be inside a [Toolbar](/packages/components/src/toolbar/README.md) or [ToolbarGroup](/packages/components/src/toolbar-group/README.md) when used to create general interfaces. If you're using it to add controls to your custom block, you should consider using [BlockControls](/docs/designers-developers/developers/tutorials/block-tutorial/block-controls-toolbar-and-sidebar.md).

## Usage

### `as` prop

You can use the `as` prop with a custom component or any HTML element.

```jsx
import { Toolbar, ToolbarItem, Button } from '@wordpress/components';

function MyToolbar() {
return (
<Toolbar label="Options">
<ToolbarItem as={ Button }>I am a toolbar button</ToolbarItem>
<ToolbarItem as="button">I am another toolbar button</ToolbarItem>
</Toolbar>
);
}
```

### render prop

You can pass children as function to get the ToolbarItem props and pass them to another component.

```jsx
import { Toolbar, ToolbarItem, DropdownMenu } from '@wordpress/components';
import { table } from '@wordpress/icons';

function MyToolbar() {
return (
<Toolbar label="Options">
<ToolbarItem>
{ ( toolbarItemHTMLProps ) => (
<DropdownMenu
icon={ table }
toggleProps={ toolbarItemHTMLProps }
label={ 'Edit table' }
controls={ [] }
/>
) }
</ToolbarItem>
</Toolbar>
);
}
```

### Inside BlockControls

If you're working on a custom block and you want to add controls to the block toolbar, you should use [BlockControls](/docs/designers-developers/developers/tutorials/block-tutorial/block-controls-toolbar-and-sidebar.md) instead. Optinally wrapping it with [ToolbarGroup](/packages/components/src/toolbar-group/README.md).

```jsx
import { BlockControls } from '@wordpress/block-editor';
import { ToolbarGroup, ToolbarItem, Button } from '@wordpress/components';

function Edit() {
return (
<BlockControls>
<ToolbarGroup>
<ToolbarItem as={ Button }>I am a toolbar button</ToolbarItem>
</ToolbarGroup>
</BlockControls>
);
}
```

## Related components

* ToolbarItem should be used inside [Toolbar](/packages/components/src/toolbar/README.md) or [ToolbarGroup](/packages/components/src/toolbar-group/README.md).
* If you want a simple toolbar button, consider using [ToolbarButton](/packages/components/src/toolbar-button/README.md) instead.
Loading

0 comments on commit af768dc

Please sign in to comment.