# Notification Bar
> A full-width bar for displaying prominent, global messages at the top of the application.

> **Tip**
>
> Not to be confused with [Notification](https://wave.volue.com/components/notifications/notification.md) — an inline, contextual message.
> Notification Bar is for global, application-wide messages displayed above the header.

## Import

```js
import { NotificationBar } from '@volue/wave-react';
```

Notification Bar is a compound component that consists of multiple parts:

- `NotificationBar.Root`: The wrapper that contains all the parts of a Notification Bar.
- `NotificationBar.Content`: The content of the Notification Bar.
- `NotificationBar.Action`: An inline text action related to the notification.
- `NotificationBar.DismissButton`: The button responsible for dismissing the Notification Bar.

## Examples

### Types

There are three types of notification bars: `info`, `warning`, and `danger`.
By default, a Notification Bar will be of `info` type.

`NotificationBar` always displays an icon appropriate to its type.

```jsx
<Flex flow="column">
  <NotificationBar.Root type="info">
    <NotificationBar.Content>
      {'You are working offline. Changes will sync when you reconnect.'}
    </NotificationBar.Content>
  </NotificationBar.Root>
  <NotificationBar.Root type="warning">
    <NotificationBar.Content>
      {'System maintenance scheduled for tonight.'}
    </NotificationBar.Content>
  </NotificationBar.Root>
  <NotificationBar.Root type="danger">
    <NotificationBar.Content>
      {'Service connection lost. Data may be outdated.'}
    </NotificationBar.Content>
  </NotificationBar.Root>
</Flex>
```

### With action

`NotificationBar.Action` lets you add an inline text action to the Notification Bar, providing the user with an opportunity to act on the information or navigate to a related view.

```jsx
<NotificationBar.Root type="warning">
  <NotificationBar.Content>
    {'System maintenance scheduled for tonight.'}
  </NotificationBar.Content>
  <NotificationBar.Action onClick={() => {}}>{'More'}</NotificationBar.Action>
</NotificationBar.Root>
```

### Dismissible

`NotificationBar` can be made dismissible via `NotificationBar.DismissButton`. It manages its own open state internally by default.

```jsx
() => {
  const [key, setKey] = React.useState(0);

  return (
    <Flex flow="column">
      <NotificationBar.Root key={key} type="info" defaultIsOpen>
        <NotificationBar.Content>
          {'You are viewing the demo environment.'}
        </NotificationBar.Content>
        <NotificationBar.DismissButton label="Dismiss notification" />
      </NotificationBar.Root>
      <Box>
        <Button
          variant="outline"
          onClick={() => setKey(k => k + 1)}
          size="small"
        >
          {'Reset'}
        </Button>
      </Box>
    </Flex>
  );
};
```

### Controlled

You can easily make the Notification Bar controlled, by passing your own state to `isOpen` prop.
`onIsOpenChange` handler is called when the state of the Notification Bar changes, allowing you to sync state.

```jsx
() => {
  const [isOpen, setIsOpen] = React.useState(true);

  return (
    <Flex flow="column">
      <NotificationBar.Root
        type="info"
        isOpen={isOpen}
        onIsOpenChange={setIsOpen}
      >
        <NotificationBar.Content>
          {'This is a controlled Notification Bar.'}
        </NotificationBar.Content>
        <NotificationBar.DismissButton label="Dismiss notification" />
      </NotificationBar.Root>
      {!isOpen && (
        <Box>
          <Button
            variant="outline"
            onClick={() => setIsOpen(true)}
            size="small"
          >
            {'Show Notification Bar'}
          </Button>
        </Box>
      )}
    </Flex>
  );
};
```

### Complete

You can combine all the parts together for a complete Notification Bar.

```jsx
() => {
  const [isInfoOpen, setIsInfoOpen] = React.useState(true);
  const [isWarningOpen, setIsWarningOpen] = React.useState(true);

  return (
    <Flex flow="column">
      <NotificationBar.Root
        type="info"
        isOpen={isInfoOpen}
        onIsOpenChange={setIsInfoOpen}
      >
        <NotificationBar.Content>
          {'A new version (0.1.1) is available.'}
        </NotificationBar.Content>
        <NotificationBar.Action onClick={() => {}}>
          {'Reload'}
        </NotificationBar.Action>
        <NotificationBar.DismissButton label="Dismiss notification" />
      </NotificationBar.Root>
      <NotificationBar.Root
        type="warning"
        isOpen={isWarningOpen}
        onIsOpenChange={setIsWarningOpen}
      >
        <NotificationBar.Content>
          {'You are viewing data in read-only mode.'}
        </NotificationBar.Content>
        <NotificationBar.Action onClick={() => {}}>
          {'Request access'}
        </NotificationBar.Action>
        <NotificationBar.DismissButton label="Dismiss notification" />
      </NotificationBar.Root>
      {(!isInfoOpen || !isWarningOpen) && (
        <Box>
          <Button
            variant="outline"
            onClick={() => {
              setIsInfoOpen(true);
              setIsWarningOpen(true);
            }}
            size="small"
          >
            {'Show Notification Bars'}
          </Button>
        </Box>
      )}
    </Flex>
  );
};
```

### Inside App Frame

`NotificationBar` is designed to integrate with [App Frame](https://wave.volue.com/components/app-frame.md) and [App Frame With Sidebar](https://wave.volue.com/components/app-frame-with-sidebar.md) layout components. Use the `AppFrame.NotificationBar` (or `AppFrameWithSidebar.NotificationBar`) slot to position the Notification Bar at the top of the layout grid, spanning the full width above all other areas.

> **Tip**
>
> Visit [App Frame docs](https://wave.volue.com/components/app-frame.md#with-notification-bar) and [App Frame With Sidebar docs](https://wave.volue.com/components/app-frame-with-sidebar.md#with-notification-bar) for integration examples.

```jsx
<AppFrame.Root css={{ maxHeight: '50vh' }}>
  <AppFrame.NotificationBar>
    <NotificationBar.Root type="warning">
      <NotificationBar.Content>
        {
          'System maintenance scheduled for tonight. Services may be temporarily unavailable.'
        }
      </NotificationBar.Content>
      <NotificationBar.Action onClick={() => {}}>
        {'More'}
      </NotificationBar.Action>
      <NotificationBar.DismissButton label="Dismiss notification" />
    </NotificationBar.Root>
  </AppFrame.NotificationBar>
  <AppFrame.AppHeader padding="spacingM">{'Header'}</AppFrame.AppHeader>
  <AppFrame.Main padding="spacingM">{'Main content'}</AppFrame.Main>
</AppFrame.Root>
```

### Inside content

`NotificationBar` can be used also as a part of the content, for example to display a message related to a specific section of the application.

#### Inside a contextual container

```jsx
<Dialog.Root size="small">
  <Dialog.Trigger as={Button} variant="outline">
    {'Open dialog'}
  </Dialog.Trigger>
  <Dialog.Box width="24rem" css={{ maxHeight: 500 }}>
    <Dialog.Header>
      <Dialog.Title>Create new instance</Dialog.Title>
      <Dialog.Close
        as={Button}
        variant="ghost"
        shape="circle"
        withLoneIcon
        aria-label="Close"
        marginLeft="auto"
      >
        <SvgIcon iconName="close" />
      </Dialog.Close>
    </Dialog.Header>
    <Dialog.Body>
      <Grid
        columns="2"
        className={applySizes({
          control: 'small'
        })}
      >
        <GridItem colSpan="2">
          <FormField.Root>
            <FormField.Label>Name</FormField.Label>
            <TextInput />
          </FormField.Root>
        </GridItem>
        <GridItem>
          <FormField.Root>
            <FormField.Label>Quantity</FormField.Label>
            <TextInput type="number" trailingVisual={'MW'} />
          </FormField.Root>
        </GridItem>
        <GridItem>
          <FormField.Root>
            <FormField.Label>Limit</FormField.Label>
            <TextInput type="number" trailingVisual={'€/MWh'} />
          </FormField.Root>
        </GridItem>
        <GridItem colSpan="2">
          <FormField.Root>
            <FormField.Label>Description</FormField.Label>
            <TextInput />
          </FormField.Root>
        </GridItem>
        <GridItem colSpan="2">
          <FormField.Root>
            <FormField.Label>Instance labels</FormField.Label>
            <CheckboxGroup>
              <Flex flow="column" gap="spacingXs">
                <Checkbox.Root value="strategic">
                  <Checkbox.Indicator />
                  <Checkbox.Label>{'Strategic'}</Checkbox.Label>
                </Checkbox.Root>
                <Checkbox.Root value="open">
                  <Checkbox.Indicator />
                  <Checkbox.Label>{'Open'}</Checkbox.Label>
                </Checkbox.Root>
                <Checkbox.Root value="closer">
                  <Checkbox.Indicator />
                  <Checkbox.Label>{'Closer'}</Checkbox.Label>
                </Checkbox.Root>
                <Checkbox.Root value="rapid">
                  <Checkbox.Indicator />
                  <Checkbox.Label>{'Rapid'}</Checkbox.Label>
                </Checkbox.Root>
              </Flex>
            </CheckboxGroup>
          </FormField.Root>
        </GridItem>
        <GridItem colSpan="2">
          <FormField.Root as={Flex} flow="column">
            <FormField.Label as="span">{'Notify me about'}</FormField.Label>
            <RadioGroup>
              <Flex flow="column" gap={'spacingXs'}>
                <Radio.Root value="all">
                  <Radio.Indicator />
                  <Radio.Label>{'All new messages'}</Radio.Label>
                </Radio.Root>
                <Radio.Root value="mentions">
                  <Radio.Indicator />
                  <Radio.Label>{'Direct messages and mentions'}</Radio.Label>
                </Radio.Root>
                <Radio.Root value="none">
                  <Radio.Indicator />
                  <Radio.Label>{'Nothing'}</Radio.Label>
                </Radio.Root>
              </Flex>
            </RadioGroup>
          </FormField.Root>
        </GridItem>
        <GridItem colSpan="2">
          <FormField.Root>
            <FormField.Label>Comment</FormField.Label>
            <Textarea placeholder="Include additional details here" />
          </FormField.Root>
        </GridItem>
      </Grid>
    </Dialog.Body>
    <NotificationBar.Root type="warning">
      <NotificationBar.Content>
        {'90% of limit consumed.'}
      </NotificationBar.Content>
      <NotificationBar.DismissButton label="Dismiss notification" />
    </NotificationBar.Root>
    <Dialog.Footer>
      <Flex>
        <Dialog.Close as={Button} variant="ghost">
          {'Close'}
        </Dialog.Close>
        <Flex marginLeft="auto">
          <Button>{'Create'}</Button>
        </Flex>
      </Flex>
    </Dialog.Footer>
  </Dialog.Box>
</Dialog.Root>
```

#### Sticky inside a table

> **Tip**
>
> When using `NotificationBar` inside a scrollable container like a table body, make sure to set `sticky` prop to either `top` or `bottom` to ensure the Notification Bar remains visible when scrolling.

```jsx
() => {
  const data = React.useMemo(
    () => [
      {
        id: 1,
        tradedQuantity: 35,
        totalQuantity: 70,
        limitPrice: 125.33,
        avgSellPrice: 105.76
      },
      {
        id: 2,
        tradedQuantity: 2,
        totalQuantity: 12,
        limitPrice: 105.76,
        avgSellPrice: 99.3
      },
      {
        id: 3,
        tradedQuantity: 3.5,
        totalQuantity: 20,
        limitPrice: 210.5,
        avgSellPrice: 128.78
      },
      {
        id: 4,
        tradedQuantity: 12,
        totalQuantity: 50,
        limitPrice: 145.2,
        avgSellPrice: 132.5
      },
      {
        id: 5,
        tradedQuantity: 8.5,
        totalQuantity: 25,
        limitPrice: 95,
        avgSellPrice: 87.45
      },
      {
        id: 6,
        tradedQuantity: 0,
        totalQuantity: 100,
        limitPrice: 220.75,
        avgSellPrice: 195.3
      },
      {
        id: 7,
        tradedQuantity: 45,
        totalQuantity: 80,
        limitPrice: 175.5,
        avgSellPrice: 168.92
      },
      {
        id: 8,
        tradedQuantity: 18,
        totalQuantity: 60,
        limitPrice: 110.25,
        avgSellPrice: 102.18
      },
      {
        id: 9,
        tradedQuantity: 6.25,
        totalQuantity: 40,
        limitPrice: 135.8,
        avgSellPrice: 128.45
      },
      {
        id: 10,
        tradedQuantity: 5,
        totalQuantity: 5,
        limitPrice: 125.12,
        avgSellPrice: 106.56
      }
    ],
    []
  );

  const formatPrice = value =>
    value.toLocaleString('en-GB', {
      minimumFractionDigits: 2
    });

  const formatQuantity = value =>
    value.toLocaleString('en-GB', {
      minimumFractionDigits: 1
    });

  return (
    <Box css={{ height: 280 }}>
      <Table.Root
        aria-label="Table example with NotificationBar"
        cellSize="small"
        rowSeparation="stripes"
      >
        <Table.Header stickyOffset={0}>
          <Table.ColumnHeader width={100}>{'Order ID'}</Table.ColumnHeader>
          <Table.ColumnHeader minWidth={160} align="right">
            <Table.ColumnHeaderContent>
              <Table.ColumnHeaderTitle>
                {'Traded quantity'}
              </Table.ColumnHeaderTitle>
              <Table.ColumnHeaderSubtitle>{'MW'}</Table.ColumnHeaderSubtitle>
            </Table.ColumnHeaderContent>
          </Table.ColumnHeader>
          <Table.ColumnHeader minWidth={160} align="right">
            <Table.ColumnHeaderContent>
              <Table.ColumnHeaderTitle>
                {'Total quantity'}
              </Table.ColumnHeaderTitle>
              <Table.ColumnHeaderSubtitle>{'MW'}</Table.ColumnHeaderSubtitle>
            </Table.ColumnHeaderContent>
          </Table.ColumnHeader>
          <Table.ColumnHeader minWidth={160} align="right">
            <Table.ColumnHeaderContent>
              <Table.ColumnHeaderTitle>{'Limit price'}</Table.ColumnHeaderTitle>
              <Table.ColumnHeaderSubtitle>
                {'EUR/MWh'}
              </Table.ColumnHeaderSubtitle>
            </Table.ColumnHeaderContent>
          </Table.ColumnHeader>
          <Table.ColumnHeader minWidth={160} align="right">
            <Table.ColumnHeaderContent>
              <Table.ColumnHeaderTitle>
                {'Avg sell price'}
              </Table.ColumnHeaderTitle>
              <Table.ColumnHeaderSubtitle>
                {'EUR/MWh'}
              </Table.ColumnHeaderSubtitle>
            </Table.ColumnHeaderContent>
          </Table.ColumnHeader>
        </Table.Header>
        <Table.Body>
          {data.map(row => (
            <Table.Row key={row.id}>
              <Table.Cell width={100}>{row.id}</Table.Cell>
              <Table.Cell minWidth={160} align="right">
                {formatQuantity(row.tradedQuantity)}
              </Table.Cell>
              <Table.Cell minWidth={160} align="right">
                {formatQuantity(row.totalQuantity)}
              </Table.Cell>
              <Table.Cell minWidth={160} align="right">
                {formatPrice(row.limitPrice)}
              </Table.Cell>
              <Table.Cell minWidth={160} align="right">
                {formatPrice(row.avgSellPrice)}
              </Table.Cell>
            </Table.Row>
          ))}
        </Table.Body>
        <NotificationBar.Root type="danger" sticky="bottom">
          <NotificationBar.Content>
            {'Action limit reached. Trading is interrupted.'}
          </NotificationBar.Content>
        </NotificationBar.Root>
      </Table.Root>
    </Box>
  );
};
```

---

## Accessibility features

`NotificationBar` automatically assigns correct ARIA role and gets announced by screen readers when it gets rendered on the screen.

- `danger` and `warning` notification bars have a `role="alert"`. With this role the screen reader will promptly interrupt any ongoing content reading and prioritise the notification content for immediate attention.
- `info` notification bars have a `role="status"`. With this role, the screen reader must finish what it started reading before the content of the Notification Bar is read out.

---

## API reference

### NotificationBar.Root

| Name             | Type                              | Default  | Description                                                                                                                                                                                                                                   | Required |
| ---------------- | --------------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `css`            | `StitchesCss`                     |          | Apply styles directly to a component in a similar way how you would define inline styles. Wave uses [Stitches](https://stitches.dev/) under the hood with a fully-typed API and support for features like tokens, media queries, or variants. |          |
| `type`           | `"info" \| "warning" \| "danger"` | `"info"` | Type of the Notification Bar.                                                                                                                                                                                                                 |          |
| `sticky`         | `"top" \| "bottom"`               |          | When set, makes the Notification Bar sticky within its container.                                                                                                                                                                             |          |
| `isOpen`         | `boolean`                         |          | The controlled open state of the Notification Bar. Use in conjunction with `onIsOpenChange`.                                                                                                                                                  |          |
| `defaultIsOpen`  | `boolean`                         |          | The open state of the Notification Bar when it's first rendered. Use when you do not need to control open state.                                                                                                                              |          |
| `onIsOpenChange` | `(isOpen: boolean) => void`       |          | Event handler called when the open state of the Notification Bar changes.                                                                                                                                                                     |          |

### NotificationBar.Content

| Name               | Type                                                      | Default      | Description                                                                                                                                                                                                                                                                                                           | Required |
| ------------------ | --------------------------------------------------------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `as`               | `keyof JSX.IntrinsicElements \| React.ComponentType<any>` | `p`          | Change the component to a different HTML tag or custom component. This will merge the original component props with the props of the supplied element/component and change the underlying DOM node.   For more details, read our [Composition](https://wave.volue.com/get-started/composition.md#polymorphism) guide. |          |
| `css`              | `StitchesCss`                                             |              | Apply styles directly to a component in a similar way how you would define inline styles. Wave uses [Stitches](https://stitches.dev/) under the hood with a fully-typed API and support for features like tokens, media queries, or variants.                                                                         |          |
| `overflowStrategy` | `"truncate" \| "wrap"`                                    | `"truncate"` | Controls how text overflow is handled. Set to `wrap` to allow multiline content.                                                                                                                                                                                                                                      |          |

### NotificationBar.Action

| Name  | Type                                                      | Default  | Description                                                                                                                                                                                                                                                                                                           | Required |
| ----- | --------------------------------------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `as`  | `keyof JSX.IntrinsicElements \| React.ComponentType<any>` | `button` | Change the component to a different HTML tag or custom component. This will merge the original component props with the props of the supplied element/component and change the underlying DOM node.   For more details, read our [Composition](https://wave.volue.com/get-started/composition.md#polymorphism) guide. |          |
| `css` | `StitchesCss`                                             |          | Apply styles directly to a component in a similar way how you would define inline styles. Wave uses [Stitches](https://stitches.dev/) under the hood with a fully-typed API and support for features like tokens, media queries, or variants.                                                                         |          |

### NotificationBar.DismissButton

| Name    | Type          | Default | Description                                                                                                                                                                                                                                   | Required |
| ------- | ------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `css`   | `StitchesCss` |         | Apply styles directly to a component in a similar way how you would define inline styles. Wave uses [Stitches](https://stitches.dev/) under the hood with a fully-typed API and support for features like tokens, media queries, or variants. |          |
| `label` | `string`      |         | The `aria-label` of the button to use for screen readers.                                                                                                                                                                                     | Yes      |
