diff --git a/src/common-messages.json b/src/common-messages.json index 40608e92a4..2ee3bc0389 100644 --- a/src/common-messages.json +++ b/src/common-messages.json @@ -5,6 +5,7 @@ "save": "Save", "cancel": "Cancel", + "continue": "Continue", "delete": "Delete", "next": "Next", "back": "Back", diff --git a/src/components/confirm-dialog/ConfirmDialog.tsx b/src/components/confirm-dialog/ConfirmDialog.tsx new file mode 100644 index 0000000000..d53090c49d --- /dev/null +++ b/src/components/confirm-dialog/ConfirmDialog.tsx @@ -0,0 +1,94 @@ +import React, { ReactElement, ReactNode, useState } from "react"; +import { + Button, + ButtonVariant, + Modal, + ModalVariant, +} from "@patternfly/react-core"; +import { useTranslation } from "react-i18next"; + +export const useConfirmDialog = ( + props: ConfirmDialogProps +): [() => void, () => ReactElement] => { + const [show, setShow] = useState(false); + + function toggleDialog() { + setShow((show) => !show); + } + + const Dialog = () => ( + + ); + return [toggleDialog, Dialog]; +}; + +export interface ConfirmDialogModalProps extends ConfirmDialogProps { + open: boolean; + toggleDialog: () => void; +} + +export type ConfirmDialogProps = { + titleKey: string; + messageKey?: string; + cancelButtonLabel?: string; + continueButtonLabel?: string; + continueButtonVariant?: ButtonVariant; + onConfirm: () => void; + onCancel?: () => void; + children?: ReactNode; +}; + +export const ConfirmDialogModal = ({ + titleKey, + messageKey, + cancelButtonLabel, + continueButtonLabel, + continueButtonVariant, + onConfirm, + onCancel, + children, + open = true, + toggleDialog, +}: ConfirmDialogModalProps) => { + const { t } = useTranslation(); + return ( + { + onConfirm(); + toggleDialog(); + }} + > + {t(continueButtonLabel || "common:continue")} + , + , + ]} + > + {!messageKey && children} + {messageKey && t(messageKey)} + + ); +}; diff --git a/src/components/confirm-dialog/__tests__/ConfirmDialog.test.tsx b/src/components/confirm-dialog/__tests__/ConfirmDialog.test.tsx new file mode 100644 index 0000000000..0a172d5d20 --- /dev/null +++ b/src/components/confirm-dialog/__tests__/ConfirmDialog.test.tsx @@ -0,0 +1,36 @@ +import React from "react"; +import { mount } from "enzyme"; +import { useConfirmDialog } from "../ConfirmDialog"; + +describe("Confirmation dialog", () => { + it("renders simple confirm dialog", () => { + const onConfirm = jest.fn(); + const Test = () => { + const [toggle, ConfirmDialog] = useConfirmDialog({ + titleKey: "Delete app02?", + messageKey: + "If you delete this client, all associated data will be removed.", + continueButtonLabel: "Delete", + onConfirm: onConfirm, + }); + return ( + <> + + + + ); + }; + + const simple = mount(); + simple.find("#show").simulate("click"); + expect(simple).toMatchSnapshot(); + + const button = simple.find("#modal-confirm").find("button"); + expect(button).not.toBeNull(); + + button!.simulate("click"); + expect(onConfirm).toBeCalled(); + }); +}); diff --git a/src/components/confirm-dialog/__tests__/__snapshots__/ConfirmDialog.test.tsx.snap b/src/components/confirm-dialog/__tests__/__snapshots__/ConfirmDialog.test.tsx.snap new file mode 100644 index 0000000000..cd4adfcea9 --- /dev/null +++ b/src/components/confirm-dialog/__tests__/__snapshots__/ConfirmDialog.test.tsx.snap @@ -0,0 +1,356 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Confirmation dialog renders simple confirm dialog 1`] = ` + + + + + + Delete + , + , + ] + } + appendTo={[Function]} + aria-describedby="" + aria-label="" + aria-labelledby="" + className="" + hasNoBodyWrapper={false} + isOpen={true} + onClose={[Function]} + ouiaSafe={true} + showClose={true} + title="Delete app02?" + variant="small" + > + +
+
+ +
+
+ + } + > + + Delete + , + , + ] + } + aria-describedby="" + aria-label="" + aria-labelledby="" + boxId="pf-modal-part-1" + className="" + descriptorId="pf-modal-part-3" + hasNoBodyWrapper={false} + isOpen={true} + labelId="pf-modal-part-2" + onClose={[Function]} + ouiaId="OUIA-Generated-Modal-small-2" + ouiaSafe={true} + showClose={true} + title="Delete app02?" + variant="small" + > + +
+ +
+ + + +
+
+
+
+
+
+
+
+
+
+`; diff --git a/src/stories/ConfirmDialog.stories.tsx b/src/stories/ConfirmDialog.stories.tsx new file mode 100644 index 0000000000..b856f5c43f --- /dev/null +++ b/src/stories/ConfirmDialog.stories.tsx @@ -0,0 +1,67 @@ +import React from "react"; +import { + TextContent, + Text, + TextVariants, + ButtonVariant, +} from "@patternfly/react-core"; +import { Meta, Story } from "@storybook/react"; +import { action } from "@storybook/addon-actions"; + +import { + ConfirmDialogModal, + ConfirmDialogModalProps, + useConfirmDialog, +} from "../components/confirm-dialog/ConfirmDialog"; + +export default { + title: "Confirmation Dialog", + component: ConfirmDialogModal, +} as Meta; + +const Template: Story = (args) => ( + +); + +export const Simple = Template.bind({}); +Simple.args = { + titleKey: "Delete app02?", + messageKey: "If you delete this client, all associated data will be removed.", + continueButtonLabel: "Delete", + continueButtonVariant: ButtonVariant.danger, +}; + +export const Children = Template.bind({}); +Children.args = { + titleKey: "Children as content!", + continueButtonVariant: ButtonVariant.primary, + children: ( + <> + + Hello World + +

Example of some other patternfly components.

+ + ), +}; + +const Test = () => { + const [toggle, Dialog] = useConfirmDialog({ + titleKey: "Delete app02?", + messageKey: + "If you delete this client, all associated data will be removed.", + continueButtonLabel: "Delete", + onConfirm: action("confirm"), + onCancel: action("cancel"), + }); + return ( + <> + + + + ); +}; + +export const Api = () => ;