Skip to content

Commit

Permalink
Update Toast component (#209)
Browse files Browse the repository at this point in the history
* fix: update default color of CopyToClipboard icon

* feat: update Toast and primitives to use sonner

* style: update toast recipe

* ci(changesets): add changeset for Toast update

* chore(toast): remove unused icon

* chore(toast): update types in stories
  • Loading branch information
hobbescodes authored Jan 7, 2024
1 parent 825f55f commit 3aad5ce
Show file tree
Hide file tree
Showing 13 changed files with 137 additions and 74 deletions.
5 changes: 5 additions & 0 deletions .changeset/hungry-maps-end.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@animareflection/ui": minor
---

Update `Toast` components to use sonner
Binary file modified bun.lockb
Binary file not shown.
2 changes: 1 addition & 1 deletion docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ Now you are ready to install the UI library. You can either install it [from the

## Remote

Install from remote repository along with required dependencies: `bun add @animareflection/ui @ark-ui/react framer-motion react-hot-toast`
Install from remote repository along with required dependencies: `bun add @animareflection/ui @ark-ui/react framer-motion sonner`

## Local

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@
"radash": "^11.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hot-toast": "^2.4.1",
"react-icons": "^4.12.0",
"sonner": "^1.3.1",
"storybook": "^7.6.6",
"storybook-dark-mode": "^3.0.3",
"ts-node": "^10.9.2",
Expand Down
60 changes: 55 additions & 5 deletions src/components/core/Toast/Toast.recipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,24 @@ import { defineSlotRecipe } from "@pandacss/dev";
export const toastRecipe = defineSlotRecipe({
className: "toast",
description: "The styles for the Toast component",
slots: ["closeTrigger", "title", "description"],
slots: ["root", "closeTrigger", "title", "description"],
base: {
root: {
display: "flex",
flexDirection: "column",
borderRadius: "md",
borderWidth: "1px",
boxShadow: "sm",
minW: "300px",
px: 5,
py: 2,
},
closeTrigger: {
display: "flex",
position: "absolute",
cursor: "pointer",
color: "fg.muted",
top: 2,
right: 2,
top: 3,
right: 3,
_hover: {
opacity: 0.8,
},
Expand All @@ -21,34 +30,75 @@ export const toastRecipe = defineSlotRecipe({
},
description: {
fontSize: "sm",
color: "fg.muted",
},
},
defaultVariants: {
variant: "unstyled",
},
variants: {
variant: {
unstyled: {
root: {
bgColor: "bg.default",
borderColor: "border.default",
},
closeTrigger: {
color: "fg.muted",
},
description: {
color: "fg.muted",
},
title: {
color: "fg.default",
},
},
success: {
root: {
bgColor: { base: "green.50", _dark: "green.950" },
borderColor: { base: "green.200", _dark: "green.800" },
},
closeTrigger: {
color: "green.500",
},
description: {
color: "green.500",
},
title: {
color: { base: "green.950", _dark: "green.50" },
},
},
error: {
root: {
bgColor: { base: "red.50", _dark: "red.950" },
borderColor: { base: "red.200", _dark: "red.800" },
},
closeTrigger: {
color: "red.500",
},
description: {
color: "red.500",
},
title: {
color: { base: "red.950", _dark: "red.50" },
},
},
loading: {
root: {
bgColor: { base: "brand.primary.50", _dark: "brand.primary.950" },
borderColor: {
base: "brand.primary.200",
_dark: "brand.primary.800",
},
},
closeTrigger: {
color: "brand.primary.500",
},
description: {
color: "brand.primary.500",
},
title: {
color: { base: "brand.primary.950", _dark: "brand.primary.50" },
},
},
},
},
Expand Down
25 changes: 16 additions & 9 deletions src/components/core/Toast/Toast.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import { FiX } from "react-icons/fi";

import Icon from "components/core/Icon/Icon";
import { Flex, panda } from "generated/panda/jsx";
import {
PrimitiveToast,
PrimitiveToastClose,
PrimitiveToastDescription,
PrimitiveToastTitle,
} from "components/primitives";

import type { FlexProps } from "generated/panda/jsx";
import type { PrimitiveToastProps } from "components/primitives";
import type { ToastVariantProps } from "generated/panda/recipes";

export interface Props extends FlexProps, ToastVariantProps {
export interface Props extends PrimitiveToastProps, ToastVariantProps {
title: string;
description?: string;
onClose?: () => void;
Expand All @@ -16,22 +21,24 @@ export interface Props extends FlexProps, ToastVariantProps {
* Core UI toast.
*/
const Toast = ({ title, description, onClose, ...rest }: Props) => (
<Flex direction="column" {...rest}>
<PrimitiveToast {...rest}>
{onClose && (
<panda.button
<PrimitiveToastClose
onClick={onClose}
aria-label="Close Toast"
alignSelf="flex-end"
>
<Icon size="sm">
<FiX />
</Icon>
</panda.button>
</PrimitiveToastClose>
)}

<panda.p>{title}</panda.p>
<panda.p>{description}</panda.p>
</Flex>
<PrimitiveToastTitle>{title}</PrimitiveToastTitle>
{description && (
<PrimitiveToastDescription>{description}</PrimitiveToastDescription>
)}
</PrimitiveToast>
);

export default Toast;
20 changes: 13 additions & 7 deletions src/components/core/Toast/Toaster.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { default as toast } from "react-hot-toast";
import { toast } from "sonner";

import { toastState } from "./Toast.spec";
import { Button, Toast, Toaster } from "components/core";
import { Flex, Grid } from "generated/panda/jsx";

import type { Meta, StoryObj } from "@storybook/react";
import type { ToastPosition } from "react-hot-toast";
import type { ToastT } from "sonner";

type Position = ToastT["position"];

type Story = StoryObj<typeof Toaster>;

Expand All @@ -18,7 +20,6 @@ const notify = () =>
description="toast description"
onClose={closeToast}
/>,
{ icon: "🏝" },
);
const success = () =>
toast.success(
Expand Down Expand Up @@ -59,12 +60,17 @@ const promise = () => {
);
};

const PositionTemplate = ({ position }: { position: ToastPosition }) => {
const PositionTemplate = ({ position }: { position: Position }) => {
const showToastPosition = () =>
toast(`Position set to ${position}`, { position });
toast(<Toast title={`Position set to ${position}`} />, { position });

return (
<Button variant="primary" minW={32} onClick={showToastPosition}>
<Button
justifyContent="center"
variant="primary"
minW={32}
onClick={showToastPosition}
>
{position}
</Button>
);
Expand Down Expand Up @@ -119,7 +125,7 @@ export const ToastState: Story = {
tags: ["test"],
};

const meta = {
const meta: Meta<typeof Toaster> = {
title: "Components/Core/Toaster",
component: Toaster,
tags: ["autodocs"],
Expand Down
45 changes: 5 additions & 40 deletions src/components/core/Toast/Toaster.tsx
Original file line number Diff line number Diff line change
@@ -1,51 +1,16 @@
import { Toaster as ReactHotToaster } from "react-hot-toast";
import { Toaster as SonnerToaster } from "sonner";

import type { ToasterProps } from "react-hot-toast";
import type { ComponentProps } from "react";

export interface Props extends ToasterProps {}
export interface Props extends ComponentProps<typeof SonnerToaster> {}

/**
* Core UI toaster.
*/
const Toaster = ({ ...props }: Props) => (
<ReactHotToaster
<SonnerToaster
toastOptions={{
style: {
background: "var(--colors-bg-default)",
color: "var(--colors-fg-default)",
borderRadius: "var(--radii-md)",
borderWidth: "1px",
borderColor: "var(--colors-border-default)",
boxShadow: "var(--shadows-lg)",
minWidth: "var(--sizes-32)",
paddingLeft: 14,
paddingRight: 14,
},
success: {
style: {
background: "var(--colors-green-100)",
color: "var(--colors-green-800)",
borderColor: "var(--colors-green-800)",
},
},
error: {
style: {
background: "var(--colors-red-100)",
color: "var(--colors-red-800)",
borderColor: "var(--colors-red-800)",
},
},
loading: {
style: {
background: "var(--colors-brand-primary-100)",
color: "var(--colors-brand-primary-800)",
borderColor: "var(--colors-brand-primary-800)",
},
iconTheme: {
primary: "var(--colors-brand-primary-800)",
secondary: "var(--colors-brand-primary-200)",
},
},
unstyled: true,
}}
{...props}
/>
Expand Down
34 changes: 34 additions & 0 deletions src/components/primitives/Toast/Toast.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { panda } from "generated/panda/jsx";
import { toast } from "generated/panda/recipes";
import { createStyleContext } from "lib/util";

import type { PandaComponent } from "generated/panda/types/jsx";
import type { ComponentProps } from "react";

const { withProvider, withContext } = createStyleContext(toast);

/**
* Core UI toast primitives.
*/
export type PrimitiveToastProps = ComponentProps<typeof PrimitiveToast>;
const PrimitiveToast: PandaComponent<typeof panda.div> = withProvider(
panda.div,
"root",
);

export type PrimitiveToastTitleProps = ComponentProps<
typeof PrimitiveToastTitle
>;
export const PrimitiveToastTitle = withContext(panda.p, "title");

export type PrimitiveToastDescriptionProps = ComponentProps<
typeof PrimitiveToastDescription
>;
export const PrimitiveToastDescription = withContext(panda.p, "description");

export type PrimitiveToastCloseProps = ComponentProps<
typeof PrimitiveToastClose
>;
export const PrimitiveToastClose = withContext(panda.button, "closeTrigger");

export default PrimitiveToast;
3 changes: 3 additions & 0 deletions src/components/primitives/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ export * from "./Tabs/Tabs";
export { default as PrimitiveTextarea } from "./Textarea/Textarea";
export * from "./Textarea/Textarea";

export { default as PrimitiveToast } from "./Toast/Toast";
export * from "./Toast/Toast";

export { default as PrimitiveToggle } from "./Toggle/Toggle";
export * from "./Toggle/Toggle";

Expand Down
2 changes: 1 addition & 1 deletion src/components/utility/CopyToClipboard/CopyToClipboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ const CopyToClipboard = ({
>
{children}
{icon && (
<Icon color="bg.default">
<Icon color="inherit">
<ClipboardIcon />
</Icon>
)}
Expand Down
5 changes: 2 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ export * from "generated/panda/css";
export * from "generated/panda/jsx";
export type { JsxStyleProps } from "generated/panda/types";

// export backfill of react hot toast functions and types
export { default as toast } from "react-hot-toast";
export type { ToastPosition } from "react-hot-toast";
// export backfill of sonner functions
export { toast } from "sonner";

// export Panda preset (to be used in downstream Panda configurations)
export { default as anirefPreset } from "lib/panda/aniref.preset";
Expand Down
8 changes: 1 addition & 7 deletions tsup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,7 @@ const tsupConfig = defineTsupConfig({
format: ["cjs", "esm"],
// NB: `peerDeps`, among others, are excluded (marked external) by default
// see https://tsup.egoist.dev/#excluding-packages
external: [
"@ark-ui/react",
"react-icons",
"next",
"framer-motion",
"react-hot-toast",
],
external: ["@ark-ui/react", "react-icons", "next", "framer-motion", "sonner"],
outDir: "build",
esbuildOptions: (opt, _ctx) => {
// https://esbuild.github.io/api/#resolve-extensions
Expand Down

1 comment on commit 3aad5ce

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deploy preview for ui-storybook ready!

✅ Preview
https://ui-storybook-lcr149rmh-animareflection.vercel.app

Built with commit 3aad5ce.
This pull request is being automatically deployed with vercel-action

Please sign in to comment.