From 44842b690be5cbfdc8a9740420679a313eaaca97 Mon Sep 17 00:00:00 2001 From: Fara Woolf Date: Tue, 5 Mar 2024 13:01:14 -0600 Subject: [PATCH] feat: toast --- package.json | 1 + src/app/app.tsx | 23 +++--- src/app/features/html-head/head-provider.tsx | 6 +- .../ui/components/toast/toast-primitive.tsx | 54 ++++++++++++++ src/app/ui/components/toast/toast.stories.tsx | 71 +++++++++++++++++++ src/app/ui/components/toast/toast.tsx | 23 ++++++ src/app/ui/components/toast/toast.utils.tsx | 16 +++++ theme/keyframes.ts | 4 ++ yarn.lock | 19 +++++ 9 files changed, 204 insertions(+), 13 deletions(-) create mode 100644 src/app/ui/components/toast/toast-primitive.tsx create mode 100644 src/app/ui/components/toast/toast.stories.tsx create mode 100644 src/app/ui/components/toast/toast.tsx create mode 100644 src/app/ui/components/toast/toast.utils.tsx diff --git a/package.json b/package.json index 97cf8396508..65274a18027 100644 --- a/package.json +++ b/package.json @@ -143,6 +143,7 @@ "@radix-ui/react-dropdown-menu": "2.0.6", "@radix-ui/react-select": "2.0.0", "@radix-ui/react-tabs": "1.0.4", + "@radix-ui/react-toast": "1.1.5", "@radix-ui/react-tooltip": "1.0.7", "@radix-ui/themes": "2.0.3", "@reduxjs/toolkit": "1.9.6", diff --git a/src/app/app.tsx b/src/app/app.tsx index ac9526927ad..3428319ca05 100644 --- a/src/app/app.tsx +++ b/src/app/app.tsx @@ -1,6 +1,7 @@ import { Suspense } from 'react'; import { Provider as ReduxProvider } from 'react-redux'; +import { Provider as RadixToastProvider } from '@radix-ui/react-toast'; import { radixBaseCSS } from '@radix-ui/themes/styles.css'; import { QueryClientProvider } from '@tanstack/react-query'; import { styled } from 'leather-styles/jsx'; @@ -26,16 +27,18 @@ export function App() { {/* TODO: this works but investigate importing radixBaseCSS in panda layer config */} - - - }> - - - - {reactQueryDevToolsEnabled && } - - - + + + + }> + + + + {reactQueryDevToolsEnabled && } + + + + diff --git a/src/app/features/html-head/head-provider.tsx b/src/app/features/html-head/head-provider.tsx index fb1f9680572..9586cdc18a4 100644 --- a/src/app/features/html-head/head-provider.tsx +++ b/src/app/features/html-head/head-provider.tsx @@ -1,13 +1,13 @@ -import { Link, HeadProvider as ReastHeadProvider, Title } from 'react-head'; +import { Link, HeadProvider as ReactHeadProvider, Title } from 'react-head'; import { useNewBrandApprover } from '@app/store/settings/settings.selectors'; export function HeadProvider() { const { hasApprovedNewBrand } = useNewBrandApprover(); return ( - + {hasApprovedNewBrand ? : } - + ); } diff --git a/src/app/ui/components/toast/toast-primitive.tsx b/src/app/ui/components/toast/toast-primitive.tsx new file mode 100644 index 00000000000..e28cc68e0fa --- /dev/null +++ b/src/app/ui/components/toast/toast-primitive.tsx @@ -0,0 +1,54 @@ +import { forwardRef } from 'react'; + +import * as RadixToast from '@radix-ui/react-toast'; +import { css } from 'leather-styles/css'; + +const toastRootStyles = css({ + bg: 'ink.text-primary', + rounded: 'xs', + boxShadow: + '0px 12px 24px 0px rgba(18, 16, 15, 0.08), 0px 4px 8px 0px rgba(18, 16, 15, 0.08), 0px 0px 2px 0px rgba(18, 16, 15, 0.08)', + pl: 'space.03', + pr: 'space.04', + py: 'space.03', + + '&[data-state=open]': { + animation: 'toastAppear 160ms cubic-bezier(0, 0.45, 0.6, 1) forwards', + }, + '&[data-state=closed]': { + animation: 'fadeout', + }, +}); +const Root: typeof RadixToast.Root = forwardRef((props, ref) => ( + +)); + +const toastViewportStyles = css({ + display: 'flex', + flexDirection: 'column', + m: '0 auto', + maxWidth: 'fit-content', + outline: 'none', + p: 'space.05', + position: 'fixed', + top: 0, + zIndex: 9999, +}); +const Viewport: typeof RadixToast.Viewport = forwardRef((props, ref) => ( + +)); + +const toastMessageStyles = css({ + color: 'ink.background-primary', + fontWeight: 500, + textStyle: 'label.02', +}); +const Title: typeof RadixToast.Title = forwardRef((props, ref) => ( + +)); + +export const ToastPrimitive = { + Root, + Viewport, + Title, +}; diff --git a/src/app/ui/components/toast/toast.stories.tsx b/src/app/ui/components/toast/toast.stories.tsx new file mode 100644 index 00000000000..0a72cca7533 --- /dev/null +++ b/src/app/ui/components/toast/toast.stories.tsx @@ -0,0 +1,71 @@ +import { Provider as RadixToastProvider } from '@radix-ui/react-toast'; +import { Meta, StoryObj } from '@storybook/react'; +import { styled } from 'leather-styles/jsx'; + +import { Toast as Component } from './toast'; +import { ToastPrimitive } from './toast-primitive'; + +const meta: Meta = { + component: Component, + tags: ['autodocs'], + title: 'Toast', + // argTypes: { + // variant: { + // options: ['error', 'info', 'success', 'undefined'], + // control: { type: 'radio' }, + // defaultValue: 'info', + // }, + // }, +}; + +export default meta; +type Story = StoryObj; + +export const Toast: Story = { + parameters: { + controls: { include: ['variant'] }, + }, + render: args => ( + + + + Placeholder message + + + + + ), +}; + +export const AnimatedInfo: Story = { + render: () => ( + + + Just a heads up + + + + ), +}; + +export const AnimatedSuccess: Story = { + render: () => ( + + + That worked well + + + + ), +}; + +export const AnimatedError: Story = { + render: () => ( + + + Something went wrong + + + + ), +}; diff --git a/src/app/ui/components/toast/toast.tsx b/src/app/ui/components/toast/toast.tsx new file mode 100644 index 00000000000..c6eed1cea8f --- /dev/null +++ b/src/app/ui/components/toast/toast.tsx @@ -0,0 +1,23 @@ +import { type ToastProps } from '@radix-ui/react-toast'; +import { HStack } from 'leather-styles/jsx'; + +import { ToastPrimitive } from './toast-primitive'; +import { type ToastVariant, getIconVariant } from './toast.utils'; + +type ToastPrimitiveProps = ToastProps & React.RefAttributes; + +interface TitleProps extends ToastPrimitiveProps { + variant: ToastVariant; +} +export function Toast({ children, variant, ...props }: TitleProps) { + return ( + + + + {getIconVariant(variant)} + {children} + + + + ); +} diff --git a/src/app/ui/components/toast/toast.utils.tsx b/src/app/ui/components/toast/toast.utils.tsx new file mode 100644 index 00000000000..47541be4d8e --- /dev/null +++ b/src/app/ui/components/toast/toast.utils.tsx @@ -0,0 +1,16 @@ +import { CheckmarkCircleIcon, ErrorIcon, InfoCircleIcon } from '@app/ui/icons'; + +export type ToastVariant = 'info' | 'success' | 'error'; + +export function getIconVariant(variant?: ToastVariant) { + switch (variant) { + case 'info': + return ; + case 'success': + return ; + case 'error': + return ; + default: + return ; + } +} diff --git a/theme/keyframes.ts b/theme/keyframes.ts index cb3c6e181da..beee9e090e6 100644 --- a/theme/keyframes.ts +++ b/theme/keyframes.ts @@ -8,4 +8,8 @@ export const keyframes: CssKeyframes = { from: { opacity: 1, transform: 'translateY(0)' }, to: { opacity: 0, transform: 'translateY(4px)' }, }, + toastAppear: { + from: { opacity: 0, transform: 'translateY(-12px) scale(0.9)' }, + to: { opacity: 1, transform: 'translateY(0) scale(1)' }, + }, }; diff --git a/yarn.lock b/yarn.lock index 5028e95db80..a66818277a1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3692,6 +3692,25 @@ "@radix-ui/react-roving-focus" "1.0.4" "@radix-ui/react-use-controllable-state" "1.0.1" +"@radix-ui/react-toast@1.1.5": + version "1.1.5" + resolved "https://registry.yarnpkg.com/@radix-ui/react-toast/-/react-toast-1.1.5.tgz#f5788761c0142a5ae9eb97f0051fd3c48106d9e6" + integrity sha512-fRLn227WHIBRSzuRzGJ8W+5YALxofH23y0MlPLddaIpLpCDqdE0NZlS2NRQDRiptfxDeeCjgFIpexB1/zkxDlw== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/primitive" "1.0.1" + "@radix-ui/react-collection" "1.0.3" + "@radix-ui/react-compose-refs" "1.0.1" + "@radix-ui/react-context" "1.0.1" + "@radix-ui/react-dismissable-layer" "1.0.5" + "@radix-ui/react-portal" "1.0.4" + "@radix-ui/react-presence" "1.0.1" + "@radix-ui/react-primitive" "1.0.3" + "@radix-ui/react-use-callback-ref" "1.0.1" + "@radix-ui/react-use-controllable-state" "1.0.1" + "@radix-ui/react-use-layout-effect" "1.0.1" + "@radix-ui/react-visually-hidden" "1.0.3" + "@radix-ui/react-toggle-group@1.0.4": version "1.0.4" resolved "https://registry.yarnpkg.com/@radix-ui/react-toggle-group/-/react-toggle-group-1.0.4.tgz#f5b5c8c477831b013bec3580c55e20a68179d6ec"