first commit
This commit is contained in:
1
calcom/packages/ui/components/toast/index.tsx
Normal file
1
calcom/packages/ui/components/toast/index.tsx
Normal file
@@ -0,0 +1 @@
|
||||
export { DefaultToast, ErrorToast, SuccessToast, WarningToast, showToast } from "./showToast";
|
||||
110
calcom/packages/ui/components/toast/showToast.tsx
Normal file
110
calcom/packages/ui/components/toast/showToast.tsx
Normal file
@@ -0,0 +1,110 @@
|
||||
import classNames from "classnames";
|
||||
import type { ToastOptions, Toast } from "react-hot-toast";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
import { Icon } from "../..";
|
||||
|
||||
type IToast = {
|
||||
message: string;
|
||||
toastVisible: boolean;
|
||||
toastId: string;
|
||||
onClose: (toastId: string) => void;
|
||||
};
|
||||
|
||||
export const SuccessToast = ({ message, toastVisible, onClose, toastId }: IToast) => (
|
||||
<button
|
||||
className={classNames(
|
||||
"data-testid-toast-success bg-brand-default text-brand mb-2 flex h-auto space-x-2 rounded-md p-3 text-sm font-semibold shadow-md rtl:space-x-reverse md:max-w-sm",
|
||||
toastVisible && "animate-fade-in-up cursor-pointer"
|
||||
)}
|
||||
onClick={() => onClose(toastId)}>
|
||||
<span className="mt-0.5">
|
||||
<Icon name="check" className="h-4 w-4" />
|
||||
</span>
|
||||
<p data-testid="toast-success" className="text-left">
|
||||
{message}
|
||||
</p>
|
||||
</button>
|
||||
);
|
||||
|
||||
export const ErrorToast = ({ message, toastVisible, onClose, toastId }: IToast) => (
|
||||
<button
|
||||
className={classNames(
|
||||
"animate-fade-in-up bg-error text-error text-brand mb-2 flex h-auto space-x-2 rounded-md p-3 text-sm font-semibold shadow-md rtl:space-x-reverse md:max-w-sm",
|
||||
toastVisible && "animate-fade-in-up cursor-pointer"
|
||||
)}
|
||||
onClick={() => onClose(toastId)}>
|
||||
<span className="mt-0.5">
|
||||
<Icon name="info" className="h-4 w-4" />
|
||||
</span>
|
||||
<p data-testid="toast-error" className="text-left">
|
||||
{message}
|
||||
</p>
|
||||
</button>
|
||||
);
|
||||
|
||||
export const WarningToast = ({ message, toastVisible, onClose, toastId }: IToast) => (
|
||||
<button
|
||||
className={classNames(
|
||||
"animate-fade-in-up bg-brand-default text-brand mb-2 flex h-auto space-x-2 rounded-md p-3 text-sm font-semibold shadow-md rtl:space-x-reverse md:max-w-sm",
|
||||
toastVisible && "animate-fade-in-up cursor-pointer"
|
||||
)}
|
||||
onClick={() => onClose(toastId)}>
|
||||
<span className="mt-0.5">
|
||||
<Icon name="info" className="h-4 w-4" />
|
||||
</span>
|
||||
<p data-testid="toast-warning" className="text-left">
|
||||
{message}
|
||||
</p>
|
||||
</button>
|
||||
);
|
||||
|
||||
export const DefaultToast = ({ message, toastVisible, onClose, toastId }: IToast) => (
|
||||
<button
|
||||
className={classNames(
|
||||
"animate-fade-in-up bg-brand-default text-brand mb-2 flex h-auto space-x-2 rounded-md p-3 text-sm font-semibold shadow-md rtl:space-x-reverse md:max-w-sm",
|
||||
toastVisible && "animate-fade-in-up cursor-pointer"
|
||||
)}
|
||||
onClick={() => onClose(toastId)}>
|
||||
<span className="mt-0.5">
|
||||
<Icon name="check" className="h-4 w-4" />
|
||||
</span>
|
||||
<p data-testid="toast-default" className="text-left">
|
||||
{message}
|
||||
</p>
|
||||
</button>
|
||||
);
|
||||
|
||||
const TOAST_VISIBLE_DURATION = 6000;
|
||||
|
||||
type ToastVariants = "success" | "warning" | "error";
|
||||
|
||||
export function showToast(
|
||||
message: string,
|
||||
variant: ToastVariants,
|
||||
// Options or duration (duration for backwards compatibility reasons)
|
||||
options: number | ToastOptions = TOAST_VISIBLE_DURATION
|
||||
) {
|
||||
//
|
||||
const _options: ToastOptions = typeof options === "number" ? { duration: options } : options;
|
||||
if (!_options.duration) _options.duration = TOAST_VISIBLE_DURATION;
|
||||
if (!_options.position) _options.position = "bottom-center";
|
||||
|
||||
const onClose = (toastId: string) => {
|
||||
toast.remove(toastId);
|
||||
};
|
||||
const toastElements: { [x in ToastVariants]: (t: Toast) => JSX.Element } = {
|
||||
success: (t) => (
|
||||
<SuccessToast message={message} toastVisible={t.visible} onClose={onClose} toastId={t.id} />
|
||||
),
|
||||
error: (t) => <ErrorToast message={message} toastVisible={t.visible} onClose={onClose} toastId={t.id} />,
|
||||
warning: (t) => (
|
||||
<WarningToast message={message} toastVisible={t.visible} onClose={onClose} toastId={t.id} />
|
||||
),
|
||||
};
|
||||
return toast.custom(
|
||||
toastElements[variant] ||
|
||||
((t) => <DefaultToast message={message} toastVisible={t.visible} onClose={onClose} toastId={t.id} />),
|
||||
_options
|
||||
);
|
||||
}
|
||||
68
calcom/packages/ui/components/toast/toast.stories.mdx
Normal file
68
calcom/packages/ui/components/toast/toast.stories.mdx
Normal file
@@ -0,0 +1,68 @@
|
||||
import { Canvas, Meta, Story, ArgsTable } from "@storybook/addon-docs";
|
||||
|
||||
import {
|
||||
Examples,
|
||||
Example,
|
||||
Note,
|
||||
Title,
|
||||
VariantsTable,
|
||||
VariantColumn,
|
||||
RowTitles,
|
||||
CustomArgsTable,
|
||||
VariantRow,
|
||||
} from "@calcom/storybook/components";
|
||||
|
||||
import { SuccessToast, ErrorToast, WarningToast, DefaultToast } from "./";
|
||||
|
||||
<Meta title="UI/Toasts" component={DefaultToast} />
|
||||
|
||||
<Title title="Toasts" suffix="Brief" subtitle="Version 2.0 — Last Update: 22 Aug 2022" />
|
||||
|
||||
## Definition
|
||||
|
||||
Toasts are used to show an action has had a impact. If a user submits a form a toast should be shown to notify the user there has been a success
|
||||
|
||||
## Structure
|
||||
|
||||
<CustomArgsTable of={DefaultToast} />
|
||||
|
||||
<Examples title="Toasts">
|
||||
<Example title="Default">
|
||||
<DefaultToast message="Default Toast" toastVisible={true} />
|
||||
</Example>
|
||||
<Example title="Success">
|
||||
<SuccessToast message="Success Toast" toastVisible={true} />
|
||||
</Example>
|
||||
<Example title="Warning">
|
||||
<WarningToast message="Warning Toast" toastVisible={true} />
|
||||
</Example>
|
||||
<Example title="Error">
|
||||
<ErrorToast message="Error Toast" toastVisible={true} />
|
||||
</Example>
|
||||
</Examples>
|
||||
|
||||
## Toast Story
|
||||
|
||||
<Canvas>
|
||||
<Story
|
||||
name="Default"
|
||||
args={{
|
||||
message: "Default Toast",
|
||||
toastVisible: true,
|
||||
}}
|
||||
argTypes={{
|
||||
message: { control: "text" },
|
||||
toastVisible: { control: "boolean" },
|
||||
}}>
|
||||
{({ message, toastVisible }) => (
|
||||
<VariantsTable titles={["Default", "Success", "Warning", "Error"]} columnMinWidth={150}>
|
||||
<VariantRow variant="Default">
|
||||
<DefaultToast message={message} toastVisible={toastVisible} />
|
||||
<SuccessToast message={message} toastVisible={toastVisible} />
|
||||
<WarningToast message={message} toastVisible={toastVisible} />
|
||||
<ErrorToast message={message} toastVisible={toastVisible} />
|
||||
</VariantRow>
|
||||
</VariantsTable>
|
||||
)}
|
||||
</Story>
|
||||
</Canvas>
|
||||
33
calcom/packages/ui/components/toast/toast.test.tsx
Normal file
33
calcom/packages/ui/components/toast/toast.test.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import { render, screen, fireEvent } from "@testing-library/react";
|
||||
import { vi } from "vitest";
|
||||
|
||||
import { SuccessToast, ErrorToast, WarningToast, DefaultToast } from "./showToast";
|
||||
|
||||
describe("Tests for Toast Components", () => {
|
||||
const testToastComponent = (Component: typeof DefaultToast, toastTestId: string) => {
|
||||
const message = "This is a test message";
|
||||
const toastId = "some-id";
|
||||
|
||||
const onCloseMock = vi.fn();
|
||||
render(<Component message={message} toastVisible={true} onClose={onCloseMock} toastId={toastId} />);
|
||||
|
||||
const toast = screen.getByTestId(toastTestId);
|
||||
expect(toast).toBeInTheDocument();
|
||||
expect(toast.textContent).toContain(message);
|
||||
|
||||
const closeButton = screen.getByRole("button");
|
||||
fireEvent.click(closeButton);
|
||||
expect(onCloseMock).toHaveBeenCalledWith(toastId);
|
||||
};
|
||||
|
||||
const toastComponents: [string, typeof DefaultToast, string][] = [
|
||||
["SuccessToast", SuccessToast, "toast-success"],
|
||||
["ErrorToast", ErrorToast, "toast-error"],
|
||||
["WarningToast", WarningToast, "toast-warning"],
|
||||
["DefaultToast", DefaultToast, "toast-default"],
|
||||
];
|
||||
|
||||
test.each(toastComponents)("Should render and close %s component", (_, ToastComponent, expectedClass) => {
|
||||
testToastComponent(ToastComponent, expectedClass);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user