Skip to content

Commit eabe36f

Browse files
Seagyn Davisseagyn
authored andcommitted
fix(toast): handle animations nicely
1 parent b6dd630 commit eabe36f

File tree

3 files changed

+166
-22
lines changed

3 files changed

+166
-22
lines changed

src/toast/index.js

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,10 @@
1-
import React, { useEffect, useState } from 'react';
1+
import React from 'react';
22
import { PropTypes } from 'prop-types';
33
import { CheckIcon, ToastContainer, WarningIcon } from './style';
44

55
const Toast = ({ children, displayTime, type }) => {
6-
const [hide, setHide] = useState(false);
7-
8-
const hideToast = () => {
9-
setHide(true);
10-
};
11-
12-
useEffect(() => {
13-
setTimeout(hideToast, displayTime);
14-
});
156
return (
16-
<ToastContainer hide={hide}>
7+
<ToastContainer displayTime={displayTime}>
178
{type === 'warning' && <WarningIcon />}
189
{type === 'success' && <CheckIcon />}
1910
{children}

src/toast/style.js

Lines changed: 124 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import styled, { css } from 'styled-components';
1+
import styled, { css, keyframes } from 'styled-components';
22
import colors from '../colors';
33
import breakpoints from '../breakpoints';
44
import { CheckCircle } from 'styled-icons/material/CheckCircle';
@@ -13,8 +13,120 @@ const Icon = css`
1313
}
1414
`;
1515

16-
const HideToast = css`
17-
display: none;
16+
const fadeOut = keyframes`
17+
from {
18+
opacity: 1;
19+
}
20+
21+
to {
22+
opacity: 0;
23+
}
24+
`;
25+
26+
const slideInTop = keyframes`
27+
0% {
28+
-webkit-transform: translateY(-500px);
29+
transform: translateY(-500px);
30+
-webkit-animation-timing-function: ease-in;
31+
animation-timing-function: ease-in;
32+
opacity: 0;
33+
}
34+
38% {
35+
-webkit-transform: translateY(0);
36+
transform: translateY(0);
37+
-webkit-animation-timing-function: ease-out;
38+
animation-timing-function: ease-out;
39+
opacity: 1;
40+
}
41+
55% {
42+
-webkit-transform: translateY(-65px);
43+
transform: translateY(-65px);
44+
-webkit-animation-timing-function: ease-in;
45+
animation-timing-function: ease-in;
46+
}
47+
72% {
48+
-webkit-transform: translateY(0);
49+
transform: translateY(0);
50+
-webkit-animation-timing-function: ease-out;
51+
animation-timing-function: ease-out;
52+
}
53+
81% {
54+
-webkit-transform: translateY(-28px);
55+
transform: translateY(-28px);
56+
-webkit-animation-timing-function: ease-in;
57+
animation-timing-function: ease-in;
58+
}
59+
90% {
60+
-webkit-transform: translateY(0);
61+
transform: translateY(0);
62+
-webkit-animation-timing-function: ease-out;
63+
animation-timing-function: ease-out;
64+
}
65+
95% {
66+
-webkit-transform: translateY(-8px);
67+
transform: translateY(-8px);
68+
-webkit-animation-timing-function: ease-in;
69+
animation-timing-function: ease-in;
70+
}
71+
100% {
72+
-webkit-transform: translateY(0);
73+
transform: translateY(0);
74+
-webkit-animation-timing-function: ease-out;
75+
animation-timing-function: ease-out;
76+
}
77+
`;
78+
79+
const slideInRight = keyframes`
80+
0% {
81+
-webkit-transform: translateX(600px);
82+
transform: translateX(600px);
83+
-webkit-animation-timing-function: ease-in;
84+
animation-timing-function: ease-in;
85+
opacity: 0;
86+
}
87+
38% {
88+
-webkit-transform: translateX(0);
89+
transform: translateX(0);
90+
-webkit-animation-timing-function: ease-out;
91+
animation-timing-function: ease-out;
92+
opacity: 1;
93+
}
94+
55% {
95+
-webkit-transform: translateX(64px);
96+
transform: translateX(64px);
97+
-webkit-animation-timing-function: ease-in;
98+
animation-timing-function: ease-in;
99+
}
100+
72% {
101+
-webkit-transform: translateX(0);
102+
transform: translateX(0);
103+
-webkit-animation-timing-function: ease-out;
104+
animation-timing-function: ease-out;
105+
}
106+
81% {
107+
-webkit-transform: translateX(24px);
108+
transform: translateX(24px);
109+
-webkit-animation-timing-function: ease-in;
110+
animation-timing-function: ease-in;
111+
}
112+
90% {
113+
-webkit-transform: translateX(0);
114+
transform: translateX(0);
115+
-webkit-animation-timing-function: ease-out;
116+
animation-timing-function: ease-out;
117+
}
118+
95% {
119+
-webkit-transform: translateX(8px);
120+
transform: translateX(8px);
121+
-webkit-animation-timing-function: ease-in;
122+
animation-timing-function: ease-in;
123+
}
124+
100% {
125+
-webkit-transform: translateX(0);
126+
transform: translateX(0);
127+
-webkit-animation-timing-function: ease-out;
128+
animation-timing-function: ease-out;
129+
}
18130
`;
19131

20132
export const CheckIcon = styled(CheckCircle)`
@@ -45,12 +157,19 @@ export const ToastContainer = styled.div`
45157
right: 0;
46158
top: 0;
47159
48-
${({ hide }) => hide && HideToast}
49-
50160
@media (min-width: ${breakpoints.small}) {
161+
animation: ${slideInRight} 1.1s cubic-bezier(0.25, 0.46, 0.45, 0.94) both,
162+
${fadeOut} 0.5s linear ${({ displayTime }) => `${displayTime}ms`} 1
163+
forwards;
51164
bottom: 0;
52165
font-size: 16px;
53166
left: auto;
54167
top: auto;
55168
}
169+
170+
@media (max-width: ${breakpoints.small}) {
171+
animation: ${slideInTop} 1.1s cubic-bezier(0.25, 0.46, 0.45, 0.94) both,
172+
${fadeOut} 0.5s linear ${({ displayTime }) => `${displayTime}ms`} 1
173+
forwards;
174+
}
56175
`;

stories/toast.js

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,54 @@
1-
import React from 'react';
1+
import React, { useState, useEffect } from 'react';
2+
import { PropTypes } from 'prop-types';
23
import { storiesOf } from '@storybook/react';
34
import { text, select, number } from '@storybook/addon-knobs';
4-
import Toast from '../src/toast';
5+
import { Button, Toast } from '../src';
56

67
const toastVariants = {
78
Success: 'success',
89
Warning: 'warning'
910
};
1011

12+
const ToastContainer = ({ displayTime, text, type }) => {
13+
const [show, setShow] = useState(false);
14+
15+
useEffect(() => {
16+
if (show) {
17+
setTimeout(() => {
18+
setShow(false);
19+
}, displayTime + 1000); // NB if you change the fadeout time you need to change this to accommodate
20+
}
21+
}, [show]);
22+
return (
23+
<>
24+
<Button
25+
onClick={() => {
26+
setShow(true);
27+
}}
28+
>
29+
Open Toast
30+
</Button>
31+
{show && (
32+
<Toast displayTime={displayTime} type={type}>
33+
{text}
34+
</Toast>
35+
)}
36+
</>
37+
);
38+
};
39+
40+
ToastContainer.propTypes = {
41+
text: PropTypes.string.isRequired,
42+
displayTime: PropTypes.number,
43+
type: PropTypes.oneOf(['success', 'warning'])
44+
};
45+
1146
storiesOf('Toast', module).add('Toast', () => {
1247
return (
13-
<Toast
48+
<ToastContainer
1449
displayTime={number('Display Time', 5000)}
1550
type={select('Type', toastVariants, 'success')}
16-
>
17-
{text('Message', 'This is a message!')}
18-
</Toast>
51+
text={text('Message', 'This is a message!')}
52+
/>
1953
);
2054
});

0 commit comments

Comments
 (0)