initial version of confirm dialog (#117)

* initial version of confirm dialog

* introduced helper funciton to toggle dialog

* review
This commit is contained in:
Erik Jan de Wit 2020-09-25 15:17:55 +02:00 committed by GitHub
parent 7161af82b8
commit 8210ba5ba0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 554 additions and 0 deletions

View file

@ -5,6 +5,7 @@
"save": "Save", "save": "Save",
"cancel": "Cancel", "cancel": "Cancel",
"continue": "Continue",
"delete": "Delete", "delete": "Delete",
"next": "Next", "next": "Next",
"back": "Back", "back": "Back",

View file

@ -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 = () => (
<ConfirmDialogModal
key="confirmDialog"
{...props}
open={show}
toggleDialog={toggleDialog}
/>
);
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 (
<Modal
title={t(titleKey)}
isOpen={open}
onClose={toggleDialog}
variant={ModalVariant.small}
actions={[
<Button
id="modal-confirm"
key="confirm"
variant={continueButtonVariant || ButtonVariant.primary}
onClick={() => {
onConfirm();
toggleDialog();
}}
>
{t(continueButtonLabel || "common:continue")}
</Button>,
<Button
id="modal-cancel"
key="cancel"
variant={ButtonVariant.secondary}
onClick={() => {
if (onCancel) onCancel();
toggleDialog();
}}
>
{t(cancelButtonLabel || "common:cancel")}
</Button>,
]}
>
{!messageKey && children}
{messageKey && t(messageKey)}
</Modal>
);
};

View file

@ -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 (
<>
<button id="show" onClick={toggle}>
Show
</button>
<ConfirmDialog />
</>
);
};
const simple = mount(<Test />);
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();
});
});

View file

@ -0,0 +1,356 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Confirmation dialog renders simple confirm dialog 1`] = `
<Test>
<button
id="show"
onClick={[Function]}
>
Show
</button>
<Dialog>
<ConfirmDialogModal
continueButtonLabel="Delete"
key="confirmDialog"
messageKey="If you delete this client, all associated data will be removed."
onConfirm={[MockFunction]}
open={true}
titleKey="Delete app02?"
toggleDialog={[Function]}
>
<Modal
actions={
Array [
<Button
id="modal-confirm"
onClick={[Function]}
variant="primary"
>
Delete
</Button>,
<Button
id="modal-cancel"
onClick={[Function]}
variant="secondary"
>
cancel
</Button>,
]
}
appendTo={[Function]}
aria-describedby=""
aria-label=""
aria-labelledby=""
className=""
hasNoBodyWrapper={false}
isOpen={true}
onClose={[Function]}
ouiaSafe={true}
showClose={true}
title="Delete app02?"
variant="small"
>
<Portal
containerInfo={
<div>
<div
class="pf-c-backdrop"
>
<div
class="pf-l-bullseye"
>
<div
aria-describedby="pf-modal-part-3"
aria-labelledby="pf-modal-part-2"
aria-modal="true"
class="pf-c-modal-box pf-m-sm"
data-ouia-component-id="OUIA-Generated-Modal-small-2"
data-ouia-component-type="PF4/ModalContent"
data-ouia-safe="true"
id="pf-modal-part-1"
role="dialog"
>
<button
aria-disabled="false"
aria-label="Close"
class="pf-c-button pf-m-plain"
data-ouia-component-id="OUIA-Generated-Button-plain-1"
data-ouia-component-type="PF4/Button"
data-ouia-safe="true"
type="button"
>
<svg
aria-hidden="true"
fill="currentColor"
height="1em"
role="img"
style="vertical-align: -0.125em;"
viewBox="0 0 352 512"
width="1em"
>
<path
d="M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z"
/>
</svg>
</button>
<header
class="pf-c-modal-box__header"
>
<h1
class="pf-c-modal-box__title pf-c-modal-box__title"
id="pf-modal-part-2"
>
Delete app02?
</h1>
</header>
<div
class="pf-c-modal-box__body"
id="pf-modal-part-3"
>
If you delete this client, all associated data will be removed.
</div>
<footer
class="pf-c-modal-box__footer"
>
<button
aria-disabled="false"
class="pf-c-button pf-m-primary"
data-ouia-component-id="OUIA-Generated-Button-primary-1"
data-ouia-component-type="PF4/Button"
data-ouia-safe="true"
id="modal-confirm"
type="button"
>
Delete
</button>
<button
aria-disabled="false"
class="pf-c-button pf-m-secondary"
data-ouia-component-id="OUIA-Generated-Button-secondary-1"
data-ouia-component-type="PF4/Button"
data-ouia-safe="true"
id="modal-cancel"
type="button"
>
cancel
</button>
</footer>
</div>
</div>
</div>
</div>
}
>
<ModalContent
actions={
Array [
<Button
id="modal-confirm"
onClick={[Function]}
variant="primary"
>
Delete
</Button>,
<Button
id="modal-cancel"
onClick={[Function]}
variant="secondary"
>
cancel
</Button>,
]
}
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"
>
<Backdrop>
<div
className="pf-c-backdrop"
>
<FocusTrap
active={true}
className="pf-l-bullseye"
focusTrapOptions={
Object {
"clickOutsideDeactivates": true,
}
}
paused={false}
>
<div
className="pf-l-bullseye"
>
<ModalBox
aria-describedby="pf-modal-part-3"
aria-label=""
aria-labelledby="pf-modal-part-2"
className=""
data-ouia-component-id="OUIA-Generated-Modal-small-2"
data-ouia-component-type="PF4/ModalContent"
data-ouia-safe={true}
id="pf-modal-part-1"
style={Object {}}
variant="small"
>
<div
aria-describedby="pf-modal-part-3"
aria-label={null}
aria-labelledby="pf-modal-part-2"
aria-modal="true"
className="pf-c-modal-box pf-m-sm"
data-ouia-component-id="OUIA-Generated-Modal-small-2"
data-ouia-component-type="PF4/ModalContent"
data-ouia-safe={true}
id="pf-modal-part-1"
role="dialog"
style={Object {}}
>
<ModalBoxCloseButton
onClose={[Function]}
>
<Button
aria-label="Close"
className=""
onClick={[Function]}
variant="plain"
>
<button
aria-disabled={false}
aria-label="Close"
className="pf-c-button pf-m-plain"
data-ouia-component-id="OUIA-Generated-Button-plain-1"
data-ouia-component-type="PF4/Button"
data-ouia-safe={true}
disabled={false}
onClick={[Function]}
type="button"
>
<TimesIcon
color="currentColor"
noVerticalAlign={false}
size="sm"
>
<svg
aria-hidden={true}
aria-labelledby={null}
fill="currentColor"
height="1em"
role="img"
style={
Object {
"verticalAlign": "-0.125em",
}
}
viewBox="0 0 352 512"
width="1em"
>
<path
d="M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z"
/>
</svg>
</TimesIcon>
</button>
</Button>
</ModalBoxCloseButton>
<ModalBoxHeader>
<header
className="pf-c-modal-box__header"
>
<ModalBoxTitle
className="pf-c-modal-box__title"
id="pf-modal-part-2"
title="Delete app02?"
>
<h1
className="pf-c-modal-box__title pf-c-modal-box__title"
id="pf-modal-part-2"
>
Delete app02?
</h1>
</ModalBoxTitle>
</header>
</ModalBoxHeader>
<ModalBoxBody
id="pf-modal-part-3"
>
<div
className="pf-c-modal-box__body"
id="pf-modal-part-3"
>
If you delete this client, all associated data will be removed.
</div>
</ModalBoxBody>
<ModalBoxFooter>
<footer
className="pf-c-modal-box__footer"
>
<Button
id="modal-confirm"
key="confirm"
onClick={[Function]}
variant="primary"
>
<button
aria-disabled={false}
aria-label={null}
className="pf-c-button pf-m-primary"
data-ouia-component-id="OUIA-Generated-Button-primary-1"
data-ouia-component-type="PF4/Button"
data-ouia-safe={true}
disabled={false}
id="modal-confirm"
onClick={[Function]}
type="button"
>
Delete
</button>
</Button>
<Button
id="modal-cancel"
key="cancel"
onClick={[Function]}
variant="secondary"
>
<button
aria-disabled={false}
aria-label={null}
className="pf-c-button pf-m-secondary"
data-ouia-component-id="OUIA-Generated-Button-secondary-1"
data-ouia-component-type="PF4/Button"
data-ouia-safe={true}
disabled={false}
id="modal-cancel"
onClick={[Function]}
type="button"
>
cancel
</button>
</Button>
</footer>
</ModalBoxFooter>
</div>
</ModalBox>
</div>
</FocusTrap>
</div>
</Backdrop>
</ModalContent>
</Portal>
</Modal>
</ConfirmDialogModal>
</Dialog>
</Test>
`;

View file

@ -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<ConfirmDialogModalProps> = (args) => (
<ConfirmDialogModal {...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: (
<>
<TextContent>
<Text component={TextVariants.h3}>Hello World</Text>
</TextContent>
<p>Example of some other patternfly components.</p>
</>
),
};
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 (
<>
<button id="show" onClick={toggle}>
Show
</button>
<Dialog />
</>
);
};
export const Api = () => <Test />;