Skip to content

Commit

Permalink
Merge pull request #151 from reactioncommerce/feat-150-mikemurray-con…
Browse files Browse the repository at this point in the history
…firm-hook

feat: Add useComfirmDialog hook
  • Loading branch information
willopez authored Feb 24, 2020
2 parents 88575a8 + 38c700b commit 131456e
Show file tree
Hide file tree
Showing 9 changed files with 535 additions and 52 deletions.
79 changes: 29 additions & 50 deletions package/src/components/ConfirmDialog/ConfirmDialog.js
Original file line number Diff line number Diff line change
@@ -1,63 +1,29 @@
import React, { Fragment, useState } from "react";
import React, { Fragment } from "react";
import PropTypes from "prop-types";
import Button from "@material-ui/core/Button";
import Dialog from "@material-ui/core/Dialog";
import DialogActions from "@material-ui/core/DialogActions";
import DialogContent from "@material-ui/core/DialogContent";
import DialogContentText from "@material-ui/core/DialogContentText";
import DialogTitle from "../DialogTitle";
import useConfirmDialog from "./helpers/useConfirmDialog";

/**
* @name ConfirmDialog
* @param {Object} props Component props
* @returns {React.Component} A React component
*/
const ConfirmDialog = React.forwardRef(function ConfirmDialog(props, ref) {
const { children, title, message, confirmActionText, cancelActionText, onConfirm } = props;
const [isOpen, setIsOpen] = useState(false);

const handleClose = () => {
setIsOpen(false);
};
const { children, ...otherProps } = props;
const {
isOpen,
openDialog,
closeDialog,
ConfirmDialog: ConfirmDialogComponent
} = useConfirmDialog({ ...otherProps });

return (
<Fragment>
{children({
openDialog: () => {
setIsOpen(true);
}
closeDialog,
isOpen,
openDialog
})}
<Dialog
aria-labelledby="confirm-action-dialog-title"
maxWidth="sm"
fullWidth={true}
onClose={handleClose}
open={isOpen}
ref={ref}
>
<DialogTitle id="confirm-action-dialog-title">{title}</DialogTitle>
{message && (
<DialogContent>
<DialogContentText>{message}</DialogContentText>
</DialogContent>
)}

<DialogActions>
<Button onClick={handleClose} color="primary" variant="outlined">
{cancelActionText}
</Button>
<Button
onClick={() => {
onConfirm();
setIsOpen(false);
}}
color="primary"
variant="contained"
>
{confirmActionText}
</Button>
</DialogActions>
</Dialog>
<ConfirmDialogComponent ref={ref} />
</Fragment>
);
});
Expand All @@ -69,17 +35,29 @@ ConfirmDialog.propTypes = {
*/
cancelActionText: PropTypes.string,
/**
* Render prop `{({ openDialog }) => ()}`
* Render prop `{({ closeDialog, isOpen, openDialog }) => ()}`
*/
children: PropTypes.func.isRequired,
children: PropTypes.func,
/**
* Text for confirm button
*/
confirmActionText: PropTypes.string,
/**
* Message body. May be a string or a React component.
* Child elements of the dialog. Use if this for rendering a custom components in the dialog.
*/
content: PropTypes.element,
/**
* Dialog open/close state
*/
isOpen: PropTypes.bool,
/**
* Message body. May be a string or a React component. Use if your message is mostly text.
*/
message: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
/**
* Close callback
*/
onClose: PropTypes.func,
/**
* Confirmation callback
*/
Expand All @@ -93,6 +71,7 @@ ConfirmDialog.propTypes = {
ConfirmDialog.defaultProps = {
cancelActionText: "Cancel",
confirmActionText: "OK",
onClose() { },
onConfirm() { }
};

Expand Down
101 changes: 100 additions & 1 deletion package/src/components/ConfirmDialog/ConfirmDialog.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,105 @@ The `ConfirmDialog` component uses [Dialog](https://material-ui.com/api/dialog),

The `ConfirmDialog` component provides a standard confirmation alert base on some user action. Pass a callback function to `onConfirm()` to allow the user to take an action after clicking the confirmation button.


#### Open Confirm dialog with a hook

This example shows how you can use the can use the confirm dialog with the `useConfirmDialog` hook. This method can also be used to open the dialog when there is no direct user interaction.

The hook `useConfirmDialog` accepts some of the same props as the `ConfirmDialog` component. The usage of the `useConfirmDialog` hook, and the available props are outlined in the examples below.

```js static
const {
isOpen, // Boolean: Open state
openDialog, // Function: Open dialog
closeDialog, // Function: Close dialog
ConfirmDialog // React.Element: Render the dialog. Must be place somewhere in your rendered component.
} = useConfirmDialog({
/* Available props */
cancelActionText: "Cancel", // Optional: Default `Cancel`
closeOnConfirm: true, // Optional: Default `true`. Should the dialog close on confirm.
confirmActionText: "Ok", // Optional: Default `OK`
content: <div></div>, // Content of the dialog. May be used alongside `message`
message: "", // Text content of the dialog. May be used alongside `content`
onClose: () => {}, // Callback on close. This is called after the dialog has been closed.
onConfirm: () => {}, // Callback on confirmation.
title // Title of the dialog.
});
```

```jsx
import Button from "../Button";
import useConfirmDialog from "./helpers/useConfirmDialog"

function MyComponent() {
const {
isOpen, // Boolean: Open state
openDialog, // Function: Open dialog
closeDialog, // Function: Close dialog
ConfirmDialog // React.Element: Render the dialog. Add somewhere in your component
} = useConfirmDialog({ // ConfirmDialog props (see above example)
title: "Archive 24 products?",
message: "Archiving products removes them from both admin and customer views.",
onConfirm: () => {
alert("Action confirmed!")
},
onClose: () => {
console.log("Dialog closed")
}
})

return (
<>
<Button color="primary" onClick={openDialog} variant="contained">Open Confirm Dialog</Button>
<ConfirmDialog />
</>
)
}

MyComponent();
```

#### Confirm dialog with additional content

```jsx
import { Avatar, List, ListItem, ListItemText, ListItemAvatar } from "@material-ui/core";
import Button from "../Button";
import useConfirmDialog from "./helpers/useConfirmDialog"

function MyComponent() {
const { ConfirmDialog, openDialog } = useConfirmDialog({
title: "Update account permissions?",
message: "Are you sure you want to update the permissions the following account(s)?",
content: (
<List>
<ListItem>
<ListItemAvatar>
<Avatar>RC</Avatar>
</ListItemAvatar>
<ListItemText>
Reaction Commerce
</ListItemText>
</ListItem>
</List>
),
onConfirm: () => {
alert("Action confirmed!")
}
})

return (
<>
<Button color="primary" onClick={openDialog} variant="contained">Open Confirm Dialog</Button>
<ConfirmDialog />
</>
)
}

MyComponent();
```

#### Basic usage

```jsx
import Button from "../Button";

Expand All @@ -18,4 +117,4 @@ import Button from "../Button";
<Button color="primary" onClick={openDialog} variant="contained">Open Confirm Dialog</Button>
)}
</ConfirmDialog>
```
```
60 changes: 60 additions & 0 deletions package/src/components/ConfirmDialog/ConfirmDialog.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from "react";
import { render, fireEvent } from "../../tests/";
import Button from "../Button";
import ConfirmDialog from "./ConfirmDialog";
import useConfirmDialog from "./helpers/useConfirmDialog";

test("basic snapshot - with opening the dialog", () => {
/* eslint-disable function-paren-newline */
Expand All @@ -23,3 +24,62 @@ test("basic snapshot - with opening the dialog", () => {
expect(getByRole("dialog")).toMatchSnapshot();
expect(asFragment()).toMatchSnapshot();
});


test("basic snapshot - with opening the dialog using the useConfirmDialog hook", () => {
// eslint-disable-next-line require-jsdoc
function TestComponent() {
const { openDialog, ConfirmDialog: ConfirmDialogComponent } = useConfirmDialog({
title: "Are you sure?",
message: "Are you sure you want to do that?"
});

return (
<>
<Button color="primary" onClick={openDialog} variant="contained">Open Confirm Dialog</Button>
<ConfirmDialogComponent />
</>
);
}

const { asFragment, getByText, getByRole } = render(<TestComponent />);

fireEvent.click(getByText("Open Confirm Dialog"));
expect(getByRole("dialog")).toBeInTheDocument();
expect(getByRole("dialog")).toHaveTextContent("Are you sure you want to do that?");
expect(getByRole("dialog")).toHaveTextContent("Are you sure?");
expect(getByRole("dialog")).toHaveTextContent("OK");
expect(getByRole("dialog")).toHaveTextContent("Cancel");
expect(getByRole("dialog")).toMatchSnapshot();
expect(asFragment()).toMatchSnapshot();
});

test("basic snapshot - with opening the dialog using the useConfirmDialog hook with more content", () => {
// eslint-disable-next-line require-jsdoc, react/no-multi-comp
function TestComponent() {
const { openDialog, ConfirmDialog: ConfirmDialogComponent } = useConfirmDialog({
title: "Are you sure?",
content: <span>More content</span>,
message: "Are you sure you want to do that?"
});

return (
<>
<Button color="primary" onClick={openDialog} variant="contained">Open Confirm Dialog</Button>
<ConfirmDialogComponent />
</>
);
}

const { asFragment, getByText, getByRole } = render(<TestComponent />);

fireEvent.click(getByText("Open Confirm Dialog"));
expect(getByRole("dialog")).toBeInTheDocument();
expect(getByRole("dialog")).toHaveTextContent("Are you sure you want to do that?");
expect(getByRole("dialog")).toHaveTextContent("More content");
expect(getByRole("dialog")).toHaveTextContent("Are you sure?");
expect(getByRole("dialog")).toHaveTextContent("OK");
expect(getByRole("dialog")).toHaveTextContent("Cancel");
expect(getByRole("dialog")).toMatchSnapshot();
expect(asFragment()).toMatchSnapshot();
});
Loading

0 comments on commit 131456e

Please sign in to comment.