2 * ============LICENSE_START===================================================
3 * SPARKY (AAI UI service)
4 * ============================================================================
5 * Copyright © 2017 AT&T Intellectual Property.
6 * Copyright © 2017 Amdocs
8 * ============================================================================
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 * ============LICENSE_END=====================================================
22 * ECOMP and OpenECOMP are trademarks
23 * and service marks of AT&T Intellectual Property.
27 * Used for inputs on a validation form.
28 * All properties will be passed on to the input element.
30 * The following properties can be set for OOB validations and callbacks:
31 - required: Boolean: Should be set to true if the input must have a value
32 - numeric: Boolean : Should be set to true id the input should be an integer
33 - onChange : Function : Will be called to validate the value if the default validations are not sufficient, should return a boolean value
34 indicating whether the value is valid
35 - didUpdateCallback :Function: Will be called after the state has been updated and the component has rerendered. This can be used if
36 there are dependencies between inputs in a form.
38 * The following properties of the state can be set to determine
39 * the state of the input from outside components:
40 - isValid : Boolean - whether the value is valid
41 - value : value for the input field,
43 - required : Boolean - whether the input value must be filled out.
45 import React from 'react';
46 import ReactDOM from 'react-dom';
47 import Validator from 'validator';
48 import Input from 'react-bootstrap/lib/Input.js';
49 import Overlay from 'react-bootstrap/lib/Overlay.js';
50 import Tooltip from 'react-bootstrap/lib/Tooltip.js';
51 import isEqual from 'lodash/lang/isEqual.js';
52 import i18n from 'utils/i18n/i18n.js';
54 import InputOptions from '../inputOptions/InputOptions.jsx';
56 const globalValidationFunctions = {
57 required: value => value !== '',
58 maxLength: (value, length) => Validator.isLength(value, {max: length}),
59 minLength: (value, length) => Validator.isLength(value, {min: length}),
60 pattern: (value, pattern) => Validator.matches(value, pattern),
61 numeric: value => Validator.isNumeric(value),
62 maxValue: (value, maxValue) => value < maxValue,
63 alphanumeric: value => Validator.isAlphanumeric(value),
64 alphanumericWithSpaces: value => Validator.isAlphanumeric(
65 value.replace(/ /g, '')),
66 validateName: value => Validator.isAlphanumeric(
67 value.replace(/\s|\.|\_|\-/g, ''), 'en-US'),
68 validateVendorName: value => Validator.isAlphanumeric(
69 value.replace(/[\x7F-\xFF]|\s/g, ''), 'en-US'),
70 freeEnglishText: value => Validator.isAlphanumeric(
71 value.replace(/\s|\.|\_|\-|\,|\(|\)|\?/g, ''), 'en-US'),
72 email: value => Validator.isEmail(value),
73 ip: value => Validator.isIP(value),
74 url: value => Validator.isURL(value)
77 const globalValidationMessagingFunctions = {
78 required: () => i18n('Field is required'),
79 maxLength: (value, maxLength) => i18n(
80 'Field value has exceeded it\'s limit, {maxLength}. current length: {length}',
85 minLength: (value, minLength) => i18n(
86 'Field value should contain at least {minLength} characters.', {minLength}),
87 pattern: (value, pattern) => i18n(
88 'Field value should match the pattern: {pattern}.', {pattern}),
89 numeric: () => i18n('Field value should contain numbers only.'),
90 maxValue: (value, maxValue) => i18n(
91 'Field value should be less then: {maxValue}.', {maxValue}),
92 alphanumeric: () => i18n(
93 'Field value should contain letters or digits only.'),
94 alphanumericWithSpaces: () => i18n(
95 'Field value should contain letters, digits or spaces only.'),
96 validateName: ()=> i18n(
97 'Field value should contain English letters, digits , spaces, underscores, dashes and dots only.'),
98 validateVendorName: ()=> i18n(
99 'Field value should contain English letters digits and spaces only.'),
100 freeEnglishText: ()=> i18n(
101 'Field value should contain English letters, digits , spaces, underscores, dashes and dots only.'),
102 email: () => i18n('Field value should be a valid email address.'),
103 ip: () => i18n('Field value should be a valid ip address.'),
104 url: () => i18n('Field value should be a valid url address.'),
105 general: () => i18n('Field value is invalid.')
108 class ValidationInput extends React.Component {
110 static contextTypes = {
111 validationParent: React.PropTypes.any,
112 isReadOnlyMode: React.PropTypes.bool
115 static defaultProps = {
118 didUpdateCallback: null,
124 onChange: React.PropTypes.func,
125 disabled: React.PropTypes.bool,
126 didUpdateCallback: React.PropTypes.func,
127 validations: React.PropTypes.object
134 value: this.props.value,
136 previousErrorMessage: '',
140 componentWillReceiveProps({value: nextValue, validations: nextValidaions}) {
141 if (this.state.wasInvalid) {
142 const {validations, value} = this.props;
143 if (value !== nextValue || !isEqual(validations, nextValidaions)) {
144 this.validate(nextValue, nextValidaions);
146 } else if (this.props.value !== nextValue) {
147 this.setState({value: nextValue});
152 let {isMultiSelect, onOtherChange, type} = this.props;
154 let groupClasses = this.props.groupClassName || '';
155 if (this.props.validations.required) {
156 groupClasses += ' required';
158 let isReadOnlyMode = this.context.isReadOnlyMode;
161 <div className='validation-input-wrapper'>
163 !isMultiSelect && !onOtherChange && type !== 'select'
166 groupClassName={groupClasses}
168 value={this.state.value}
169 disabled={isReadOnlyMode || Boolean(this.props.disabled)}
170 bsStyle={this.state.style}
171 onChange={() => this.changedInput()}
172 onBlur={() => this.blurInput()}>
173 {this.props.children}
177 (isMultiSelect || onOtherChange || type === 'select')
178 && <InputOptions onInputChange={() => this.changedInput()}
179 onBlur={() => this.blurInput()}
180 hasError={!this.state.isValid}
181 ref={'_myInput'} {...this.props} />
183 {this.renderOverlay()}
189 let position = 'right';
190 if (this.props.type === 'text'
191 || this.props.type === 'email'
192 || this.props.type === 'number'
193 || this.props.type === 'password'
199 let validationMessage = this.state.error.message ||
200 this.state.previousErrorMessage;
203 show={!this.state.isValid}
205 target={() => {let target = ReactDOM.findDOMNode(this.refs._myInput); return target.offsetParent ? target : undefined;}}
208 id={`error-${validationMessage.replace(' ','-')}`}
209 className='validation-error-message'>
216 componentDidMount() {
217 if (this.context.validationParent) {
218 this.context.validationParent.register(this);
222 componentDidUpdate(prevProps, prevState) {
223 if (this.context.validationParent) {
224 if (prevState.isValid !== this.state.isValid) {
225 this.context.validationParent.childValidStateChanged(this,
229 if (this.props.didUpdateCallback) {
230 this.props.didUpdateCallback();
235 componentWillUnmount() {
236 if (this.context.validationParent) {
237 this.context.validationParent.unregister(this);
242 * Adding same method as the actual input component
246 if (this.props.type === 'checkbox') {
247 return this.refs._myInput.getChecked();
249 return this.refs._myInput.getValue();
253 this.setState({value: this.props.value});
258 * internal method that validated the value. includes callback to the
261 * @param validations - map containing validation id and the limitation
262 * describing the validation.
265 validateValue = (value, validations) => {
266 let {customValidationFunction} = validations;
269 for (let validation in validations) {
270 if ('customValidationFunction' !== validation) {
271 if (validations[validation]) {
272 if (!globalValidationFunctions[validation](value,
273 validations[validation])) {
274 error.id = validation;
276 globalValidationMessagingFunctions[validation](value,
277 validations[validation]);
283 let customValidationResult = customValidationFunction(value);
285 if (customValidationResult !== true) {
288 if (typeof customValidationResult === 'string') {//custom validation error message supplied.
289 error.message = customValidationResult;
291 error.message = globalValidationMessagingFunctions.general();
307 * Internal method that handles the change event of the input. validates and
312 let {isValid, error} = this.state.wasInvalid ? this.validate() : this.state;
313 let onChange = this.props.onChange;
315 onChange(this.getValue(), isValid, error);
320 if (!this.state.wasInvalid) {
321 this.setState({wasInvalid: true});
324 let {isValid, error} = !this.state.wasInvalid
327 let onBlur = this.props.onBlur;
329 onBlur(this.getValue(), isValid, error);
333 validate(value = this.getValue(), validations = this.props.validations) {
334 let validationStatus = this.validateValue(value, validations);
335 let {isValid, error} = validationStatus;
336 let _style = isValid ? null : 'error';
341 previousErrorMessage: this.state.error.message || '',
343 wasInvalid: !isValid || this.state.wasInvalid
346 return validationStatus;
350 return this.state.isValid;
354 export default ValidationInput;