Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Dialog Component #886

Merged
merged 23 commits into from
Jan 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions src/components/layout-elements/Dialog/Dialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import React from "react";
import { Dialog as HeadlessuiDialog, Transition } from "@headlessui/react";
import { makeClassName, tremorTwMerge } from "lib";

const makeDisplayClassName = makeClassName("dialog");

type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };
type XOR<T, U> = T | U extends object ? (Without<T, U> & U) | (Without<U, T> & T) : T | U;

export type DialogProps = React.HTMLAttributes<HTMLDivElement> & {
open: boolean;
onClose: (val: boolean) => void;
overlayClassName?: string;
} & XOR<{ unmount?: boolean }, { static?: boolean }>;

const Dialog = React.forwardRef<HTMLDivElement, DialogProps>((props, ref) => {
const { children, className, overlayClassName, ...other } = props;

return (
<Transition as={React.Fragment} appear show={props.open}>
<HeadlessuiDialog
as="div"
ref={ref}
{...other}
className={tremorTwMerge(makeDisplayClassName("root"), "relative z-50", className)}
>
<Transition.Child
as={React.Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div
className={tremorTwMerge(
"fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity",
overlayClassName,
)}
></div>
</Transition.Child>

<div className="fixed inset-0 overflow-y-auto">
<div className="flex min-h-full items-center justify-center p-4">{children}</div>
</div>
</HeadlessuiDialog>
</Transition>
);
});

Dialog.displayName = "Dialog";

export default Dialog;
49 changes: 49 additions & 0 deletions src/components/layout-elements/Dialog/DialogPanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React from "react";
import { Dialog as HeadlessuiDialog, Transition } from "@headlessui/react";
import { makeClassName, spacing, tremorTwMerge } from "lib";
import { RootStylesContext } from "contexts";

const makeDisplayClassName = makeClassName("dialog");

export type DialogPanelProps = React.HTMLAttributes<HTMLDivElement>;

const DialogPanel = React.forwardRef<HTMLDivElement, DialogPanelProps>((props, ref) => {
const { children, className, ...other } = props;
const rootStyles =
React.useContext(RootStylesContext) ??
tremorTwMerge(spacing.threeXl.paddingAll, "rounded-tremor-default");

return (
<Transition.Child
as={React.Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95"
>
<HeadlessuiDialog.Panel
ref={ref}
className={tremorTwMerge(
makeDisplayClassName("panel"),
// light
"bg-tremor-background text-tremor-content ring-tremor-ring",
// dark
"dark:bg-dark-tremor-background dark:text-dark-tremor-content dark:ring-dark-tremor-ring",
// common
"overflow-hidden text-left shadow-xl ring-1 shadow-tremor transition-all transform",
rootStyles,
className,
)}
{...other}
>
{children}
</HeadlessuiDialog.Panel>
</Transition.Child>
);
});

DialogPanel.displayName = "DialogPanel";

export default DialogPanel;
2 changes: 2 additions & 0 deletions src/components/layout-elements/Dialog/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default as Dialog, type DialogProps } from "./Dialog";
export { default as DialogPanel, type DialogPanelProps } from "./DialogPanel";
1 change: 1 addition & 0 deletions src/components/layout-elements/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from "./Card";
export * from "./Divider";
export * from "./Flex";
export * from "./Grid";
export * from "./Dialog";
21 changes: 21 additions & 0 deletions src/stories/layout-elements/Dialog.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { Meta, StoryObj } from "@storybook/react";

import { Dialog } from "components";
import SimpleDialog from "./helpers/SimpleDialog";

const meta: Meta<typeof Dialog> = {
title: "UI/Layout/Dialog",
component: Dialog,
parameters: {
sourceLink:
"https://github.com/tremorlabs/tremor/tree/main/src/components/layout-elements/Dialog",
},
};

export default meta;
type Story = StoryObj<typeof Dialog>;

export const Default: Story = {
render: SimpleDialog,
parameters: {},
};
28 changes: 28 additions & 0 deletions src/stories/layout-elements/helpers/SimpleDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React, { useState } from "react";
import { Dialog, DialogPanel, Button, Title } from "components";

const SimpleDialog = () => {
const [isOpen, setIsOpen] = useState(false);
return (
<>
<div className="text-center">
<Button variant="primary" onClick={() => setIsOpen(true)}>
Open Dialog
</Button>
</div>
<Dialog open={isOpen} onClose={(val) => setIsOpen(val)} static={true}>
<DialogPanel className="max-w-md">
<Title className="mb-3">Account Created Successfully</Title>
Your account has been created successfully. You can now login to your account. For more
information, please contact us.
<div className="mt-3">
<Button variant="light" onClick={() => setIsOpen(false)}>
Got it!
</Button>
</div>
</DialogPanel>
</Dialog>
</>
);
};
export default SimpleDialog;
19 changes: 19 additions & 0 deletions src/tests/layout-elements/Dialog.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from "react";
import { act, render } from "@testing-library/react";

import { Dialog, DialogPanel } from "components";

describe("Dialog", () => {
test("renders the Dialog component", async () => {
const { findByText } = render(
<Dialog open onClose={() => {}}>
<DialogPanel>Test</DialogPanel>
</Dialog>,
);
await act(async () => {
await new Promise((resolve) => setTimeout(resolve, 300));
});
const message = await findByText("Test");
expect(message.textContent).toBe("Test");
});
});
Loading