src/views/form/form.view.js
import React, { Component } from 'react';
import PropTypes from 'prop-types';
/**
* Controls the form element and validates all children when trying to submit
*
* @extends {react~Component}
*
* @example
* <Form action='http://someaction.example.com/'>
* <ImageField id='avatar' name='avatar' label='Avatar' />
* <Field id='username' name='username'
* label='Username' placeholder='Enter your username'
* suggestion='Only accepts letters and numbers'
* patternError='Input format is wrong, only accepts letters and numbers'
* required pattern='^[a-zA-Z0-9]+$' />
* <Field id='name' name='name'
* label='Name' placeholder='Enter your full name' />
* <EmailField id='email' name='email'
* label='Email' placeholder='Enter your email address' />
* <DropdownField id='sex' name='sex' label='Sex' placeholder='Select one' required>
* <option value='M'>Male</option>
* <option value='F'>Female</option>
* </DropdownField>
* <TextareaField id='comments' name='comments' label='Comments'
* suggestion='This field has a 100 character limit'
* maxChars={100} maxCharsError='Character limit reached' />
* </Form>
*/
class Form extends Component {
/**
* @type {Object}
* @property {Array<HTMLOptionElement>} children - Elements to show inside the form
* @property {String} action - The form action attribute
* @property {Function} onSubmit - Triggers when the user submits a valid form
* @property {Function} [onCancel] - Triggers when the user clicks the cancel button
* @property {String} [method='GET'] - The method to use when submitting the form
*/
static propTypes = {
children: PropTypes.array.isRequired,
action: PropTypes.string.isRequired,
onSubmit: PropTypes.func.isRequired,
onCancel: PropTypes.func,
method: PropTypes.string,
};
/**
* @ignore
*/
static defaultProps = {
children: [],
method: 'GET',
};
/**
* @ignore
*/
constructor(props) {
super(props);
/**
* @ignore
*/
this.instances = {};
}
/**
* @ignore
*/
componentWillMount() {
/**
* @ignore
*/
this.children = this.props.children.map(el => React.cloneElement(el, {
key: el.props.id || Math.random().toString(36).slice(-8),
ref: this.addChildInstance.bind(this, el),
}));
}
/**
* @ignore
*/
addChildInstance(el, instance) {
if (el.props.id && typeof el.type !== 'string') {
this.instances[el.props.id] = instance;
}
}
/**
* @ignore
*/
onSubmit(e) {
e.preventDefault();
const invalidFields = Object.keys(this.instances).filter((key) => {
const component = this.instances[key];
component.validate();
return !component.isValid;
});
const { onSubmit } = this.props;
if (onSubmit && !invalidFields.length) {
onSubmit(e);
}
}
onCancel() {
const { onCancel } = this.props;
if (onCancel) {
onCancel();
}
}
/**
* @ignore
*/
render() {
const { action, method } = this.props;
return (
<form
action={action} method={method}
onSubmit={e => this.onSubmit(e)}
noValidate>
{this.children}
<div className='buttonGroup'>
<button
className='button button--secondary' type='button'
onClick={() => this.onCancel()}>Cancel</button>
<button className='button' type='submit'>Save</button>
</div>
</form>
);
}
}
export default Form;