Add new code new version
[sdc.git] / openecomp-ui / src / nfvo-components / input / validation / ValidationForm.jsx
1 /**
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.
5  *
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
10  */
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';
15
16 class ValidationForm extends React.Component {
17
18         static childContextTypes = {
19                 validationParent: React.PropTypes.any,
20                 isReadOnlyMode: React.PropTypes.bool,
21                 validationSchema: React.PropTypes.instanceOf(JSONSchema),
22                 validationData: React.PropTypes.object
23         };
24
25         static defaultProps = {
26                 hasButtons : true,
27                 onSubmit : null,
28                 onReset :  null,
29                 labledButtons: true,
30                 onValidChange :  null,
31                 isValid: true
32         };
33
34         static propTypes = {
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
45         };
46
47         state = {
48                 isValid: this.props.isValid
49         };
50
51         constructor(props) {
52                 super(props);
53                 this.validationComponents = [];
54         }
55
56         componentWillMount() {
57                 let {schema, data} = this.props;
58                 if (schema) {
59                         this.processSchema(schema, data);
60                 }
61         }
62
63         componentWillReceiveProps(nextProps) {
64                 let {schema, data} = this.props;
65                 let {schema: nextSchema, data: nextData} = nextProps;
66
67                 if (schema !== nextSchema || data !== nextData) {
68                         if (!schema || !nextSchema) {
69                                 throw new Error('ValidationForm: dynamically adding/removing schema is not supported');
70                         }
71
72                         if (schema !== nextSchema) {
73                                 this.processSchema(nextSchema, nextData);
74                         } else {
75                                 this.setState({data: nextData});
76                         }
77                 }
78         }
79
80         processSchema(rawSchema, rawData) {
81                 let schema = new JSONSchema();
82                 schema.setSchema(rawSchema);
83                 let data = schema.processData(rawData);
84                 this.setState({
85                         schema,
86                         data
87                 });
88         }
89
90         render() {
91                 // eslint-disable-next-line no-unused-vars
92                 let {isValid, isReadOnlyMode, hasButtons, onSubmit, labledButtons, onValidChange, onValidityChanged, schema, data, children, ...formProps} = this.props;
93                 return (
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}/>}
97                         </form>
98                 );
99         }
100
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;
107                 });
108                 if(isFormValid && this.props.onSubmit) {
109                         return this.props.onSubmit(event);
110                 } else if(!isFormValid) {
111                         this.setState({isValid: false});
112                 }
113         }
114
115         componentWillUpdate(nextProps, nextState) {
116                 if(this.state.isValid !== nextState.isValid && this.props.onValidityChanged) {
117                         this.props.onValidityChanged(nextState.isValid);
118                 }
119         }
120
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});
127                         }
128                 } else if(this.state.isValid !== prevState.isValid) {
129                         if (this.props.hasButtons) {
130                                 this.refs.buttons.setState({isValid: this.state.isValid});
131                         }
132                         // callback in case form is part of bigger picture in view
133                         if (this.props.onValidChange) {
134                                 this.props.onValidChange(this.state.isValid);
135                         }
136                 }
137         }
138
139         componentDidMount() {
140                 if (this.props.hasButtons) {
141                         this.refs.buttons.setState({isValid: this.state.isValid});
142                 }
143         }
144
145
146         getChildContext() {
147                 return {
148                         validationParent: this,
149                         isReadOnlyMode: this.props.isReadOnlyMode,
150                         validationSchema: this.state.schema,
151                         validationData: this.state.data
152                 };
153         }
154
155
156         /***
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.
160          *
161          * @param validationComponent
162          * @param isValid
163          */
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();
169                         });
170
171                         if (oldState !== newState) {
172                                 this.setState({isValid: newState});
173                         }
174                 }
175         }
176
177         register(validationComponent) {
178                 if (this.state.schema) {
179                         // TODO: register
180                 } else {
181                         this.validationComponents.push(validationComponent);
182                 }
183         }
184
185         unregister(validationComponent) {
186                 this.childValidStateChanged(validationComponent, true);
187                 this.validationComponents = this.validationComponents.filter(otherValidationComponent => validationComponent !== otherValidationComponent);
188         }
189
190         onValueChanged(pointer, value, isValid, error) {
191                 this.props.onDataChanged({
192                         data: JSONPointer.setValue(this.props.data, pointer, value),
193                         isValid,
194                         error
195                 });
196         }
197 }
198
199
200 export default ValidationForm;