[SDC-29] Amdocs OnBoard 1707 initial commit.
[sdc.git] / openecomp-ui / src / nfvo-components / input / validation / InputOptions.jsx
1 /*!
2  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
13  * or implied. See the License for the specific language governing
14  * permissions and limitations under the License.
15  */
16 import React from 'react';
17 import ReactDOM from 'react-dom';
18 import i18n from 'nfvo-utils/i18n/i18n.js';
19 import classNames from 'classnames';
20 import Select from 'nfvo-components/input/SelectInput.jsx';
21 import Overlay from 'react-bootstrap/lib/Overlay.js';
22 import Tooltip from 'react-bootstrap/lib/Tooltip.js';
23
24 export const other = {OTHER: 'Other'};
25
26 class InputOptions extends React.Component {
27
28         static propTypes = {
29                 values: React.PropTypes.arrayOf(React.PropTypes.shape({
30                         enum: React.PropTypes.string,
31                         title: React.PropTypes.string
32                 })),
33                 isEnabledOther: React.PropTypes.bool,
34                 label: React.PropTypes.string,
35                 selectedValue: React.PropTypes.string,
36                 multiSelectedEnum: React.PropTypes.oneOfType([
37                         React.PropTypes.string,
38                         React.PropTypes.array
39                 ]),
40                 selectedEnum: React.PropTypes.string,
41                 otherValue: React.PropTypes.string,
42                 overlayPos: React.PropTypes.string,
43                 onEnumChange: React.PropTypes.func,
44                 onOtherChange: React.PropTypes.func,
45                 onBlur: React.PropTypes.func,
46                 isRequired: React.PropTypes.bool,
47                 isMultiSelect: React.PropTypes.bool,
48                 isValid: React.PropTypes.bool,
49                 disabled: React.PropTypes.bool
50         };
51
52         state = {
53                 otherInputDisabled: !this.props.otherValue
54         };
55
56         oldProps = {
57                 selectedEnum: '',
58                 otherValue: '',
59                 multiSelectedEnum: []
60         };
61
62         render() {
63                 let {label, isRequired, values, otherValue, onOtherChange, isMultiSelect, onBlur, multiSelectedEnum, selectedEnum, isValid, children, isReadOnlyMode} = this.props;
64                 const dataTestId = this.props['data-test-id'] ? {'data-test-id': this.props['data-test-id']} : {};
65                 let currentMultiSelectedEnum = [];
66                 let currentSelectedEnum = '';
67                 let {otherInputDisabled} = this.state;
68                 if (isMultiSelect) {
69                         currentMultiSelectedEnum = multiSelectedEnum;
70                         if(!otherInputDisabled) {
71                                 currentSelectedEnum = multiSelectedEnum ? multiSelectedEnum.toString() : undefined;
72                         }
73                 }
74                 else if(selectedEnum){
75                         currentSelectedEnum = selectedEnum;
76                 }
77                 if (!onBlur) {
78                         onBlur = () => {};
79                 }
80
81                 return(
82                         <div className='validation-input-wrapper' >
83                                 <div className={classNames('form-group', {'required' : isRequired, 'has-error' : !isValid})} >
84                                         {label && <label className='control-label'>{label}</label>}
85                                         {isMultiSelect && otherInputDisabled ?
86                                                 <Select
87                                                         {...dataTestId}
88                                                         ref={(input) => this.input = input}
89                                                         value={currentMultiSelectedEnum}
90                                                         className='options-input'
91                                                         clearable={false}
92                                                         required={isRequired}
93                                                         disabled={isReadOnlyMode || Boolean(this.props.disabled)}
94                                                         onBlur={() => onBlur()}
95                                                         onMultiSelectChanged={value => this.multiSelectEnumChanged(value)}
96                                                         options={this.renderMultiSelectOptions(values)}
97                                                         multi/> :
98                                                 <div className={classNames('input-options',{'has-error' : !isValid})} >
99                                                         <select
100                                                                 {...dataTestId}
101                                                                 ref={(input) => this.input = input}
102                                                                 label={label}
103                                                                 className='form-control input-options-select'
104                                                                 value={currentSelectedEnum}
105                                                                 style={{'width' : otherInputDisabled ? '100%' : '100px'}}
106                                                                 onBlur={() => onBlur()}
107                                                                 disabled={isReadOnlyMode || Boolean(this.props.disabled)}
108                                                                 onChange={ value => this.enumChanged(value)}
109                                                                 type='select'>
110                                                                 {children || (values && values.length && values.map((val, index) => this.renderOptions(val, index)))}
111                                                                 {onOtherChange && <option key='other' value={other.OTHER}>{i18n(other.OTHER)}</option>}
112                                                         </select>
113
114                                                         {!otherInputDisabled && <div className='input-options-separator'/>}
115                                                         <input
116                                                                 className='form-control input-options-other'
117                                                                 placeholder={i18n('other')}
118                                                                 ref={(otherValue) => this.otherValue = otherValue}
119                                                                 style={{'display' : otherInputDisabled ? 'none' : 'block'}}
120                                                                 disabled={isReadOnlyMode || Boolean(this.props.disabled)}
121                                                                 value={otherValue || ''}
122                                                                 onBlur={() => onBlur()}
123                                                                 onChange={() => this.changedOtherInput()}/>
124                                                 </div>
125                                         }
126                                         </div>
127                                 { this.renderErrorOverlay() }
128                         </div>
129                 );
130         }
131
132         renderOptions(val, index){
133                 return (
134                         <option key={index} value={val.enum}>{val.title}</option>
135                 );
136         }
137
138
139         renderMultiSelectOptions(values) {
140                 let {onOtherChange} = this.props;
141                 let optionsList = [];
142                 if (onOtherChange) {
143                         optionsList = values.map(option => {
144                                 return {
145                                         label: option.title,
146                                         value: option.enum,
147                                 };
148                         }).concat([{
149                                 label: i18n(other.OTHER),
150                                 value: i18n(other.OTHER),
151                         }]);
152                 }
153                 else {
154                         optionsList = values.map(option => {
155                                 return {
156                                         label: option.title,
157                                         value: option.enum,
158                                 };
159                         });
160                 }
161                 if (optionsList.length > 0 && optionsList[0].value === '') {
162                         optionsList.shift();
163                 }
164                 return optionsList;
165         }
166
167         renderErrorOverlay() {
168                 let position = 'right';
169                 const {errorText = '', isValid = true, type, overlayPos} = this.props;
170
171                 if (overlayPos) {
172                         position = overlayPos;
173                 }
174                 else if (type === 'text'
175                         || type === 'email'
176                         || type === 'number'
177                         || type === 'password') {
178                         position = 'bottom';
179                 }
180
181                 return (
182                         <Overlay
183                                 show={!isValid}
184                                 placement={position}
185                                 target={() => {
186                                         let {otherInputDisabled} = this.state;
187                                         let target = otherInputDisabled ? ReactDOM.findDOMNode(this.input) :  ReactDOM.findDOMNode(this.otherValue);
188                                         return target.offsetParent ? target : undefined;
189                                 }}
190                                 container={this}>
191                                 <Tooltip
192                                         id={`error-${errorText.replace(' ', '-')}`}
193                                         className='validation-error-message'>
194                                         {errorText}
195                                 </Tooltip>
196                         </Overlay>
197                 );
198         }
199
200         getValue() {
201                 let res = '';
202                 let {isMultiSelect} = this.props;
203                 let {otherInputDisabled} = this.state;
204
205                 if (otherInputDisabled) {
206                         res = isMultiSelect ? this.input.getValue() : this.input.value;
207                 } else {
208                         res = this.otherValue.value;
209                 }
210                 return res;
211         }
212
213         enumChanged() {
214                 let enumValue = this.input.value;
215                 let {onEnumChange, onOtherChange, isMultiSelect, onChange} = this.props;
216                 this.setState({
217                         otherInputDisabled: !Boolean(onOtherChange) || enumValue !== other.OTHER
218                 });
219
220                 let value = isMultiSelect ? [enumValue] : enumValue;
221                 if (onEnumChange) {
222                         onEnumChange(value);
223                 }
224                 if (onChange) {
225                         onChange(value);
226                 }
227         }
228
229         multiSelectEnumChanged(enumValue) {
230                 let {onEnumChange, onOtherChange} = this.props;
231                 let selectedValues = enumValue.map(enumVal => {
232                         return enumVal.value;
233                 });
234
235                 if (this.state.otherInputDisabled === false) {
236                         selectedValues.shift();
237                 }
238                 else if (selectedValues.includes(i18n(other.OTHER))) {
239                         selectedValues = [i18n(other.OTHER)];
240                 }
241
242                 this.setState({
243                         otherInputDisabled: !Boolean(onOtherChange) || !selectedValues.includes(i18n(other.OTHER))
244                 });
245                 onEnumChange(selectedValues);
246         }
247
248         changedOtherInput() {
249                 let {onOtherChange} = this.props;
250                 onOtherChange(this.otherValue.value);
251         }
252
253         componentDidUpdate() {
254                 let {otherValue, selectedEnum, onInputChange, multiSelectedEnum} = this.props;
255                 if (this.oldProps.otherValue !== otherValue
256                         || this.oldProps.selectedEnum !== selectedEnum
257                         || this.oldProps.multiSelectedEnum !== multiSelectedEnum) {
258                         this.oldProps = {
259                                 otherValue,
260                                 selectedEnum,
261                                 multiSelectedEnum
262                         };
263                         onInputChange();
264                 }
265         }
266
267         static getTitleByName(values, name) {
268                 for (let key of Object.keys(values)) {
269                         let option = values[key].find(option => option.enum === name);
270                         if (option) {
271                                 return option.title;
272                         }
273                 }
274                 return name;
275         }
276
277 }
278
279 export default InputOptions;