# Popover

A Popover displays floating informative and actionable content positioned in
relation to a target. The Popover can contain content, media, or other
components.

## Design & usage guidelines

Some scenarios for Popover include the following:

### Informational

Introducing the user to a new experience, whether their first time using a
product or to introduce a new or updated functionality in an existing
experience, Popover can be an excellent choice to highlight a specific piece of
the experience.

If there is an "acknowledgement" CTA for the user to confirm that they
understand the change, use a `learning` [Button](../Button/Button.md).

See
[Popover/Informational](/storybook/web/?path=/story/components-overlays-popover--informational)

### Functional

> **WARNING:** A "functional" Popover hasn't been implemented in a componentized fashion yet.

Reveal a list of available actions to the user. For example, if the user clicks
on an element and there are four potential actions they might take, Popover is a
great way to present those actions. If you're looking to provide a menu of
actions that comes complete with a trigger button, [Menu](../Menu/Menu.md) has
that bundle ready to go.

## Related components

To add a menu button that presents multiple actions to the user, use
[Menu](../Menu/Menu.md).

To add a hint about a UI element's function in a permanent fashion (ie revealing
a Button's label on hover), use [Tooltip](../Tooltip/Tooltip.md).

To add an inline informational element that the user can dismiss, consider if
[Banner](../Banner/Banner.md) is the right fit for your use case.

## Content guidelines

* Popover text content should be concise and clear. Try not to go over three
  lines so the user can get back to what they were doing!
* In "informational" usage, Popover may have a CTA that allows the user to
  "acknowledge" and dismiss the Popover; this does not replace the need for the
  dismiss button.

## Accessibility

Popover has a role of `dialog` as it is an element that the user will be "in
dialogue" with, whether selecting an action, confirming acknowledgement, or
dismissing the Popover.

Depending on your use case, you may need to add focus management to your usage
of Popover; for example, if selecting a Button opens a Popover, you will want to
then set focus to the Popover, and if the user dismisses the Popover, return
focus to the button that opened the Popover.

If your element is effectively acting as an inline DOM element that the user
would otherwise encounter when traversing the page, this focus management may
not be necessary.


## Component customization

### Composable usage

Popover exposes its internal building blocks as subcomponents:
`Popover.Provider`, `Popover.DismissButton` and `Popover.Arrow`. This gives you
more control over the Popover's appearance and behaviour.

Here's a basic example of how our current non-composable Popover is used:

```tsx
<Popover
  attachTo={elementRef}
  open={showPopover}
  onRequestClose={() => setShowPopover(false)}
>
  <Content>
    Popover content goes here
  </Content>
</Popover>
```

Using Popover's built-in subcomponents, this UI can alternatively be expressed as:

```tsx
<Popover.Provider attachTo={elementRef} open={showPopover}>
  <Popover.DismissButton onClick={() => setShowPopover(false)} />
  <Content>
    Popover content goes here
  </Content>
  <Popover.Arrow />
</Popover.Provider>
```

If you don't want to render the dismiss button or arrow, you can omit them like
so:

```tsx
<Popover.Provider attachTo={elementRef} open={showPopover}>
  <Content>
    Popover content goes here
  </Content>
</Popover.Provider>
```

If you need more control over the dismiss button, you can pass a custom child
component into `Popover.DismissButton`:

```tsx
<Popover.Provider attachTo={elementRef} open={showPopover}>
  <Popover.DismissButton>
    <Button onClick={() => setShowPopover(false)} variation="subtle">
      <Button.Icon name="eyeCrossed" />
    </Button>
  </Popover.DismissButton>
  <Content>
    Popover content goes here
  </Content>
  <Popover.Arrow />
</Popover.Provider>
```

### UNSAFE\_ props (advanced usage)

General information for using `UNSAFE_` props can be found
[here](../customizing-components/customizing-components.md).

Popover has three elements that can be targeted with classes or styles. These
are the container, the dismiss button container, and the arrow.

**Note**: Use of `UNSAFE_` props is **at your own risk** and should be
considered a **last resort**. Future Popover updates may lead to unintended
breakages.

#### UNSAFE\_className

Use `UNSAFE_className` to apply custom classes to the Popover. This can be
useful for applying styles via CSS Modules.

```tsx
// YourComponent.tsx
UNSAFE_className={{
  container: styles.customPopoverBackground,
  dismissButtonContainer: styles.customDismissButton,
  arrow: styles.customArrow,
}}

// YourComponent.module.css
.customPopoverBackground {
  background-color: var(--color-surface--background);
}

.customDismissButton {
  background-color: var(--color-indigo);
}

.customArrow {
  padding-left: var(--space-extravagant);
}
```

You can also use the `UNSAFE_className` prop to apply custom classes to any
element within the Popover.

```tsx
UNSAFE_className={{ container: "custom-container-class" }}
```

If you're using Popover with its composable subcomponents, you'll need to pass
`UNSAFE_className` to the individual subcomponents accordingly:

```tsx
<Popover.Provider
  attachTo={elementRef}
  open={showPopover}
  UNSAFE_className={{
    container: styles.customPopoverBackground,
  }}
>
  <Popover.DismissButton
    onClick={() => setShowPopover(false)}
    UNSAFE_className={{
      dismissButtonContainer: styles.customDismissButton,
    }}
  />
  <Content>
    Popover content goes here
  </Content>
  <Popover.Arrow
    UNSAFE_className={{
      arrow: styles.customArrow,
    }}
  />
</Popover.Provider>
```

#### UNSAFE\_style

Use `UNSAFE_style` to apply inline custom styles to the Popover.

```tsx
UNSAFE_style={{
  container: { backgroundColor: "lightblue" },
  dismissButtonContainer: { paddingRight: "var(--space-larger)" },
  arrow: { paddingRight: "var(--space-extravagant)" },
}}
```

If you're using Popover with its composable subcomponents, you'll need to pass
`UNSAFE_style` to the individual subcomponents accordingly:

```tsx
<Popover.Provider
  attachTo={elementRef}
  open={showPopover}
  UNSAFE_style={{
    container: {
      backgroundColor: "orange",
    },
  }}
>
  <Popover.DismissButton
    onClick={() => setShowPopover(false)}
    UNSAFE_style={{
      dismissButtonContainer: {
        backgroundColor: "blue",
      },
    }}
  />
  <Content>
    Popover content goes here
  </Content>
  <Popover.Arrow
    UNSAFE_style={{
      arrow: {
        backgroundColor: "red",
      },
    }}
  />
</Popover.Provider>
```


## Props

### Web

#### Popover

| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `attachTo` | `Element | RefObject<Element>` | Yes | — | Element the Popover will attach to and point at. A `useRef` must be attached to an html element and passed as an atta... |
| `children` | `ReactNode` | Yes | — | Popover content. |
| `open` | `boolean` | Yes | — | Control Popover visibility. |
| `onRequestClose` | `() => void` | No | — | Callback executed when the user wants to close/dismiss the Popover |
| `preferredPlacement` | `"auto" | "left" | "right" | "top" | "bottom"` | No | `auto` | Describes the preferred placement of the Popover. |
| `UNSAFE_className` | `{ container?: string; dismissButtonContainer?: string; arrow?: string; }` | No | — | **Use at your own risk:** Custom class names for specific elements. This should only be used as a **last resort**. Us... |
| `UNSAFE_style` | `{ container?: CSSProperties; dismissButtonContainer?: CSSProperties; arrow?: CSSProperties; }` | No | — | **Use at your own risk:** Custom style for specific elements. This should only be used as a **last resort**. Using th... |

#### Popover.Provider

| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `attachTo` | `Element | RefObject<Element>` | Yes | — | Element the Popover will attach to and point at. A `useRef` must be attached to an html element and passed as an atta... |
| `open` | `boolean` | Yes | — | Control Popover visibility. |
| `preferredPlacement` | `"auto" | "left" | "right" | "top" | "bottom"` | No | `auto` | Describes the preferred placement of the Popover. |
| `UNSAFE_className` | `{ container?: string; }` | No | — | **Use at your own risk:** Custom class names for specific elements. This should only be used as a **last resort**. Us... |
| `UNSAFE_style` | `{ container?: CSSProperties; }` | No | — | **Use at your own risk:** Custom style for specific elements. This should only be used as a **last resort**. Using th... |

#### Popover.Arrow

| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `UNSAFE_className` | `{ arrow?: string; }` | No | — | **Use at your own risk:** Custom class names for specific elements. This should only be used as a **last resort**. Us... |
| `UNSAFE_style` | `{ arrow?: CSSProperties; }` | No | — | **Use at your own risk:** Custom style for specific elements. This should only be used as a **last resort**. Using th... |

#### Popover.DismissButton

| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `ariaLabel` | `string` | No | — |  |
| `onClick` | `(event: MouseEvent<HTMLAnchorElement | HTMLButtonElement, MouseEvent>) => void` | No | — |  |
| `UNSAFE_className` | `{ dismissButtonContainer?: string; }` | No | — | **Use at your own risk:** Custom class names for specific elements. This should only be used as a **last resort**. Us... |
| `UNSAFE_style` | `{ dismissButtonContainer?: CSSProperties; }` | No | — | **Use at your own risk:** Custom style for specific elements. This should only be used as a **last resort**. Using th... |
