All files / boundless/packages/boundless-button index.js

91.67% Statements 11/12
87.5% Branches 7/8
87.5% Functions 7/8
100% Lines 10/10
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116                                                                                                                                                      15x       5x   5x 5x       13x   11x   11x     10x 10x         35x                                
import React, {PropTypes} from 'react';
import cx from 'classnames';
 
import omit from 'boundless-utils-omit-keys';
 
/**
__A control with "pressed" state support.__
 
Button has two modes of operation:
 
1. stateless (like a normal `<button>`)
   ```jsx
   <Button onPressed={doSomething}>foo</Button>
   ```
 
   > Note that instead of `onClick`, Button uses `onPressed`. This is because the component also listens for keyboard <kbd>Enter</kbd> events, so `onClick` only tells half the story. You can still bind to that particular React event though if there's a need to tell the difference between clicks and Enter presses.
 
2. stateful (like a toggle, e.g. bold-mode in a rich text editor)
 
   "stateful" mode is triggered by passing a boolean to `props.pressed`. This enables the button to act like a controlled component. The `onUnpressed` event callback will also now be fired when appropriate.
 
   ```jsx
   <Button
       pressed={true}
       onPressed={doSomething}
       onUnpressed={doSomethingElse}>
       foo
   </Button>
   ```
 */
export default class Button extends React.PureComponent {
    static propTypes = {
        /**
         * any [React-supported attribute](https://facebook.github.io/react/docs/tags-and-attributes.html#html-attributes)
         */
        '*': PropTypes.any,
 
        /**
         * Any valid HTML tag name or a ReactComponent, anything that can be passed as the
         * first argument to `React.createElement`; note that this component sets the `role` and `aria-checked`
         * attributes so non-`<button>` elements will still behave like a button for screen readers
         */
        component: PropTypes.oneOfType([
            PropTypes.string,
            PropTypes.func,
        ]),
 
        /**
         * use this callback instead of `onClick` (it's `onClick` + `onKeyDown:Enter`); fires for both button modes
         */
        onPressed: PropTypes.func,
 
        /**
         * called when the element becomes "unpressed"; only fires when the Button is in stateful mode
         */
        onUnpressed: PropTypes.func,
 
        /**
         * passthrough to `aria-pressed`; using this prop turns on the `onUnpressed` callback when applicable
         */
        pressed: PropTypes.bool,
    }
 
    static defaultProps = {
        component: 'button',
        onClick: () => {},
        onKeyDown: () => {},
        onPressed: () => {},
        onUnpressed: () => {},
        pressed: undefined,
    }
 
    static internalKeys = Object.keys(Button.defaultProps)
 
    fireStatefulCallback(event) {
        this.props[this.props.pressed ? 'onUnpressed' : 'onPressed'](event);
    }
 
    handleClick = (event) => {
        Iif (this.props.disabled) { return; }
 
        this.props.onClick(event);
        this.fireStatefulCallback(event);
    }
 
    handleKeyDown = (event) => {
        if (this.props.disabled) { return; }
 
        this.props.onKeyDown(event);
 
        switch (event.key) {
        case 'Enter':
        case 'Space':
            event.preventDefault();
            this.fireStatefulCallback(event);
        }
    }
 
    render() {
        return (
            <this.props.component
                {...omit(this.props, Button.internalKeys)}
                className={cx('b-button', this.props.className, {
                    'b-button-pressable': this.props.pressed !== undefined,
                    'b-button-pressed': this.props.pressed,
                })}
                aria-pressed={this.props.pressed}
                role='button'
                onKeyDown={this.handleKeyDown}
                onClick={this.handleClick}>
                {this.props.children}
            </this.props.component>
        );
    }
}