Repository URL to install this package:
|
Version:
8.1.0-rc.1 ▾
|
import React, { Component } from 'react';
import classnames from 'classnames';
import PropTypes from 'prop-types';
import IconButton from '../../controls/Button/IconButton';
import CloseIcon from '../../visuals/Icon/svg/ic_close.svg';
// unique id for a11y labels
let uniqueNagIdCounter = 0;
const nextUniqueNagId = () => ++uniqueNagIdCounter; // eslint-disable-line no-plusplus
class Nag extends Component {
static propTypes = {
/** A nag always needs a close label (as an `alertdialog` it always has a visually hidden close button) */
closeLabel: PropTypes.string.isRequired,
/** Content of notification */
children: PropTypes.node.isRequired,
/** As a function, it will be called with { dismiss: PropTypes.func } which triggers the fadeout */
actions: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
/** Nag 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']),
/** Controls wether the Nag is dismissable or autocloses: when provided with a `onClose` function the nag becomes dismissable and does not autoclose. */
onClose: PropTypes.func,
/** If true, the close button is only visible for screen readers, and after `autoCloseTimeout` ms the Nag automatically closes and calls `onAutoClose` */
autoClose: PropTypes.bool,
/** After this timeout, when there is no `onClose` provided, the nag will autoclose. */
autoCloseTimeout: PropTypes.number,
/** Callback triggered after autoclosing */
onAutoClose: PropTypes.func,
};
static defaultProps = {
type: 'success',
actions: null,
onClose: null,
autoClose: false,
onAutoClose: null,
autoCloseTimeout: 5000,
docked: null,
};
// nag phases: a nag always cycles forward through 'entering', 'closing' and 'closed' for visual candy
static phases = {
ENTERING: 'entering',
CLOSING: 'closing',
CLOSED: 'closed',
};
state = { phase: Nag.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 { onClose, autoClose, onAutoClose } = this.props;
this.setState({ phase: Nag.phases.CLOSING }, () => {
this.closingTimeoutId = setTimeout(() => {
this.setState({
phase: Nag.phases.CLOSED,
});
if (typeof onClose === 'function') {
onClose();
}
if (autoClose && typeof onAutoClose === 'function') {
onAutoClose();
}
}, 300);
});
};
render() {
// some props are not used in render but we pluck them to keep able to pass down arbitrary
// attributes to root level DOM element and avoid warnings for unrecognized prop on dom element
const {
closeLabel,
children,
actions,
type,
docked,
autoClose,
// eslint-disable-next-line no-unused-vars
onClose,
// 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 = nextUniqueNagId();
return phase === Nag.phases.CLOSED ? null : (
<div
role="alertdialog"
aria-labelledby={`Nag-uniqueLabel${uniqueId}`}
className={classnames('Nag', `Nag--${type}`, `Nag--${phase}`, { [`Nag--dock${docked}`]: docked })}
{...props}
>
<div className={classnames('Nag-closeButton', { 'u-srOnly': autoClose })}>
<IconButton icon={CloseIcon} semantic={closeLabel} variant="linkWhite" onClick={this.close} />
</div>
<div className="Nag-content" id={`Nag-uniqueLabel${uniqueId}`}>
{children}
</div>
{actions && (
<div className="Nag-actions">
{typeof actions === 'function' ? actions({ dismiss: this.close }) : actions}
</div>
)}
</div>
);
}
}
export default Nag;