diff --git a/.loki/reference/chrome_laptop_toast_ToastHavingDetailModal_Default.png b/.loki/reference/chrome_laptop_toast_ToastHavingDetailModal_Default.png new file mode 100644 index 00000000..361e5672 Binary files /dev/null and b/.loki/reference/chrome_laptop_toast_ToastHavingDetailModal_Default.png differ diff --git a/.loki/reference/chrome_laptop_toast_Toast_Default.png b/.loki/reference/chrome_laptop_toast_Toast_Default.png new file mode 100644 index 00000000..361e5672 Binary files /dev/null and b/.loki/reference/chrome_laptop_toast_Toast_Default.png differ diff --git a/src/components/toast/Toast.stories.tsx b/src/components/toast/Toast.stories.tsx new file mode 100644 index 00000000..c6147d28 --- /dev/null +++ b/src/components/toast/Toast.stories.tsx @@ -0,0 +1,18 @@ +import React from "react"; +import { toast } from "react-toastify"; +import { Toast } from "./Toast"; +import { Toaster } from "./Toaster"; + +export default { + component: Toast, + title: "toast/Toast", +}; + +export const Default = (): JSX.Element => { + return ( +
+ + +
+ ); +}; diff --git a/src/components/toast/Toast.test.tsx b/src/components/toast/Toast.test.tsx new file mode 100644 index 00000000..95affea6 --- /dev/null +++ b/src/components/toast/Toast.test.tsx @@ -0,0 +1,14 @@ +import "@testing-library/jest-dom/extend-expect"; +import { screen } from "@testing-library/react"; +import React from "react"; +import { render } from "../../test-utils"; +import { Default } from "./Toast.stories"; + +describe("Toast", () => { + test("should toast alert", async () => { + render(); + expect(screen.queryAllByRole("alert")).toHaveLength(0); + screen.getByRole("button").click(); + await expect(screen.findAllByRole("alert")).resolves.toBeDefined(); + }); +}); diff --git a/src/components/toast/Toast.tsx b/src/components/toast/Toast.tsx new file mode 100644 index 00000000..f9060f8a --- /dev/null +++ b/src/components/toast/Toast.tsx @@ -0,0 +1,13 @@ +import Alert, { AlertProps } from "@mui/material/Alert"; +import React from "react"; + +export type ToastProps = AlertProps; +export const Toast = ({ sx, ...delegated }: ToastProps): JSX.Element => { + return ( + + ); +}; diff --git a/src/components/toast/ToastHavingDetailInfo.stories.tsx b/src/components/toast/ToastHavingDetailInfo.stories.tsx new file mode 100644 index 00000000..0ad55e1f --- /dev/null +++ b/src/components/toast/ToastHavingDetailInfo.stories.tsx @@ -0,0 +1,36 @@ +import React from "react"; +import { toast } from "react-toastify"; +import { ErrorMessage } from "../ErrorMessage"; +import { ToastHavingDetailModal } from "./ToastHavingDetailInfo"; +import { Toaster } from "./Toaster"; + +export default { + component: ToastHavingDetailModal, + title: "toast/ToastHavingDetailModal", +}; + +export const Default = (): JSX.Element => { + return ( +
+ + +
+ ); +}; diff --git a/src/components/toast/ToastHavingDetailInfo.test.tsx b/src/components/toast/ToastHavingDetailInfo.test.tsx new file mode 100644 index 00000000..f3675ab8 --- /dev/null +++ b/src/components/toast/ToastHavingDetailInfo.test.tsx @@ -0,0 +1,19 @@ +import "@testing-library/jest-dom/extend-expect"; +import { getByRole } from "@testing-library/dom"; +import { screen } from "@testing-library/react"; +import React from "react"; +import { render } from "../../test-utils"; +import { Default } from "./ToastHavingDetailInfo.stories"; + +describe("Toast", () => { + test("should toast alert having button that display alert dialog", async () => { + render(); + expect(screen.queryAllByRole("alert")).toHaveLength(0); + screen.getByRole("button").click(); + await expect(screen.findAllByRole("alert")).resolves.toBeDefined(); + + const toast = screen.getAllByRole("alert")[0]; + getByRole(toast, "button").click(); + expect(screen.getByRole("alertdialog")).toBeDefined(); + }); +}); diff --git a/src/components/toast/ToastHavingDetailInfo.tsx b/src/components/toast/ToastHavingDetailInfo.tsx new file mode 100644 index 00000000..f6062600 --- /dev/null +++ b/src/components/toast/ToastHavingDetailInfo.tsx @@ -0,0 +1,44 @@ +import Button, { ButtonProps } from "@mui/material/Button"; +import Dialog from "@mui/material/Dialog"; +import React, { useState, ReactNode } from "react"; +import { DialogCloseButton } from "../dialog/DialogCloseButton"; +import { DialogWrapper } from "../dialog/DialogWrapper"; +import { Toast, ToastProps } from "./Toast"; + +export type ToastHavingDetailModalProps = { + detailContent: ReactNode; + detailButtonProps?: Omit; +} & Omit; +export const ToastHavingDetailModal = ({ + detailContent, + detailButtonProps, + ...delegated +}: ToastHavingDetailModalProps): JSX.Element => { + const [open, setOpen] = useState(false); + return ( + <> + { + e.stopPropagation(); + setOpen(true); + }} + color="inherit" + variant="text" + {...detailButtonProps} + > + {detailButtonProps?.children ?? "Detail"} + + } + /> + setOpen(false)} role="alertdialog"> + + setOpen(false)} /> + {detailContent} + + + + ); +}; diff --git a/src/components/toast/Toaster.tsx b/src/components/toast/Toaster.tsx new file mode 100644 index 00000000..9edf856b --- /dev/null +++ b/src/components/toast/Toaster.tsx @@ -0,0 +1,27 @@ +import { useTheme } from "@mui/material/styles"; +import React from "react"; +import { ToastContainer, ToastContainerProps } from "react-toastify"; +import "react-toastify/dist/ReactToastify.css"; + +export type ToasterProps = ToastContainerProps; +export const Toaster = ({ + position, + theme, + progressStyle, + limit, + ...delegated +}: ToasterProps): JSX.Element => { + const muiTheme = useTheme(); + return ( + + ); +};