@salesforce/design-system-react
Version:
Salesforce Lightning Design System for React
273 lines (252 loc) • 7.15 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 */
// Implements the [Button Stateful design pattern](https://lightningdesignsystem.com/components/buttons/#flavor-stateful) in React.
// Based on SLDS v2.1.1
// ## Dependencies
// ### React
import React from 'react';
import PropTypes from 'prop-types';
// ### classNames
import classNames from 'classnames';
// ### isBoolean
import isBoolean from 'lodash.isboolean';
// ### isFunction
import isFunction from 'lodash.isfunction';
// ## Children
import ButtonIcon from '../icon/button-icon';
import { BUTTON_STATEFUL } from '../../utilities/constants';
const propTypes = {
/**
* Specifies the current state of the button. If set, the button will act as a ['controlled' component](https://facebook.github.io/react/docs/forms.html#controlled-components).
*/
active: PropTypes.bool,
/**
* Text that is visually hidden but read aloud by screenreaders to tell the user what the icon means. This should also include the state of the button.
* If the button has an icon and a visible label, you can omit the <code>assistiveText</code> prop and use the <code>label</code> prop.
*/
assistiveText: PropTypes.string,
/**
* Disables the button and adds disabled styling.
*/
disabled: PropTypes.bool,
/**
* Name of the icon. Visit <a href='http://www.lightningdesignsystem.com/resources/icons'>Lightning Design System Icons</a> to reference icon names.
*/
iconName: PropTypes.string,
/**
* Determines the size of the icon.
*/
iconSize: PropTypes.oneOf(['x-small', 'small', 'medium', 'large']),
/**
* If true, button/icon is white. Meant for buttons or utility icons on dark backgrounds.
*/
inverse: PropTypes.bool,
/**
* Triggered when focus is removed.
*/
onBlur: PropTypes.func,
/**
* Triggered when the button is clicked.
*/
onClick: PropTypes.func,
/**
* Triggered when component is focused.
*/
onFocus: PropTypes.func,
/**
* Triggered when a key is pressed down
*/
onKeyDown: PropTypes.func,
/**
* Triggered when a key is pressed and released
*/
onKeyPress: PropTypes.func,
/**
* Triggered when a key is released
*/
onKeyUp: PropTypes.func,
/**
* Triggered when a mouse button is pressed down
*/
onMouseDown: PropTypes.func,
/**
* Triggered when a mouse arrow hovers
*/
onMouseEnter: PropTypes.func,
/**
* Triggered when a mouse arrow no longer hovers
*/
onMouseLeave: PropTypes.func,
/**
* If true, button scales to 100% width on small form factors.
*/
responsive: PropTypes.bool,
/**
* Initial label and icon (optional) of button.
*/
stateOne: PropTypes.object,
/**
* Selected label and icon (optional) of button.
*/
stateTwo: PropTypes.object,
/**
* Deselect label and icon (optional) of button.
*/
stateThree: PropTypes.object,
/**
* Write "-1" if you don't want the user to tab to the button.
*/
tabIndex: PropTypes.string,
/**
* [Deprecated] Tooltip on button. Button should be a child of `Tooltip` instead.
*/
tooltip: PropTypes.node,
/**
* Different types of buttons
*/
variant: PropTypes.oneOf(['base', 'neutral', 'brand', 'destructive', 'icon']),
};
// i18n
const defaultProps = {
disabled: false,
iconSize: 'medium',
responsive: false,
stateOne: { iconName: 'add', label: 'Follow' },
stateTwo: { iconName: 'check', label: 'Following' },
stateThree: { iconName: 'close', label: 'Unfollow' },
};
/**
* The ButtonStateful component is a variant of the Lightning Design System Button component. It is used for buttons that have a state of unselected or selected.
* For icon buttons, use <code>variant='icon'</code>. For buttons with labels or buttons with labels and icons, pass data to the state props (ie. <code>stateOne={{iconName: 'add', label: 'Join'}}</code>).
*/
class ButtonStateful extends React.Component {
constructor (props) {
super(props);
this.state = { active: false };
}
getClassName (active) {
return classNames(this.props.className, 'slds-button', {
'slds-button--neutral': this.props.variant !== 'icon',
'slds-button--inverse': this.props.variant === 'inverse',
'slds-not-selected': !active,
'slds-is-selected': active,
'slds-max-small-button--stretch': this.props.responsive,
'slds-button--icon-border': this.props.variant === 'icon',
});
}
handleBlur = (e) => {
if (this.props.onBlur) this.props.onBlur(e);
e.currentTarget.blur();
};
handleClick = (e) => {
if (isFunction(this.props.onClick)) this.props.onClick(e);
if (!isBoolean(this.props.active)) {
this.setState({ active: !this.state.active });
}
};
render () {
const {
active,
assistiveText,
disabled,
iconName,
iconSize,
id,
onFocus,
onKeyDown,
onKeyPress,
onKeyUp,
onMouseDown,
onMouseEnter,
// onMouseLeave, // This prop isn't used anywhere! But removing it would be a breaking change
stateOne,
stateTwo,
stateThree,
tabIndex,
variant,
} = this.props;
const isActive = isBoolean(active) ? active : this.state.active;
if (variant === 'icon') {
return (
<button
aria-live="polite"
className={this.getClassName(isActive)}
disabled={disabled}
id={id}
onBlur={this.handleBlur}
onClick={this.handleClick}
onFocus={onFocus}
onKeyDown={onKeyDown}
onKeyPress={onKeyPress}
onKeyUp={onKeyUp}
onMouseDown={onMouseDown}
onMouseEnter={onMouseEnter}
onMouseLeave={this.handleBlur}
tabIndex={tabIndex}
>
<ButtonIcon
disabled={disabled}
name={iconName}
size={iconSize}
className="slds-button__icon--stateful"
/>
{assistiveText ? (
<span className="slds-assistive-text">{assistiveText}</span>
) : null}
</button>
);
}
return (
<button
aria-live="assertive"
className={this.getClassName(isActive)}
disabled={disabled}
id={id}
onBlur={this.handleBlur}
onClick={this.handleClick}
onFocus={onFocus}
onKeyDown={onKeyDown}
onKeyPress={onKeyPress}
onKeyUp={onKeyUp}
onMouseEnter={onMouseEnter}
onMouseLeave={this.handleBlur}
tabIndex={tabIndex}
>
<span className="slds-text-not-selected">
<ButtonIcon
disabled={disabled}
name={stateOne.iconName}
size="small"
position="left"
className="slds-button__icon--stateful"
/>
{stateOne.label}
</span>
<span className="slds-text-selected">
<ButtonIcon
disabled={disabled}
name={stateTwo.iconName}
size="small"
position="left"
className="slds-button__icon--stateful"
/>
{stateTwo.label}
</span>
<span className="slds-text-selected-focus">
<ButtonIcon
disabled={disabled}
name={stateThree.iconName}
size="small"
position="left"
className="slds-button__icon--stateful"
/>
{stateThree.label}
</span>
</button>
);
}
}
ButtonStateful.displayName = BUTTON_STATEFUL;
ButtonStateful.propTypes = propTypes;
ButtonStateful.defaultProps = defaultProps;
export default ButtonStateful;