Skip to content

Commit

Permalink
fix: improve a11y on some components (#1045)
Browse files Browse the repository at this point in the history
  • Loading branch information
KirillDyachkovskiy authored Nov 8, 2023
1 parent f31d5a5 commit c6c6138
Show file tree
Hide file tree
Showing 17 changed files with 185 additions and 145 deletions.
3 changes: 2 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"selector": "TSTypeReference>TSQualifiedName[left.name='React'][right.name='FC']",
"message": "Don't use React.FC"
}
]
],
"jsx-a11y/no-autofocus": 0
},
"overrides": [
{
Expand Down
46 changes: 33 additions & 13 deletions src/components/DropdownMenu/DropdownMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';

import {Ellipsis} from '@gravity-ui/icons';

import {useActionHandlers} from '../../hooks/useActionHandlers';
import {Button} from '../Button';
import type {ButtonProps} from '../Button';
import {Icon} from '../Icon';
Expand All @@ -27,6 +28,11 @@ import {toItemList} from './utils/toItemList';

import './DropdownMenu.scss';

type SwitcherProps = {
onKeyDown: React.KeyboardEventHandler<HTMLElement>;
onClick: React.MouseEventHandler<HTMLElement>;
};

export type DropdownMenuProps<T> = {
/**
* Array of items.
Expand Down Expand Up @@ -56,8 +62,13 @@ export type DropdownMenuProps<T> = {
disabled?: boolean;
/**
* Menu toggle control.
* @deprecated Use renderSwitcher instead
*/
switcher?: React.ReactNode;
/**
* Menu toggle control.
*/
renderSwitcher?: (props: SwitcherProps) => React.ReactNode;
switcherWrapperClassName?: string;
/**
* Overrides the default switcher button props.
Expand Down Expand Up @@ -94,6 +105,7 @@ const DropdownMenu = <T,>({
data,
disabled,
switcher,
renderSwitcher,
switcherWrapperClassName,
defaultSwitcherProps,
defaultSwitcherClassName,
Expand Down Expand Up @@ -126,7 +138,7 @@ const DropdownMenu = <T,>({
[items],
);

const handleSwitcherClick: React.MouseEventHandler<HTMLDivElement> = (event) => {
const handleSwitcherClick: React.MouseEventHandler<HTMLElement> = (event) => {
if (disabled) {
return;
}
Expand All @@ -135,26 +147,34 @@ const DropdownMenu = <T,>({
togglePopup();
};

const {onKeyDown: handleSwitcherKeyDown} = useActionHandlers(handleSwitcherClick);

return (
<DropdownMenuContext.Provider value={contextValue}>
{/*as this div has Button component as child, clicking on it one fires onClick of this div on bubbling*/}
{/* FIXME remove switcher prop and this wrapper */}
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */}
<div
ref={anchorRef}
className={cnDropdownMenu('switcher-wrapper', switcherWrapperClassName)}
onClick={handleSwitcherClick}
>
{switcher || (
<Button
view="flat"
size={size}
{...defaultSwitcherProps}
className={cnDropdownMenu('switcher-button', defaultSwitcherClassName)}
disabled={disabled}
>
{icon}
</Button>
)}
{renderSwitcher?.({
onClick: handleSwitcherClick,
onKeyDown: handleSwitcherKeyDown,
}) ||
switcher || (
<Button
view="flat"
size={size}
// FIXME remove switcher prop and uncomment onClick handler
// onClick={handleSwitcherClick}
{...defaultSwitcherProps}
className={cnDropdownMenu('switcher-button', defaultSwitcherClassName)}
disabled={disabled}
>
{icon}
</Button>
)}
</div>
<DropdownMenuNavigationContextProvider anchorRef={anchorRef} disabled={!isPopupShown}>
<DropdownMenuPopup
Expand Down
55 changes: 31 additions & 24 deletions src/components/DropdownMenu/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import {DropdownMenu} from '@gravity-ui/uikit';
```

This is a dropdown menu component with item grouping, submenus, and a customizable toggle. The dropdown menu items are configured with the `items` prop. By default, the menu toggle is a button with the ellipsis icon (****), which can be overridden with the `switcher` prop.
This is a dropdown menu component with item grouping, submenus, and a customizable toggle. The dropdown menu items are configured with the `items` prop. By default, the menu toggle is a button with the ellipsis icon (****), which can be overridden with the `renderSwitcher` prop.

<!--LANDING_BLOCK
Expand Down Expand Up @@ -326,18 +326,16 @@ LANDING_BLOCK-->

## Custom menu toggle

The menu toggle is configured with the `switcher` prop. It can be any React component (or any `React.ReactNode` in the TypeScript terms). By default, the menu toggle is a button with the ellipsis icon (****).
The menu toggle is configured with the `renderSwitcher` prop. It can be any function that returns a React component (or any `(props: SwitcherProps) => React.ReactNode` in the TypeScript terms, see [`SwitcherProps`](#switcherprops) below). By default, the menu toggle is a button with the ellipsis icon (****).

<!--LANDING_BLOCK
<ExampleBlock
code={`
<DropdownMenu
switcher={
<div style={{cursor: 'pointer', borderBottom: '1px dotted'}}>
John Doe
</div>
}
renderSwitcher={(props) => (
<div {...props} style={{cursor: 'pointer', borderBottom: '1px dotted'}}>John Doe</div>
)}
items={[
{
action: () => console.log('Rename'),
Expand All @@ -353,11 +351,9 @@ The menu toggle is configured with the `switcher` prop. It can be any React comp
`}
>
<UIKit.DropdownMenu
switcher={
<div style={{cursor: 'pointer', borderBottom: '1px dotted'}}>
John Doe
</div>
}
renderSwitcher={(props) => (
<div {...props} style={{cursor: 'pointer', borderBottom: '1px dotted'}}>John Doe</div>
)}
items={[
{
action: () => console.log('Rename'),
Expand All @@ -378,7 +374,11 @@ LANDING_BLOCK-->

```jsx
<DropdownMenu
switcher={<div style={{cursor: 'pointer', borderBottom: '1px dotted'}}>John Doe</div>}
renderSwitcher={(props) => (
<div {...props} style={{cursor: 'pointer', borderBottom: '1px dotted'}}>
John Doe
</div>
)}
items={[
{
action: () => console.log('Rename'),
Expand All @@ -401,18 +401,18 @@ The example above is oversimplified to demonstrate the idea of the customizable

Custom icons can be added to a `DropdownMenu` item by assigning the `iconStart` or `iconEnd` property. By default, `DropdownMenu` items go without icons.

The menu toggle icon can be changed with the `DropdownMenu`'s `switcher` prop. By default, the menu toggle is a button with the ellipsis icon (****).
The menu toggle icon can be changed with the `DropdownMenu`'s `renderSwitcher` prop. By default, the menu toggle is a button with the ellipsis icon (****).

<!--LANDING_BLOCK
<ExampleBlock
code={`
<DropdownMenu
switcher={
<Button view="flat">
renderSwitcher={(props) => (
<Button {...props} view="flat">
<Icon size={16} data={Bars} />
</Button>
}
)}
items={[
{
iconStart: <Icon size={16} data={Pencil} />,
Expand All @@ -430,16 +430,16 @@ The menu toggle icon can be changed with the `DropdownMenu`'s `switcher` prop. B
`}
>
<UIKit.DropdownMenu
switcher={
<UIKit.Button view="flat">
renderSwitcher={(props) => (
<UIKit.Button {...props} view="flat">
<UIKit.Icon
data={() => (
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16"><path fill="currentColor" fill-rule="evenodd" d="M1.25 3.25A.75.75 0 0 1 2 2.5h12A.75.75 0 0 1 14 4H2a.75.75 0 0 1-.75-.75Zm0 4.75A.75.75 0 0 1 2 7.25h12a.75.75 0 0 1 0 1.5H2A.75.75 0 0 1 1.25 8ZM2 12a.75.75 0 0 0 0 1.5h12a.75.75 0 0 0 0-1.5H2Z" clip-rule="evenodd"></path></svg>
)}
size={16}
/>
</UIKit.Button>
}
)}
items={[
{
iconStart: (
Expand Down Expand Up @@ -476,11 +476,11 @@ LANDING_BLOCK-->

```jsx
<DropdownMenu
switcher={
<Button view="flat">
renderSwitcher={(props) => (
<Button {...props} view="flat">
<Icon size={16} data={Bars} />
</Button>
}
)}
items={[
{
iconStart: <Icon size={16} data={Pencil} />,
Expand Down Expand Up @@ -508,7 +508,7 @@ LANDING_BLOCK-->
| `icon` | Icon of the default `switcher`. | `React.ReactNode` | Ellipsis icon |
| `size` | Applied both to the default `switcher` and the menu. | `'s' \| 'm' \| 'l' \| 'xl'` | `'m'` |
| `disabled` | Setting this prop to `true` disables the `switcher` button and prevents the menu from opening. | `boolean` | |
| `switcher` | Menu toggle control. | `React.ReactNode` | |
| `renderSwitcher` | Render function for the menu toggle control. | `React.ReactNode` | |
| `switcherWrapperClassName` | Value for the `className` prop of the `switcher`'s parent component. | `string` | |
| `defaultSwitcherProps` | Default `switcher` props. | `ButtonProps` | |
| `defaultSwitcherClassName` | Value for the `className` prop of the default `switcher`. | `string` | |
Expand Down Expand Up @@ -542,3 +542,10 @@ This type describes individual dropdown menu items.
| `popupProps` | Submenu popup props. | `string` | |
| `path` | Path of indexes from the root to the current item. | `number[]` | |
| `closeMenu` | Custom `closeMenu` callback. It can be called instead of closing the main menu and used to close submenus before the main menu. | `() => void` | |

### SwitcherProps

| Name | Description | Type |
| :---------- | :------------------------------------------------------------- | :----------: |
| `onClick` | Called when the switcher is clicked. | `() => void` |
| `onKeyDown` | Called when the switcher is focused and action key is pressed. | `() => void` |
Loading

0 comments on commit c6c6138

Please sign in to comment.