@salesforce/design-system-react
Version:
Salesforce Lightning Design System for React
248 lines (220 loc) • 6.43 kB
JSX
/* Copyright (c) 2015-present, salesforce.com, inc. All rights reserved */
/* Licensed under BSD 3-Clause - see LICENSE.txt or git.io/sfdc-license */
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import Button from '../button';
import Icon from '../icon';
import checkProps from './check-props';
const displayName = 'Notification';
const propTypes = {
iconCategory: PropTypes.string,
/**
* Custom classes applied to Notification element.
*/
className: PropTypes.string,
/**
* Message for Notification.
*/
content: PropTypes.node.isRequired,
/**
* If true, close button appears for users to dismiss Notification.
*/
dismissible: PropTypes.bool,
/**
* If duration exists, the Notification will disappear after that amount of time.
*/
duration: PropTypes.number,
/**
* Name of the icon. Visit <a href='http://www.lightningdesignsystem.com/resources/icons'>Lighning Design System Icons</a> to reference icon names.
*/
iconName: PropTypes.string,
isOpen: PropTypes.bool.isRequired,
onDismiss: PropTypes.func,
/**
* Styling for Notification background.
*/
texture: PropTypes.bool,
/**
* Styling for Notification background color. Please reference <a href='http://www.lightningdesignsystem.com/components/utilities/themes/#color'>Lighning Design System Themes > Color</a>.
*/
theme: PropTypes.oneOf(['success', 'warning', 'error', 'offline']),
variant: PropTypes.oneOf(['alert', 'toast']).isRequired,
};
const defaultProps = {
iconCategory: 'utility',
dismissible: true,
isOpen: false,
texture: false,
};
/**
* ** Notification is deprecated. Please use an Alert and Toast instead.**
* The Notification component is the Alert and Toast variants of the Lightning Design System Notification component. For prompt notifications, use the <a href='#/modal'>Modal</a> component with <code>prompt={true}</code>.
* The Notification opens from a state change outside of the component itself (pass this state to the <code>isOpen</code> prop).
*/
class Notification extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.timeout = null;
}
componentDidMount() {
checkProps('Notification', this.props);
if (this.props.duration) {
this.timeout = setTimeout(() => {
this.onDismiss();
}, this.props.duration);
}
}
// eslint-disable-next-line camelcase, react/sort-comp
UNSAFE_componentWillReceiveProps(nextProps) {
if (nextProps.duration) {
if (this.timeout) {
clearTimeout(this.timeout);
}
if (nextProps.isOpen) {
this.timeout = setTimeout(() => {
this.onDismiss();
}, this.props.duration);
}
}
if (nextProps.isOpen !== this.props.isOpen) {
this.setState({ returnFocusTo: document.activeElement });
}
}
componentDidUpdate(prevProps) {
if (prevProps.isOpen !== this.props.isOpen) {
const btn = this.dismissBtnRef;
if (btn) btn.focus();
}
}
onDismiss = () => {
if (this.timeout) {
clearTimeout(this.timeout);
this.timeout = null;
}
if (this.props.onDismiss) this.props.onDismiss();
if (this.state.returnFocusTo && this.state.returnFocusTo.focus) {
this.state.returnFocusTo.focus();
}
};
getClassName() {
return classNames(this.props.className, 'slds-notify', {
[`slds-notify_${this.props.variant}`]: this.props.variant,
[`slds-theme_${this.props.theme}`]: this.props.theme,
'slds-theme_alert-texture': this.props.texture,
});
}
/*
* The parent container with role='alert' only announces its content if there is a change inside of it.
* Because React renders the entire element to the DOM, we must switch out a blank div for the real content.
* Bummer, I know.
*/
// eslint-disable-next-line class-methods-use-this
blankContent() {
return <div />;
}
renderAlertContent() {
return (
<h2 id="dialogTitle">
{this.renderIcon()}
{this.props.content}
</h2>
);
}
renderClose() {
if (this.props.dismissible) {
let size = null;
if (this.props.variant === 'toast') size = 'large';
// i18n
return (
<Button
assistiveText={{ icon: 'Dismiss Notification' }}
iconCategory="utility"
iconName="close"
iconSize={size}
inverse
className="slds-notify__close"
onClick={this.onDismiss}
buttonRef={(dismissBtn) => {
this.dismissBtnRef = dismissBtn;
}}
variant="icon"
/>
);
}
return null;
}
renderContent() {
return (
<div>
<span className="slds-assistive-text">{this.props.theme}</span>
{this.renderClose()}
{this.props.variant === 'toast' ? this.renderToastContent() : null}
{this.props.variant === 'alert' ? this.renderAlertContent() : null}
</div>
);
}
renderIcon() {
if (this.props.iconName) {
let classes = '';
if (this.props.variant === 'alert') {
classes = 'slds-m-right_x-small';
} else if (this.props.variant === 'toast') {
classes = 'slds-m-right_small slds-col slds-no-flex';
}
return (
<Icon
category={this.props.iconCategory}
className={classes}
inverse
name={this.props.iconName}
size="small"
/>
);
}
return null;
}
renderToastContent() {
return (
<section className="notify__content slds-grid">
{this.renderIcon()}
<div className="slds-col slds-align-middle">
<h2 id="dialogTitle" className="slds-text-heading_small">
{this.props.content}
</h2>
</div>
</section>
);
}
render() {
// TODO: If there are multiple notifications on a page, we must 'hide' the ones that aren't open.
// Need to find a better way to do this than using width:0 to override slds-notify-container.
let styles;
if (!this.props.isOpen) {
styles = { width: '0px' };
} else {
styles =
this.props.variant === 'toast'
? { width: 'auto', left: '50%', transform: 'translateX(-50%)' }
: { width: '100%' };
}
const alertStyles = !this.props.isOpen ? { display: 'none' } : null;
return (
<div className="slds-notify-container" style={styles}>
<div
className={this.getClassName()}
role="alertdialog"
aria-labelledby="dialogTitle"
style={alertStyles}
>
{this.props.isOpen ? this.renderContent() : this.blankContent()}
</div>
</div>
);
}
}
Notification.displayName = displayName;
Notification.propTypes = propTypes;
Notification.defaultProps = defaultProps;
export default Notification;