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 134 135 136 137            134x 134x                                     134x                                                                         268x               36x       9x                   49x   49x                                                                             19x   19x 14x 14x            
import _ from 'lodash';
import React from 'react';
import PropTypes from 'prop-types';
import { lucidClassNames } from '../../util/style-helpers';
import { createClass, omitProps } from '../../util/component-types';
 
const cx = lucidClassNames.bind('&-RadioButton');
const { bool, func, object, string } = 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}
				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;