Other option for license agreement term

Issue-Id: SDC-290

Change-Id: Ifeae84fd96175b656814e8b70bc67789f57ef78e
Signed-off-by: ilanap <ilanap@amdocs.com>
diff --git a/openecomp-ui/src/nfvo-components/input/inputOptions/InputOptions.jsx b/openecomp-ui/src/nfvo-components/input/inputOptions/InputOptions.jsx
deleted file mode 100644
index e8aadc4..0000000
--- a/openecomp-ui/src/nfvo-components/input/inputOptions/InputOptions.jsx
+++ /dev/null
@@ -1,246 +0,0 @@
-/*!
- * 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';
-import Select from 'nfvo-components/input/SelectInput.jsx';
-
-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,
-		onEnumChange: React.PropTypes.func,
-		onOtherChange: React.PropTypes.func,
-		onBlur: React.PropTypes.func,
-		isRequired: React.PropTypes.bool,
-		isMultiSelect: React.PropTypes.bool,
-		hasError: React.PropTypes.bool,
-		disabled: React.PropTypes.bool
-	};
-
-
-	static contextTypes = {
-		isReadOnlyMode: React.PropTypes.bool
-	};
-
-	state = {
-		otherInputDisabled: !this.props.otherValue
-	};
-
-	oldProps = {
-		selectedEnum: '',
-		otherValue: '',
-		multiSelectedEnum: []
-	};
-
-	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;
-		if (isMultiSelect) {
-			currentMultiSelectedEnum = multiSelectedEnum;
-			if(!otherInputDisabled) {
-				currentSelectedEnum = multiSelectedEnum ? multiSelectedEnum.toString() : undefined;
-			}
-		}
-		else if(selectedEnum){
-			currentSelectedEnum = selectedEnum;
-		}
-		if (!onBlur) {
-			onBlur = () => {};
-		}
-
-		let isReadOnlyMode = this.context.isReadOnlyMode;
-
-		return(
-			<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'
-						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' : hasError})}>
-						<select
-							{...dataTestId}
-							ref={'_myInput'}
-							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'
-							style={{'display' : otherInputDisabled ? 'none' : 'block'}}
-							disabled={isReadOnlyMode || Boolean(this.props.disabled)}
-							value={otherValue || ''}
-							onBlur={() => onBlur()}
-							onChange={() => this.changedOtherInput()}/>
-					</div>
-				}
-			</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;
-	}
-
-	getValue() {
-		let res = '';
-		let {isMultiSelect} = this.props;
-		let {otherInputDisabled} = this.state;
-
-		if (otherInputDisabled) {
-			res = isMultiSelect ? this.refs._myInput.getValue() : this.refs._myInput.value;
-		} else {
-			res = this.refs._otherValue.value;
-		}
-		return res;
-	}
-
-	enumChanged() {
-		let enumValue = this.refs._myInput.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.refs._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-utils/Validator.js b/openecomp-ui/src/nfvo-utils/Validator.js
index 1df82a2..3f6a00e 100644
--- a/openecomp-ui/src/nfvo-utils/Validator.js
+++ b/openecomp-ui/src/nfvo-utils/Validator.js
@@ -46,7 +46,25 @@
 			email: value => ValidatorJS.isEmail(value),
 			ip: value => ValidatorJS.isIP(value),
 			url: value => ValidatorJS.isURL(value),
-			alphanumericWithUnderscores: value => ValidatorJS.isAlphanumeric(value.replace(/_/g, ''))
+			alphanumericWithUnderscores: value => ValidatorJS.isAlphanumeric(value.replace(/_/g, '')),
+			requiredChoiceWithOther: (value, otherValue) => {
+				let chosen = value.choice;
+				// if we have an empty multiple select we have a problem since it's required
+				let validationFunc = this.globalValidationFunctions['required'];
+				if (value.choices) {
+					if (value.choices.length === 0) {
+						return  false;
+					} else {
+						// continuing validation with the first chosen value in case we have the 'Other' field
+						chosen = value.choices[0];
+					}
+				}
+				if (chosen !== otherValue) {
+					return validationFunc(chosen, true);
+				} else { // when 'Other' was chosen, validate other value
+					return validationFunc(value.other, true);
+				}
+			}
 		};
 	}
 
@@ -54,6 +72,7 @@
 		return {
 			required: () => i18n('Field is required'),
 			requiredChooseOption: () => i18n('Field should have one of these options'),
+			requiredChoiceWithOther: () => i18n('Field is required'),
 			maxLength: (value, maxLength) => i18n('Field value has exceeded it\'s limit, {maxLength}. current length: {length}', {
 				length: value.length,
 				maxLength
diff --git a/openecomp-ui/src/sdc-app/common/reducers/PlainDataReducer.js b/openecomp-ui/src/sdc-app/common/reducers/PlainDataReducer.js
index 2276984..49f1e3d 100644
--- a/openecomp-ui/src/sdc-app/common/reducers/PlainDataReducer.js
+++ b/openecomp-ui/src/sdc-app/common/reducers/PlainDataReducer.js
@@ -16,7 +16,7 @@
 import {actionTypes} from './PlainDataReducerConstants.js';
 import Validator from 'nfvo-utils/Validator.js';
 import forOwn from 'lodash/forOwn.js';
-import {other as optionInputOther} from 'nfvo-components/input/inputOptions/InputOptions.jsx';
+import {other as optionInputOther} from 'nfvo-components/input/validation/InputOptions.jsx';
 
 function updateDataAndValidateReducer(state = {}, action) {
 	let genericFieldInfoCopy;
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditorReducer.js
index 5be1405..9cff279 100644
--- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditorReducer.js
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditorReducer.js
@@ -14,6 +14,7 @@
  * permissions and limitations under the License.
  */
 import {actionTypes, defaultState, LA_EDITOR_FORM, enums as LicenseAgreementEnums} from './LicenseAgreementConstants.js';
+import {other as optionInputOther} from 'nfvo-components/input/validation/InputOptions.jsx';
 
 export default (state = {}, action) => {
 	switch (action.type) {
@@ -38,7 +39,7 @@
 					'licenseTerm' : {
 						isValid: true,
 						errorText: '',
-						validations: [{type: 'required', data: true}],
+						validations: [{type: 'required', data: true}, {type: 'requiredChoiceWithOther', data: optionInputOther.OTHER}],
 						tabId: LicenseAgreementEnums.SELECTED_LICENSE_AGREEMENT_TAB.GENERAL
 					},
 					'name' : {
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditorView.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditorView.jsx
index a15e5da..0b41868 100644
--- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditorView.jsx
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementEditorView.jsx
@@ -21,9 +21,11 @@
 import Tabs from 'nfvo-components/input/validation/Tabs.jsx';
 import Tab from 'sdc-ui/lib/react/Tab.js';
 import Input from 'nfvo-components/input/validation/Input.jsx';
+import InputOptions from 'nfvo-components/input/validation/InputOptions.jsx';
 import DualListboxView from 'nfvo-components/input/dualListbox/DualListboxView.jsx';
 import i18n from 'nfvo-utils/i18n/i18n.js';
 import Validator from 'nfvo-utils/Validator.js';
+import {other as optionInputOther} from 'nfvo-components/input/validation/InputOptions.jsx';
 
 import {enums as LicenseAgreementEnums, optionsInputValues as LicenseAgreementOptionsInputValues, LA_EDITOR_FORM} from './LicenseAgreementConstants.js';
 
@@ -43,7 +45,7 @@
 });
 
 
-const GeneralTabContent = ({data, genericFieldInfo, onDataChanged, validateName, validateLTChoice}) => {
+const GeneralTabContent = ({data, genericFieldInfo, onDataChanged, validateName}) => {
 	let {name, description, requirementsAndConstrains, licenseTerm} = data;
 	return (
 		<GridSection>
@@ -67,24 +69,22 @@
 					data-test-id='create-la-requirements-constants'
 					name='license-agreement-requirements-and-constraints'
 					type='textarea'/>
-				<Input
+				<InputOptions
+					onInputChange={()=>{}}
+					isMultiSelect={false}
+					onEnumChange={licenseTerm => onDataChanged({licenseTerm:{choice: licenseTerm, other: ''}},
+						LA_EDITOR_FORM)}
+					onOtherChange={licenseTerm => onDataChanged({licenseTerm:{choice: optionInputOther.OTHER,
+						other: licenseTerm}}, LA_EDITOR_FORM)}
 					label={i18n('License Term')}
-					type='select'
-					value={licenseTerm && licenseTerm.choice}
+					data-test-id='create-la-license-term'
 					isRequired={true}
-					onChange={e => {
-						const selectedIndex = e.target.selectedIndex;
-						const licenseTerm = e.target.options[selectedIndex].value;
-						onDataChanged({licenseTerm:{choice: licenseTerm, other: ''}}, LA_EDITOR_FORM, { licenseTerm: validateLTChoice });
-					}}
+					type='select'
+					selectedEnum={licenseTerm && licenseTerm.choice}
+					otherValue={licenseTerm && licenseTerm.other}
+					values={LicenseAgreementOptionsInputValues.LICENSE_MODEL_TYPE}
 					isValid={genericFieldInfo.licenseTerm.isValid}
-					errorText={genericFieldInfo.licenseTerm.errorText}
-					className='input-options-select'
-					groupClassName='bootstrap-input-options'
-					data-test-id='create-la-license-term' >
-					{LicenseAgreementOptionsInputValues.LICENSE_MODEL_TYPE.map(mtype =>
-						<option key={mtype.enum} value={mtype.enum}>{`${mtype.title}`}</option>)}
-				</Input>
+					errorText={genericFieldInfo.licenseTerm.errorText} />
 			</GridItem>
 			<GridItem colSpan={2} stretch>
 				<Input
@@ -151,7 +151,7 @@
 							data-test-id='general-tab'
 							title={i18n('General')}>
 								<fieldset disabled={isReadOnlyMode}>
-									<GeneralTabContent data={data} genericFieldInfo={genericFieldInfo} onDataChanged={onDataChanged} validateLTChoice={(value)=>this.validateLTChoice(value)}
+									<GeneralTabContent data={data} genericFieldInfo={genericFieldInfo} onDataChanged={onDataChanged}
 										   validateName={(value)=>this.validateName(value)}/>
 								</fieldset>
 						</Tab>
@@ -181,12 +181,6 @@
 		this.props.onSubmit({licenseAgreement, previousLicenseAgreement});
 	}
 
-	validateLTChoice(value) {
-		if (!value.choice) {
-			return {isValid: false, errorText: i18n('Field is required')};
-		}
-		return {isValid: true, errorText: ''};
-	}
 
 	validateName(value) {
 		const {data: {id}, LANames} = this.props;
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/limits/LimitEditor.jsx b/openecomp-ui/src/sdc-app/onboarding/licenseModel/limits/LimitEditor.jsx
index 5c4e50d..d4b7e5c 100644
--- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/limits/LimitEditor.jsx
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/limits/LimitEditor.jsx
@@ -7,7 +7,7 @@
 import {LIMITS_FORM_NAME, selectValues} from './LimitEditorConstants.js';
 import Button from 'sdc-ui/lib/react/Button.js';
 import Validator from 'nfvo-utils/Validator.js';
-import {other as optionInputOther} from 'nfvo-components/input/inputOptions/InputOptions.jsx';
+import {other as optionInputOther} from 'nfvo-components/input/validation/InputOptions.jsx';
 import InputOptions from 'nfvo-components/input/validation/InputOptions.jsx';
 
 const LimitPropType = React.PropTypes.shape({
@@ -96,9 +96,9 @@
 								isMultiSelect={false}
 								isRequired={true}
 								onEnumChange={metric => onDataChanged({metric:{choice: metric, other: ''}},
-									LIMITS_FORM_NAME, {metric: this.validateChoiceWithOther})}
+									LIMITS_FORM_NAME)}
 								onOtherChange={metric => onDataChanged({metric:{choice: optionInputOther.OTHER,
-									other: metric}}, LIMITS_FORM_NAME, {metric: this.validateChoiceWithOther})}
+									other: metric}}, LIMITS_FORM_NAME)}
 								label={i18n('Metric')}
 								data-test-id='limit-editor-metric'
 								type='select'
@@ -196,23 +196,6 @@
 		{isValid: false, errorText: i18n('Limit by the name \'' + value + '\' already exists. Limit name must be unique')};
 	}
 
-	validateChoiceWithOther(value) {
-		let chosen = value.choice;
-		// if we have an empty multiple select we have a problem since it's required
-		if (value.choices) {
-			if (value.choices.length === 0) {
-				return  Validator.validate('field', '', [{type: 'required', data: true}]);
-			} else {
-				// continuing validation with the first chosen value in case we have the 'Other' field
-				chosen = value.choices[0];
-			}
-		}
-		if (chosen !== optionInputOther.OTHER) {
-			return  Validator.validate('field', chosen, [{type: 'required', data: true}]);
-		} else { // when 'Other' was chosen, validate other value
-			return  Validator.validate('field', value.other, [{type: 'required', data: true}]);
-		}
-	}
 
 	submit() {
 		if (!this.props.formReady) {
diff --git a/openecomp-ui/src/sdc-app/onboarding/licenseModel/limits/LimitEditorReducer.js b/openecomp-ui/src/sdc-app/onboarding/licenseModel/limits/LimitEditorReducer.js
index de9e7c8..99d94a0 100644
--- a/openecomp-ui/src/sdc-app/onboarding/licenseModel/limits/LimitEditorReducer.js
+++ b/openecomp-ui/src/sdc-app/onboarding/licenseModel/limits/LimitEditorReducer.js
@@ -15,6 +15,7 @@
  */
 
 import {actionTypes, LIMITS_FORM_NAME, defaultState} from './LimitEditorConstants.js';
+import {other as optionInputOther} from 'nfvo-components/input/validation/InputOptions.jsx';
 
 export default (state = {}, action) => {
 	switch (action.type) {
@@ -38,7 +39,7 @@
 					'metric' : {
 						isValid: true,
 						errorText: '',
-						validations: []
+						validations: [{type: 'required', data: true}, {type: 'requiredChoiceWithOther', data: optionInputOther.OTHER}]
 					},
 					'value' : {
 						isValid: true,