All files / components/RadioButton RadioButton.jsx

100% Statements 12/12
100% Branches 4/4
100% Functions 0/0
100% Lines 12/12
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          120x           120x                                     120x                                                                         120x               51x       9x                   64x   64x                                                             34x   34x 24x 24x            
import _ from 'lodash';
import React from 'react';
import { lucidClassNames } from '../../util/style-helpers';
import { createClass, omitProps } from '../../util/component-types';
 
const cx = lucidClassNames.bind('&-RadioButton');
const {
	bool,
	func,
	object,
	string,
} = React.PropTypes;
 
/**
 * {"categories": ["controls", "toggles"]}
 *
 * This is a toggle -- a component that is in one of two particular states at
 * any given moment in time -- that features a custom visualization of the
 * native radio button control to reflect its current state.
 *
 * The radio button is different from a standard toggle in that when it is in
 * the selected state user events do not cause it to toggle to the unselected
 * state so the `onSelect` function is called only when `isSelected` is false.
 *
 * It uses a hidden native radio button control under the hood but leverages
 * other HTML elements to visualize its state.
 *
 * Any props that are not explicitly defined in `propTypes` are spread onto the
 * native control.
 */
const RadioButton = createClass({
	displayName: 'RadioButton',
	propTypes: {
		/**
		 * Appended to the component-specific class names set on the root
		 * element.
		 */
		className: string,
 
		/**
		 * Indicates whether the component should appear and act disabled by
		 * having a "greyed out" palette and ignoring user interactions.
		 */
		isDisabled: bool,
 
		/**
		 * Indicates that the component is in the "selected" state when true
		 * and in the "unselected" state when false.
		 */
		isSelected: bool,
 
		/**
		 * Called when the user clicks on the component or when they press the
		 * space key while the component is in focus, and only called when the
		 * component is in the unselected state.
		 *
		 * Signature: `(true, { event, props }) => {}`
		 */
		onSelect: func,
 
		/**
		 * Passed through to the root element.
		 */
		style: object,
	},
 
	getDefaultProps() {
		return {
			isDisabled: false,
			isSelected: false,
			onSelect: _.noop,
		};
	},
 
	componentDidMount() {
		this.nativeElement = this.refs.nativeElement;
	},
 
	handleSpanClick(e) {
		e.preventDefault();
	},
 
	render() {
		const {
			className,
			isDisabled,
			isSelected,
			style,
			...passThroughs,
		} = this.props;
 
		return (
			<span
					className={cx('&', {
						'&-is-disabled': isDisabled,
						'&-is-selected': isSelected,
					}, className)}
					onClick={this.handleClicked}
					onTouchEnd={this.handleClicked}
					style={style}
			>
				<input
						onChange={_.noop}
						{...omitProps(passThroughs, RadioButton, ['children', 'callbackId'])}
						checked={isSelected}
						className={cx('&-native')}
						disabled={isDisabled}
						ref='nativeElement'
						type='radio'
				/>
				<span onClick={this.handleSpanClick} className={cx('&-visualization-glow')} />
				<span onClick={this.handleSpanClick} className={cx('&-visualization-container')} />
				<span onClick={this.handleSpanClick} className={cx('&-visualization-dot')} />
			</span>
		);
	},
 
	handleClicked(event) {
		const {
			isDisabled,
			isSelected,
			onSelect,
		} = this.props;
 
		if (!isDisabled && !isSelected) {
			onSelect(true, { event, props: this.props });
			this.nativeElement.focus();
		}
	},
});
 
export default RadioButton;