import PropsTable from 'terra-application/lib/modal-manager/ModalManager?dev-site-props-table';

# ModalManager

The ModalManager exposes its children to modal presentation APIs through the
[DisclosureManagerContext](/application/terra-application/contexts/disclosure-manager-context).

## Usage

```jsx
import ModalManager from 'terra-application/lib/modal-manager';
```

## Props

<PropsTable />

## Features

### Exception Handling

ModalManager renders an [ApplicationErrorBoundary](/application/terra-application/components/application-error-boundary)
around its disclosed components. If the disclosed components throw exceptions during React lifecycle functions, ModalManager will
catch the exceptions and render a styled error component in place of the children.

> If the disclosed modal contents do not utilize the DisclosureManagerHeaderAdapter and render their own modal header instead,
> it is recommended that those components render an additional ApplicationErrorBoundary below their modal header to keep the
> header controls for modal dismissal exposed.

### Loading Overlays

ModalManager renders an [ApplicationLoadingOverlayProvider](/application/terra-application/components/application-loading-overlay-provider)
around its disclosed components. Any [ApplicationLoadingOverlay](/application/terra-application/components/application-loading-overlay)
components rendered by children will cause a loading overlay to be rendered over the modal's contents.

> If the disclosed modal contents do not utilize the DisclosureManagerHeaderAdapter and render their own modal header instead,
> it is recommended that those components render an additional ApplicationLoadingOverlayProvider below their modal header to keep
> the header controls for modal dismissal exposed while the overlay is active.

### Status Views

ModalManager renders an [ApplicationStatusOverlayProvider](/application/terra-application/components/application-status-overlay-provider)
around its disclosed components. Any [ApplicationStatusOverlay](/application/terra-application/components/application-status-overlay)
components rendered by children will cause a status view to be rendered over the modal's contents.

> If the disclosed modal contents do not utilize the DisclosureManagerHeaderAdapter and render their own modal header instead,
> it is recommended that those components render an additional ApplicationStatusOverlayProvider below their modal header to keep
> the header controls for modal dismissal exposed while the status view is active.

### Unsaved Changes

ModalManager monitors its disclosed content for the presence of rendered [NavigationPrompts](/application/terra-application/components/navigation-prompt)
within its disclosed content. ModalManager will ensure that the user is prompted prior to dismissing the presented modal if any
[NavigationPrompts](/application/terra-application/components/navigation-prompt) are rendered at the time of the dismissal request.

## Details

### Implementation Requirements

The ModalManager utilizes the [DisclosureManagerContext](/application/terra-application/contexts/disclosure-manager-context) to manage disclosure requests.

The ModalManager responds to `"modal"` disclosure type requests. Components that wish to disclose content using the ModalManager
should provide a preferred type of `"modal"`.

### DisclosureManagerHeaderAdapter Support

If a component disclosed by the ModalManager renders a `DisclosureManagerHeaderAdapter`, the ModalManager will render an ActionHeader
and provide the standard disclosure navigation controls (close, go back, maximize/minimize, etc.) within it. The disclosed component
can use the `DisclosureManagerHeaderAdapter` to inject its own title and CollapsibleButtonView into the ActionHeader.

If the disclosed component does **not** render a `DisclosureManagerHeaderAdapter`, the ModalManager will **not** render an ActionHeader itself.
In this case, it is assumed that the disclosed component is rendering its own header. The disclosed component is responsible for rendering
the appropriate controls to navigate the disclosure stack.

> Note: The DisclosureManagerHeaderAdapter is the preferred way to present a header within the ModalManager.
> In a future major release, the ModalManager will **always** render the header and navigation controls, regardless of the presence of a DisclosureManagerHeaderAdapter.

### Disclosure Accessory

The `disclosureAccessory` prop allows consumers of the ModalManager to render a static component above the disclosed modal content.
The provided component will be rendered below the standard ActionHeader and above the disclosed content. This can be used to easily
provide additional context to every disclosed component. This component is provided once to the ModalManager instance, not on a per-disclosure basis,
and each component in the disclosure stack will be decorated with the same accessory component.

### Example

```jsx
import React from 'react';
import Button from 'terra-button';
import ModalManager, { disclosureType } from 'terra-application/lib/modal-manager';
import { withDisclosureManager, DisclosureManagerContext, DisclosureManagerHeaderAdapter } from 'terra-application/lib/disclosure-manager';
import CollapsibleMenuView from 'terra-collapsible-menu-view';

const ModalComponentB = () => (
  <React.Fragment>
    <DisclosureManagerHeaderAdapter
      title="Modal Component B"
    />
    <p>I am ModalComponentB!</p>
  </React.Fragment>
);

const ModalComponentA = () => {
  const disclosureManager = React.useContext(DisclosureManagerContext);

  return (
    <div>
      <DisclosureManagerHeaderAdapter
        title="Modal Component A"
        collapsibleMenuView={<CollapsibleMenuView />}
      />
      <p>I am ModalComponentA!</p>
      <Button
        text="Disclose ModalComponentB"
        onClick={() => {
          disclosureManager.disclose({
            preferredType: 'modal',
            size: 'large',
            content: {
              key: 'modal-component-b-instance',
              component: <ModalComponentB />
            }
          });
        }}
      />
    </div>
  );
}

const MyContentComponent = withDisclosureManager(({ disclosureManager }) => (
  <div>
    <p>I am MyContentComponent!</p>
    <Button
      text="Disclose ModalComponentA"
      onClick={() => {
        disclosureManager.disclose({
          preferredType: 'modal',
          size: 'large',
          content: {
            key: 'modal-component-a-instance',
            component: <ModalComponentA />
          }
        });
      }}
    />
  </div>
));

MyContentComponent.propTypes = {
  disclosureManager: disclosureManagerShape,
}

const MyModalManagerComponent = () => (
  <ModalManager
    disclosureAccessory={<div>Disclosure Accessory</div>}
  >
    <MyContentComponent />
  </ModalManager>
);
```
