Skip to content

Commit

Permalink
Adding ability to pass in a ReactNode into the modal body (#205)
Browse files Browse the repository at this point in the history
* Accepting react node in confirmation modal

* adding test

* fixing bug

* fixing api
  • Loading branch information
fneves authored Nov 13, 2023
1 parent e8a048a commit 6ef0617
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 45 deletions.
2 changes: 1 addition & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ const App = () => {
<ConfirmationDialog
title="Confirmation Dialog Example"
message="This is a simple dialog that will be reused across the application"
onPrimaryActionClick={() => {
onConfirm={() => {
console.log("close");
}}
>
Expand Down
12 changes: 5 additions & 7 deletions src/components/ConfirmationDialog/ConfirmationDialog.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { Link } from "@/components";
import { GridCenter } from "../commonElement";
import {
ConfirmationDialog,
Expand All @@ -10,21 +9,20 @@ const ConfirmationDialogComponent = ({
title,
message,
primaryActionLabel,
onPrimaryActionClick,
onConfirm,
secondaryActionLabel,
onOpenChange,
onCancel,
}: ConfirmationDialogProps) => (
<GridCenter>
<ConfirmationDialog
open={open}
title={title}
message={message}
onOpenChange={onOpenChange}
onCancel={onCancel}
primaryActionLabel={primaryActionLabel}
onPrimaryActionClick={onPrimaryActionClick}
onConfirm={onConfirm}
secondaryActionLabel={secondaryActionLabel}
>
<Link>Open dialog</Link>
</ConfirmationDialog>
</GridCenter>
);
Expand All @@ -50,7 +48,7 @@ export const Playground = {
onOpenChange: (b: boolean) => {
void b;
},
onPrimaryAcyionClick: () => {
onPrimaryActionClick: () => {
console.log("Click");
},
primaryActionLabel: "Confirm",
Expand Down
46 changes: 22 additions & 24 deletions src/components/ConfirmationDialog/ConfirmationDialog.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,16 @@ import {
ConfirmationDialogProps,
} from "@/components/ConfirmationDialog/ConfirmationDialog";
import { fireEvent } from "@testing-library/dom";
import { Button } from "@/components";

describe("Dialog Component", () => {
const renderDialog = (
{
title = "Dialog Title",
message = "Are you sure you want to proceed?",
onPrimaryActionClick = () => {
onConfirm = () => {
void undefined;
},
onOpenChange,
onCancel,
open,
primaryActionLabel = "Confirm",
secondaryActionLabel = "Cancel",
Expand All @@ -25,10 +24,10 @@ describe("Dialog Component", () => {
<ConfirmationDialog
title={title}
message={message}
onPrimaryActionClick={onPrimaryActionClick}
onConfirm={onConfirm}
primaryActionLabel={primaryActionLabel}
secondaryActionLabel={secondaryActionLabel}
onOpenChange={onOpenChange}
onCancel={onCancel}
open={open}
children={children}
/>
Expand Down Expand Up @@ -65,40 +64,34 @@ describe("Dialog Component", () => {
it("closes the dialog on primary action click", () => {
const title = "Dialog Title";
const primaryActionLabel = "PrimaryAction";
const triggerLabel = "Open Dialog";
const children = <Button label={triggerLabel} />;

let open = true;
const { getByText, queryAllByText } = renderDialog({
title,
primaryActionLabel,
children,
open,
onCancel: () => open = false
});

const triggerBtn = getByText(triggerLabel);
fireEvent.click(triggerBtn);
expect(queryAllByText(title).length).toEqual(1);
const primaryBtn = getByText(primaryActionLabel);
fireEvent.click(primaryBtn);
expect(queryAllByText(title).length).toEqual(0);
expect(open).toEqual(false);
});

it("executes the primary action on primary action click", () => {
let counter = 0;
const title = "Dialog Title";
const primaryActionLabel = "PrimaryAction";
const triggerLabel = "Open Dialog";
const onPrimaryActionClick = () => counter++;
const children = <Button label={triggerLabel} />;
const onConfirm = () => counter++;

const { getByText } = renderDialog({
title,
primaryActionLabel,
onPrimaryActionClick,
children,
onConfirm,
open: true,
});

const triggerBtn = getByText(triggerLabel);
fireEvent.click(triggerBtn);
const primaryBtn = getByText(primaryActionLabel);
fireEvent.click(primaryBtn);
expect(counter).toEqual(1);
Expand All @@ -114,20 +107,25 @@ describe("Dialog Component", () => {
it("closes the dialog on secondary action click", () => {
const title = "Dialog Title";
const secondaryActionLabel = "SecondaryAction";
const triggerLabel = "Open Dialog";
const children = <Button label={triggerLabel} />;

let open = true;
const { getByText, queryAllByText } = renderDialog({
title,
secondaryActionLabel,
children,
open,
onCancel: () => open = false
});

const triggerBtn = getByText(triggerLabel);
fireEvent.click(triggerBtn);
expect(queryAllByText(title).length).toEqual(1);
const secondaryActionBtn = getByText(secondaryActionLabel);
fireEvent.click(secondaryActionBtn);
expect(queryAllByText(title).length).toEqual(0);
expect(open).toEqual(false);
});

it("fails to render in case you provide both children and message props", () => {
const children = <div>test</div>;
const message = "this is a test message";

expect(() => renderDialog({ children, message, open: true })).toThrowError("You can't pass children and message props at the same time");
});
});
34 changes: 21 additions & 13 deletions src/components/ConfirmationDialog/ConfirmationDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ type DialogPrimaryAction = "primary" | "danger";

export interface ConfirmationDialogProps {
open?: boolean;
onOpenChange?: (b: boolean) => void;
onCancel?: () => void;
title: string;
message: string;
primaryActionType?: DialogPrimaryAction;
primaryActionLabel?: string;
secondaryActionLabel?: string;
onPrimaryActionClick?: (() => void) | (() => Promise<void>);
message?: string;
children?: ReactNode;
loading?: boolean;
onConfirm?: (() => void) | (() => Promise<void>);
}

const ActionsWrapper = styled.div`
Expand All @@ -24,35 +25,42 @@ const ActionsWrapper = styled.div`

export const ConfirmationDialog = ({
open,
onOpenChange,
onCancel,
title,
message,
loading,
primaryActionType = "primary",
primaryActionLabel = "Confirm",
secondaryActionLabel = "Cancel",
onPrimaryActionClick,
onConfirm,
children,
}: ConfirmationDialogProps): ReactElement => {
if (children && message) {
throw new Error(
"You can't pass children and message props at the same time"
);
}

return (
<Dialog
open={open}
onOpenChange={onOpenChange}
onOpenChange={(open: boolean) => {
!open && onCancel && onCancel();
}}
>
{children && <Dialog.Trigger>{children}</Dialog.Trigger>}
<Dialog.Content title={title}>
<Text>{message}</Text>
{children ? children : <Text>{message}</Text>}
<Separator size="xl" />
<ActionsWrapper>
<Dialog.Close label={secondaryActionLabel} />
<Dialog.Close
loading={!!loading}
disabled={!!loading}
type={primaryActionType}
label={primaryActionLabel}
onClick={() => {
if (onPrimaryActionClick) {
onPrimaryActionClick();
}
if (onOpenChange) {
onOpenChange(false);
if (onConfirm) {
onConfirm();
}
}}
/>
Expand Down

1 comment on commit 6ef0617

@vercel
Copy link

@vercel vercel bot commented on 6ef0617 Nov 13, 2023

Choose a reason for hiding this comment

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

Successfully deployed to the following URLs:

click-ui – ./

click-ui.vercel.app
click-ui-git-main-clickhouse.vercel.app
click-ui-clickhouse.vercel.app

Please sign in to comment.