Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/HugoGiraudel/react-a11y-dialog

A React component for a11y-dialog
https://github.com/HugoGiraudel/react-a11y-dialog

a11y accessibility dialog hook modal react

Last synced: 3 months ago
JSON representation

A React component for a11y-dialog

Awesome Lists containing this project

README

        

# React A11yDialog

react-a11y-dialog provides a thin (~600b) React component and hook for [a11y-dialog](https://github.com/KittyGiraudel/a11y-dialog) relying on [React portals](https://reactjs.org/docs/portals.html) to ease the use of accessible dialog windows in React applications.

Version compatibility:

- For React versions **before** 16, use `[email protected]`.
- For React versions **before** 16.8, use `[email protected]`.
- For React 16.8 and later, use the latest version

_Special thanks to Moritz Kröger (@morkro), Mayank (@mayank99) and EJ Mason (@mxmason) for their kind help in making that library better._

- [Install](#install)
- [API](#api)
- [Hook](#hook)
- [Server-side Rendering](#server-side-rendering)
- [Mocking portals in tests](#mocking-portals-in-tests)
- [Example](#example)

## Install

```sh
npm install --save react-a11y-dialog
```

## API

| Name | Type | Required | Default | Description |
| :-- | :-- | :-- | :-- | :-- |
| `id` | `string` | **true** | — | ExpandThe HTML `id` attribute of the dialog element, internally used by a11y-dialog to manipulate the dialog. |
| `title` | `node` | **true** | — | ExpandThe title of the dialog, mandatory in the document to provide context to assistive technology. Could be [hidden with CSS](https://kittygiraudel.com/2016/10/13/css-hide-and-seek/) (while remaining accessible). |
| `dialogRoot` | `string` | false | `document.body` | ExpandThe container for the dialog to be rendered into ([React portal](https://reactjs.org/docs/portals.html)’s root). |
| `dialogRef` | `function` | false | `() => {}` | Expand A function called when the component has mounted, receiving the [instance of A11yDialog](https://a11y-dialog.netlify.app/usage/instantiation/#js-api) so that it can be programmatically accessed later on. |
| `titleId` | `string` | false | `${props.id}-title` | ExpandThe HTML `id` attribute of the dialog’s title element, used by assistive technologies to provide context and meaning to the dialog window. |
| `closeButtonLabel` | `string` | false | Close this dialog window | ExpandThe HTML `aria-label` attribute of the close button, used by assistive technologies to provide extra meaning to the usual cross-mark. |
| `closeButtonContent` | `node` | false | `\u00D7` (×) | ExpandThe string that is the inner HTML of the close button. |
| `closeButtonPosition` | `string` | false | first | ExpandWhether to render the close button as first element, last element or not at all. Options are: `first`, `last` and `none`. ⚠️ **Caution!** Setting it to `none` without providing a close button manually will be a critical accessibility issue. |
| `classNames` | `object` | false | {} | ExpandObject of classes for each HTML element of the dialog element. Keys are: `container`, `overlay`, `dialog`, `title`, `closeButton`. See [a11y-dialog docs](https://a11y-dialog.netlify.app/usage/markup) for reference. |
| `role` | `string` | false | dialog | ExpandThe `role` attribute of the dialog element, either `dialog` (default) or `alertdialog` to make it a modal (preventing closing on click outside of ESC key). |

## Hook

The library exports both `A11yDialog`, a React component rendering a dialog while performing the `a11y-dialog` bindings under the hood, and a `useA11yDialog` hook providing only the binding logic without any markup.

Using the hook can be handy when building your own dialog. Beware though, **it is an advanced feature**. Make sure to [stick to the expected markup](https://a11y-dialog.netlify.app/usage/markup).

```js
import { useA11yDialog } from 'react-a11y-dialog'

const MyCustomDialog = props => {
// `instance` is the `a11y-dialog` instance.
// `attr` is an object with the following keys:
// - `container`: the dialog container
// - `overlay`: the dialog overlay (sometimes called backdrop)
// - `dialog`: the actual dialog box
// - `title`: the dialog mandatory title
// - `closeButton`: the dialog close button
const [instance, attr] = useA11yDialog({
// The required HTML `id` attribute of the dialog element, internally used
// a11y-dialog to manipulate the dialog.
id: 'my-dialog',
// The optional `role` attribute of the dialog element, either `dialog`
// (default) or `alertdialog` to make it a modal (preventing closing on
// click outside of ESC key).
role: 'dialog',
})

const dialog = ReactDOM.createPortal(




Your dialog title

Your dialog content


Close dialog


,
document.body
)

return (
<>
instance.show()}>
Open dialog

{dialog}
>
)
}
```

## Server-side rendering

The `A11yDialog` React component does not render anything on the server, and waits for client-side JavaScript to kick in to render the dialog through the React portal.

## Mocking portals in tests

When you’re using `react-a11y-dialog` in your unit tests, it might be necessary to mock React Portals and inject them to the root DOM before your tests are running. To accomplish that, create helper functions that attach all portals before a test and remove them afterwards.

```js
const ROOT_PORTAL_IDS = ['dialog-root']

export const addPortalRoots = () => {
for (const id of ROOT_PORTAL_IDS) {
if (!global.document.querySelector('#' + id)) {
const rootNode = global.document.createElement('div')
rootNode.setAttribute('id', id)
global.document.body.appendChild(rootNode)
}
}
}

export const removePortalRoots = () => {
for (const id of rootPortalIds) {
global.document.querySelector('#' + id)?.remove()
}
}
```

And then use them in your tests.

```js
describe('Testing MyComponent', () => {
beforeAll(() => addPortalRoots())
afterAll(() => removePortalRoots())
})
```

## Example

The following example is a minimal setup of `react-a11y-dialog`. Additionally, you will need to add the required styles as per the recommendations from the [`a11y-dialog` styling docs](https://a11y-dialog.netlify.app/usage/styling). How you integrate these styles is left to your discretion and depends on the styling layer you’ve chosen for your project (classes, inline styles, CSS Modules, CSS-in-JS…). For anything but inline styles, styles will typically need to be passed via the `classNames` object prop, and as such will end up being applied to the elements rendered by React.

```jsx
import { A11yDialog } from 'react-a11y-dialog'

const App = props => {
const dialog = React.useRef()

return (


dialog.current.show()}>
Open the dialog

(dialog.current = instance)}
title='The dialog title'
>

Some content for the dialog.




)
}

ReactDOM.render(, document.querySelector('#root'))
```

## Migrating to v7

Version 7 now relies on [email protected]. It should be largely backward compatible with version 6 though.

- Make sure to read the [a11y-dialog migration guide](https://a11y-dialog.netlify.app/migrating-to-v8) to adjust your a11y-dialog usage.
- Typing has been consolidated.
- Distribution has been improved (CJS + ESM, both normal and minified).
- The `A11yDialogInstance` type is re-exported from `a11y-dialog` for convenience.

## Migrating to v6

Version 6 now relies on [email protected]. See the [a11y-dialog migration guide](https://a11y-dialog.netlify.app/migrating-to-v7). Most notable changes requiring some update:

- The `inner` container is no longer a thing.
- The `appRoot` prop is no longer a thing.
- The `dialogRoot` prop now defaults to `document.body`.
- The `useDialogElement` prop is no longer supported (`` is no longer supported).