From dc9ce1f4a8916d98d6974eb0419cd82f006a2cd9 Mon Sep 17 00:00:00 2001 From: jongomez Date: Thu, 5 Oct 2023 11:05:19 +0100 Subject: [PATCH] feat: toast componet v0.1 - missing position and animation props --- .../components/CSSBaseline/CSSBaseline.tsx | 2 + .../src/components/Toast/Toast.classes.ts | 24 ++++ .../src/components/Toast/Toast.stories.tsx | 43 +++++++ .../src/components/Toast/Toast.styles.ts | 106 ++++++++++++++++ .../lsd-react/src/components/Toast/Toast.tsx | 116 ++++++++++++++++++ .../lsd-react/src/components/Toast/index.ts | 1 + packages/lsd-react/src/index.ts | 1 + 7 files changed, 293 insertions(+) create mode 100644 packages/lsd-react/src/components/Toast/Toast.classes.ts create mode 100644 packages/lsd-react/src/components/Toast/Toast.stories.tsx create mode 100644 packages/lsd-react/src/components/Toast/Toast.styles.ts create mode 100644 packages/lsd-react/src/components/Toast/Toast.tsx create mode 100644 packages/lsd-react/src/components/Toast/index.ts diff --git a/packages/lsd-react/src/components/CSSBaseline/CSSBaseline.tsx b/packages/lsd-react/src/components/CSSBaseline/CSSBaseline.tsx index f0e0388..b56a16c 100644 --- a/packages/lsd-react/src/components/CSSBaseline/CSSBaseline.tsx +++ b/packages/lsd-react/src/components/CSSBaseline/CSSBaseline.tsx @@ -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 | SerializedStyles> = [ @@ -80,6 +81,7 @@ const componentStyles: Array | SerializedStyles> = DatePickerStyles, DateFieldStyles, CalendarStyles, + ToastStyles, ] export const CSSBaseline: React.FC<{ theme?: Theme }> = ({ diff --git a/packages/lsd-react/src/components/Toast/Toast.classes.ts b/packages/lsd-react/src/components/Toast/Toast.classes.ts new file mode 100644 index 0000000..b5c49e2 --- /dev/null +++ b/packages/lsd-react/src/components/Toast/Toast.classes.ts @@ -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', +} diff --git a/packages/lsd-react/src/components/Toast/Toast.stories.tsx b/packages/lsd-react/src/components/Toast/Toast.stories.tsx new file mode 100644 index 0000000..438ed18 --- /dev/null +++ b/packages/lsd-react/src/components/Toast/Toast.stories.tsx @@ -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 = ({ buttonText, ...args }) => { + const [isVisible, setIsVisible] = useState(args.isOpen) + + return ( +
+ + setIsVisible(false)} + buttonText={buttonText} + onButtonClick={() => alert('Button clicked!')} + /> +
+ ) +} + +Root.args = { + title: 'Toast Title', + information: '', + size: 'large', + buttonText: 'Click me', + isOpen: false, + inline: true, +} diff --git a/packages/lsd-react/src/components/Toast/Toast.styles.ts b/packages/lsd-react/src/components/Toast/Toast.styles.ts new file mode 100644 index 0000000..28c3b74 --- /dev/null +++ b/packages/lsd-react/src/components/Toast/Toast.styles.ts @@ -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; + } + } +` diff --git a/packages/lsd-react/src/components/Toast/Toast.tsx b/packages/lsd-react/src/components/Toast/Toast.tsx new file mode 100644 index 0000000..53b33bc --- /dev/null +++ b/packages/lsd-react/src/components/Toast/Toast.tsx @@ -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, 'label'> & { + isOpen: boolean + title: string + information?: string + inline?: boolean + buttonText?: string + onButtonClick?: () => void + onClose?: () => void + size?: 'large' | 'medium' | 'small' + } + +export const Toast: React.FC & { + 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 ( +
+
+
+ {!!title && ( + + + {title} + + )} + + {!!information && ( + + {information} + + )} + + {!inline && !!buttonText && ( + + )} +
+ +
+ +
+
+ + + + +
+ ) +} + +Toast.classes = toastClasses diff --git a/packages/lsd-react/src/components/Toast/index.ts b/packages/lsd-react/src/components/Toast/index.ts new file mode 100644 index 0000000..c2014f7 --- /dev/null +++ b/packages/lsd-react/src/components/Toast/index.ts @@ -0,0 +1 @@ +export * from './Toast' diff --git a/packages/lsd-react/src/index.ts b/packages/lsd-react/src/index.ts index 0a70eb2..ad44d23 100644 --- a/packages/lsd-react/src/index.ts +++ b/packages/lsd-react/src/index.ts @@ -37,3 +37,4 @@ export * from './components/ModalFooter' export * from './components/DateField' export * from './components/DatePicker' export * from './components/Calendar' +export * from './components/Toast'