-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #112 from reactioncommerce/toasts
feat(toast): Toast component
- Loading branch information
Showing
12 changed files
with
671 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import React from "react"; | ||
import PropTypes from "prop-types"; | ||
import { Snackbar } from "@material-ui/core"; | ||
import { ToastWrapper } from "./helpers"; | ||
|
||
/** | ||
* @name Toast | ||
* @param {Object} props Component props | ||
* @returns {React.Component} returns a React component | ||
*/ | ||
const Toast = React.forwardRef(function Toast(props, ref) { | ||
const { message, variant, title, onClose, ...otherProps } = props; | ||
|
||
return ( | ||
<Snackbar | ||
ref={ref} | ||
{...otherProps} | ||
> | ||
<ToastWrapper | ||
props={otherProps} | ||
variant={variant} | ||
title={title} | ||
message={message} | ||
onClose={onClose} | ||
/> | ||
</Snackbar> | ||
); | ||
}); | ||
|
||
|
||
Toast.propTypes = { | ||
/** | ||
* Message: Node, <span>Message goes here</span> | ||
*/ | ||
message: PropTypes.node, | ||
/** | ||
* onClose: Callback fired when the component requests to be closed and when the X icon button is clicked | ||
*/ | ||
onClose: PropTypes.func.isRequired, | ||
/** | ||
* Title: Optional string, displayed in bold | ||
*/ | ||
title: PropTypes.string, | ||
/** | ||
* Variant: Info, Success, Warning, Error | ||
*/ | ||
variant: PropTypes.oneOf(["error", "info", "success", "warning"]).isRequired | ||
}; | ||
|
||
Toast.defaultProps = { | ||
variant: "info" | ||
}; | ||
|
||
export default Toast; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,204 @@ | ||
### Overview | ||
|
||
Toasts are used to give action-based feedback messages and convey critical or informational account-related messages. Use Toasts when a user needs more detailed information for an action. | ||
|
||
The Toast component inherits from the Material-UI [Snackbar component](https://material-ui.com/components/snackbars/). Refer to the Material-UI [Snackbar docs](https://material-ui.com/api/snackbar/) for more information. | ||
|
||
### Usage | ||
|
||
Toasts are most often used when the user has taken an action. Messages appear in context and communicate when that action is successful, unsuccessful, or that it otherwise needs attention and further context. | ||
|
||
Language should be polite, clear and concise. Optionally, a title can be added to a Toast to give clarity, or when there are 2 or more lines of information to display. | ||
|
||
Toasts should guide the user into taking corrective action if necessary. | ||
|
||
Users should be able to dismiss Toasts when appropriate. Information and success alerts can close automatically after 10 seconds. Error alerts should be persistent, and close only when action is resolved. | ||
|
||
#### Types | ||
|
||
##### Default: Information | ||
|
||
- Used when there is information or tips that users can benefit from | ||
- Can close automatically after 10 seconds | ||
|
||
```jsx | ||
import Button from "../Button"; | ||
import IconButton from "@material-ui/core/IconButton"; | ||
|
||
function OpenToast(props) { | ||
const [open, setOpen] = React.useState(false); | ||
|
||
const handleClose = (event, reason) => { | ||
if (reason === 'clickaway') { | ||
return; | ||
} | ||
|
||
setOpen(false); | ||
}; | ||
|
||
return ( | ||
<div> | ||
<Button variant="contained" color="primary" onClick={() => setOpen(true)}>Open information toast</Button> | ||
<Button variant="outlined" color="primary" onClick={() => setOpen(false)}>Close information toast</Button> | ||
<Toast | ||
open={open} | ||
onClose={handleClose} | ||
message={props.message} | ||
variant={props.variant} | ||
title={props.title} | ||
/> | ||
</div> | ||
); | ||
} | ||
|
||
<OpenToast message={<span>Information toast</span>} /> | ||
``` | ||
##### Success | ||
|
||
- Used when an action has been completed successfully | ||
- Can close automatically after 10 seconds | ||
|
||
```jsx | ||
import Button from "../Button"; | ||
import IconButton from "@material-ui/core/IconButton"; | ||
|
||
function OpenToast(props) { | ||
const [open, setOpen] = React.useState(false); | ||
|
||
const handleClose = (event, reason) => { | ||
if (reason === 'clickaway') { | ||
return; | ||
} | ||
|
||
setOpen(false); | ||
}; | ||
|
||
return ( | ||
<div> | ||
<Button variant="contained" color="primary" onClick={() => setOpen(true)}>Open success toast</Button> | ||
<Button variant="outlined" color="primary" onClick={() => setOpen(false)}>Close success toast</Button> | ||
<Toast | ||
open={open} | ||
onClose={handleClose} | ||
message={props.message} | ||
variant={props.variant} | ||
title={props.title} | ||
/> | ||
</div> | ||
); | ||
} | ||
|
||
<OpenToast message={<span>Success toast</span>} variant="success" /> | ||
``` | ||
|
||
##### Warning | ||
|
||
- Used when an action or item needs attention | ||
- Should not close automatically, unless the action has been resolved | ||
|
||
```jsx | ||
import Button from "../Button"; | ||
import IconButton from "@material-ui/core/IconButton"; | ||
|
||
function OpenToast(props) { | ||
const [open, setOpen] = React.useState(false); | ||
|
||
const handleClose = (event, reason) => { | ||
if (reason === 'clickaway') { | ||
return; | ||
} | ||
|
||
setOpen(false); | ||
}; | ||
|
||
return ( | ||
<div> | ||
<Button variant="contained" color="primary" onClick={() => setOpen(true)}>Open warning toast</Button> | ||
<Button variant="outlined" color="primary" onClick={() => setOpen(false)}>Close warning toast</Button> | ||
<Toast | ||
open={open} | ||
onClose={handleClose} | ||
message={props.message} | ||
variant={props.variant} | ||
title={props.title} | ||
/> | ||
</div> | ||
); | ||
} | ||
|
||
<OpenToast message={<span>Warning toast</span>} variant="warning" /> | ||
``` | ||
##### Error | ||
|
||
- Used when the system has failed to complete an action, or the user has made an error | ||
- Should not close automatically, unless the action has been resolved | ||
|
||
```jsx | ||
import Button from "../Button"; | ||
import IconButton from "@material-ui/core/IconButton"; | ||
|
||
function OpenToast(props) { | ||
const [open, setOpen] = React.useState(false); | ||
|
||
const handleClose = (event, reason) => { | ||
if (reason === 'clickaway') { | ||
return; | ||
} | ||
|
||
setOpen(false); | ||
}; | ||
|
||
return ( | ||
<div> | ||
<Button variant="contained" color="error" onClick={() => setOpen(true)}>Open error toast</Button> | ||
<Button variant="outlined" color="error" onClick={() => setOpen(false)}>Close error toast</Button> | ||
<Toast | ||
open={open} | ||
onClose={handleClose} | ||
message={props.message} | ||
variant={props.variant} | ||
title={props.title} | ||
/> | ||
</div> | ||
); | ||
} | ||
|
||
<OpenToast message={<span>Error toast</span>} variant="error" /> | ||
``` | ||
|
||
#### Toasts with Titles | ||
|
||
- All toasts can also have a `Title` | ||
|
||
```jsx | ||
import Button from "../Button"; | ||
import IconButton from "@material-ui/core/IconButton"; | ||
|
||
function OpenToast(props) { | ||
const [open, setOpen] = React.useState(false); | ||
|
||
const handleClose = (event, reason) => { | ||
if (reason === 'clickaway') { | ||
return; | ||
} | ||
|
||
setOpen(false); | ||
}; | ||
|
||
return ( | ||
<div> | ||
<Button variant="contained" color="primary" onClick={() => setOpen(true)}>Open title toast</Button> | ||
<Button variant="outlined" color="primary" onClick={() => setOpen(false)}>Close title toast</Button> | ||
<Toast | ||
open={open} | ||
onClose={handleClose} | ||
message={props.message} | ||
variant={props.variant} | ||
title={props.title} | ||
/> | ||
</div> | ||
); | ||
} | ||
|
||
<OpenToast message={<span>Information toast with title</span>} title="Title" /> | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import React from "react"; | ||
import { render } from "../../tests/index.js"; | ||
import Toast from "./Toast"; | ||
|
||
test("basic snapshot - only default props", () => { | ||
const { asFragment } = render(<Toast message="Test message" open/>); | ||
expect(asFragment()).toMatchSnapshot(); | ||
}); | ||
|
||
test("basic snapshot - information variant", () => { | ||
const { asFragment } = render(<Toast message="Test message" variant="info" open/>); | ||
expect(asFragment()).toMatchSnapshot(); | ||
}); | ||
|
||
test("basic snapshot - success variant", () => { | ||
const { asFragment } = render(<Toast message="Test message" variant="success" open/>); | ||
expect(asFragment()).toMatchSnapshot(); | ||
}); | ||
|
||
test("basic snapshot - error variant", () => { | ||
const { asFragment } = render(<Toast message="Test message" variant="error" open/>); | ||
expect(asFragment()).toMatchSnapshot(); | ||
}); | ||
|
||
test("basic snapshot - warning variant", () => { | ||
const { asFragment } = render(<Toast message="Test message" variant="warning" open/>); | ||
expect(asFragment()).toMatchSnapshot(); | ||
}); |
Oops, something went wrong.