-
Notifications
You must be signed in to change notification settings - Fork 296
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* toast component * added tests Co-authored-by: Omri Avigdor <[email protected]>
- Loading branch information
Showing
9 changed files
with
410 additions
and
2 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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,158 @@ | ||
import NOOP from "lodash/noop"; | ||
import React, { useMemo, useCallback, useRef } from "react"; | ||
import PropTypes from "prop-types"; | ||
import cx from "classnames"; | ||
import { CSSTransition } from "react-transition-group"; | ||
import Button from "../Button/Button"; | ||
import Icon from "../Icon/Icon"; | ||
import Check from "../Icon/Icons/components/Check"; | ||
import Alert from "../Icon/Icons/components/Alert"; | ||
import Info from "../Icon/Icons/components/Info"; | ||
import CloseSmall from "../Icon/Icons/components/CloseSmall"; | ||
import { TOAST_TYPES } from "./ToastConstants"; | ||
import "./Toast.scss"; | ||
|
||
const defaultIconMap = { | ||
[TOAST_TYPES.NORMAL]: Info, | ||
[TOAST_TYPES.POSITIVE]: Check, | ||
[TOAST_TYPES.NEGATIVE]: Alert | ||
}; | ||
|
||
const getIcon = (type, icon) => { | ||
/* icon may be node a may be a string */ | ||
if (icon && typeof icon === "object") { | ||
return icon; | ||
} | ||
return icon || defaultIconMap[type] ? ( | ||
<Icon | ||
iconType={icon ? Icon.type.ICON_FONT : Icon.type.SVG} | ||
clickable={false} | ||
icon={icon || defaultIconMap[type]} | ||
iconSize="24px" | ||
ignoreFocusStyle | ||
/> | ||
) : null; | ||
}; | ||
|
||
const Toast = ({ | ||
open, | ||
autoHideDuration, | ||
type, | ||
icon, | ||
hideIcon, | ||
action, | ||
children, | ||
closeable, | ||
onClose | ||
}) => { | ||
const classNames = useMemo( | ||
() => cx("monday-style-toast", `monday-style-toast--type-${type}`), | ||
[type] | ||
); | ||
const handleClose = useCallback(() => { | ||
if (onClose) { | ||
onClose(); | ||
} | ||
}, [onClose]); | ||
/* Timer */ | ||
const timerAutoHide = useRef(); | ||
const setAutoHideTimer = useCallback( | ||
duration => { | ||
if (!onClose || duration == null) { | ||
return; | ||
} | ||
|
||
clearTimeout(timerAutoHide.current); | ||
timerAutoHide.current = setTimeout(() => { | ||
handleClose(null, "timeout"); | ||
}, duration); | ||
}, | ||
[handleClose, onClose] | ||
); | ||
React.useEffect(() => { | ||
if (open && autoHideDuration > 0) { | ||
setAutoHideTimer(autoHideDuration); | ||
} | ||
|
||
return () => { | ||
clearTimeout(timerAutoHide.current); | ||
}; | ||
}, [open, autoHideDuration, setAutoHideTimer]); | ||
const iconElement = !hideIcon && getIcon(type, icon); | ||
|
||
return ( | ||
<CSSTransition | ||
in={open} | ||
classNames="monday-style-toast-animation" | ||
timeout={400} | ||
unmountOnExit | ||
> | ||
<div className={classNames} role="alert" aria-live="polite"> | ||
{iconElement && ( | ||
<div className="monday-style-toast-icon">{iconElement}</div> | ||
)} | ||
<div | ||
className={cx("monday-style-toast-content", { | ||
"monday-style-toast-content-no-icon": !iconElement | ||
})} | ||
> | ||
{children} | ||
</div> | ||
{action && <div className="monday-style-toast-action">{action}</div>} | ||
{closeable && ( | ||
<Button | ||
onClick={handleClose} | ||
size={Button.sizes.SMALL} | ||
kind={Button.kinds.TERTIARY} | ||
color={Button.colors.ON_PRIMARY_COLOR} | ||
ariaLabel="close-toast" | ||
> | ||
<Icon | ||
iconType={Icon.type.SVG} | ||
clickable={false} | ||
icon={CloseSmall} | ||
iconSize="20px" | ||
ignoreFocusStyle | ||
/> | ||
</Button> | ||
)} | ||
</div> | ||
</CSSTransition> | ||
); | ||
}; | ||
|
||
Toast.propTypes = { | ||
/** If true, Toast is open (visible) */ | ||
open: PropTypes.bool, | ||
type: PropTypes.oneOf([ | ||
TOAST_TYPES.NORMAL, | ||
TOAST_TYPES.POSITIVE, | ||
TOAST_TYPES.NEGATIVE | ||
]), | ||
/** Possible to override the dafult icon */ | ||
icon: PropTypes.oneOfType([PropTypes.string, PropTypes.element]), | ||
/** If true, won't show the icon */ | ||
hideIcon: PropTypes.bool, | ||
/** The action to display */ | ||
action: PropTypes.element, | ||
/** If false, won't show the close button */ | ||
closeable: PropTypes.bool, | ||
onClose: PropTypes.func, | ||
/** The number of milliseconds to wait before | ||
* automatically closing the Toast | ||
* (0 or null cancels this behaviour) */ | ||
autoHideDuration: PropTypes.number | ||
}; | ||
|
||
Toast.defaultProps = { | ||
type: TOAST_TYPES.NORMAL, | ||
open: false, | ||
action: null, | ||
hideIcon: false, | ||
icon: null, | ||
closeable: true, | ||
autoHideDuration: null, | ||
onClose: NOOP | ||
}; | ||
|
||
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,103 @@ | ||
@import "../../styles/global-css-settings.scss"; | ||
@import "../../styles/typography.scss"; | ||
@import "../../styles/themes.scss"; | ||
|
||
.monday-style-toast { | ||
@include font-paragraph(); | ||
@include box-shadow-medium(); | ||
@include theme-prop(color, text-color-on-primary); | ||
margin: $spacing-medium; | ||
position: fixed; | ||
top: 0; | ||
left: 50%; | ||
right: auto; | ||
transform: translate(-50%,0); | ||
padding: $spacing-small; | ||
align-items: center; | ||
display: flex; | ||
min-width: 390px; | ||
border-radius: $border-radius-small; | ||
|
||
&-icon { | ||
display: flex; | ||
padding-left: $spacing-small; | ||
padding-right: $spacing-small; | ||
} | ||
|
||
&-content { | ||
line-height: 22px; | ||
font-size: 14px; | ||
flex-grow: 1; | ||
&-no-icon { | ||
padding-left: $spacing-small; | ||
} | ||
} | ||
|
||
&-action { | ||
padding-right: $spacing-small; | ||
} | ||
|
||
&--type { | ||
&-normal { | ||
@include theme-prop(background-color, primary-color); | ||
} | ||
&-positive { | ||
@include theme-prop(background-color, positive-color); | ||
} | ||
&-negative { | ||
@include theme-prop(background-color, negative-color); | ||
} | ||
} | ||
|
||
&-animation { | ||
&-enter-active, &-exit-active { | ||
animation-iteration-count: 1; | ||
animation-direction: forwards; | ||
animation-fill-mode: forwards; | ||
} | ||
&-enter-active { | ||
animation-duration: 400ms; | ||
animation-name: toast-pop-in; | ||
} | ||
|
||
&-exit-active { | ||
animation-duration: 160ms; | ||
animation-name: toast-pop-out; | ||
} | ||
} | ||
} | ||
|
||
@keyframes toast-pop-in { | ||
0% { | ||
transform: translate(-50%,-115px); | ||
} | ||
25% { | ||
// down 130 px | ||
transform: translate(-50%,15px); | ||
animation-timing-function: cubic-bezier(0,0,0.35,1); | ||
} | ||
|
||
66% { | ||
// up 20 px | ||
transform: translate(-50%,-5px); | ||
animation-timing-function: cubic-bezier(0.4,0,1,1); | ||
} | ||
|
||
100% { | ||
// down 5 px | ||
transform: translate(-50%,0px); | ||
animation-timing-function: cubic-bezier(0.4,0,1,1); | ||
} | ||
} | ||
|
||
|
||
@keyframes toast-pop-out { | ||
0% { | ||
transform: translate(-50%,0); | ||
} | ||
100% { | ||
// up 130 px | ||
transform: translate(-50%,-130px); | ||
animation-timing-function: cubic-bezier(0,0,0.35,1); | ||
} | ||
} |
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,5 @@ | ||
export const TOAST_TYPES = { | ||
NORMAL: "normal", | ||
POSITIVE: "positive", | ||
NEGATIVE: "negative" | ||
}; |
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,76 @@ | ||
import { useState } from "react"; | ||
import { number, select, boolean } from "@storybook/addon-knobs"; | ||
import { TOAST_TYPES } from "../ToastConstants"; | ||
import Toast from "../Toast"; | ||
import Button from "../../Button/Button"; | ||
import Icon from "../../Icon/Icon"; | ||
import Send from "../../Icon/Icons/components/Send"; | ||
import { StoryStateColumn, StoryStateRow } from "../../storybook-helpers"; | ||
|
||
export const Toasts = () => { | ||
const sendIconElement = ( | ||
<Icon | ||
iconType={Icon.type.SVG} | ||
clickable={false} | ||
icon={Send} | ||
iconSize="20px" | ||
ignoreFocusStyle | ||
/> | ||
); | ||
const knobs = { | ||
type: select("type", { | ||
Normal: TOAST_TYPES.NORMAL, | ||
Positive: TOAST_TYPES.POSITIVE, | ||
Negative: TOAST_TYPES.NEGATIVE, | ||
}), | ||
closeable: boolean("closeable", true), | ||
autoHideDuration: number("autoHideDuration", 0), | ||
icon: select("icon", { | ||
default: null, | ||
star: "fa fa-star", | ||
send: "send" | ||
}), | ||
hideIcon: boolean("hideIcon", false) | ||
}; | ||
const [toastOpen, setToastOpen] = useState(false); | ||
const toggleToast = () => setToastOpen(open => !open); | ||
const closeToast = () => setToastOpen(false); | ||
let icon = knobs.icon; | ||
if (knobs.icon === "send") { | ||
icon = sendIconElement; | ||
} | ||
return ( | ||
<section> | ||
<StoryStateRow> | ||
<StoryStateColumn> | ||
<Button onClick={() => toggleToast()}>Toggle Toast!</Button> | ||
<Toast | ||
open={toastOpen} | ||
onClose={() => closeToast()} | ||
action={( | ||
<Button | ||
size={Button.sizes.SMALL} | ||
kind={Button.kinds.SECONDARY} | ||
color={Button.colors.ON_PRIMARY_COLOR} | ||
> | ||
Undo 5 | ||
</Button> | ||
)} | ||
type={knobs.type} | ||
icon={icon} | ||
closeable={knobs.closeable} | ||
autoHideDuration={knobs.autoHideDuration} | ||
hideIcon={knobs.hideIcon} | ||
> | ||
Something Happened | ||
</Toast> | ||
</StoryStateColumn> | ||
</StoryStateRow> | ||
</section> | ||
); | ||
}; | ||
|
||
export default { | ||
title: "Components|Toast", | ||
component: Toast | ||
}; |
Oops, something went wrong.