stories/atoms/components/toast/Toast.stories.mdx
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>