1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-02 06:07:06 +01:00
metamask-extension/ui/components/component-library/modal
2023-05-19 13:20:15 -07:00
..
__snapshots__ Adding Modal and updating ModalContent component (#19020) 2023-05-19 13:20:15 -07:00
index.ts Adding Modal and updating ModalContent component (#19020) 2023-05-19 13:20:15 -07:00
modal.context.ts Adding Modal and updating ModalContent component (#19020) 2023-05-19 13:20:15 -07:00
modal.stories.tsx Adding Modal and updating ModalContent component (#19020) 2023-05-19 13:20:15 -07:00
modal.test.tsx Adding Modal and updating ModalContent component (#19020) 2023-05-19 13:20:15 -07:00
modal.tsx Adding Modal and updating ModalContent component (#19020) 2023-05-19 13:20:15 -07:00
modal.types.ts Adding Modal and updating ModalContent component (#19020) 2023-05-19 13:20:15 -07:00
README.mdx Adding Modal and updating ModalContent component (#19020) 2023-05-19 13:20:15 -07:00

import { Story, Canvas, ArgsTable } from '@storybook/addon-docs';

import { Modal } from './modal';

# Modal

The `Modal` focuses the user's attention exclusively on information via a window that is overlaid on primary content. It should be used with the `ModalOverlay`, `ModalContent` and `ModalHeader` components to create a complete modal.

<Canvas>
  <Story id="components-componentlibrary-modal--default-story" />
</Canvas>

## Props

The `Modal` accepts all props below as well as all [Box](/docs/components-ui-box--default-story#props) component props

<ArgsTable of={Modal} />

### Usage

The `Modal` component is a very atomic level component that is meant to be used with `ModalOverlay`, `ModalContent` and `ModalHeader`.

When the modal opens:

- Focus is trapped within the modal and set to the first tabbable element.
- Content behind a modal dialog is inert, meaning that users cannot interact with it.
- Use the `isOpen` prop to control whether the modal is open or closed.
- Use the `onClose` prop to fire a callback when the modal is closed. This is used for the `isClosedOnOutsideClick` prop and the `isClosedOnEscapeKey`.

<Canvas>
  <Story id="components-componentlibrary-modal--usage" />
</Canvas>

```jsx
import React, { useState, useRef } from 'react';
import { Modal, ModalOverlay, ModalContent, ModalHeader, Text, Button } from '../../component-library';

const [open, setOpen] = useState(false);

const handleOnClick = () => {
  setOpen(true);
};

const handleOnClose = () => {
  setOpen(false);
};

<Button onClick={handleOnClick}>OpenModal</Button>
<Modal
  isOpen={open}
  onClose={handleOnClose}
>
  <ModalOverlay />
  <ModalContent>
    <ModalHeader onClose={handleOnClose} onBack={handleOnClose}>
      Modal Header
    </ModalHeader>
    <Text>Children</Text>
  </ModalContent>
</Modal>
```

### Is Closed On Outside Click

Use the `isClosedOnOutsideClick` prop to control whether the modal should close when the user clicks outside of the modal.

Defaults to `true`.

<Canvas>
  <Story id="components-componentlibrary-modal--is-closed-on-outside-click" />
</Canvas>

```jsx
import { Modal } from '../../component-library';

<Modal isClosedOnOutsideClick={false} />;
```

### Is Closed On Escape Key

Use the `isClosedOnEscapeKey` prop to control whether the modal should close when the user presses the escape key.

Defaults to `true`.

<Canvas>
  <Story id="components-componentlibrary-modal--is-closed-on-escape-key" />
</Canvas>

```jsx
import { Modal } from '../../component-library';

<Modal isClosedOnEscapeKey={false} />;
```

### Initial Focus Ref

Use the `initialFocusRef` to set the `ref` of the element to receive focus initially. This is useful for input elements that should receive focus when the modal opens.

<Canvas>
  <Story id="components-componentlibrary-modal--initial-focus-ref" />
</Canvas>

```jsx
import React, { useState, useRef } from 'react';
import { Modal, ModalOverlay, ModalContent, ModalHeader, TextFieldSearch, Button } from '../../component-library';

// Ref to set initial focus
const inputRef = React.useRef<HTMLDivElement>(null);

const [open, setOpen] = useState(false);

const handleOnClick = () => {
  setOpen(true);
};

const handleOnClose = () => {
  setOpen(false);
};

<Button onClick={handleOnClick}>Open modal</Button>
<Modal
  isOpen={isOpen}
  onClose={handleOnClose}
  initialFocusRef={inputRef}
>
  <ModalOverlay />
  <ModalContent >
    <ModalHeader
      onClose={handleOnClose}
      onBack={handleOnClose}
      marginBottom={4}
    >
      Modal Header
    </ModalHeader>
    <TextFieldSearch
      placeholder="Search"
      inputProps={{ ref: inputRef }}
      width={BLOCK_SIZES.FULL}
    />
  </ModalContent>
</Modal>
```

### Final Focus Ref

Use the `finalFocusRef` to set the `ref` of the element to receive focus when the modal closes.

<Canvas>
  <Story id="components-componentlibrary-modal--final-focus-ref" />
</Canvas>

```jsx
import React, { useState, useRef } from 'react';
import { Modal, ModalOverlay, ModalContent, ModalHeader, TextFieldSearch, Button } from '../../component-library';

// Ref to set focus after modal closes
const buttonRef = React.useRef<HTMLButtonElement>(null);

const [open, setOpen] = useState(false);

const handleOnClick = () => {
  setOpen(true);
};

const handleOnClose = () => {
  setOpen(false);
};

<Button onClick={handleOnClick} marginRight={4}>
  Open modal
</Button>
<button ref={buttonRef}>Receives focus after close</button>
<Modal
  isOpen={isOpen}
  onClose={handleOnClose}
  finalFocusRef={buttonRef}
>
  <ModalOverlay />
  <ModalContent >
    <ModalHeader
      onClose={handleOnClose}
      onBack={handleOnClose}
      marginBottom={4}
    >
      Modal Header
    </ModalHeader>
    <Text>{args.children}</Text>
  </ModalContent>
</Modal>
```

### Restore Focus

Use the `restoreFocus` prop to restore focus to the element that triggered the `Modal` once it unmounts

Defaults to `false`

<Canvas>
  <Story id="components-componentlibrary-modal--restore-focus" />
</Canvas>

```jsx
import { Modal } from '../../component-library';

<Modal restoreFocus={true} />;
```

### Auto Focus

If `true`, the first focusable element within the `children` will auto-focused once `Modal` mounts. Depending on the content of `Modal` this is usually the back or close button in the `ModalHeader`.

Defaults to `true`

<Canvas>
  <Story id="components-componentlibrary-modal--auto-focus" />
</Canvas>

```jsx
import { Modal } from '../../component-library';

<Modal autoFocus={false} />;
```

## Accessibility

### Keyboard and Focus Management

- When the modal opens, focus is trapped within it.
- When the modal opens, focus is automatically set to the first enabled element, or the element from `initialFocusRef`.
- When the modal closes, focus returns to the element that was focused before the modal activated, or the element from `finalFocusRef`.
- Clicking on the overlay closes the Modal.
- Pressing ESC closes the Modal.
- Scrolling is blocked on the elements behind the modal.
- The modal is rendered in a portal attached to the end of document.body to break it out of the source order and make it easy to add aria-hidden to its siblings.

### ARIA

- The `ModalContent` has aria-modal="true" and role="dialog"
- The `ModalOverlay` has aria-hidden="true"