Added notifications. Closes #101

- Using react-notifications to manage redux state
- Refactored Toast component to be stateless
- Toasts can be stacked
- Cleaned up CSS
- Updated stories
This commit is contained in:
Andrey Okonetchnikov
2016-10-17 12:35:31 +02:00
parent 863d90c8ee
commit f3b448106d
9 changed files with 126 additions and 109 deletions

View File

@ -2,10 +2,13 @@
--defaultColor: #333;
--defaultColorLight: #eee;
--backgroundColor: #fff;
--shadowColor: rgba(0, 0, 0, 0.117647);
--shadowColor: rgba(0, 0, 0, .25);
--infoColor: #69c;
--successColor: #1c7;
--warningColor: #fa0;
--errorColor: #f52;
--borderRadius: 2px;
--topmostZindex: 99999;
}
.base {
@ -13,14 +16,14 @@
}
.container {
color: var(--defaultColor);
background-color: var(--backgroundColor);
color: var(--defaultColor);
}
.rounded {
border-radius: 2px;
border-radius: var(--borderRadius);
}
.depth {
box-shadow: var(--shadowColor) 0px 1px 6px, var(--shadowColor) 0px 1px 4px;
box-shadow: var(--shadowColor) 0 1px 6px;
}

View File

@ -1,40 +1,41 @@
@import "../theme.css";
@import '../theme.css';
.toast {
composes: base container rounded depth;
position: absolute;
top: 10px;
right: 10px;
z-index: 100;
width: 350px;
padding: 20px 10px;
font-size: 0.9rem;
text-align: center;
color: var(--defaultColorLight);
overflow: hidden;
opacity: 1;
transition: opacity .3s ease-in;
:root {
--iconSize: 30px;
}
.hidden {
opacity: 0;
.root {
composes: base container rounded depth from '../theme.css';
overflow: hidden;
margin: 10px;
padding: 10px 10px 15px;
color: var(--defaultColorLight);
}
.icon {
position: absolute;
top: calc(50% - 15px);
left: 15px;
font-size: 30px;
position: relative;
top: .15em;
margin-right: .25em;
font-size: var(--iconSize);
line-height: var(--iconSize);
}
.info {
composes: root;
background-color: var(--infoColor);
}
.success {
composes: root;
background-color: var(--successColor);
}
.warning {
composes: root;
background-color: var(--warningColor);
}
.error {
.danger {
composes: root;
background-color: var(--errorColor);
}

View File

@ -2,69 +2,23 @@ import React, { PropTypes } from 'react';
import { Icon } from '../index';
import styles from './Toast.css';
export default class Toast extends React.Component {
const icons = {
info: 'info',
success: 'check',
warning: 'attention',
danger: 'alert',
};
state = {
shown: false
};
componentWillMount() {
if (this.props.show) {
this.autoHideTimeout();
this.setState({ shown: true });
}
}
componentWillReceiveProps(nextProps) {
if (nextProps !== this.props) {
if (nextProps.show) this.autoHideTimeout();
this.setState({ shown: nextProps.show });
}
}
componentWillUnmount() {
if (this.timeOut) {
clearTimeout(this.timeOut);
}
}
autoHideTimeout = () => {
clearTimeout(this.timeOut);
this.timeOut = setTimeout(() => {
this.setState({ shown: false });
}, 4000);
};
render() {
const { style, type, className, children } = this.props;
const icons = {
success: 'check',
warning: 'attention',
error: 'alert'
};
const classes = [styles.toast];
if (className) classes.push(className);
let icon = '';
if (type) {
classes.push(styles[type]);
icon = <Icon type={icons[type]} className={styles.icon} />;
}
if (!this.state.shown) {
classes.push(styles.hidden);
}
return (
<div className={classes.join(' ')} style={style}>{icon}{children}</div>
);
}
export default function Toast({ kind, message }) {
return (
<div className={styles[kind]}>
<Icon type={icons[kind]} className={styles.icon} />
{message}
</div>
);
}
Toast.propTypes = {
style: PropTypes.object,
type: PropTypes.oneOf(['success', 'warning', 'error']).isRequired,
className: PropTypes.string,
show: PropTypes.bool,
children: PropTypes.node
kind: PropTypes.oneOf(['info', 'success', 'warning', 'danger']).isRequired,
message: PropTypes.string,
};

View File

@ -1,19 +1,41 @@
import React from 'react';
import { Toast } from '../UI';
import { storiesOf } from '@kadira/storybook';
import { Toast } from '../UI';
const containerStyle = {
position: 'fixed',
top: 0,
right: 0,
width: 360,
height: '100%',
};
storiesOf('Toast', module)
.add('All kinds stacked', () => (
<div style={containerStyle}>
<Toast kind="info" message="A Toast Message" />
<Toast kind="success" message="A Toast Message" />
<Toast kind="warning" message="A Toast Message" />
<Toast kind="danger" message="A Toast Message" />
</div>
))
.add('Info', () => (
<div style={containerStyle}>
<Toast kind="info" message="A Toast Message" />
</div>
))
.add('Success', () => (
<div>
<Toast type='success' show>A Toast Message</Toast>
<div style={containerStyle}>
<Toast kind="success" message="A Toast Message" />
</div>
)).add('Waring', () => (
<div>
<Toast type='warning' show>A Toast Message</Toast>
))
.add('Waring', () => (
<div style={containerStyle}>
<Toast kind="warning" message="A Toast Message" />
</div>
)).add('Error', () => (
<div>
<Toast type='error' show>A Toast Message</Toast>
))
.add('Error', () => (
<div style={containerStyle}>
<Toast kind="danger" message="A Toast Message" />
</div>
));