All files / components/ButtonGroup ButtonGroup.jsx

100% Statements 13/13
100% Branches 2/2
100% Functions 1/1
100% Lines 13/13
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 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136                  134x   134x                 134x                                                                                       268x                 2x 2x                 2x 1x     2x                 10x   10x         10x           21x                                                    
import _ from 'lodash';
import Button from '../Button/Button';
import React from 'react';
import PropTypes from 'prop-types';
import { lucidClassNames } from '../../util/style-helpers';
import { createClass, findTypes, omitProps } from '../../util/component-types';
import reducers from './ButtonGroup.reducers';
import { buildHybridComponent } from '../../util/state-management';
 
const cx = lucidClassNames.bind('&-ButtonGroup');
 
const { any, func, arrayOf, number } = PropTypes;
 
/**
 *
 * {"categories": ["controls", "buttons"], "madeFrom": ["Button"]}
 *
 * Button groups allow you to pair buttons together to form a seamless cluster.
 * Any props not explicitly called out are spread on to the root component.
 */
const ButtonGroup = createClass({
	displayName: 'ButtonGroup',
 
	components: {
		/**
		 * Renders a `<Button`> inside the `ButtonGroup`.
		 */
		Button: createClass({
			displayName: 'ButtonGroup.Button',
		}),
	},
 
	reducers: reducers,
 
	propTypes: {
		/**
		 * A function that is called with the index of the child button clicked.
		 * `props` refers to the child button props.
		 *
		 * Signature: `(selectedIndex, { event, props }) => {}`
		 */
		onSelect: func,
 
		/**
		 * Appended to the component-specific class names set on the root
		 * element. Value is run through the `classnames` library.
		 */
		className: any,
 
		/**
		 * All children should be `ButtonGroup.Button`s and they support the same
		 * props as `Button`s.
		 */
		children: any,
 
		/**
		 * An array of currently selected `ButtonGroup.Button`s indices. You can
		 * also pass the prop `isActive` to individual `ButtonGroup.Button`
		 * components.
		 */
		selectedIndices: arrayOf(number),
	},
 
	getDefaultProps() {
		return {
			onSelect: _.noop,
			className: null,
			children: null,
			selectedIndices: [],
		};
	},
 
	handleSelect({ event, props: childProps }) {
		const { callbackId } = childProps;
		const clickedButtonProps = _.get(
			findTypes(this.props, ButtonGroup.Button)[callbackId],
			'props',
			{}
		);
 
		// If the consumer passed in an `onClick` to the child `ButtonGroup.Button`
		// component, we should make sure to call that in addition to the
		// `ButtonGroup`'s `onSelect`.
		if (_.isFunction(clickedButtonProps.onClick)) {
			clickedButtonProps.onClick({ event, props: childProps });
		}
 
		this.props.onSelect(callbackId, { event, props: childProps });
	},
 
	render() {
		const {
			selectedIndices,
			className,
			children,
			...passThroughs
		} = this.props;
 
		const buttonChildProps = _.map(
			findTypes(this.props, ButtonGroup.Button),
			'props'
		);
 
		return (
			<span
				{...omitProps(passThroughs, ButtonGroup)}
				className={cx('&', className)}
			>
				{_.map(buttonChildProps, (buttonChildProp, index) => {
					return (
						// The order of the spread operator below is important. If the
						// consumer puts `isActive` directly on a `ButtonGroup.Button`, we
						// want that to take precedence over the `selectedIndices` prop on
						// the parent `ButtonGroup`. However, we want our `onClick` at the
						// bottom because we manually handle passing the event to the
						// `ButtonGroup.Button`'s `onClick` if it exists.
						(
							<Button
								isActive={_.includes(selectedIndices, index)}
								{...buttonChildProp}
								key={index}
								callbackId={index}
								onClick={this.handleSelect}
							/>
						)
					);
				})}
				{children}
			</span>
		);
	},
});
 
export default buildHybridComponent(ButtonGroup);
export { ButtonGroup as ButtonGroupDumb };