react-conventions
Version:
An open source set of React components that implement Ambassador's Design and UX patterns.
170 lines (152 loc) • 4.84 kB
JavaScript
import React from 'react'
import classNames from 'classnames/bind'
import Radio from './Radio'
import style from './style.scss'
class RadioGroup extends React.Component {
constructor(props) {
super(props)
}
state = {
value: this.props.value,
options: []
}
static defaultProps = {
disabled: false,
required: false,
labelPosition: 'right'
}
static propTypes = {
/**
* Text shown above the radio group.
*/
label: React.PropTypes.string,
/**
* The name that will be applied to all radio buttons inside it.
*/
name: React.PropTypes.string.isRequired,
/**
* Whether the radio group is required.
*/
required: React.PropTypes.bool,
/**
* Whether the radio group is disabled.
*/
disabled: React.PropTypes.bool,
/**
* A list of options for the radio group.
*/
options: React.PropTypes.array,
/**
* Which option is checked.
*/
value: React.PropTypes.string,
/**
* Where the label will be placed for all radio buttons. This will override any labelPosition properties defined for an individual radio button.
*/
labelPosition: React.PropTypes.oneOf(['left', 'right']),
/**
* A callback function to be called when an option is changed.
*/
changeCallback: React.PropTypes.func
}
componentWillMount = () => {
//form an array of options based on the children that were passed in
//this can be done in the case of a RadioGroup with explicit children (see docs example)
if (this.props.children) {
const childOptions = this.props.children.reduce((options, child) => {
if (child.type === Radio) {
options.push({ name: child.props.name, label: child.props.label })
}
return options
}, [])
this.setState({options: childOptions})
}
if (typeof this.state.value !== 'undefined' && (this.state.options || this.props.options)) {
this.checkItem(this.state.value, this.state.options || this.props.options)
}
}
componentWillReceiveProps = (nextProps) => {
if (nextProps.value !== this.state.value) {
this.setState({value: nextProps.value}, () => {
this.checkItem(nextProps.value, this.state.options || nextProps.options)
})
}
}
handleChange = (event, value) => {
event.persist()
if (value !== this.state.value) {
this.setState({value: value}, () => {
this.checkItem(value, this.state.options || this.props.options)
})
if (typeof this.props.changeCallback === 'function') {
this.props.changeCallback(event, value)
}
}
}
checkItem = (value, options) => {
let index = this.getIndex(value, options)
if (index >= 0) {
options[index].checked = true
}
}
getIndex = (value, options) => {
let optionIndex = -1
options.map((radio, index) => {
if (radio.value === value) {
optionIndex = index
return
}
})
return optionIndex
}
getOptions = () => {
const groupName = this.props.name
const groupLabelPosition = this.props.labelPosition
const { options, label, name, value, required, labelPosition, changeCallback, ...other } = this.props
//this means explicit radio buttons were defined (usually paired with other form fields)
//we create an options array in the state (because there is no options in props) for checkItem to use
if (this.props.children) {
return this.props.children.map((child, index) => {
if (child.type === Radio) {
return React.cloneElement(child, {
key: index,
name: groupName,
checked: this.state.value === child.props.value,
changeCallback: this.handleChange,
labelPosition: groupLabelPosition
})
}
else {
return child
}
})
}
//this means a normal RadioGroup with an options array was defined
else {
return this.props.options.map((option) =>
<Radio
key={option.value}
value={option.value}
label={option.label}
name={groupName}
checked={this.state.value === option.value}
labelPosition={groupLabelPosition}
optClass={option.optClass}
changeCallback={this.handleChange}
{...other} />
)
}
}
render() {
const cx = classNames.bind(style)
const radioGroupClass = cx(style['radio-group'], this.props.optClass)
return (
<div className={radioGroupClass}>
{this.props.required ? <span className={style.asterisk}>*</span> : null}
{this.props.label ? <label className={style['radio-group-label']}>{this.props.label}</label> : null}
{this.getOptions()}
</div>
)
}
}
export default RadioGroup