blob: 9b11d27c2ba62c38dad0dad084e823ef3f005949 [file] [log] [blame]
AviZi280f8012017-06-09 02:39:56 +03001/*!
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 */
16import React from 'react';
17import ReactDOM from 'react-dom';
18import i18n from 'nfvo-utils/i18n/i18n.js';
19import classNames from 'classnames';
20import Select from 'nfvo-components/input/SelectInput.jsx';
21import Overlay from 'react-bootstrap/lib/Overlay.js';
22import Tooltip from 'react-bootstrap/lib/Tooltip.js';
23
24export const other = {OTHER: 'Other'};
25
26class 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 = '';
Avi Ziv61070c92017-07-26 17:37:57 +030067 let otherInputDisabled = (isMultiSelect && (multiSelectedEnum === undefined || multiSelectedEnum.length === 0 || multiSelectedEnum[0] !== other.OTHER))
68 || (!isMultiSelect && (selectedEnum === undefined || selectedEnum !== other.OTHER));
AviZi280f8012017-06-09 02:39:56 +030069 if (isMultiSelect) {
70 currentMultiSelectedEnum = multiSelectedEnum;
71 if(!otherInputDisabled) {
72 currentSelectedEnum = multiSelectedEnum ? multiSelectedEnum.toString() : undefined;
73 }
74 }
75 else if(selectedEnum){
76 currentSelectedEnum = selectedEnum;
77 }
78 if (!onBlur) {
79 onBlur = () => {};
80 }
81
82 return(
83 <div className='validation-input-wrapper' >
84 <div className={classNames('form-group', {'required' : isRequired, 'has-error' : !isValid})} >
85 {label && <label className='control-label'>{label}</label>}
86 {isMultiSelect && otherInputDisabled ?
87 <Select
88 {...dataTestId}
89 ref={(input) => this.input = input}
90 value={currentMultiSelectedEnum}
91 className='options-input'
92 clearable={false}
93 required={isRequired}
94 disabled={isReadOnlyMode || Boolean(this.props.disabled)}
95 onBlur={() => onBlur()}
96 onMultiSelectChanged={value => this.multiSelectEnumChanged(value)}
97 options={this.renderMultiSelectOptions(values)}
98 multi/> :
99 <div className={classNames('input-options',{'has-error' : !isValid})} >
100 <select
101 {...dataTestId}
102 ref={(input) => this.input = input}
103 label={label}
104 className='form-control input-options-select'
105 value={currentSelectedEnum}
106 style={{'width' : otherInputDisabled ? '100%' : '100px'}}
107 onBlur={() => onBlur()}
108 disabled={isReadOnlyMode || Boolean(this.props.disabled)}
109 onChange={ value => this.enumChanged(value)}
110 type='select'>
111 {children || (values && values.length && values.map((val, index) => this.renderOptions(val, index)))}
112 {onOtherChange && <option key='other' value={other.OTHER}>{i18n(other.OTHER)}</option>}
113 </select>
114
115 {!otherInputDisabled && <div className='input-options-separator'/>}
116 <input
117 className='form-control input-options-other'
118 placeholder={i18n('other')}
119 ref={(otherValue) => this.otherValue = otherValue}
120 style={{'display' : otherInputDisabled ? 'none' : 'block'}}
121 disabled={isReadOnlyMode || Boolean(this.props.disabled)}
122 value={otherValue || ''}
123 onBlur={() => onBlur()}
124 onChange={() => this.changedOtherInput()}/>
125 </div>
126 }
127 </div>
128 { this.renderErrorOverlay() }
129 </div>
130 );
131 }
132
133 renderOptions(val, index){
134 return (
135 <option key={index} value={val.enum}>{val.title}</option>
136 );
137 }
138
139
140 renderMultiSelectOptions(values) {
141 let {onOtherChange} = this.props;
142 let optionsList = [];
143 if (onOtherChange) {
144 optionsList = values.map(option => {
145 return {
146 label: option.title,
147 value: option.enum,
148 };
149 }).concat([{
150 label: i18n(other.OTHER),
151 value: i18n(other.OTHER),
152 }]);
153 }
154 else {
155 optionsList = values.map(option => {
156 return {
157 label: option.title,
158 value: option.enum,
159 };
160 });
161 }
162 if (optionsList.length > 0 && optionsList[0].value === '') {
163 optionsList.shift();
164 }
165 return optionsList;
166 }
167
168 renderErrorOverlay() {
169 let position = 'right';
170 const {errorText = '', isValid = true, type, overlayPos} = this.props;
171
172 if (overlayPos) {
173 position = overlayPos;
174 }
175 else if (type === 'text'
176 || type === 'email'
177 || type === 'number'
178 || type === 'password') {
179 position = 'bottom';
180 }
181
182 return (
183 <Overlay
184 show={!isValid}
185 placement={position}
186 target={() => {
187 let {otherInputDisabled} = this.state;
188 let target = otherInputDisabled ? ReactDOM.findDOMNode(this.input) : ReactDOM.findDOMNode(this.otherValue);
189 return target.offsetParent ? target : undefined;
190 }}
191 container={this}>
192 <Tooltip
193 id={`error-${errorText.replace(' ', '-')}`}
194 className='validation-error-message'>
195 {errorText}
196 </Tooltip>
197 </Overlay>
198 );
199 }
200
201 getValue() {
202 let res = '';
203 let {isMultiSelect} = this.props;
204 let {otherInputDisabled} = this.state;
205
206 if (otherInputDisabled) {
207 res = isMultiSelect ? this.input.getValue() : this.input.value;
208 } else {
209 res = this.otherValue.value;
210 }
211 return res;
212 }
213
214 enumChanged() {
215 let enumValue = this.input.value;
216 let {onEnumChange, onOtherChange, isMultiSelect, onChange} = this.props;
217 this.setState({
218 otherInputDisabled: !Boolean(onOtherChange) || enumValue !== other.OTHER
219 });
220
221 let value = isMultiSelect ? [enumValue] : enumValue;
222 if (onEnumChange) {
223 onEnumChange(value);
224 }
225 if (onChange) {
226 onChange(value);
227 }
228 }
229
230 multiSelectEnumChanged(enumValue) {
231 let {onEnumChange, onOtherChange} = this.props;
232 let selectedValues = enumValue.map(enumVal => {
233 return enumVal.value;
234 });
235
236 if (this.state.otherInputDisabled === false) {
237 selectedValues.shift();
238 }
239 else if (selectedValues.includes(i18n(other.OTHER))) {
240 selectedValues = [i18n(other.OTHER)];
241 }
242
243 this.setState({
244 otherInputDisabled: !Boolean(onOtherChange) || !selectedValues.includes(i18n(other.OTHER))
245 });
246 onEnumChange(selectedValues);
247 }
248
249 changedOtherInput() {
250 let {onOtherChange} = this.props;
251 onOtherChange(this.otherValue.value);
252 }
253
254 componentDidUpdate() {
255 let {otherValue, selectedEnum, onInputChange, multiSelectedEnum} = this.props;
256 if (this.oldProps.otherValue !== otherValue
257 || this.oldProps.selectedEnum !== selectedEnum
258 || this.oldProps.multiSelectedEnum !== multiSelectedEnum) {
259 this.oldProps = {
260 otherValue,
261 selectedEnum,
262 multiSelectedEnum
263 };
264 onInputChange();
265 }
266 }
267
268 static getTitleByName(values, name) {
269 for (let key of Object.keys(values)) {
270 let option = values[key].find(option => option.enum === name);
271 if (option) {
272 return option.title;
273 }
274 }
275 return name;
276 }
277
278}
279
280export default InputOptions;