feat: toast componet v0.1 - missing position and animation props

This commit is contained in:
jongomez 2023-10-05 11:05:19 +01:00 committed by Jon
parent ade4af1856
commit dc9ce1f4a8
7 changed files with 293 additions and 0 deletions

View File

@ -39,6 +39,7 @@ import { NumberInputStyles } from '../NumberInput/NumberInput.styles'
import { ModalStyles } from '../Modal/Modal.styles'
import { ModalFooterStyles } from '../ModalFooter/ModalFooter.styles'
import { ModalBodyStyles } from '../ModalBody/ModalBody.styles'
import { ToastStyles } from '../Toast/Toast.styles'
const componentStyles: Array<ReturnType<typeof withTheme> | SerializedStyles> =
[
@ -80,6 +81,7 @@ const componentStyles: Array<ReturnType<typeof withTheme> | SerializedStyles> =
DatePickerStyles,
DateFieldStyles,
CalendarStyles,
ToastStyles,
]
export const CSSBaseline: React.FC<{ theme?: Theme }> = ({

View File

@ -0,0 +1,24 @@
export const toastClasses = {
root: `lsd-toast`,
inlineContainer: 'lsd-toast__inline-container',
blockContainer: 'lsd-toast__block-container',
large: 'lsd-toast--large',
medium: 'lsd-toast--medium',
small: 'lsd-toast--small',
textContainer: 'lsd-toast__text-container',
title: 'lsd-toast__title',
information: 'lsd-toast__information',
inlineButtonContainer: 'lsd-toast__inline-button-container',
hiddenButtonContainer: 'lsd-toast__hidden-button-container',
blockButton: 'lsd-toast__block-button',
closeButton: 'lsd-toast__close-button',
actionButton: 'lsd-toast__action-button',
errorIcon: 'lsd-toast__error-icon',
errorIconContainer: 'lsd-toast__error-icon-container',
}

View File

@ -0,0 +1,43 @@
import React, { useState } from 'react'
import { Meta, Story } from '@storybook/react'
import { Toast, ToastProps } from './Toast'
import { Button } from '../Button'
export default {
title: 'Toast',
component: Toast,
argTypes: {
size: {
type: {
name: 'enum',
value: ['small', 'medium', 'large'],
},
},
},
} as Meta
export const Root: Story<ToastProps> = ({ buttonText, ...args }) => {
const [isVisible, setIsVisible] = useState(args.isOpen)
return (
<div style={{ width: 400, position: 'relative' }}>
<Button onClick={() => setIsVisible(!isVisible)}>Show Toast</Button>
<Toast
{...args}
isOpen={isVisible}
onClose={() => setIsVisible(false)}
buttonText={buttonText}
onButtonClick={() => alert('Button clicked!')}
/>
</div>
)
}
Root.args = {
title: 'Toast Title',
information: '',
size: 'large',
buttonText: 'Click me',
isOpen: false,
inline: true,
}

View File

@ -0,0 +1,106 @@
import { css } from '@emotion/react'
import { toastClasses } from './Toast.classes'
export const ToastStyles = css`
.${toastClasses.root} {
box-sizing: border-box;
display: flex;
position: fixed;
bottom: 20px;
left: 20px;
background: rgb(var(--lsd-surface-primary));
border: 1px solid rgb(var(--lsd-border-primary));
padding: 8px;
align-items: center;
z-index: 9999;
}
.${toastClasses.inlineButtonContainer} {
margin: 0 8px;
}
.${toastClasses.hiddenButtonContainer} {
visibility: hidden;
}
.${toastClasses.blockButton} {
margin-top: 18px;
margin-bottom: 12px;
}
.${toastClasses.inlineContainer} {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
}
.${toastClasses.blockContainer} {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
}
.${toastClasses.textContainer} {
display: flex;
flex-direction: column;
flex-grow: 1;
color: rgb(var(--lsd-text-secondary));
margin-left: 32px;
}
.${toastClasses.title} {
position: relative;
font-weight: bold;
}
.${toastClasses.information} {
margin-top: 4px;
}
.${toastClasses.actionButton} {
height: 28px;
min-width: 60px;
width: fit-content;
padding: 6px 12px;
}
.${toastClasses.closeButton} {
margin-bottom: auto;
flex-shrink: 0;
height: 28px;
width: 28px;
}
.${toastClasses.errorIconContainer} {
width: 26px;
display: flex;
position: relative;
margin-bottom: auto;
}
.${toastClasses.errorIcon} {
position: absolute;
top: 5px;
left: -26px;
}
.${toastClasses.large} {
.${toastClasses.textContainer} {
min-width: 204px;
}
}
.${toastClasses.medium} {
.${toastClasses.textContainer} {
min-width: 184px;
}
}
.${toastClasses.small} {
.${toastClasses.textContainer} {
min-width: 144px;
}
}
`

View File

@ -0,0 +1,116 @@
import clsx from 'clsx'
import React from 'react'
import {
CommonProps,
omitCommonProps,
useCommonProps,
} from '../../utils/useCommonProps'
import { toastClasses } from './Toast.classes'
import { CloseIcon, ErrorIcon } from '../Icons'
import { IconButton } from '../IconButton'
import { Typography } from '../Typography'
import { Button } from '../Button'
export type ToastProps = CommonProps &
Omit<React.HTMLAttributes<HTMLDivElement>, 'label'> & {
isOpen: boolean
title: string
information?: string
inline?: boolean
buttonText?: string
onButtonClick?: () => void
onClose?: () => void
size?: 'large' | 'medium' | 'small'
}
export const Toast: React.FC<ToastProps> & {
classes: typeof toastClasses
} = ({
isOpen,
title,
information,
inline = true,
buttonText,
onButtonClick,
onClose,
size = 'large',
children,
...props
}) => {
const commonProps = useCommonProps(props)
const isInlineButtonHidden = !inline || !!information || !buttonText
// If the toast is not open, do not render anything.
if (!isOpen) {
return null
}
return (
<div
{...omitCommonProps(props)}
className={clsx(
commonProps.className,
toastClasses.root,
toastClasses[size],
)}
>
<div
className={
inline ? toastClasses.inlineContainer : toastClasses.blockContainer
}
>
<div className={clsx(toastClasses.textContainer)}>
{!!title && (
<Typography className={toastClasses.title} component="div">
<ErrorIcon
color="primary"
className={toastClasses.errorIcon}
style={{ width: '16px' }}
/>
{title}
</Typography>
)}
{!!information && (
<Typography className={toastClasses.information} component="div">
{information}
</Typography>
)}
{!inline && !!buttonText && (
<Button
onClick={onButtonClick}
className={clsx(
toastClasses.actionButton,
toastClasses.blockButton,
)}
>
{buttonText}
</Button>
)}
</div>
<div
className={clsx(
toastClasses.inlineButtonContainer,
isInlineButtonHidden && toastClasses.hiddenButtonContainer,
)}
>
<Button onClick={onButtonClick} className={toastClasses.actionButton}>
{`${isInlineButtonHidden ? '' : buttonText}`}
</Button>
</div>
</div>
<IconButton
onClick={onClose}
className={toastClasses.closeButton}
size="medium"
>
<CloseIcon color="primary" />
</IconButton>
</div>
)
}
Toast.classes = toastClasses

View File

@ -0,0 +1 @@
export * from './Toast'

View File

@ -37,3 +37,4 @@ export * from './components/ModalFooter'
export * from './components/DateField'
export * from './components/DatePicker'
export * from './components/Calendar'
export * from './components/Toast'