stories/atoms/components/toast/Toast.stories.mdx

Summary

Maintainability
Test Coverage
import { ArgsTable, Meta, Story, Canvas, Preview, Props } from '@storybook/addon-docs'
import { useState, useEffect } from 'react'
import { Button } from 'ui/atoms/button/Button'
import { Toast } from 'ui/atoms/toast/Toast'
import { ThemeProvider } from 'ui/atoms/theme/ThemeProvider'
import { ThemeSwitcher } from 'ui/atoms/theme/ThemeSwitcher'
import { Typography } from 'ui/atoms/typography/Typography'
import BotAnikImage from '../../../assets/botanik-logo.png'

<Meta
  title="Atoms/Toast"
  component={Toast}
  parameters={{
    actions: { argTypesRegex: '^on.*' },
    docs: {
      source: {
        type: 'code'
      }
    }
  }}
  decorators={[
    Story => (
      <ThemeProvider>
        <div className="story-theme-switcher">
          <ThemeSwitcher />
        </div>
        <div className="component-story-main">
          <Story />
        </div>
      </ThemeProvider>
    )
  ]}
/>

# Toast

> Displays information following a user action.

[![stability-unstable](https://img.shields.io/badge/stability-unstable-yellow.svg)](https://github.com/emersion/stability-badges#unstable)

## Overview

### Description

A Toast is a modern display component that allows information to be displayed discreetly and non-intrusively following a user action.  
Several severity levels make it possible to fully configure the type of message to convey to the user.

---

<Story
  name="Overview"
  argTypes={{ description: { type: { name: 'string' } } }}
  args={{
    title: 'Transaction',
    description: 'Your data space has been successfully created!',
    severityLevel: 'success',
    isOpened: false,
    preventAutoClose: false
  }}
>
  {args => {
    const [isOpened, setIsOpened] = useState(false)
    useEffect(() => {
      setIsOpened(args.isOpened)
    }, [args.isOpened])
    return (
      <div
        style={{
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
          gap: '25px'
        }}
      >
        <Button label="Click-me!" onClick={() => setIsOpened(true)} />
        <Toast
          description={args.description}
          isOpened={isOpened}
          onOpenChange={() => setIsOpened(false)}
          title={args.title}
          severityLevel={args.severityLevel}
          autoDuration={args.autoDuration}
          preventAutoClose={args.preventAutoClose}
        />
      </div>
    )
  }}
</Story>

---

### Properties

<ArgsTable story="Overview" />

## Severity Levels

The `Toast` component can be configured with multiple severity levels that follows theming' conventional naming:

- **Success**: informs the user about any task that has succeeded in the system.
- **Error**: reports to the user an error related to any failed task in the system.
- **Warning**: reports to the user an error related to any task that may result in an error in the system.
- **Info**: sends back to the user related information of any kind.

<Canvas>
  <Story name="Severity levels">
    {args => {
      const [state, setState] = useState({ name: '', isOpened: false })
      const handleState = (name, isOpened) => () => {
        setState({ name, isOpened })
      }
      return (
        <div
          style={{
            display: 'flex',
            flexWrap: 'wrap',
            alignItems: 'center',
            justifyContent: 'center',
            gap: '25px'
          }}
        >
          <div>
            <Button
              label="I'm a success level!"
              onClick={handleState('success', true)}
              backgroundColor="success"
            />
            <Toast
              description="Your own success message here"
              isOpened={Object.values(state).includes('success') ? state['isOpened'] : false}
              onOpenChange={handleState('success', false)}
              title="Success"
              severityLevel="success"
            />
          </div>
          <div>
            <Button
              label="I'm an error level!"
              onClick={handleState('error', true)}
              backgroundColor="error"
            />
            <Toast
              description="Your own error message here"
              isOpened={Object.values(state).includes('error') ? state['isOpened'] : false}
              onOpenChange={handleState('error', false)}
              title="Error"
              severityLevel="error"
            />
          </div>
          <div>
            <Button
              label="I'm a warning level!"
              onClick={handleState('warning', true)}
              backgroundColor="warning"
            />
            <Toast
              description="Your own warning message here"
              isOpened={Object.values(state).includes('warning') ? state['isOpened'] : false}
              onOpenChange={handleState('warning', false)}
              title="Warning"
              severityLevel="warning"
            />
          </div>
          <div>
            <Button
              label="I'm an info level!"
              onClick={handleState('info', true)}
              backgroundColor="info"
            />
            <Toast
              description="Your own info message here"
              isOpened={Object.values(state).includes('info') ? state['isOpened'] : false}
              onOpenChange={handleState('info', false)}
              title="Info"
              severityLevel="info"
            />
          </div>
        </div>
      )
    }}
  </Story>
</Canvas>

## Duration

The `Toast` component comes with an `autoDuration` property that is the time in milliseconds that should
elapse before automatically closing the toast.  
`preventAutoClose` will override `autoDuration` and keep the `toast` open if set, as well as provide a close button.

<Canvas>
  <Story name="Duration">
    {args => {
      const [state, setState] = useState({ name: '', isOpened: false })
      const handleState = (name, isOpened) => () => {
        setState({ name, isOpened })
      }
      return (
        <div
          style={{
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            gap: '25px'
          }}
        >
          <div>
            <Button
              label="Short auto-duration!"
              onClick={handleState('short', true)}
              backgroundColor="secondary"
            />
            <Toast
              description="My auto-duration is 1000ms"
              isOpened={Object.values(state).includes('short') ? state['isOpened'] : false}
              onOpenChange={handleState('short', false)}
              title="Short auto-duration"
              severityLevel="success"
              autoDuration={1000}
            />
          </div>
          <div>
            <Button
              label="Long auto-duration!"
              onClick={handleState('long', true)}
              backgroundColor="primary"
            />
            <Toast
              description="My auto-duration is 7000ms"
              isOpened={Object.values(state).includes('long') ? state['isOpened'] : false}
              onOpenChange={handleState('long', false)}
              title="Long auto-duration"
              severityLevel="success"
              autoDuration={7000}
            />
          </div>
          <div>
            <Button
              label="Prevent automatic close"
              onClick={handleState('prevent close', true)}
              backgroundColor="secondary"
            />
            <Toast
              description="Click the button or drag to close"
              isOpened={Object.values(state).includes('prevent close') ? state['isOpened'] : false}
              onOpenChange={handleState('prevent close', false)}
              title="No auto close"
              severityLevel="success"
              preventAutoClose
            />
          </div>
        </div>
      )
    }}
  </Story>
</Canvas>

## Content

The `Toast` component has a flexible content which notably allows the user to indicate a `title` and a `description`.  
With `description` users can decide whether to show a JSX element or a simple string.  
These two elements are optional and are not inter-related.

<Canvas>
  <Story
    name="Content"
    parameters={{
      docs: {
        source: {
          type: 'code'
        }
      }
    }}
  >
    {args => {
      const [state, setState] = useState({ name: '', isOpened: false })
      const handleState = (name, isOpened) => () => {
        setState({ name, isOpened })
      }
      const handleClick = () => {
        window.open('https://github.com/bot-anik')
      }
      return (
        <div
          style={{
            display: 'flex',
            flexWrap: 'wrap',
            alignItems: 'center',
            justifyContent: 'center',
            gap: '25px'
          }}
        >
          <div>
            <Button
              label="Title only"
              onClick={handleState('title', true)}
              backgroundColor="secondary"
            />
            <Toast
              isOpened={Object.values(state).includes('title') ? state['isOpened'] : false}
              onOpenChange={handleState('title', false)}
              title="Title only"
              severityLevel="success"
            />
          </div>
          <div>
            <Button
              label="Description only"
              onClick={handleState('desc', true)}
              backgroundColor="primary"
            />
            <Toast
              description="I only have a description"
              isOpened={Object.values(state).includes('desc') ? state['isOpened'] : false}
              onOpenChange={handleState('desc', false)}
              severityLevel="success"
            />
          </div>
          <div>
            <Button
              label="JSX element description"
              onClick={handleState('bot anik', true)}
              backgroundColor="secondary"
            />
            <Toast
              title="Bot Anik"
              description={
                <div style={{ display: 'flex', flexDirection: 'column' }}>
                  <Typography as="span" color="invariant-text" fontSize="small" fontWeight="light">
                    Bot Anik is a bot like no other, she'll kick you around without any trouble! She
                    gets rid of all who messes around, no one can beat our bot! 🎵
                  </Typography>
                  <div style={{ display: 'flex', marginTop: '20px' }}>
                    <img style={{ width: '50px' }} src={BotAnikImage} />
                    <Button
                      label="Learn more about Bot Anik"
                      onClick={handleClick}
                      backgroundColor="primary"
                    />
                  </div>
                </div>
              }
              isOpened={Object.values(state).includes('bot anik') ? state['isOpened'] : false}
              onOpenChange={handleState('bot anik', false)}
              severityLevel="success"
              preventAutoClose
            />
          </div>
        </div>
      )
    }}
  </Story>
</Canvas>