summaryrefslogtreecommitdiffstats
path: root/openecomp-ui/src/nfvo-components/input
diff options
context:
space:
mode:
Diffstat (limited to 'openecomp-ui/src/nfvo-components/input')
-rw-r--r--openecomp-ui/src/nfvo-components/input/ExpandableInput.jsx140
-rw-r--r--openecomp-ui/src/nfvo-components/input/SelectInput.jsx18
-rw-r--r--openecomp-ui/src/nfvo-components/input/ToggleInput.jsx15
-rw-r--r--openecomp-ui/src/nfvo-components/input/dualListbox/DualListboxView.jsx67
-rw-r--r--openecomp-ui/src/nfvo-components/input/inputOptions/InputOptions.jsx55
-rw-r--r--openecomp-ui/src/nfvo-components/input/validation/Form.jsx114
-rw-r--r--openecomp-ui/src/nfvo-components/input/validation/Input.jsx180
-rw-r--r--openecomp-ui/src/nfvo-components/input/validation/InputOptions.jsx279
-rw-r--r--openecomp-ui/src/nfvo-components/input/validation/InputWrapper.jsx134
-rw-r--r--openecomp-ui/src/nfvo-components/input/validation/Tabs.jsx79
-rw-r--r--openecomp-ui/src/nfvo-components/input/validation/ValidationButtons.jsx21
-rw-r--r--openecomp-ui/src/nfvo-components/input/validation/ValidationForm.jsx200
-rw-r--r--openecomp-ui/src/nfvo-components/input/validation/ValidationInput.jsx509
-rw-r--r--openecomp-ui/src/nfvo-components/input/validation/ValidationTab.jsx107
-rw-r--r--openecomp-ui/src/nfvo-components/input/validation/ValidationTabs.jsx72
15 files changed, 1009 insertions, 981 deletions
diff --git a/openecomp-ui/src/nfvo-components/input/ExpandableInput.jsx b/openecomp-ui/src/nfvo-components/input/ExpandableInput.jsx
index 3ac3fcad28..e2ee40fcd2 100644
--- a/openecomp-ui/src/nfvo-components/input/ExpandableInput.jsx
+++ b/openecomp-ui/src/nfvo-components/input/ExpandableInput.jsx
@@ -1,77 +1,115 @@
+/*!
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
import React from 'react';
-import FontAwesome from 'react-fontawesome';
-import classnames from 'classnames';
-import Input from 'react-bootstrap/lib/Input';
+import ReactDOM from 'react-dom';
+import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx';
+import Input from 'nfvo-components/input/validation/InputWrapper.jsx';
+const ExpandableInputClosed = ({iconType, onClick}) => (
+ <SVGIcon className='expandable-input-wrapper closed' name={iconType} onClick={onClick} />
+);
-class ExpandableInput extends React.Component {
- constructor(props){
- super(props);
- this.state = {showInput: false, value: ''};
- this.toggleInput = this.toggleInput.bind(this);
- this.handleFocus = this.handleFocus.bind(this);
- this.handleInput = this.handleInput.bind(this);
- this.handleClose = this.handleClose.bind(this);
+class ExpandableInputOpened extends React.Component {
+ componentDidMount(){
+ this.rawDomNode = ReactDOM.findDOMNode(this.searchInputNode.inputWrapper);
+ this.rawDomNode.focus();
}
- toggleInput(){
- if (!this.state.showInput){
- this.searchInputNode.refs.input.focus();
- } else {
- this.setState({showInput: false});
+ componentWillReceiveProps(newProps){
+ if (!newProps.value){
+ if (!(document.activeElement === this.rawDomNode)){
+ this.props.handleBlur();
+ }
}
}
- handleInput(e){
- let {onChange} = this.props;
+ handleClose(){
+ this.props.onChange('');
+ this.rawDomNode.focus();
+ }
- this.setState({value: e.target.value});
- onChange(e);
+ handleKeyDown(e){
+ if (e.key === 'Escape'){
+ e.preventDefault();
+ if (this.props.value) {
+ this.handleClose();
+ } else {
+ this.rawDomNode.blur();
+ }
+ };
}
- handleClose(){
- this.handleInput({target: {value: ''}});
- this.searchInputNode.refs.input.focus();
+ render() {
+ let {iconType, value, onChange, handleBlur} = this.props;
+ return (
+ <div className='expandable-input-wrapper opened' key='expandable'>
+ <Input
+ type='text'
+ value={value}
+ ref={(input) => this.searchInputNode = input}
+ className='expandable-active'
+ groupClassName='expandable-input-control'
+ onChange={e => onChange(e)}
+ onKeyDown={e => this.handleKeyDown(e)}
+ onBlur={handleBlur}/>
+ {value && <SVGIcon onClick={() => this.handleClose()} name='close' />}
+ {!value && <SVGIcon name={iconType} onClick={handleBlur}/>}
+ </div>
+ );
}
+}
+
+class ExpandableInput extends React.Component {
- handleFocus(){
- if (!this.state.showInput){
- this.setState({showInput: true});
+ static propTypes = {
+ iconType: React.PropTypes.string,
+ onChange: React.PropTypes.func,
+ value: React.PropTypes.string
+ };
+
+ state = {showInput: false};
+
+ closeInput(){
+ if (!this.props.value) {
+ this.setState({showInput: false});
}
}
getValue(){
- return this.state.value;
+ return this.props.value;
}
render(){
- let {iconType} = this.props;
-
- let inputClasses = classnames({
- 'expandable-active': this.state.showInput,
- 'expandable-not-active': !this.state.showInput
- });
-
- let iconClasses = classnames(
- 'expandable-icon',
- {'expandable-icon-active': this.state.showInput}
- );
-
+ let {iconType, value, onChange = false} = this.props;
return (
- <div className='expandable-input-wrapper'>
- <Input
- type='text'
- value={this.state.value}
- ref={(input) => this.searchInputNode = input}
- className={inputClasses}
- groupClassName='expandable-input-control'
- onChange={e => this.handleInput(e)}
- onFocus={this.handleFocus}/>
- {this.state.showInput && this.state.value && <FontAwesome onClick={this.handleClose} name='close' className='expandable-close-button'/>}
- {!this.state.value && <FontAwesome onClick={this.toggleInput} name={iconType} className={iconClasses}/>}
+ <div className='expandable-input-top'>
+ {this.state.showInput &&
+ <ExpandableInputOpened
+ key='open'
+ iconType={iconType}
+ onChange={onChange}
+ value={value}
+ handleKeyDown={(e) => this.handleKeyDown(e)}
+ handleBlur={() => this.closeInput()}/>
+ }
+ {!this.state.showInput && <ExpandableInputClosed key='closed' iconType={iconType} onClick={() => this.setState({showInput: true})} />}
</div>
- );
+ );
}
}
+
export default ExpandableInput;
diff --git a/openecomp-ui/src/nfvo-components/input/SelectInput.jsx b/openecomp-ui/src/nfvo-components/input/SelectInput.jsx
index 1036ac41c3..03c727379e 100644
--- a/openecomp-ui/src/nfvo-components/input/SelectInput.jsx
+++ b/openecomp-ui/src/nfvo-components/input/SelectInput.jsx
@@ -1,3 +1,18 @@
+/*!
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
/**
* The HTML structure here is aligned with bootstrap HTML structure for form elements.
* In this way we have proper styling and it is aligned with other form elements on screen.
@@ -20,8 +35,9 @@ class SelectInput extends Component {
render() {
let {label, value, ...other} = this.props;
+ const dataTestId = this.props['data-test-id'] ? {'data-test-id': this.props['data-test-id']} : {};
return (
- <div className='validation-input-wrapper dropdown-multi-select'>
+ <div {...dataTestId} className='validation-input-wrapper dropdown-multi-select'>
<div className='form-group'>
{label && <label className='control-label'>{label}</label>}
<Select ref='_myInput' onChange={value => this.onSelectChanged(value)} {...other} value={value} />
diff --git a/openecomp-ui/src/nfvo-components/input/ToggleInput.jsx b/openecomp-ui/src/nfvo-components/input/ToggleInput.jsx
index 873d3ded65..7bbafa3696 100644
--- a/openecomp-ui/src/nfvo-components/input/ToggleInput.jsx
+++ b/openecomp-ui/src/nfvo-components/input/ToggleInput.jsx
@@ -1,3 +1,18 @@
+/*!
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
import React from 'react';
export default
diff --git a/openecomp-ui/src/nfvo-components/input/dualListbox/DualListboxView.jsx b/openecomp-ui/src/nfvo-components/input/dualListbox/DualListboxView.jsx
index 171bead9bb..c60d6f777e 100644
--- a/openecomp-ui/src/nfvo-components/input/dualListbox/DualListboxView.jsx
+++ b/openecomp-ui/src/nfvo-components/input/dualListbox/DualListboxView.jsx
@@ -1,6 +1,21 @@
+/*!
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
import React from 'react';
-import FontAwesome from 'react-fontawesome';
-import Input from 'react-bootstrap/lib/Input.js';
+import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx';
+import Input from 'nfvo-components/input/validation/InputWrapper.jsx';
class DualListboxView extends React.Component {
@@ -30,37 +45,32 @@ class DualListboxView extends React.Component {
state = {
availableListFilter: '',
- selectedValuesListFilter: ''
- };
-
- static contextTypes = {
- isReadOnlyMode: React.PropTypes.bool
+ selectedValuesListFilter: '',
+ selectedValues: []
};
render() {
- let {availableList, selectedValuesList, filterTitle} = this.props;
+ let {availableList, selectedValuesList, filterTitle, isReadOnlyMode} = this.props;
let {availableListFilter, selectedValuesListFilter} = this.state;
- let isReadOnlyMode = this.context.isReadOnlyMode;
let unselectedList = availableList.filter(availableItem => !selectedValuesList.find(value => value === availableItem.id));
let selectedList = availableList.filter(availableItem => selectedValuesList.find(value => value === availableItem.id));
selectedList = selectedList.sort((a, b) => selectedValuesList.indexOf(a.id) - selectedValuesList.indexOf(b.id));
-
return (
<div className='dual-list-box'>
{this.renderListbox(filterTitle.left, unselectedList, {
value: availableListFilter,
ref: 'availableListFilter',
disabled: isReadOnlyMode,
- onChange: () => this.setState({availableListFilter: this.refs.availableListFilter.getValue()})
- }, {ref: 'availableValues', disabled: isReadOnlyMode})}
+ onChange: (value) => this.setState({availableListFilter: value})
+ }, {ref: 'availableValues', disabled: isReadOnlyMode, testId: 'available',})}
{this.renderOperationsBar(isReadOnlyMode)}
{this.renderListbox(filterTitle.right, selectedList, {
value: selectedValuesListFilter,
ref: 'selectedValuesListFilter',
disabled: isReadOnlyMode,
- onChange: () => this.setState({selectedValuesListFilter: this.refs.selectedValuesListFilter.getValue()})
- }, {ref: 'selectedValues', disabled: isReadOnlyMode})}
+ onChange: (value) => this.setState({selectedValuesListFilter: value})
+ }, {ref: 'selectedValues', disabled: isReadOnlyMode, testId: 'selected'})}
</div>
);
}
@@ -69,21 +79,25 @@ class DualListboxView extends React.Component {
let regExFilter = new RegExp(escape(filterProps.value), 'i');
let matchedItems = list.filter(item => item.name.match(regExFilter));
let unMatchedItems = list.filter(item => !item.name.match(regExFilter));
-
-
return (
<div className='dual-search-multi-select-section'>
<p>{filterTitle}</p>
<div className='dual-text-box-search search-wrapper'>
- <Input name='search-input-control' type='text' groupClassName='search-input-control' {...filterProps}/>
- <FontAwesome name='search' className='search-icon'/>
+ <Input data-test-id={`${props.testId}-search-input`}
+ name='search-input-control' type='text'
+ groupClassName='search-input-control'
+ {...filterProps}/>
+ <SVGIcon name='search' className='search-icon'/>
</div>
<Input
multiple
+ onChange={(event) => this.onSelectItems(event.target.selectedOptions)}
groupClassName='dual-list-box-multi-select'
type='select'
name='dual-list-box-multi-select'
- {...props}>
+ data-test-id={`${props.testId}-select-input`}
+ disabled={props.disabled}
+ ref={props.ref}>
{matchedItems.map(item => this.renderOption(item.id, item.name))}
{matchedItems.length && unMatchedItems.length && <option style={{pointerEvents: 'none'}}>--------------------</option>}
{unMatchedItems.map(item => this.renderOption(item.id, item.name))}
@@ -92,6 +106,11 @@ class DualListboxView extends React.Component {
);
}
+ onSelectItems(selectedOptions) {
+ let selectedValues = Object.keys(selectedOptions).map((k) => selectedOptions[k].value);
+ this.setState({selectedValues});
+ }
+
renderOption(value, name) {
return (<option className='dual-list-box-multi-select-text' key={value} value={value}>{name}</option>);
}
@@ -107,17 +126,19 @@ class DualListboxView extends React.Component {
);
}
- renderOperationBarButton(onClick, fontAwesomeIconName){
- return (<div className='dual-list-option' onClick={onClick}><FontAwesome name={fontAwesomeIconName}/></div>);
+ renderOperationBarButton(onClick, iconName){
+ return (<div className='dual-list-option' data-test-id={`operation-icon-${iconName}`} onClick={onClick}><SVGIcon name={iconName}/></div>);
}
addToSelectedList() {
- this.props.onChange(this.props.selectedValuesList.concat(this.refs.availableValues.getValue()));
+ this.props.onChange(this.props.selectedValuesList.concat(this.state.selectedValues));
+ this.setState({selectedValues: []});
}
removeFromSelectedList() {
- const selectedValues = this.refs.selectedValues.getValue();
+ const selectedValues = this.state.selectedValues;
this.props.onChange(this.props.selectedValuesList.filter(value => !selectedValues.find(selectedValue => selectedValue === value)));
+ this.setState({selectedValues: []});
}
addAllToSelectedList() {
diff --git a/openecomp-ui/src/nfvo-components/input/inputOptions/InputOptions.jsx b/openecomp-ui/src/nfvo-components/input/inputOptions/InputOptions.jsx
index 5daaffea41..e8aadc4357 100644
--- a/openecomp-ui/src/nfvo-components/input/inputOptions/InputOptions.jsx
+++ b/openecomp-ui/src/nfvo-components/input/inputOptions/InputOptions.jsx
@@ -1,3 +1,18 @@
+/*!
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
import React from 'react';
import i18n from 'nfvo-utils/i18n/i18n.js';
import classNames from 'classnames';
@@ -13,15 +28,21 @@ class InputOptions extends React.Component {
title: React.PropTypes.string
})),
isEnabledOther: React.PropTypes.bool,
- title: React.PropTypes.string,
+ label: React.PropTypes.string,
selectedValue: React.PropTypes.string,
- multiSelectedEnum: React.PropTypes.array,
+ multiSelectedEnum: React.PropTypes.oneOfType([
+ React.PropTypes.string,
+ React.PropTypes.array
+ ]),
selectedEnum: React.PropTypes.string,
otherValue: React.PropTypes.string,
onEnumChange: React.PropTypes.func,
onOtherChange: React.PropTypes.func,
+ onBlur: React.PropTypes.func,
isRequired: React.PropTypes.bool,
- isMultiSelect: React.PropTypes.bool
+ isMultiSelect: React.PropTypes.bool,
+ hasError: React.PropTypes.bool,
+ disabled: React.PropTypes.bool
};
@@ -41,7 +62,7 @@ class InputOptions extends React.Component {
render() {
let {label, isRequired, values, otherValue, onOtherChange, isMultiSelect, onBlur, multiSelectedEnum, selectedEnum, hasError, validations, children} = this.props;
-
+ const dataTestId = this.props['data-test-id'] ? {'data-test-id': this.props['data-test-id']} : {};
let currentMultiSelectedEnum = [];
let currentSelectedEnum = '';
let {otherInputDisabled} = this.state;
@@ -54,14 +75,18 @@ class InputOptions extends React.Component {
else if(selectedEnum){
currentSelectedEnum = selectedEnum;
}
+ if (!onBlur) {
+ onBlur = () => {};
+ }
let isReadOnlyMode = this.context.isReadOnlyMode;
return(
- <div className={classNames('form-group', {'required' : validations.required , 'has-error' : hasError})}>
+ <div className={classNames('form-group', {'required' : (validations && validations.required) || isRequired, 'has-error' : hasError})}>
{label && <label className='control-label'>{label}</label>}
{isMultiSelect && otherInputDisabled ?
<Select
+ {...dataTestId}
ref='_myInput'
value={currentMultiSelectedEnum}
className='options-input'
@@ -74,18 +99,18 @@ class InputOptions extends React.Component {
multi/> :
<div className={classNames('input-options',{'has-error' : hasError})}>
<select
+ {...dataTestId}
ref={'_myInput'}
label={label}
className='form-control input-options-select'
value={currentSelectedEnum}
- style={{'width' : otherInputDisabled ? '100%' : '95px'}}
+ style={{'width' : otherInputDisabled ? '100%' : '100px'}}
onBlur={() => onBlur()}
disabled={isReadOnlyMode || Boolean(this.props.disabled)}
onChange={ value => this.enumChanged(value)}
type='select'>
- {values && values.length && values.map(val => this.renderOptions(val))}
+ {children || (values && values.length && values.map((val, index) => this.renderOptions(val, index)))}
{onOtherChange && <option key='other' value={other.OTHER}>{i18n(other.OTHER)}</option>}
- {children}
</select>
{!otherInputDisabled && <div className='input-options-separator'/>}
@@ -104,9 +129,9 @@ class InputOptions extends React.Component {
);
}
- renderOptions(val){
- return(
- <option key={val.enum} value={val.enum}>{val.title}</option>
+ renderOptions(val, index){
+ return (
+ <option key={index} value={val.enum}>{val.title}</option>
);
}
@@ -154,9 +179,9 @@ class InputOptions extends React.Component {
enumChanged() {
let enumValue = this.refs._myInput.value;
- let {onEnumChange, isMultiSelect, onChange} = this.props;
+ let {onEnumChange, onOtherChange, isMultiSelect, onChange} = this.props;
this.setState({
- otherInputDisabled: enumValue !== other.OTHER
+ otherInputDisabled: !Boolean(onOtherChange) || enumValue !== other.OTHER
});
let value = isMultiSelect ? [enumValue] : enumValue;
@@ -169,7 +194,7 @@ class InputOptions extends React.Component {
}
multiSelectEnumChanged(enumValue) {
- let {onEnumChange} = this.props;
+ let {onEnumChange, onOtherChange} = this.props;
let selectedValues = enumValue.map(enumVal => {
return enumVal.value;
});
@@ -182,7 +207,7 @@ class InputOptions extends React.Component {
}
this.setState({
- otherInputDisabled: !selectedValues.includes(i18n(other.OTHER))
+ otherInputDisabled: !Boolean(onOtherChange) || !selectedValues.includes(i18n(other.OTHER))
});
onEnumChange(selectedValues);
}
diff --git a/openecomp-ui/src/nfvo-components/input/validation/Form.jsx b/openecomp-ui/src/nfvo-components/input/validation/Form.jsx
new file mode 100644
index 0000000000..47922f86a0
--- /dev/null
+++ b/openecomp-ui/src/nfvo-components/input/validation/Form.jsx
@@ -0,0 +1,114 @@
+/*!
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+import React from 'react';
+import ValidationButtons from './ValidationButtons.jsx';
+
+class Form extends React.Component {
+
+ static defaultProps = {
+ hasButtons : true,
+ onSubmit : null,
+ onReset : null,
+ labledButtons: true,
+ onValidChange : null,
+ isValid: true
+ };
+
+ static propTypes = {
+ isValid : React.PropTypes.bool,
+ formReady : React.PropTypes.bool,
+ isReadOnlyMode : React.PropTypes.bool,
+ hasButtons : React.PropTypes.bool,
+ onSubmit : React.PropTypes.func,
+ onReset : React.PropTypes.func,
+ labledButtons: React.PropTypes.bool,
+ onValidChange : React.PropTypes.func,
+ onValidityChanged: React.PropTypes.func,
+ onValidateForm: React.PropTypes.func
+ };
+
+ constructor(props) {
+ super(props);
+ }
+
+
+ render() {
+ // eslint-disable-next-line no-unused-vars
+ let {isValid, formReady, onValidateForm, isReadOnlyMode, hasButtons, onSubmit, labledButtons, onValidChange, onValidityChanged, onDataChanged, children, ...formProps} = this.props;
+ return (
+ <form {...formProps} ref={(form) => this.form = form} onSubmit={event => this.handleFormValidation(event)}>
+ <div className='validation-form-content'>
+ <fieldset disabled={isReadOnlyMode}>
+ {children}
+ </fieldset>
+ </div>
+ {hasButtons && <ValidationButtons labledButtons={labledButtons} ref={(buttons) => this.buttons = buttons} isReadOnlyMode={isReadOnlyMode}/>}
+ </form>
+ );
+ }
+
+ handleFormValidation(event) {
+ event.preventDefault();
+ if (this.props.onValidateForm && !this.props.formReady){
+ return this.props.onValidateForm();
+ } else {
+ return this.handleFormSubmit(event);
+ }
+ }
+ handleFormSubmit(event) {
+ if (event) {
+ event.preventDefault();
+ }
+ if(this.props.onSubmit) {
+ return this.props.onSubmit(event);
+ }
+ }
+
+ componentDidMount() {
+ if (this.props.hasButtons) {
+ this.buttons.setState({isValid: this.props.isValid});
+ }
+ }
+
+
+
+ componentDidUpdate(prevProps) {
+ // only handling this programatically if the validation of the form is done outside of the view
+ // (example with a form that is dependent on the state of other forms)
+ if (prevProps.isValid !== this.props.isValid) {
+ if (this.props.hasButtons) {
+ this.buttons.setState({isValid: this.props.isValid});
+ }
+ // callback in case form is part of bigger picture in view
+ if (this.props.onValidChange) {
+ this.props.onValidChange(this.props.isValid);
+ }
+
+ // TODO - maybe this has to be part of componentWillUpdate
+ if(this.props.onValidityChanged) {
+ this.props.onValidityChanged(this.props.isValid);
+ }
+ }
+ if (this.props.formReady) { // if form validation succeeded -> continue with submit
+ this.handleFormSubmit();
+ }
+ }
+
+}
+
+
+export default Form;
diff --git a/openecomp-ui/src/nfvo-components/input/validation/Input.jsx b/openecomp-ui/src/nfvo-components/input/validation/Input.jsx
new file mode 100644
index 0000000000..59c35d7993
--- /dev/null
+++ b/openecomp-ui/src/nfvo-components/input/validation/Input.jsx
@@ -0,0 +1,180 @@
+/*!
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+import React from 'react';
+import ReactDOM from 'react-dom';
+import classNames from 'classnames';
+import Checkbox from 'react-bootstrap/lib/Checkbox.js';
+import Radio from 'react-bootstrap/lib/Radio.js';
+import FormGroup from 'react-bootstrap/lib/FormGroup.js';
+import FormControl from 'react-bootstrap/lib/FormControl.js';
+import Overlay from 'react-bootstrap/lib/Overlay.js';
+import Tooltip from 'react-bootstrap/lib/Tooltip.js';
+
+class Input extends React.Component {
+
+ state = {
+ value: this.props.value,
+ checked: this.props.checked,
+ selectedValues: []
+ }
+
+ render() {
+ const {label, isReadOnlyMode, value, onBlur, onKeyDown, type, disabled, checked, name} = this.props;
+ // eslint-disable-next-line no-unused-vars
+ const {groupClassName, isValid = true, errorText, isRequired, ...inputProps} = this.props;
+ let wrapperClassName = (type !== 'radio') ? 'validation-input-wrapper' : 'form-group';
+ if (disabled) {
+ wrapperClassName += ' disabled';
+ }
+ return(
+ <div className={wrapperClassName}>
+ <FormGroup className={classNames('form-group', [groupClassName], {'required' : isRequired , 'has-error' : !isValid})} >
+ {(label && (type !== 'checkbox' && type !== 'radio')) && <label className='control-label'>{label}</label>}
+ {(type === 'text' || type === 'number') &&
+ <FormControl
+ bsClass={'form-control input-options-other'}
+ onChange={(e) => this.onChange(e)}
+ disabled={isReadOnlyMode || Boolean(disabled)}
+ onBlur={onBlur}
+ onKeyDown={onKeyDown}
+ value={value || ''}
+ inputRef={(input) => this.input = input}
+ type={type}
+ data-test-id={this.props['data-test-id']}/>}
+
+ {type === 'textarea' &&
+ <FormControl
+ className='form-control input-options-other'
+ disabled={isReadOnlyMode || Boolean(disabled)}
+ value={value || ''}
+ onBlur={onBlur}
+ onKeyDown={onKeyDown}
+ componentClass={type}
+ onChange={(e) => this.onChange(e)}
+ inputRef={(input) => this.input = input}
+ data-test-id={this.props['data-test-id']}/>}
+
+ {type === 'checkbox' &&
+ <Checkbox
+ className={classNames({'required' : isRequired , 'has-error' : !isValid})}
+ onChange={(e)=>this.onChangeCheckBox(e)}
+ disabled={isReadOnlyMode || Boolean(disabled)}
+ checked={value}
+ data-test-id={this.props['data-test-id']}>{label}</Checkbox>}
+
+ {type === 'radio' &&
+ <Radio name={name}
+ checked={checked}
+ disabled={isReadOnlyMode || Boolean(disabled)}
+ value={value}
+ onChange={(e)=>this.onChangeRadio(e)}
+ data-test-id={this.props['data-test-id']}>{label}</Radio>}
+ {type === 'select' &&
+ <FormControl onClick={ (e) => this.optionSelect(e) }
+ componentClass={type}
+ inputRef={(input) => this.input = input}
+ name={name} {...inputProps}
+ data-test-id={this.props['data-test-id']}/>}
+ </FormGroup>
+ { this.renderErrorOverlay() }
+ </div>
+ );
+ }
+
+ getValue() {
+ return this.props.type !== 'select' ? this.state.value : this.state.selectedValues;
+ }
+
+ getChecked() {
+ return this.state.checked;
+ }
+
+ optionSelect(e) {
+ let selectedValues = [];
+ if (e.target.value) {
+ selectedValues.push(e.target.value);
+ }
+ this.setState({
+ selectedValues
+ });
+ }
+
+ onChange(e) {
+ const {onChange, type} = this.props;
+ let value = e.target.value;
+ if (type === 'number') {
+ value = Number(value);
+ }
+ this.setState({
+ value
+ });
+ onChange(value);
+ }
+
+ onChangeCheckBox(e) {
+ let {onChange} = this.props;
+ this.setState({
+ checked: e.target.checked
+ });
+ onChange(e.target.checked);
+ }
+
+ onChangeRadio(e) {
+ let {onChange} = this.props;
+ this.setState({
+ checked: e.target.checked
+ });
+ onChange(this.state.value);
+ }
+
+ focus() {
+ ReactDOM.findDOMNode(this.input).focus();
+ }
+
+ renderErrorOverlay() {
+ let position = 'right';
+ const {errorText = '', isValid = true, type, overlayPos} = this.props;
+
+ if (overlayPos) {
+ position = overlayPos;
+ }
+ else if (type === 'text'
+ || type === 'email'
+ || type === 'number'
+ || type === 'password') {
+ position = 'bottom';
+ }
+
+ return (
+ <Overlay
+ show={!isValid}
+ placement={position}
+ target={() => {
+ let target = ReactDOM.findDOMNode(this.input);
+ return target.offsetParent ? target : undefined;
+ }}
+ container={this}>
+ <Tooltip
+ id={`error-${errorText.replace(' ', '-')}`}
+ className='validation-error-message'>
+ {errorText}
+ </Tooltip>
+ </Overlay>
+ );
+ }
+
+}
+export default Input;
diff --git a/openecomp-ui/src/nfvo-components/input/validation/InputOptions.jsx b/openecomp-ui/src/nfvo-components/input/validation/InputOptions.jsx
new file mode 100644
index 0000000000..6e54254eb0
--- /dev/null
+++ b/openecomp-ui/src/nfvo-components/input/validation/InputOptions.jsx
@@ -0,0 +1,279 @@
+/*!
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+import React from 'react';
+import ReactDOM from 'react-dom';
+import i18n from 'nfvo-utils/i18n/i18n.js';
+import classNames from 'classnames';
+import Select from 'nfvo-components/input/SelectInput.jsx';
+import Overlay from 'react-bootstrap/lib/Overlay.js';
+import Tooltip from 'react-bootstrap/lib/Tooltip.js';
+
+export const other = {OTHER: 'Other'};
+
+class InputOptions extends React.Component {
+
+ static propTypes = {
+ values: React.PropTypes.arrayOf(React.PropTypes.shape({
+ enum: React.PropTypes.string,
+ title: React.PropTypes.string
+ })),
+ isEnabledOther: React.PropTypes.bool,
+ label: React.PropTypes.string,
+ selectedValue: React.PropTypes.string,
+ multiSelectedEnum: React.PropTypes.oneOfType([
+ React.PropTypes.string,
+ React.PropTypes.array
+ ]),
+ selectedEnum: React.PropTypes.string,
+ otherValue: React.PropTypes.string,
+ overlayPos: React.PropTypes.string,
+ onEnumChange: React.PropTypes.func,
+ onOtherChange: React.PropTypes.func,
+ onBlur: React.PropTypes.func,
+ isRequired: React.PropTypes.bool,
+ isMultiSelect: React.PropTypes.bool,
+ isValid: React.PropTypes.bool,
+ disabled: React.PropTypes.bool
+ };
+
+ state = {
+ otherInputDisabled: !this.props.otherValue
+ };
+
+ oldProps = {
+ selectedEnum: '',
+ otherValue: '',
+ multiSelectedEnum: []
+ };
+
+ render() {
+ let {label, isRequired, values, otherValue, onOtherChange, isMultiSelect, onBlur, multiSelectedEnum, selectedEnum, isValid, children, isReadOnlyMode} = this.props;
+ const dataTestId = this.props['data-test-id'] ? {'data-test-id': this.props['data-test-id']} : {};
+ let currentMultiSelectedEnum = [];
+ let currentSelectedEnum = '';
+ let {otherInputDisabled} = this.state;
+ if (isMultiSelect) {
+ currentMultiSelectedEnum = multiSelectedEnum;
+ if(!otherInputDisabled) {
+ currentSelectedEnum = multiSelectedEnum ? multiSelectedEnum.toString() : undefined;
+ }
+ }
+ else if(selectedEnum){
+ currentSelectedEnum = selectedEnum;
+ }
+ if (!onBlur) {
+ onBlur = () => {};
+ }
+
+ return(
+ <div className='validation-input-wrapper' >
+ <div className={classNames('form-group', {'required' : isRequired, 'has-error' : !isValid})} >
+ {label && <label className='control-label'>{label}</label>}
+ {isMultiSelect && otherInputDisabled ?
+ <Select
+ {...dataTestId}
+ ref={(input) => this.input = input}
+ value={currentMultiSelectedEnum}
+ className='options-input'
+ clearable={false}
+ required={isRequired}
+ disabled={isReadOnlyMode || Boolean(this.props.disabled)}
+ onBlur={() => onBlur()}
+ onMultiSelectChanged={value => this.multiSelectEnumChanged(value)}
+ options={this.renderMultiSelectOptions(values)}
+ multi/> :
+ <div className={classNames('input-options',{'has-error' : !isValid})} >
+ <select
+ {...dataTestId}
+ ref={(input) => this.input = input}
+ label={label}
+ className='form-control input-options-select'
+ value={currentSelectedEnum}
+ style={{'width' : otherInputDisabled ? '100%' : '100px'}}
+ onBlur={() => onBlur()}
+ disabled={isReadOnlyMode || Boolean(this.props.disabled)}
+ onChange={ value => this.enumChanged(value)}
+ type='select'>
+ {children || (values && values.length && values.map((val, index) => this.renderOptions(val, index)))}
+ {onOtherChange && <option key='other' value={other.OTHER}>{i18n(other.OTHER)}</option>}
+ </select>
+
+ {!otherInputDisabled && <div className='input-options-separator'/>}
+ <input
+ className='form-control input-options-other'
+ placeholder={i18n('other')}
+ ref={(otherValue) => this.otherValue = otherValue}
+ style={{'display' : otherInputDisabled ? 'none' : 'block'}}
+ disabled={isReadOnlyMode || Boolean(this.props.disabled)}
+ value={otherValue || ''}
+ onBlur={() => onBlur()}
+ onChange={() => this.changedOtherInput()}/>
+ </div>
+ }
+ </div>
+ { this.renderErrorOverlay() }
+ </div>
+ );
+ }
+
+ renderOptions(val, index){
+ return (
+ <option key={index} value={val.enum}>{val.title}</option>
+ );
+ }
+
+
+ renderMultiSelectOptions(values) {
+ let {onOtherChange} = this.props;
+ let optionsList = [];
+ if (onOtherChange) {
+ optionsList = values.map(option => {
+ return {
+ label: option.title,
+ value: option.enum,
+ };
+ }).concat([{
+ label: i18n(other.OTHER),
+ value: i18n(other.OTHER),
+ }]);
+ }
+ else {
+ optionsList = values.map(option => {
+ return {
+ label: option.title,
+ value: option.enum,
+ };
+ });
+ }
+ if (optionsList.length > 0 && optionsList[0].value === '') {
+ optionsList.shift();
+ }
+ return optionsList;
+ }
+
+ renderErrorOverlay() {
+ let position = 'right';
+ const {errorText = '', isValid = true, type, overlayPos} = this.props;
+
+ if (overlayPos) {
+ position = overlayPos;
+ }
+ else if (type === 'text'
+ || type === 'email'
+ || type === 'number'
+ || type === 'password') {
+ position = 'bottom';
+ }
+
+ return (
+ <Overlay
+ show={!isValid}
+ placement={position}
+ target={() => {
+ let {otherInputDisabled} = this.state;
+ let target = otherInputDisabled ? ReactDOM.findDOMNode(this.input) : ReactDOM.findDOMNode(this.otherValue);
+ return target.offsetParent ? target : undefined;
+ }}
+ container={this}>
+ <Tooltip
+ id={`error-${errorText.replace(' ', '-')}`}
+ className='validation-error-message'>
+ {errorText}
+ </Tooltip>
+ </Overlay>
+ );
+ }
+
+ getValue() {
+ let res = '';
+ let {isMultiSelect} = this.props;
+ let {otherInputDisabled} = this.state;
+
+ if (otherInputDisabled) {
+ res = isMultiSelect ? this.input.getValue() : this.input.value;
+ } else {
+ res = this.otherValue.value;
+ }
+ return res;
+ }
+
+ enumChanged() {
+ let enumValue = this.input.value;
+ let {onEnumChange, onOtherChange, isMultiSelect, onChange} = this.props;
+ this.setState({
+ otherInputDisabled: !Boolean(onOtherChange) || enumValue !== other.OTHER
+ });
+
+ let value = isMultiSelect ? [enumValue] : enumValue;
+ if (onEnumChange) {
+ onEnumChange(value);
+ }
+ if (onChange) {
+ onChange(value);
+ }
+ }
+
+ multiSelectEnumChanged(enumValue) {
+ let {onEnumChange, onOtherChange} = this.props;
+ let selectedValues = enumValue.map(enumVal => {
+ return enumVal.value;
+ });
+
+ if (this.state.otherInputDisabled === false) {
+ selectedValues.shift();
+ }
+ else if (selectedValues.includes(i18n(other.OTHER))) {
+ selectedValues = [i18n(other.OTHER)];
+ }
+
+ this.setState({
+ otherInputDisabled: !Boolean(onOtherChange) || !selectedValues.includes(i18n(other.OTHER))
+ });
+ onEnumChange(selectedValues);
+ }
+
+ changedOtherInput() {
+ let {onOtherChange} = this.props;
+ onOtherChange(this.otherValue.value);
+ }
+
+ componentDidUpdate() {
+ let {otherValue, selectedEnum, onInputChange, multiSelectedEnum} = this.props;
+ if (this.oldProps.otherValue !== otherValue
+ || this.oldProps.selectedEnum !== selectedEnum
+ || this.oldProps.multiSelectedEnum !== multiSelectedEnum) {
+ this.oldProps = {
+ otherValue,
+ selectedEnum,
+ multiSelectedEnum
+ };
+ onInputChange();
+ }
+ }
+
+ static getTitleByName(values, name) {
+ for (let key of Object.keys(values)) {
+ let option = values[key].find(option => option.enum === name);
+ if (option) {
+ return option.title;
+ }
+ }
+ return name;
+ }
+
+}
+
+export default InputOptions;
diff --git a/openecomp-ui/src/nfvo-components/input/validation/InputWrapper.jsx b/openecomp-ui/src/nfvo-components/input/validation/InputWrapper.jsx
new file mode 100644
index 0000000000..5ca716cc20
--- /dev/null
+++ b/openecomp-ui/src/nfvo-components/input/validation/InputWrapper.jsx
@@ -0,0 +1,134 @@
+/*!
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+import React from 'react';
+import ReactDOM from 'react-dom';
+import classNames from 'classnames';
+import Checkbox from 'react-bootstrap/lib/Checkbox.js';
+import Radio from 'react-bootstrap/lib/Radio.js';
+import FormGroup from 'react-bootstrap/lib/FormGroup.js';
+import FormControl from 'react-bootstrap/lib/FormControl.js';
+
+class InputWrapper extends React.Component {
+
+ state = {
+ value: this.props.value,
+ checked: this.props.checked,
+ selectedValues: []
+ }
+
+ render() {
+ const {label, hasError, validations = {}, isReadOnlyMode, value, onBlur, onKeyDown, type, disabled, checked, name} = this.props;
+ const {groupClassName, ...inputProps} = this.props;
+ return(
+ <FormGroup className={classNames('form-group', [groupClassName], {'required' : validations.required , 'has-error' : hasError})} >
+ {(label && (type !== 'checkbox' && type !== 'radio')) && <label className='control-label'>{label}</label>}
+ {(type === 'text' || type === 'number') &&
+ <FormControl
+ bsClass={'form-control input-options-other'}
+ onChange={(e) => this.onChange(e)}
+ disabled={isReadOnlyMode || Boolean(disabled)}
+ onBlur={onBlur}
+ onKeyDown={onKeyDown}
+ value={value || ''}
+ ref={(input) => this.inputWrapper = input}
+ type={type}
+ data-test-id={this.props['data-test-id']}/>}
+
+ {type === 'textarea' &&
+ <FormControl
+ className='form-control input-options-other'
+ disabled={isReadOnlyMode || Boolean(disabled)}
+ value={value || ''}
+ onBlur={onBlur}
+ onKeyDown={onKeyDown}
+ componentClass={type}
+ onChange={(e) => this.onChange(e)}
+ data-test-id={this.props['data-test-id']}/>}
+
+ {type === 'checkbox' &&
+ <Checkbox
+ className={classNames({'required' : validations.required , 'has-error' : hasError})}
+ onChange={(e)=>this.onChangeCheckBox(e)}
+ disabled={isReadOnlyMode || Boolean(disabled)}
+ checked={value}
+ data-test-id={this.props['data-test-id']}>{label}</Checkbox>}
+
+ {type === 'radio' &&
+ <Radio name={name}
+ checked={checked}
+ disabled={isReadOnlyMode || Boolean(disabled)}
+ value={value}
+ onChange={(e)=>this.onChangeRadio(e)}
+ data-test-id={this.props['data-test-id']}>{label}</Radio>}
+ {type === 'select' &&
+ <FormControl onClick={ (e) => this.optionSelect(e) }
+ componentClass={type}
+ name={name} {...inputProps}
+ data-test-id={this.props['data-test-id']}/>}
+
+ </FormGroup>
+
+ );
+ }
+
+ getValue() {
+ return this.props.type !== 'select' ? this.state.value : this.state.selectedValues;
+ }
+
+ getChecked() {
+ return this.state.checked;
+ }
+
+ optionSelect(e) {
+ let selectedValues = [];
+ if (e.target.value) {
+ selectedValues.push(e.target.value);
+ }
+ this.setState({
+ selectedValues
+ });
+ }
+
+ onChange(e) {
+ let {onChange} = this.props;
+ this.setState({
+ value: e.target.value
+ });
+ onChange(e.target.value);
+ }
+
+ onChangeCheckBox(e) {
+ let {onChange} = this.props;
+ this.setState({
+ checked: e.target.checked
+ });
+ onChange(e.target.checked);
+ }
+
+ onChangeRadio(e) {
+ let {onChange} = this.props;
+ this.setState({
+ checked: e.target.checked
+ });
+ onChange(this.state.value);
+ }
+
+ focus() {
+ ReactDOM.findDOMNode(this.inputWrapper).focus();
+ }
+
+}
+export default InputWrapper;
diff --git a/openecomp-ui/src/nfvo-components/input/validation/Tabs.jsx b/openecomp-ui/src/nfvo-components/input/validation/Tabs.jsx
new file mode 100644
index 0000000000..95144b1468
--- /dev/null
+++ b/openecomp-ui/src/nfvo-components/input/validation/Tabs.jsx
@@ -0,0 +1,79 @@
+/*!
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+import React from 'react';
+import ReactDOM from 'react-dom';
+import {default as BTabs} from 'react-bootstrap/lib/Tabs.js';
+import Overlay from 'react-bootstrap/lib/Overlay.js';
+import Tooltip from 'react-bootstrap/lib/Tooltip.js';
+
+import i18n from 'nfvo-utils/i18n/i18n.js';
+
+export default
+class Tabs extends React.Component {
+
+ static propTypes = {
+ children: React.PropTypes.node
+ };
+
+ cloneTab(element) {
+ const {invalidTabs} = this.props;
+ return React.cloneElement(
+ element,
+ {
+ key: element.props.eventKey,
+ tabClassName: invalidTabs.indexOf(element.props.eventKey) > -1 ? 'invalid-tab' : 'valid-tab'
+ }
+ );
+ }
+
+ showTabsError() {
+ const {invalidTabs} = this.props;
+ const showError = ((invalidTabs.length === 1 && invalidTabs[0] !== this.props.activeKey) || (invalidTabs.length > 1));
+ return showError;
+ }
+
+ render() {
+ // eslint-disable-next-line no-unused-vars
+ let {invalidTabs, ...tabProps} = this.props;
+ return (
+ <div>
+ <BTabs {...tabProps} ref='tabsList' id='tabsList' >
+ {this.props.children.map(element => this.cloneTab(element))}
+ </BTabs>
+ <Overlay
+ animation={false}
+ show={this.showTabsError()}
+ placement='bottom'
+ containerPadding={50}
+ target={() => {
+ let target = ReactDOM.findDOMNode(this.refs.tabsList).querySelector('ul > li.invalid-tab:not(.active):nth-of-type(n)');
+ return target && target.offsetParent ? target : undefined;
+ }
+ }
+ container={() => {
+ let target = ReactDOM.findDOMNode(this.refs.tabsList).querySelector('ul > li.invalid-tab:not(.active):nth-of-type(n)');
+ return target && target.offsetParent ? target.offsetParent : this;
+ }}>
+ <Tooltip
+ id='error-some-tabs-contain-errors'
+ className='validation-error-message'>
+ {i18n('One or more tabs are invalid')}
+ </Tooltip>
+ </Overlay>
+ </div>
+ );
+ }
+}
diff --git a/openecomp-ui/src/nfvo-components/input/validation/ValidationButtons.jsx b/openecomp-ui/src/nfvo-components/input/validation/ValidationButtons.jsx
index a87c8d6f40..ebb1473c04 100644
--- a/openecomp-ui/src/nfvo-components/input/validation/ValidationButtons.jsx
+++ b/openecomp-ui/src/nfvo-components/input/validation/ValidationButtons.jsx
@@ -1,3 +1,18 @@
+/*!
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
/**
* Holds the buttons for save/reset for forms.
* Used by the ValidationForm that changes the state of the buttons according to its own state.
@@ -8,7 +23,7 @@
import React from 'react';
import i18n from 'nfvo-utils/i18n/i18n.js';
import Button from 'react-bootstrap/lib/Button.js';
-import FontAwesome from 'react-fontawesome';
+import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx';
class ValidationButtons extends React.Component {
@@ -22,8 +37,8 @@ class ValidationButtons extends React.Component {
};
render() {
- var submitBtn = this.props.labledButtons ? i18n('Save') : <FontAwesome className='check' name='check'/>;
- var closeBtn = this.props.labledButtons ? i18n('Cancel') : <FontAwesome className='close' name='close'/>;
+ var submitBtn = this.props.labledButtons ? i18n('Save') : <SVGIcon className='check' name='check'/>;
+ var closeBtn = this.props.labledButtons ? i18n('Cancel') : <SVGIcon className='close' name='close'/>;
return (
<div className='validation-buttons'>
{!this.props.isReadOnlyMode ?
diff --git a/openecomp-ui/src/nfvo-components/input/validation/ValidationForm.jsx b/openecomp-ui/src/nfvo-components/input/validation/ValidationForm.jsx
deleted file mode 100644
index 098ccf1fd4..0000000000
--- a/openecomp-ui/src/nfvo-components/input/validation/ValidationForm.jsx
+++ /dev/null
@@ -1,200 +0,0 @@
-/**
- * ValidationForm should be used in order to have a form that handles it's internal validation state.
- * All ValidationInputs inside the form are checked for validity and the styling and submit buttons
- * are updated accordingly.
- *
- * The properties that ahould be given to the form:
- * labledButtons - whether or not use icons only as the form default buttons or use buttons with labels
- * onSubmit - function for click on the submit button
- * onReset - function for click on the reset button
- */
-import React from 'react';
-import JSONSchema from 'nfvo-utils/json/JSONSchema.js';
-import JSONPointer from 'nfvo-utils/json/JSONPointer.js';
-import ValidationButtons from './ValidationButtons.jsx';
-
-class ValidationForm extends React.Component {
-
- static childContextTypes = {
- validationParent: React.PropTypes.any,
- isReadOnlyMode: React.PropTypes.bool,
- validationSchema: React.PropTypes.instanceOf(JSONSchema),
- validationData: React.PropTypes.object
- };
-
- static defaultProps = {
- hasButtons : true,
- onSubmit : null,
- onReset : null,
- labledButtons: true,
- onValidChange : null,
- isValid: true
- };
-
- static propTypes = {
- isValid : React.PropTypes.bool,
- isReadOnlyMode : React.PropTypes.bool,
- hasButtons : React.PropTypes.bool,
- onSubmit : React.PropTypes.func,
- onReset : React.PropTypes.func,
- labledButtons: React.PropTypes.bool,
- onValidChange : React.PropTypes.func,
- onValidityChanged: React.PropTypes.func,
- schema: React.PropTypes.object,
- data: React.PropTypes.object
- };
-
- state = {
- isValid: this.props.isValid
- };
-
- constructor(props) {
- super(props);
- this.validationComponents = [];
- }
-
- componentWillMount() {
- let {schema, data} = this.props;
- if (schema) {
- this.processSchema(schema, data);
- }
- }
-
- componentWillReceiveProps(nextProps) {
- let {schema, data} = this.props;
- let {schema: nextSchema, data: nextData} = nextProps;
-
- if (schema !== nextSchema || data !== nextData) {
- if (!schema || !nextSchema) {
- throw new Error('ValidationForm: dynamically adding/removing schema is not supported');
- }
-
- if (schema !== nextSchema) {
- this.processSchema(nextSchema, nextData);
- } else {
- this.setState({data: nextData});
- }
- }
- }
-
- processSchema(rawSchema, rawData) {
- let schema = new JSONSchema();
- schema.setSchema(rawSchema);
- let data = schema.processData(rawData);
- this.setState({
- schema,
- data
- });
- }
-
- render() {
- // eslint-disable-next-line no-unused-vars
- let {isValid, isReadOnlyMode, hasButtons, onSubmit, labledButtons, onValidChange, onValidityChanged, schema, data, children, ...formProps} = this.props;
- return (
- <form {...formProps} onSubmit={event => this.handleFormSubmit(event)}>
- <div className='validation-form-content'>{children}</div>
- {hasButtons && <ValidationButtons labledButtons={labledButtons} ref='buttons' isReadOnlyMode={isReadOnlyMode}/>}
- </form>
- );
- }
-
- handleFormSubmit(event) {
- event.preventDefault();
- let isFormValid = true;
- this.validationComponents.forEach(validationComponent => {
- const isInputValid = validationComponent.validate().isValid;
- isFormValid = isInputValid && isFormValid;
- });
- if(isFormValid && this.props.onSubmit) {
- return this.props.onSubmit(event);
- } else if(!isFormValid) {
- this.setState({isValid: false});
- }
- }
-
- componentWillUpdate(nextProps, nextState) {
- if(this.state.isValid !== nextState.isValid && this.props.onValidityChanged) {
- this.props.onValidityChanged(nextState.isValid);
- }
- }
-
- componentDidUpdate(prevProps, prevState) {
- // only handling this programatically if the validation of the form is done outside of the view
- // (example with a form that is dependent on the state of other forms)
- if (prevProps.isValid !== this.props.isValid) {
- if (this.props.hasButtons) {
- this.refs.buttons.setState({isValid: this.state.isValid});
- }
- } else if(this.state.isValid !== prevState.isValid) {
- if (this.props.hasButtons) {
- this.refs.buttons.setState({isValid: this.state.isValid});
- }
- // callback in case form is part of bigger picture in view
- if (this.props.onValidChange) {
- this.props.onValidChange(this.state.isValid);
- }
- }
- }
-
- componentDidMount() {
- if (this.props.hasButtons) {
- this.refs.buttons.setState({isValid: this.state.isValid});
- }
- }
-
-
- getChildContext() {
- return {
- validationParent: this,
- isReadOnlyMode: this.props.isReadOnlyMode,
- validationSchema: this.state.schema,
- validationData: this.state.data
- };
- }
-
-
- /***
- * Used by ValidationInput in order to let the (parent) form know
- * the valid state. If there is a change in the state of the form,
- * the buttons will be updated.
- *
- * @param validationComponent
- * @param isValid
- */
- childValidStateChanged(validationComponent, isValid) {
- if (isValid !== this.state.isValid) {
- let oldState = this.state.isValid;
- let newState = isValid && this.validationComponents.filter(otherValidationComponent => validationComponent !== otherValidationComponent).every(otherValidationComponent => {
- return otherValidationComponent.isValid();
- });
-
- if (oldState !== newState) {
- this.setState({isValid: newState});
- }
- }
- }
-
- register(validationComponent) {
- if (this.state.schema) {
- // TODO: register
- } else {
- this.validationComponents.push(validationComponent);
- }
- }
-
- unregister(validationComponent) {
- this.childValidStateChanged(validationComponent, true);
- this.validationComponents = this.validationComponents.filter(otherValidationComponent => validationComponent !== otherValidationComponent);
- }
-
- onValueChanged(pointer, value, isValid, error) {
- this.props.onDataChanged({
- data: JSONPointer.setValue(this.props.data, pointer, value),
- isValid,
- error
- });
- }
-}
-
-
-export default ValidationForm;
diff --git a/openecomp-ui/src/nfvo-components/input/validation/ValidationInput.jsx b/openecomp-ui/src/nfvo-components/input/validation/ValidationInput.jsx
deleted file mode 100644
index 0f14307645..0000000000
--- a/openecomp-ui/src/nfvo-components/input/validation/ValidationInput.jsx
+++ /dev/null
@@ -1,509 +0,0 @@
-/**
- * Used for inputs on a validation form.
- * All properties will be passed on to the input element.
- *
- * The following properties can be set for OOB validations and callbacks:
- - required: Boolean: Should be set to true if the input must have a value
- - numeric: Boolean : Should be set to true id the input should be an integer
- - onChange : Function : Will be called to validate the value if the default validations are not sufficient, should return a boolean value
- indicating whether the value is valid
- - didUpdateCallback :Function: Will be called after the state has been updated and the component has rerendered. This can be used if
- there are dependencies between inputs in a form.
- *
- * The following properties of the state can be set to determine
- * the state of the input from outside components:
- - isValid : Boolean - whether the value is valid
- - value : value for the input field,
- - disabled : Boolean,
- - required : Boolean - whether the input value must be filled out.
- */
-import React from 'react';
-import ReactDOM from 'react-dom';
-import Validator from 'validator';
-import FormGroup from 'react-bootstrap/lib/FormGroup.js';
-import Input from 'react-bootstrap/lib/Input.js';
-import Overlay from 'react-bootstrap/lib/Overlay.js';
-import Tooltip from 'react-bootstrap/lib/Tooltip.js';
-import isEqual from 'lodash/isEqual.js';
-import i18n from 'nfvo-utils/i18n/i18n.js';
-import JSONSchema from 'nfvo-utils/json/JSONSchema.js';
-import JSONPointer from 'nfvo-utils/json/JSONPointer.js';
-
-
-import InputOptions from '../inputOptions/InputOptions.jsx';
-
-const globalValidationFunctions = {
- required: value => value !== '',
- maxLength: (value, length) => Validator.isLength(value, {max: length}),
- minLength: (value, length) => Validator.isLength(value, {min: length}),
- pattern: (value, pattern) => Validator.matches(value, pattern),
- numeric: value => {
- if (value === '') {
- // to allow empty value which is not zero
- return true;
- }
- return Validator.isNumeric(value);
- },
- maxValue: (value, maxValue) => value < maxValue,
- minValue: (value, minValue) => value >= minValue,
- alphanumeric: value => Validator.isAlphanumeric(value),
- alphanumericWithSpaces: value => Validator.isAlphanumeric(value.replace(/ /g, '')),
- validateName: value => Validator.isAlphanumeric(value.replace(/\s|\.|\_|\-/g, ''), 'en-US'),
- validateVendorName: value => Validator.isAlphanumeric(value.replace(/[\x7F-\xFF]|\s/g, ''), 'en-US'),
- freeEnglishText: value => Validator.isAlphanumeric(value.replace(/\s|\.|\_|\-|\,|\(|\)|\?/g, ''), 'en-US'),
- email: value => Validator.isEmail(value),
- ip: value => Validator.isIP(value),
- url: value => Validator.isURL(value)
-};
-
-const globalValidationMessagingFunctions = {
- required: () => i18n('Field is required'),
- maxLength: (value, maxLength) => i18n('Field value has exceeded it\'s limit, {maxLength}. current length: {length}', {
- length: value.length,
- maxLength
- }),
- minLength: (value, minLength) => i18n('Field value should contain at least {minLength} characters.', {minLength}),
- pattern: (value, pattern) => i18n('Field value should match the pattern: {pattern}.', {pattern}),
- numeric: () => i18n('Field value should contain numbers only.'),
- maxValue: (value, maxValue) => i18n('Field value should be less than: {maxValue}.', {maxValue}),
- minValue: (value, minValue) => i18n('Field value should be at least: {minValue}.', {minValue}),
- alphanumeric: () => i18n('Field value should contain letters or digits only.'),
- alphanumericWithSpaces: () => i18n('Field value should contain letters, digits or spaces only.'),
- validateName: ()=> i18n('Field value should contain English letters, digits , spaces, underscores, dashes and dots only.'),
- validateVendorName: ()=> i18n('Field value should contain English letters digits and spaces only.'),
- freeEnglishText: ()=> i18n('Field value should contain English letters, digits , spaces, underscores, dashes and dots only.'),
- email: () => i18n('Field value should be a valid email address.'),
- ip: () => i18n('Field value should be a valid ip address.'),
- url: () => i18n('Field value should be a valid url address.'),
- general: () => i18n('Field value is invalid.')
-};
-
-class ValidationInput extends React.Component {
-
- static contextTypes = {
- validationParent: React.PropTypes.any,
- isReadOnlyMode: React.PropTypes.bool,
- validationSchema: React.PropTypes.instanceOf(JSONSchema),
- validationData: React.PropTypes.object
- };
-
- static defaultProps = {
- onChange: null,
- disabled: null,
- didUpdateCallback: null,
- validations: {},
- value: ''
- };
-
- static propTypes = {
- type: React.PropTypes.string.isRequired,
- onChange: React.PropTypes.func,
- disabled: React.PropTypes.bool,
- didUpdateCallback: React.PropTypes.func,
- validations: React.PropTypes.object,
- isMultiSelect: React.PropTypes.bool,
- onOtherChange: React.PropTypes.func,
- pointer: React.PropTypes.string
- };
-
-
- state = {
- isValid: true,
- style: null,
- value: this.props.value,
- error: {},
- previousErrorMessage: '',
- wasInvalid: false,
- validations: this.props.validations,
- isMultiSelect: this.props.isMultiSelect
- };
-
- componentWillMount() {
- if (this.context.validationSchema) {
- let {validationSchema: schema, validationData: data} = this.context,
- {pointer} = this.props;
-
- if (!schema.exists(pointer)) {
- console.error(`Field doesn't exists in the schema ${pointer}`);
- }
-
- let value = JSONPointer.getValue(data, pointer);
- if (value === undefined) {
- value = schema.getDefault(pointer);
- if (value === undefined) {
- value = '';
- }
- }
- this.setState({value});
-
- let enums = schema.getEnum(pointer);
- if (enums) {
- let values = enums.map(value => ({enum: value, title: value, groupName: pointer})),
- isMultiSelect = schema.isArray(pointer);
-
- if (!isMultiSelect && this.props.type !== 'radiogroup') {
- values = [{enum: '', title: i18n('Select...')}, ...values];
- }
- if (isMultiSelect && Array.isArray(value) && value.length === 0) {
- value = '';
- }
-
- this.setState({
- isMultiSelect,
- values,
- onEnumChange: value => this.changedInputOptions(value),
- value
- });
- }
-
- this.setState({validations: this.extractValidationsFromSchema(schema, pointer, this.props)});
- }
- }
-
- extractValidationsFromSchema(schema, pointer, props) {
- /* props are here to get precedence over the scheme definitions */
- let validations = {};
-
- if (schema.isRequired(pointer)) {
- validations.required = true;
- }
-
- if (schema.isNumber(pointer)) {
- validations.numeric = true;
-
- const maxValue = props.validations.maxValue || schema.getMaxValue(pointer);
- if (maxValue !== undefined) {
- validations.maxValue = maxValue;
- }
-
- const minValue = props.validations.minValue || schema.getMinValue(pointer);
- if (minValue !== undefined) {
- validations.minValue = minValue;
- }
- }
-
-
- if (schema.isString(pointer)) {
-
- const pattern = schema.getPattern(pointer);
- if (pattern) {
- validations.pattern = pattern;
- }
-
- const maxLength = schema.getMaxLength(pointer);
- if (maxLength !== undefined) {
- validations.maxLength = maxLength;
- }
-
- const minLength = schema.getMinLength(pointer);
- if (minLength !== undefined) {
- validations.minLength = minLength;
- }
- }
-
- return validations;
- }
-
- componentWillReceiveProps({value: nextValue, validations: nextValidations, pointer: nextPointer}, nextContext) {
- const {validations, value} = this.props;
- const validationsChanged = !isEqual(validations, nextValidations);
- if (nextContext.validationSchema) {
- if (this.props.pointer !== nextPointer ||
- this.context.validationData !== nextContext.validationData) {
- let currentValue = JSONPointer.getValue(this.context.validationData, this.props.pointer),
- nextValue = JSONPointer.getValue(nextContext.validationData, nextPointer);
- if(nextValue === undefined) {
- nextValue = '';
- }
- if (this.state.isMultiSelect && Array.isArray(nextValue) && nextValue.length === 0) {
- nextValue = '';
- }
- if (currentValue !== nextValue) {
- this.setState({value: nextValue});
- }
- if (validationsChanged) {
- this.setState({
- validations: this.extractValidationsFromSchema(nextContext.validationSchema, nextPointer, {validations: nextValidations})
- });
- }
- }
- } else {
- if (validationsChanged) {
- this.setState({validations: nextValidations});
- }
- if (this.state.wasInvalid && (value !== nextValue || validationsChanged)) {
- this.validate(nextValue, nextValidations);
- } else if (value !== nextValue) {
- this.setState({value: nextValue});
- }
- }
- }
-
- shouldTypeBeNumberBySchemeDefinition(pointer) {
- return this.context.validationSchema &&
- this.context.validationSchema.isNumber(pointer);
- }
-
- hasEnum(pointer) {
- return this.context.validationSchema &&
- this.context.validationSchema.getEnum(pointer);
- }
-
- render() {
- let {value, isMultiSelect, values, onEnumChange, style, isValid, validations} = this.state;
- let {onOtherChange, type, pointer} = this.props;
- if (this.shouldTypeBeNumberBySchemeDefinition(pointer) && !this.hasEnum(pointer)) {
- type = 'number';
- }
- let props = {...this.props};
-
- let groupClasses = this.props.groupClassName || '';
- if (validations.required) {
- groupClasses += ' required';
- }
- let isReadOnlyMode = this.context.isReadOnlyMode;
-
- if (value === true && (type === 'checkbox' || type === 'radio')) {
- props.checked = true;
- }
- return (
- <div className='validation-input-wrapper'>
- {
- !isMultiSelect && !onOtherChange && type !== 'select' && type !== 'radiogroup'
- && <Input
- {...props}
- type={type}
- groupClassName={groupClasses}
- ref={'_myInput'}
- value={value}
- disabled={isReadOnlyMode || Boolean(this.props.disabled)}
- bsStyle={style}
- onChange={() => this.changedInput()}
- onBlur={() => this.blurInput()}>
- {this.props.children}
- </Input>
- }
- {
- type === 'radiogroup'
- && <FormGroup>
- {
- values.map(val =>
- <Input disabled={isReadOnlyMode || Boolean(this.props.disabled)}
- inline={true}
- ref={'_myInput' + (typeof val.enum === 'string' ? val.enum.replace(/\W/g, '_') : val.enum)}
- value={val.enum} checked={value === val.enum}
- type='radio' label={val.title}
- name={val.groupName}
- onChange={() => this.changedInput()}/>
- )
- }
- </FormGroup>
- }
- {
- (isMultiSelect || onOtherChange || type === 'select')
- && <InputOptions
- onInputChange={() => this.changedInput()}
- onBlur={() => this.blurInput()}
- hasError={!isValid}
- ref={'_myInput'}
- isMultiSelect={isMultiSelect}
- values={values}
- onEnumChange={onEnumChange}
- selectedEnum={value}
- multiSelectedEnum={value}
- {...props} />
- }
- {this.renderOverlay()}
- </div>
- );
- }
-
- renderOverlay() {
- let position = 'right';
- if (this.props.type === 'text'
- || this.props.type === 'email'
- || this.props.type === 'number'
- || this.props.type === 'password'
-
- ) {
- position = 'bottom';
- }
-
- let validationMessage = this.state.error.message || this.state.previousErrorMessage;
- return (
- <Overlay
- show={!this.state.isValid}
- placement={position}
- target={() => {
- let target = ReactDOM.findDOMNode(this.refs._myInput);
- return target.offsetParent ? target : undefined;
- }}
- container={this}>
- <Tooltip
- id={`error-${validationMessage.replace(' ', '-')}`}
- className='validation-error-message'>
- {validationMessage}
- </Tooltip>
- </Overlay>
- );
- }
-
- componentDidMount() {
- if (this.context.validationParent) {
- this.context.validationParent.register(this);
- }
- }
-
- componentDidUpdate(prevProps, prevState) {
- if (this.context.validationParent) {
- if (prevState.isValid !== this.state.isValid) {
- this.context.validationParent.childValidStateChanged(this, this.state.isValid);
- }
- }
- if (this.props.didUpdateCallback) {
- this.props.didUpdateCallback();
- }
-
- }
-
- componentWillUnmount() {
- if (this.context.validationParent) {
- this.context.validationParent.unregister(this);
- }
- }
-
- isNumberInputElement() {
- return this.props.type === 'number' || this.refs._myInput.props.type === 'number';
- }
-
- /***
- * Adding same method as the actual input component
- * @returns {*}
- */
- getValue() {
- if (this.props.type === 'checkbox') {
- return this.refs._myInput.getChecked();
- }
- if (this.props.type === 'radiogroup') {
- for (let key in this.refs) { // finding the value of the radio button that was checked
- if (this.refs[key].getChecked()) {
- return this.refs[key].getValue();
- }
- }
- }
- if (this.isNumberInputElement()) {
- return Number(this.refs._myInput.getValue());
- }
-
- return this.refs._myInput.getValue();
- }
-
- resetValue() {
- this.setState({value: this.props.value});
- }
-
-
- /***
- * internal method that validated the value. includes callback to the onChange method
- * @param value
- * @param validations - map containing validation id and the limitation describing the validation.
- * @returns {object}
- */
- validateValue = (value, validations) => {
- let {customValidationFunction} = validations;
- let error = {};
- let isValid = true;
- for (let validation in validations) {
- if ('customValidationFunction' !== validation) {
- if (validations[validation]) {
- if (!globalValidationFunctions[validation](value, validations[validation])) {
- error.id = validation;
- error.message = globalValidationMessagingFunctions[validation](value, validations[validation]);
- isValid = false;
- break;
- }
- }
- } else {
- let customValidationResult = customValidationFunction(value);
-
- if (customValidationResult !== true) {
- error.id = 'custom';
- isValid = false;
- if (typeof customValidationResult === 'string') {//custom validation error message supplied.
- error.message = customValidationResult;
- } else {
- error.message = globalValidationMessagingFunctions.general();
- }
- break;
- }
-
-
- }
- }
-
- return {
- isValid,
- error
- };
- };
-
- /***
- * Internal method that handles the change event of the input. validates and updates the state.
- */
- changedInput() {
-
- let {isValid, error} = this.state.wasInvalid ? this.validate() : this.state;
- let onChange = this.props.onChange;
- if (onChange) {
- onChange(this.getValue(), isValid, error);
- }
- if (this.context.validationSchema) {
- let value = this.getValue();
- if (this.state.isMultiSelect && value === '') {
- value = [];
- }
- if (this.shouldTypeBeNumberBySchemeDefinition(this.props.pointer)) {
- value = Number(value);
- }
- this.context.validationParent.onValueChanged(this.props.pointer, value, isValid, error);
- }
- }
-
- changedInputOptions(value) {
- this.context.validationParent.onValueChanged(this.props.pointer, value, true);
- }
-
- blurInput() {
- if (!this.state.wasInvalid) {
- this.setState({wasInvalid: true});
- }
-
- let {isValid, error} = !this.state.wasInvalid ? this.validate() : this.state;
- let onBlur = this.props.onBlur;
- if (onBlur) {
- onBlur(this.getValue(), isValid, error);
- }
- }
-
- validate(value = this.getValue(), validations = this.state.validations) {
- let validationStatus = this.validateValue(value, validations);
- let {isValid, error} = validationStatus;
- let _style = isValid ? null : 'error';
- this.setState({
- isValid,
- error,
- value,
- previousErrorMessage: this.state.error.message || '',
- style: _style,
- wasInvalid: !isValid || this.state.wasInvalid
- });
-
- return validationStatus;
- }
-
- isValid() {
- return this.state.isValid;
- }
-
-}
-export default ValidationInput;
diff --git a/openecomp-ui/src/nfvo-components/input/validation/ValidationTab.jsx b/openecomp-ui/src/nfvo-components/input/validation/ValidationTab.jsx
deleted file mode 100644
index 6036518288..0000000000
--- a/openecomp-ui/src/nfvo-components/input/validation/ValidationTab.jsx
+++ /dev/null
@@ -1,107 +0,0 @@
-import React from 'react';
-import Tab from 'react-bootstrap/lib/Tab.js';
-
-export default
-class ValidationTab extends React.Component {
-
- static propTypes = {
- children: React.PropTypes.node,
- eventKey: React.PropTypes.any.isRequired,
- onValidationStateChange: React.PropTypes.func //This property is assigned dynamically via React.cloneElement. lookup ValidationTabs.jsx. therefore it cannot be stated as required!
- };
-
- constructor(props) {
- super(props);
- this.validationComponents = [];
- }
-
- static childContextTypes = {
- validationParent: React.PropTypes.any
- };
-
- static contextTypes = {
- validationParent: React.PropTypes.any
- };
-
- getChildContext() {
- return {validationParent: this};
- }
-
- state = {
- isValid: true,
- notifyParent: false
- };
-
- componentDidMount() {
- let validationParent = this.context.validationParent;
- if (validationParent) {
- validationParent.register(this);
- }
- }
-
- componentWillUnmount() {
- let validationParent = this.context.validationParent;
- if (validationParent) {
- validationParent.unregister(this);
- }
- }
-
- register(validationComponent) {
- this.validationComponents.push(validationComponent);
- }
-
- unregister(validationComponent) {
- this.childValidStateChanged(validationComponent, true);
- this.validationComponents = this.validationComponents.filter(otherValidationComponent => validationComponent !== otherValidationComponent);
- }
-
- notifyValidStateChangedToParent(isValid) {
-
- let validationParent = this.context.validationParent;
- if (validationParent) {
- validationParent.childValidStateChanged(this, isValid);
- }
- }
-
- childValidStateChanged(validationComponent, isValid) {
-
- const currentValidState = this.state.isValid;
- if (isValid !== currentValidState) {
- let filteredValidationComponents = this.validationComponents.filter(otherValidationComponent => validationComponent !== otherValidationComponent);
- let newValidState = isValid && filteredValidationComponents.every(otherValidationComponent => {
- return otherValidationComponent.isValid();
- });
- this.setState({isValid: newValidState, notifyParent: true});
- }
- }
-
- validate() {
- let isValid = true;
- this.validationComponents.forEach(validationComponent => {
- const isValidationComponentValid = validationComponent.validate().isValid;
- isValid = isValidationComponentValid && isValid;
- });
- this.setState({isValid, notifyParent: false});
- return {isValid};
- }
-
- componentDidUpdate(prevProps, prevState) {
- if(prevState.isValid !== this.state.isValid) {
- if(this.state.notifyParent) {
- this.notifyValidStateChangedToParent(this.state.isValid);
- }
- this.props.onValidationStateChange(this.props.eventKey, this.state.isValid);
- }
- }
-
- isValid() {
- return this.state.isValid;
- }
-
- render() {
- let {children, ...tabProps} = this.props;
- return (
- <Tab {...tabProps}>{children}</Tab>
- );
- }
-}
diff --git a/openecomp-ui/src/nfvo-components/input/validation/ValidationTabs.jsx b/openecomp-ui/src/nfvo-components/input/validation/ValidationTabs.jsx
deleted file mode 100644
index 6eda4b9827..0000000000
--- a/openecomp-ui/src/nfvo-components/input/validation/ValidationTabs.jsx
+++ /dev/null
@@ -1,72 +0,0 @@
-import React from 'react';
-import ReactDOM from 'react-dom';
-import Tabs from 'react-bootstrap/lib/Tabs.js';
-import Overlay from 'react-bootstrap/lib/Overlay.js';
-import Tooltip from 'react-bootstrap/lib/Tooltip.js';
-
-import i18n from 'nfvo-utils/i18n/i18n.js';
-
-export default
-class ValidationTab extends React.Component {
-
- static propTypes = {
- children: React.PropTypes.node
- };
-
- state = {
- invalidTabs: []
- };
-
- cloneTab(element) {
- const {invalidTabs} = this.state;
- return React.cloneElement(
- element,
- {
- key: element.props.eventKey,
- tabClassName: invalidTabs.indexOf(element.props.eventKey) > -1 ? 'invalid-tab' : 'valid-tab',
- onValidationStateChange: (eventKey, isValid) => this.validTabStateChanged(eventKey, isValid)
- }
- );
- }
-
- validTabStateChanged(eventKey, isValid) {
- let {invalidTabs} = this.state;
- let invalidTabIndex = invalidTabs.indexOf(eventKey);
- if (isValid && invalidTabIndex > -1) {
- this.setState({invalidTabs: invalidTabs.filter(otherEventKey => eventKey !== otherEventKey)});
- } else if (!isValid && invalidTabIndex === -1) {
- this.setState({invalidTabs: [...invalidTabs, eventKey]});
- }
- }
-
- showTabsError() {
- const {invalidTabs} = this.state;
- return invalidTabs.length > 0 && (invalidTabs.length > 1 || invalidTabs[0] !== this.props.activeKey);
- }
-
- render() {
- return (
- <div>
- <Tabs {...this.props} ref='tabsList'>
- {this.props.children.map(element => this.cloneTab(element))}
- </Tabs>
- <Overlay
- animation={false}
- show={this.showTabsError()}
- placement='bottom'
- target={() => {
- let target = ReactDOM.findDOMNode(this.refs.tabsList).querySelector('ul > li.invalid-tab:not(.active):nth-of-type(n)');
- return target && target.offsetParent ? target : undefined;
- }
- }
- container={this}>
- <Tooltip
- id='error-some-tabs-contain-errors'
- className='validation-error-message'>
- {i18n('One or more tabs are invalid')}
- </Tooltip>
- </Overlay>
- </div>
- );
- }
-}