2 * ValidationForm should be used in order to have a form that handles it's internal validation state.
3 * All ValidationInputs inside the form are checked for validity and the styling and submit buttons
4 * are updated accordingly.
6 * The properties that ahould be given to the form:
7 * labledButtons - whether or not use icons only as the form default buttons or use buttons with labels
8 * onSubmit - function for click on the submit button
9 * onReset - function for click on the reset button
11 import React from 'react';
12 import JSONSchema from 'nfvo-utils/json/JSONSchema.js';
13 import JSONPointer from 'nfvo-utils/json/JSONPointer.js';
14 import ValidationButtons from './ValidationButtons.jsx';
16 class ValidationForm extends React.Component {
18 static childContextTypes = {
19 validationParent: React.PropTypes.any,
20 isReadOnlyMode: React.PropTypes.bool,
21 validationSchema: React.PropTypes.instanceOf(JSONSchema),
22 validationData: React.PropTypes.object
25 static defaultProps = {
35 isValid : React.PropTypes.bool,
36 isReadOnlyMode : React.PropTypes.bool,
37 hasButtons : React.PropTypes.bool,
38 onSubmit : React.PropTypes.func,
39 onReset : React.PropTypes.func,
40 labledButtons: React.PropTypes.bool,
41 onValidChange : React.PropTypes.func,
42 onValidityChanged: React.PropTypes.func,
43 schema: React.PropTypes.object,
44 data: React.PropTypes.object
48 isValid: this.props.isValid
53 this.validationComponents = [];
56 componentWillMount() {
57 let {schema, data} = this.props;
59 this.processSchema(schema, data);
63 componentWillReceiveProps(nextProps) {
64 let {schema, data} = this.props;
65 let {schema: nextSchema, data: nextData} = nextProps;
67 if (schema !== nextSchema || data !== nextData) {
68 if (!schema || !nextSchema) {
69 throw new Error('ValidationForm: dynamically adding/removing schema is not supported');
72 if (schema !== nextSchema) {
73 this.processSchema(nextSchema, nextData);
75 this.setState({data: nextData});
80 processSchema(rawSchema, rawData) {
81 let schema = new JSONSchema();
82 schema.setSchema(rawSchema);
83 let data = schema.processData(rawData);
91 // eslint-disable-next-line no-unused-vars
92 let {isValid, isReadOnlyMode, hasButtons, onSubmit, labledButtons, onValidChange, onValidityChanged, schema, data, children, ...formProps} = this.props;
94 <form {...formProps} onSubmit={event => this.handleFormSubmit(event)}>
95 <div className='validation-form-content'>{children}</div>
96 {hasButtons && <ValidationButtons labledButtons={labledButtons} ref='buttons' isReadOnlyMode={isReadOnlyMode}/>}
101 handleFormSubmit(event) {
102 event.preventDefault();
103 let isFormValid = true;
104 this.validationComponents.forEach(validationComponent => {
105 const isInputValid = validationComponent.validate().isValid;
106 isFormValid = isInputValid && isFormValid;
108 if(isFormValid && this.props.onSubmit) {
109 return this.props.onSubmit(event);
110 } else if(!isFormValid) {
111 this.setState({isValid: false});
115 componentWillUpdate(nextProps, nextState) {
116 if(this.state.isValid !== nextState.isValid && this.props.onValidityChanged) {
117 this.props.onValidityChanged(nextState.isValid);
121 componentDidUpdate(prevProps, prevState) {
122 // only handling this programatically if the validation of the form is done outside of the view
123 // (example with a form that is dependent on the state of other forms)
124 if (prevProps.isValid !== this.props.isValid) {
125 if (this.props.hasButtons) {
126 this.refs.buttons.setState({isValid: this.state.isValid});
128 } else if(this.state.isValid !== prevState.isValid) {
129 if (this.props.hasButtons) {
130 this.refs.buttons.setState({isValid: this.state.isValid});
132 // callback in case form is part of bigger picture in view
133 if (this.props.onValidChange) {
134 this.props.onValidChange(this.state.isValid);
139 componentDidMount() {
140 if (this.props.hasButtons) {
141 this.refs.buttons.setState({isValid: this.state.isValid});
148 validationParent: this,
149 isReadOnlyMode: this.props.isReadOnlyMode,
150 validationSchema: this.state.schema,
151 validationData: this.state.data
157 * Used by ValidationInput in order to let the (parent) form know
158 * the valid state. If there is a change in the state of the form,
159 * the buttons will be updated.
161 * @param validationComponent
164 childValidStateChanged(validationComponent, isValid) {
165 if (isValid !== this.state.isValid) {
166 let oldState = this.state.isValid;
167 let newState = isValid && this.validationComponents.filter(otherValidationComponent => validationComponent !== otherValidationComponent).every(otherValidationComponent => {
168 return otherValidationComponent.isValid();
171 if (oldState !== newState) {
172 this.setState({isValid: newState});
177 register(validationComponent) {
178 if (this.state.schema) {
181 this.validationComponents.push(validationComponent);
185 unregister(validationComponent) {
186 this.childValidStateChanged(validationComponent, true);
187 this.validationComponents = this.validationComponents.filter(otherValidationComponent => validationComponent !== otherValidationComponent);
190 onValueChanged(pointer, value, isValid, error) {
191 this.props.onDataChanged({
192 data: JSONPointer.setValue(this.props.data, pointer, value),
200 export default ValidationForm;