Repository URL to install this package:
|
Version:
8.1.0-rc.5 ▾
|
import React, { Component } from 'react';
import classnames from 'classnames';
import PropTypes from 'prop-types';
// Unique id for a11y labels
let uniqueNotificationIdCounter = 0;
const nextUniqueNotificationId = () => ++uniqueNotificationIdCounter; // eslint-disable-line no-plusplus
class Notification extends Component {
static propTypes = {
/** Content of notification */
children: PropTypes.node.isRequired,
/** Add custom actions to the Notification */
actions: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
/** Notification type, defaults to success */
type: PropTypes.oneOf(['error', 'success', 'notice', 'neutral']),
/** For visually "docking" the notifications to a sibing container */
docked: PropTypes.oneOf(['top', 'bottom']),
/** If true, the Notification will auto close after the `autoCloseTimeout` has expired, then calls the `onAutoClose` callback, if supplied */
autoClose: PropTypes.bool,
/** After this timeout, when `autoClose` is true, the notification will autoclose. */
autoCloseTimeout: PropTypes.number,
/** Callback triggered after autoclosing */
onAutoClose: PropTypes.func,
};
static defaultProps = {
type: 'success',
actions: null,
autoClose: false,
onAutoClose: null,
autoCloseTimeout: 5000,
docked: null,
};
// Notification phases: a Notification cycles through 'entering' > 'closing' > 'closed' for visual candy
static phases = {
ENTERING: 'entering',
CLOSING: 'closing',
CLOSED: 'closed',
};
state = { phase: Notification.phases.ENTERING };
componentDidMount() {
const { autoClose, autoCloseTimeout } = this.props;
if (autoClose) {
this.closeTimeoutId = setTimeout(this.close, autoCloseTimeout);
}
}
componentWillUnmount() {
clearTimeout(this.closeTimeoutId);
clearTimeout(this.closingTimeoutId);
}
close = () => {
const { autoClose, onAutoClose } = this.props;
this.setState({ phase: Notification.phases.CLOSING }, () => {
this.closingTimeoutId = setTimeout(() => {
this.setState({
phase: Notification.phases.CLOSED,
});
if (autoClose && typeof onAutoClose === 'function') {
onAutoClose();
}
}, 300);
});
};
render() {
// Some props are not used in render but we pluck them to be able to pass them down as arbitrary
// attributes to the root level DOM element and avoid warnings for unrecognized prop on DOM element
const {
children,
actions,
type,
docked,
// eslint-disable-next-line no-unused-vars
autoClose,
// eslint-disable-next-line no-unused-vars
autoCloseTimeout,
// eslint-disable-next-line no-unused-vars
onAutoClose,
...props
} = this.props;
const { phase } = this.state;
const uniqueId = nextUniqueNotificationId();
return phase === Notification.phases.CLOSED ? null : (
<div
role="alert"
aria-labelledby={`Notification-uniqueLabel${uniqueId}`}
className={classnames('Notification', `Notification--${type}`, `Notification--${phase}`, {
[`Notification--dock${docked}`]: docked,
})}
{...props}
>
<div className="Notification-content" id={`Notification-uniqueLabel${uniqueId}`}>
{children}
</div>
{actions ? (
<div className="Notification-actions">{typeof actions === 'function' ? actions() : actions}</div>
) : null}
</div>
);
}
}
export default Notification;