Back to React Spectrum

Dialog

packages/react-aria-components/docs/Dialog.mdx

2022-12-1611.9 KB
Original Source

{/* Copyright 2020 Adobe. All rights reserved. This file is licensed to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */}

import {Layout} from '@react-spectrum/docs'; export default Layout;

import docs from 'docs:react-aria-components'; import overlayStatelyDocs from 'docs:@react-stately/overlays'; import {PropTable, HeaderInfo, TypeLink, PageDescription, StateTable, ContextTable, VersionBadge, ClassAPI} from '@react-spectrum/docs'; import styles from '@react-spectrum/docs/src/docs.css'; import packageData from 'react-aria-components/package.json'; import Anatomy from '/packages/react-aria/docs/dialog/anatomy.svg'; import ChevronRight from '@spectrum-icons/workflow/ChevronRight'; import {Divider} from '@react-spectrum/divider'; import {ExampleCard} from '@react-spectrum/docs/src/ExampleCard'; import {ExampleList} from '@react-spectrum/docs/src/ExampleList'; import {Keyboard} from '@react-spectrum/text'; import {StarterKits} from '@react-spectrum/docs/src/StarterKits';


category: Overlays keywords: [dialog, popover, aria] type: component

Dialog

<PageDescription>{docs.exports.Dialog.description}</PageDescription>

<HeaderInfo packageData={packageData} componentNames={['Dialog']} sourceData={[ {type: 'W3C', url: 'https://www.w3.org/WAI/ARIA/apg/patterns/dialogmodal/'} ]} />

Example

tsx
import {DialogTrigger, Modal, Dialog, Button, Heading, TextField, Label, Input} from 'react-aria-components';

<DialogTrigger>
  <Button>Sign up…</Button>
  <Modal>
    <Dialog>
      <form>
        <Heading slot="title">Sign up</Heading>
        <TextField autoFocus>
          <Label>First Name</Label>
          <Input />
        </TextField>
        <TextField>
          <Label>Last Name</Label>
          <Input />
        </TextField>
        <Button slot="close" style={{marginTop: 8}}>
          Submit
        </Button>
      </form>
    </Dialog>
  </Modal>
</DialogTrigger>
<details> <summary style={{fontWeight: 'bold'}}><ChevronRight size="S" /> Show CSS</summary> ```css hidden @import "@react-aria/example-theme"; @import './Button.mdx' layer(button); @import './TextField.mdx' layer(textfield); @import './Popover.mdx' layer(popover); @import './Modal.mdx' layer(modal); ```
css
.react-aria-Dialog {
  outline: none;
  padding: 30px;
  max-height: inherit;
  box-sizing: border-box;
  overflow: auto;

  .react-aria-Heading[slot=title] {
    line-height: 1em;
    margin-top: 0;
  }
}
</details>

Features

The HTML <dialog> element can be used to build dialogs. However, it is not yet widely supported across browsers, and building fully accessible custom dialogs from scratch is very difficult and error prone. Dialog helps achieve accessible dialogs that can be styled as needed.

  • Flexible – Dialogs can be used within a Modal or Popover to create many types of overlay elements.
  • Accessible – Exposed to assistive technology as a dialog or alertdialog with ARIA. The dialog is automatically labeled by a nested <Heading> element. Content outside the dialog is hidden from assistive technologies while it is open.
  • Focus management – Focus is moved into the dialog on mount, and restored to the trigger element on unmount. While open, focus is contained within the dialog, preventing the user from tabbing outside.

Anatomy

<Anatomy />

A dialog consists of a container element and an optional title and close button. It can be placed within a Modal or Popover, to create modal dialogs, popovers, and other types of overlays. A DialogTrigger can be used to open a dialog overlay in response to a user action, e.g. clicking a button.

tsx
import {DialogTrigger, Modal, Dialog, Button, Heading} from 'react-aria-components';

<DialogTrigger>
  <Button />
  <Modal>
    <Dialog>
      <Heading slot="title" />
      <Button slot="close" />
    </Dialog>
  </Modal>
</DialogTrigger>

If a dialog does not have a visible heading element, an aria-label or aria-labelledby prop must be passed instead to identify the element to assistive technology.

Examples

<ExampleList tag="dialog" />

Starter kits

To help kick-start your project, we offer starter kits that include example implementations of all React Aria components with various styling solutions. All components are fully styled, including support for dark mode, high contrast mode, and all UI states. Each starter comes with a pre-configured Storybook that you can experiment with, or use as a starting point for your own component library.

<StarterKits component="dialog" tailwindComponent="alertdialog" />

Popover

A Dialog may be placed within a Popover to display it in context with a trigger element.

tsx
import {Popover, OverlayArrow} from 'react-aria-components';

<DialogTrigger>
  <Button aria-label="Help"></Button>
  <Popover>
    <OverlayArrow>
      <svg width={12} height={12} viewBox="0 0 12 12"><path d="M0 0 L6 6 L12 0" /></svg>
    </OverlayArrow>
    <Dialog>
      <Heading slot="title">Help</Heading>
      <p>For help accessing your account, please contact support.</p>
    </Dialog>
  </Popover>
</DialogTrigger>

Alert dialog

Alert dialogs are a special type of dialog meant to present a prompt that the user must confirm before an action proceeds. An alert dialog may also behave differently with assistive technologies, such as playing a system alert sound when opening. Use the role="alertdialog" prop on the <Dialog> element to make an alert dialog.

tsx
<DialogTrigger>
  <Button>Delete…</Button>
  <Modal>
    <Dialog role="alertdialog">
      {({close}) => (
        <>
          <Heading slot="title">Delete file</Heading>
          <p>This will permanently delete the selected file. Continue?</p>
          <div style={{display: 'flex', gap: 8}}>
            <Button onPress={close}>Cancel</Button>
            <Button onPress={close}>Delete</Button>
          </div>
        </>
      )}
    </Dialog>
  </Modal>
</DialogTrigger>

Custom trigger

DialogTrigger works out of the box with any pressable React Aria component (e.g. Button, Link, etc.). Custom trigger elements such as third party components and other DOM elements are also supported by wrapping them with the <Pressable> component, or using the usePress hook.

tsx
import {Pressable} from 'react-aria-components';

<DialogTrigger>
  <Pressable>
    <span role="button">Custom trigger</span>
  </Pressable>
  <Modal>
    <Dialog>
      <Heading slot="title">Dialog</Heading>
      <p>This dialog was triggered by a custom button.</p>
      <Button slot="close">Close</Button>
    </Dialog>
  </Modal>
</DialogTrigger>

Note that any <Pressable> child must have an interactive ARIA role or use an appropriate semantic HTML element so that screen readers can announce the trigger. Trigger components must forward their ref and spread all props to a DOM element.

tsx
const CustomTrigger = React.forwardRef((props, ref) => (
  <button {...props} ref={ref} />
));

Props

DialogTrigger

<PropTable component={docs.exports.DialogTrigger} links={docs.links} />

Dialog

<PropTable component={docs.exports.Dialog} links={docs.links} />

Heading

A <Heading> accepts all HTML attributes.

Button

A <Button slot="close"> element can be placed inside a <Dialog> to close it when pressed.

<details> <summary style={{fontWeight: 'bold'}}><ChevronRight size="S" /> Show props</summary> <PropTable component={docs.exports.Button} links={docs.links} /> </details>

Styling

React Aria components can be styled in many ways, including using CSS classes, inline styles, utility classes (e.g. Tailwind), CSS-in-JS (e.g. Styled Components), etc. By default, all components include a builtin className attribute which can be targeted using CSS selectors. These follow the react-aria-ComponentName naming convention.

css
.react-aria-Dialog {
  /* ... */
}

A custom className can also be specified on any component. This overrides the default className provided by React Aria with your own.

jsx
<Dialog className="my-dialog">
</Dialog>

The selectors for each component used in a Dialog are documented below.

DialogTrigger

The DialogTrigger component does not render any DOM elements (it only passes through its children) so it does not support styling. If you need a wrapper element, add one yourself inside the <DialogTrigger>.

jsx
<DialogTrigger>
  <div className="my-dialog-trigger">
  </div>
</DialogTrigger>

Dialog

A Dialog can be targeted with the .react-aria-Dialog CSS selector, or by overriding with a custom className.

Heading

A Heading can be targeted with the .react-aria-Heading CSS selector, or by overriding with a custom className.

Advanced customization

Custom children

DialogTrigger and Dialog pass props to their child components, such as the trigger button and modal overlay, via their associated contexts. These contexts are exported so you can also consume them in your own custom components. This enables you to reuse existing components from your app or component library together with React Aria Components.

<ContextTable components={['Button', 'Dialog', 'Modal', 'Popover', 'Heading']} docs={docs} />

This example consumes from HeadingContext in an existing styled heading component to make it compatible with React Aria Components. The <TypeLink links={docs.links} type={docs.exports.useContextProps} /> hook merges the local props and ref with the ones provided via context by Dialog.

tsx
import type {HeadingProps} from 'react-aria-components';
import {HeadingContext, useContextProps} from 'react-aria-components';

const MyCustomHeading = React.forwardRef((props: HeadingProps, ref: React.ForwardedRef<HTMLHeadingElement>) => {
  // Merge the local props and ref with the ones provided via context.
  ///- begin highlight -///
  [props, ref] = useContextProps(props, ref, HeadingContext);
  ///- end highlight -///

  // ... your existing Heading component
  return <h2 {...props} ref={ref} />;
});

Now you can use MyCustomHeading within a Dialog, in place of the builtin React Aria Components Heading.

tsx
<Dialog>
  <MyCustomHeading>Dialog title</MyCustomHeading>
</Dialog>

State

DialogTrigger provides an <TypeLink links={overlayStatelyDocs.links} type={overlayStatelyDocs.exports.OverlayTriggerState} /> object to its children via OverlayTriggerStateContext. This can be used to access and manipulate the dialog trigger's state.

This example shows a CloseButton component that can be placed within a DialogTrigger to close the overlay.

tsx
import {OverlayTriggerStateContext} from 'react-aria-components';

function CloseButton() {
  /*- begin highlight -*/
  let state = React.useContext(OverlayTriggerStateContext)!;
  /*- end highlight -*/
  return <Button onPress={() => state.close()}>Close</Button>;
}

<DialogTrigger>
  <Button>About</Button>
  <Modal isDismissable>
    <Dialog>
      <Heading slot="title">About</Heading>
      <p>Copyright © 2023 Adobe. All rights reserved.</p>
      <CloseButton />
    </Dialog>
  </Modal>
</DialogTrigger>

Hooks

If you need to customize things further, such as accessing internal state or customizing DOM structure, you can drop down to the lower level Hook-based API. See useDialog for more details.