UNPKG

optimizely-oui

Version:

Optimizely's Component Library.

190 lines (177 loc) 5.61 kB
import React from "react"; import PropTypes from "prop-types"; import classNames from "classnames"; import Spinner from "../Spinner"; import Icon from "react-oui-icons"; /** * @param {Object} props - Properties passed to component * @returns {ReactElement} */ const Button = ({ ariaLabel, ariaHasPopup, buttonRef, children, className, isActive, isDisabled, isLink, isLoading, isSubmit, leftIcon, loadingText, onBlur, onClick, onMouseDown, rightIcon, size, style, testSection, title, width = "default", ...props }) => { const buttonClassNames = classNames("oui-button", { [`oui-button--${style}`]: style, [`oui-button--${size}`]: size, [`oui-button--${width}`]: width, "is-active": isActive, "oui-button--loading": isLoading, "flex--inline flex-justified--center flex-align--center": leftIcon || rightIcon, }, className); const type = isSubmit ? "submit" : "button"; function handleOnClick(event) { if (isDisabled || isLoading) { return; } onClick(event); } const leftIconClasses = classNames("flex", "flex-self--center", { "push--right": size !== "small", "push-half--right": size === "small", }); const leftIconComp = leftIcon ? ( <div className={leftIconClasses}> <Icon name={leftIcon} size={size === "large" ? "medium" : "small"} /> </div> ) : ( "" ); const rightIconClasses = classNames("flex", "flex-self--center", { "push--left": size !== "small", "push-half--left": size === "small", }); const rightIconComp = rightIcon ? ( <div className={rightIconClasses}> <Icon name={rightIcon} size={size === "large" ? "medium" : "small"} /> </div> ) : ( "" ); if (isLink) { return ( <div data-oui-component={true} className={buttonClassNames} disabled={isDisabled} onBlur={onBlur} data-test-section={testSection} ref={buttonRef} {...props} > {leftIcon && leftIconComp} {children} {rightIcon && rightIconComp} </div> ); } return ( <button data-oui-component={true} className={buttonClassNames} disabled={isDisabled || isLoading} type={type} onBlur={onBlur} onClick={handleOnClick} onMouseDown={onMouseDown} data-test-section={testSection} aria-label={ariaLabel} aria-haspopup={ariaHasPopup} aria-live="polite" title={title} ref={buttonRef} {...props} > {isLoading ? <Spinner data-test-section="button-spinner" size="tiny" /> : leftIcon && leftIconComp} {isLoading ? loadingText || "Processing" : children} {!isLoading && rightIcon && rightIconComp} </button> ); }; Button.propTypes = { /** Should be true if this button displays a menu or tooltip */ ariaHasPopup: PropTypes.bool, /** Describes buttons that have an icon but no text */ ariaLabel: PropTypes.string, /** React ref to the underlying button component */ buttonRef: PropTypes.oneOfType([PropTypes.func, PropTypes.shape({ current: PropTypes.instanceOf(Element) })]), /** Text within the button */ children: PropTypes.node.isRequired, /** CSS class names. */ className: PropTypes.string, /** Render button with active state */ isActive: PropTypes.bool, /** Prevent users from interacting with the button */ isDisabled: PropTypes.bool, /** Changes the button to a div for insertion within a Link component */ isLink: PropTypes.bool, /** When true, adds a spinner to the button and disables the button */ isLoading: PropTypes.bool, /** Make the button act as a submit button */ isSubmit: PropTypes.bool, /** Icon to display on the left */ leftIcon: PropTypes.node, /** When the button adds a spinner, it displays this text */ loadingText: PropTypes.string, /** Function that fires when the button loses focus */ onBlur: PropTypes.func, /** Function that fires when the button is clicked on */ onClick: PropTypes.func, /** Function that fires when the button is mouse downed */ onMouseDown: PropTypes.func, /** Icon to display on the right */ rightIcon: PropTypes.node, /** Various height and width options */ size: PropTypes.oneOf(["tiny", "small", "large", "narrow", "tight"]), /** Various color options */ style: PropTypes.oneOf([ "highlight", "danger", "danger-outline", "outline", "outline-reverse", "plain", "toggle", "underline", "unstyled", ]), /** Hook for automated JavaScript tests */ testSection: PropTypes.string, /** Title of the button shown as tooltip text when the button is hovered */ title: PropTypes.string, /** Various height and width options */ width: PropTypes.oneOf(["default", "full"]), }; Button.defaultProps = { isLink: false, isLoading: false, isDisabled: false, isSubmit: false, loadingText: "", onBlur: () => null, onClick: () => null, onMouseDown: () => null, width: "default", }; Button.displayName = "Button"; export default Button;