import { ArgTypes, Meta, Story } from "@storybook/addon-docs/blocks";
import { Modal } from ".";
import * as ModalStories from "./Modal.stories";
import TestingDocs from "../../../../../.storybook/pages/jestHTMLDialogPolyfillTestingDocs.mdx";

<Meta of={ModalStories} />

# Modal

The `Modal` component is used for displaying data that overlays the content
beneath. It can be comprised of header and main content sections, both of which
are optional.

## Install

```bash
yarn add @simplybusiness/mobius
```

## Usage

```js
import { Modal } from "@simplybusiness/mobius";
```

## Examples

### Basic Modal

Modal `open` state should be set in a parent container and passed in as a prop, along with an `onClose` function to set open state to false (or otherwise hide the modal).

You will also need to set some trigger element or event to show the modal. Ordinarily that will be a button, but any user event will work.

<Story of={ModalStories.Default} />

```jsx
const Demo = () => {
  const [open, setOpen] = useState(false);

  return (
    <>
      <Button onClick={() => setOpen(true)}>Show Modal</Button>

      <Modal isOpen={open} onClose={() => setOpen(false)}>
        <Modal.Header>The Latin Modal</Modal.Header>
        <Modal.Content>
          Aliquam fringilla quam vel purus rhoncus interdum in quis sapien. Sed
          at eros et lectus mollis posuere. Morbi augue augue, venenatis vel
          consectetur sed, vulputate et lorem. Integer venenatis nibh molestie
          commodo lobortis. Phasellus sit amet dignissim nisi. Vivamus nisi
          augue, scelerisque commodo libero vitae, vulputate interdum ex.
          Integer non risus sollicitudin, cursus ipsum vitae, rhoncus nulla.
        </Modal.Content>
      </Modal>
    </>
  );
};
```

### Fullscreen Modal

Set the `isFullScreen` prop to `true`.

<Story of={ModalStories.FullScreen} />

```jsx
const FullScreenDemo = () => {
  const [open, setOpen] = useState(false);

  return (
    <>
      <Button onClick={() => setOpen(true)} variant="secondary">
        Show Fullscreen Modal 🚀
      </Button>

      <Modal size="fullscreen" isOpen={open} onClose={() => setOpen(false)}>
        <Modal.Header>The Latin Modal</Modal.Header>
        <Modal.Content>...</Modal.Content>
      </Modal>
    </>
  );
};
```

### With Animation

Set the `animation` prop to either `slideUp` or `fade`.

_Note:_ the animation will be disabled if user has reduced motion preference enabled on their device.

<Story of={ModalStories.WithAnimation} />

```jsx
const WithAnimationDemo = () => {
  const [open, setOpen] = useState(false);

  return (
    <>
      <Button onClick={() => setOpen(true)}>
        Show Modal With Animation 🧙‍♂️
      </Button>

      <Modal isOpen={open} onClose={() => setOpen(false)} animation="slideUp">
        <Modal.Header>The Latin Modal</Modal.Header>
        <Modal.Content>
          Aliquam fringilla quam vel purus rhoncus interdum in quis sapien. Sed
          at eros et lectus mollis posuere. Morbi augue augue, venenatis vel
          consectetur sed, vulputate et lorem. Integer venenatis nibh molestie
          commodo lobortis. Phasellus sit amet dignissim nisi. Vivamus nisi
          augue, scelerisque commodo libero vitae, vulputate interdum ex.
          Integer non risus sollicitudin, cursus ipsum vitae, rhoncus nulla.
        </Modal.Content>
      </Modal>
    </>
  );
};
```

### Closing Modal from a child component

As of `@simplybusiness/mobius` version 4.5.0 `Modal` component can be closed from a child component using the `useModal()` hook.

The hook provides `onClose` method and `closeLabel` string that can be used to close the modal.

`onClose` method will ensure that the modal is closed using its CSS transition, as well as `onClose` prop firing, if provided.

If you previously used `onClose` and/or `closeLabel` props provided by default to all children props, you will need to replace these with `useModal()`.

Parent component for both old and new implementations remains the same:

```jsx
import { Modal, Button } from "@simplybusiness/mobius";

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

  return (
    <>
      <Button onClick={() => setOpen(true)}>Open</Button>

      <Modal isOpen={open} onClose={() => setOpen(false)} animation="slideUp">
        <Modal.Header>Title</Modal.Header>
        <Modal.Content>Content</Modal.Content>
      </Modal>
    </>
  );
};
```

Old implementation for child component:

```jsx
import { Button } from "@simplybusiness/mobius";

const ChildComponent = ({ onClose, closeLabel }) => (
  <Button onClick={onClose}>{closeLabel}</Button>
);
```

New implementation for child component:

```jsx
import { useModal, Button } from "@simplybusiness/mobius";

const ChildComponent = () => {
  const { onClose, closeLabel } = useModal();

  return <Button onClick={onClose}>{closeLabel}</Button>;
};
```

The motivation behind this change is to provide a more consistent and predictable API for the `Modal` component.

This particularly applies to components that already have props named `onClose` and `closeLabel`, which may lead to unexpected side effects.

### Nested Modals

<Story of={ModalStories.WithNested} />

```jsx
const ChildModal = () => {
  const [showModal, setShowModal] = useState<boolean>(false);
  const show = () => setShowModal(true);
  const hide = () => setShowModal(false);

  return (
    <>
      <Button onClick={show}>Show child modal</Button>
      <Modal isOpen={showModal} onClose={hide} animation="slideUp">
        <Modal.Header>Howdy</Modal.Header>
        <Modal.Content>
          <Text>
            Closing or cancelling (with Escape key) should leave the parent
            modal open.
          </Text>
        </Modal.Content>
      </Modal>
    </>
  );
};

const Nested = ({ animation, closeLabel }: ModalProps) => {
  const [open, setOpen] = useState(false);

  return (
    <>
      <Button onClick={() => setOpen(true)}>Show Nested Modal</Button>
      <Modal
        isOpen={open}
        onClose={() => setOpen(false)}
        animation={animation}
        closeLabel={closeLabel}
      >
        <Modal.Header>The Latin Modal</Modal.Header>
        <Modal.Content>
          <Text>
            Closing or cancelling the next modal should leave me open.
          </Text>
          <ChildModal />
        </Modal.Content>
      </Modal>
    </>
  );
};
```

## Closing a Modal from within a child `<iframe>`

### Step 1: Create embeddable `<iframe>`

1.  Create `example` directory with `index.html` file in it:

```
mkdir example && cd example && touch index.html
```

2.  Edit your `index.html` file as follows:

```html
<html>
  <body>
    <button type="button" id="close-button">Close this window</button>
    <script>
      const button = document.getElementById("close-button");
      const handleClick = () =>
        window.parent.postMessage({ mobiusCloseModal: true }, "*");
      button.addEventListener("click", handleClick);
    </script>
  </body>
</html>
```

3.  Start a simple HTTP server from within the `example` directory:

```
npx serve .
```

### Step 2: Embed the `<iframe>` in a React component

```jsx
import { useEffect, useCallback } from "react";

export const ExampleContent = ({ onClose }) => {
  const iframeUrl = "http://localhost:3000";

  const handleMessage = useCallback(
    (event: MessageEvent) => {
      const { data } = event;

      if (event.origin.startsWith(iframeUrl)) {
        const { mobiusCloseModal } = data;

        if (mobiusCloseModal) {
          onClose();
        }
      }
    },
    [iframeUrl],
  );

  useEffect(() => {
    window.addEventListener("message", handleMessage);

    return () => window.removeEventListener("message", handleMessage);
  }, [handleMessage]);

  return (
    <iframe
      src={iframeUrl}
      style={{
        backgroundColor: "lightgrey",
        border: "none",
        width: "100%",
        height: "100%",
      }}
    />
  );
};
```

<TestingDocs />

## Props

<ArgTypes of={Modal} />

## Component HTML Structure and Class names

The following HTML is rendered for a simple Modal:

```html
<dialog class="mobius-modal --is-open --is-md">
  <header class="mobius-modal__header">
    <button aria-label="Close" class="mobius-modal__close">
      <svg class="mobius-icon svg-inline--mobius-icon" ...>{cross}</svg>
    </button>
  </header>
  <div class="mobius-modal__content">Sample content</div>
</dialog>
```

Class names are augmented with the following flags if true (affected classes shown above):

- \--slide-up
- \--is-fullscreen
