react-conventions
Version:
An open source set of React components that implement Ambassador's Design and UX patterns.
140 lines (122 loc) • 3.87 kB
JavaScript
import React from 'react'
import debounce from 'lodash/debounce'
import Immutable from 'immutable'
import style from './style.scss'
import optclass from '../internal/OptClass'
class FormGroup extends React.Component {
constructor(props) {
super(props)
this.debounce = debounce(this.handleChange, this.props.debounceTime)
}
static propTypes = {
/**
* A configuration object of name/value pairs
* that correspond to the form fields names.
*/
schema: React.PropTypes.object,
/**
* A callback function to be called when a form value changes.
*/
changeCallback: React.PropTypes.func,
/**
* A callback function to be called when the form is submitted.
*/
submitCallback: React.PropTypes.func,
/**
* Optional CSS class(es) to be used for local styles (string or array of strings)
*/
optClass: React.PropTypes.oneOfType([
React.PropTypes.array,
React.PropTypes.string
]),
/**
* Option to turn off form wrapper (for nested components)
*/
nested: React.PropTypes.bool,
/**
* Option to turn off debounce when something in the form group changes
*/
debounceTime: React.PropTypes.number
}
static defaultProps = {
debounceTime: 0
}
componentWillReceiveProps(nextProps) {
const nextPropsSchema = Immutable.fromJS(nextProps.schema)
const thisPropsSchema = Immutable.fromJS(this.props.schema)
if(!Immutable.is(nextPropsSchema, thisPropsSchema)) {
this.setState({
fields: Immutable.fromJS(nextProps.schema)
})
}
}
componentWillMount = () => {
this.setState({
fields: Immutable.fromJS(this.props.schema)
})
}
handleSubmit = (event) => {
event.preventDefault()
if (typeof this.props.submitCallback === 'function') {
this.props.submitCallback(event, this.state.fields.toJS())
}
}
handleChange = (event) => {
let val = event.target.value
// Handle checkbox values
if (event.target.type === 'checkbox') {
val = event.target.checked
}
this.setState(prevState => ({
fields: prevState.fields.setIn([event.target.name, 'value'], val)
}), () => {
if (this.props.changeCallback) {
this.props.changeCallback(this.state.fields.toJS())
}
})
}
getElements(children) {
return React.Children.map(children, child => {
let childProps = {}
if (child.props) {
const name = child.props.name
const value = this.state.fields.getIn([name, 'value'])
const valueIsImmutable = Immutable.Iterable.isIterable(value)
const valueProp = valueIsImmutable ? value.toJS() : value
if (this.state.fields.has(name) && React.isValidElement(child)) {
childProps = {
changeCallback: this.props.debounceTime ? this.debounce : this.handleChange,
value: valueProp
}
}
childProps.children = this.getElements(child.props.children)
return React.cloneElement(child, childProps)
}
return child
})
}
renderForm = () => {
const elements = this.getElements(this.props.children)
const formGroupClass = optclass(style, 'form-group', this.props.optClass)
let formWrapper
if (!this.props.nested) {
formWrapper = <form className={formGroupClass} onSubmit={this.handleSubmit}>
<fieldset className={style.fieldset}>
{elements}
</fieldset>
</form>
} else {
const fieldsetClass = optclass(style, 'fieldset', this.props.optClass)
formWrapper = <fieldset className={fieldsetClass}>
{elements}
</fieldset>
}
return formWrapper
}
render() {
return (
this.renderForm()
)
}
}
export default FormGroup