blob: 019b6a5c70f042431d8ba25b0526586d1de60a1e [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';
talig8e9c0652017-12-20 14:30:43 +020017import PropTypes from 'prop-types';
AviZi280f8012017-06-09 02:39:56 +030018import ReactDOM from 'react-dom';
19import i18n from 'nfvo-utils/i18n/i18n.js';
20import classNames from 'classnames';
21import Select from 'nfvo-components/input/SelectInput.jsx';
22import Overlay from 'react-bootstrap/lib/Overlay.js';
23import Tooltip from 'react-bootstrap/lib/Tooltip.js';
24
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +020025export const other = { OTHER: 'Other' };
AviZi280f8012017-06-09 02:39:56 +030026
27class InputOptions extends React.Component {
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +020028 static propTypes = {
29 values: PropTypes.arrayOf(
30 PropTypes.shape({
31 enum: PropTypes.string,
32 title: PropTypes.string
33 })
34 ),
35 isEnabledOther: PropTypes.bool,
36 label: PropTypes.string,
37 selectedValue: PropTypes.string,
38 multiSelectedEnum: PropTypes.oneOfType([
39 PropTypes.string,
40 PropTypes.array
41 ]),
42 selectedEnum: PropTypes.string,
43 otherValue: PropTypes.string,
44 overlayPos: PropTypes.string,
45 onEnumChange: PropTypes.func,
46 onOtherChange: PropTypes.func,
47 onBlur: PropTypes.func,
48 isRequired: PropTypes.bool,
49 isMultiSelect: PropTypes.bool,
50 isValid: PropTypes.bool,
51 disabled: PropTypes.bool
52 };
AviZi280f8012017-06-09 02:39:56 +030053
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +020054 state = {
55 otherInputDisabled: !this.props.otherValue
56 };
AviZi280f8012017-06-09 02:39:56 +030057
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +020058 oldProps = {
59 selectedEnum: '',
60 otherValue: '',
61 multiSelectedEnum: []
62 };
AviZi280f8012017-06-09 02:39:56 +030063
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +020064 render() {
65 let {
66 label,
67 isRequired,
68 values,
69 otherValue,
70 onOtherChange,
71 isMultiSelect,
72 onBlur,
73 multiSelectedEnum,
74 selectedEnum,
75 isValid,
76 children,
77 isReadOnlyMode
78 } = this.props;
79 const dataTestId = this.props['data-test-id']
80 ? { 'data-test-id': this.props['data-test-id'] }
81 : {};
82 let currentMultiSelectedEnum = [];
83 let currentSelectedEnum = '';
84 let otherInputDisabled =
85 (isMultiSelect &&
86 (multiSelectedEnum === undefined ||
87 multiSelectedEnum.length === 0 ||
88 multiSelectedEnum[0] !== other.OTHER)) ||
89 (!isMultiSelect &&
90 (selectedEnum === undefined || selectedEnum !== other.OTHER));
91 if (isMultiSelect) {
92 currentMultiSelectedEnum = multiSelectedEnum;
93 if (!otherInputDisabled) {
94 currentSelectedEnum = multiSelectedEnum
95 ? multiSelectedEnum.toString()
96 : undefined;
97 }
98 } else if (selectedEnum) {
99 currentSelectedEnum = selectedEnum;
100 }
101 if (!onBlur) {
102 onBlur = () => {};
103 }
AviZi280f8012017-06-09 02:39:56 +0300104
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +0200105 return (
106 <div className="validation-input-wrapper">
107 <div
108 className={classNames('form-group', {
109 required: isRequired,
110 'has-error': !isValid
111 })}>
112 {label && <label className="control-label">{label}</label>}
113 {isMultiSelect && otherInputDisabled ? (
114 <Select
115 {...dataTestId}
116 ref={input => (this.input = input)}
117 value={currentMultiSelectedEnum}
118 className="options-input"
119 clearable={false}
120 required={isRequired}
121 disabled={
122 isReadOnlyMode || Boolean(this.props.disabled)
123 }
124 onBlur={() => onBlur()}
125 onMultiSelectChanged={value =>
126 this.multiSelectEnumChanged(value)
127 }
128 options={this.renderMultiSelectOptions(values)}
129 multi
130 />
131 ) : (
132 <div
133 className={classNames('input-options', {
134 'has-error': !isValid
135 })}>
136 <select
137 {...dataTestId}
138 ref={input => (this.input = input)}
139 label={label}
140 className="form-control input-options-select"
141 value={currentSelectedEnum}
142 style={{
143 width: otherInputDisabled ? '100%' : '100px'
144 }}
145 onBlur={() => onBlur()}
146 disabled={
147 isReadOnlyMode ||
148 Boolean(this.props.disabled)
149 }
150 onChange={value => this.enumChanged(value)}
151 type="select">
152 {children ||
153 (values &&
154 values.length &&
155 values.map((val, index) =>
156 this.renderOptions(val, index)
157 ))}
158 {onOtherChange && (
159 <option key="other" value={other.OTHER}>
160 {i18n(other.OTHER)}
161 </option>
162 )}
163 </select>
AviZi280f8012017-06-09 02:39:56 +0300164
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +0200165 {!otherInputDisabled && (
166 <div className="input-options-separator" />
167 )}
168 <input
169 className="form-control input-options-other"
170 placeholder={i18n('other')}
171 ref={otherValue =>
172 (this.otherValue = otherValue)
173 }
174 style={{
175 display: otherInputDisabled
176 ? 'none'
177 : 'block'
178 }}
179 disabled={
180 isReadOnlyMode ||
181 Boolean(this.props.disabled)
182 }
183 value={otherValue || ''}
184 onBlur={() => onBlur()}
185 onChange={() => this.changedOtherInput()}
186 />
187 </div>
188 )}
189 </div>
190 {this.renderErrorOverlay()}
191 </div>
192 );
193 }
AviZi280f8012017-06-09 02:39:56 +0300194
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +0200195 renderOptions(val, index) {
196 return (
197 <option key={index} value={val.enum}>
198 {val.title}
199 </option>
200 );
201 }
AviZi280f8012017-06-09 02:39:56 +0300202
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +0200203 renderMultiSelectOptions(values) {
204 let { onOtherChange } = this.props;
205 let optionsList = [];
206 if (onOtherChange) {
207 optionsList = values
208 .map(option => {
209 return {
210 label: option.title,
211 value: option.enum
212 };
213 })
214 .concat([
215 {
216 label: i18n(other.OTHER),
217 value: i18n(other.OTHER)
218 }
219 ]);
220 } else {
221 optionsList = values.map(option => {
222 return {
223 label: option.title,
224 value: option.enum
225 };
226 });
227 }
228 if (optionsList.length > 0 && optionsList[0].value === '') {
229 optionsList.shift();
230 }
231 return optionsList;
232 }
AviZi280f8012017-06-09 02:39:56 +0300233
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +0200234 renderErrorOverlay() {
235 let position = 'right';
236 const { errorText = '', isValid = true, type, overlayPos } = this.props;
AviZi280f8012017-06-09 02:39:56 +0300237
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +0200238 if (overlayPos) {
239 position = overlayPos;
240 } else if (
241 type === 'text' ||
242 type === 'email' ||
243 type === 'number' ||
244 type === 'password'
245 ) {
246 position = 'bottom';
247 }
AviZi280f8012017-06-09 02:39:56 +0300248
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +0200249 return (
250 <Overlay
251 show={!isValid}
252 placement={position}
253 target={() => {
254 let { otherInputDisabled } = this.state;
255 let target = otherInputDisabled
256 ? ReactDOM.findDOMNode(this.input)
257 : ReactDOM.findDOMNode(this.otherValue);
258 return target.offsetParent ? target : undefined;
259 }}
260 container={this}>
261 <Tooltip
262 id={`error-${errorText.replace(' ', '-')}`}
263 className="validation-error-message">
264 {errorText}
265 </Tooltip>
266 </Overlay>
267 );
268 }
AviZi280f8012017-06-09 02:39:56 +0300269
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +0200270 getValue() {
271 let res = '';
272 let { isMultiSelect } = this.props;
273 let { otherInputDisabled } = this.state;
AviZi280f8012017-06-09 02:39:56 +0300274
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +0200275 if (otherInputDisabled) {
276 res = isMultiSelect ? this.input.getValue() : this.input.value;
277 } else {
278 res = this.otherValue.value;
279 }
280 return res;
281 }
AviZi280f8012017-06-09 02:39:56 +0300282
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +0200283 enumChanged() {
284 let enumValue = this.input.value;
285 let {
286 onEnumChange,
287 onOtherChange,
288 isMultiSelect,
289 onChange
290 } = this.props;
291 this.setState({
292 otherInputDisabled:
293 !Boolean(onOtherChange) || enumValue !== other.OTHER
294 });
AviZi280f8012017-06-09 02:39:56 +0300295
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +0200296 let value = isMultiSelect ? [enumValue] : enumValue;
297 if (onEnumChange) {
298 onEnumChange(value);
299 }
300 if (onChange) {
301 onChange(value);
302 }
303 }
AviZi280f8012017-06-09 02:39:56 +0300304
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +0200305 multiSelectEnumChanged(enumValue) {
306 let { onEnumChange, onOtherChange } = this.props;
307 let selectedValues = enumValue.map(enumVal => {
308 return enumVal.value;
309 });
AviZi280f8012017-06-09 02:39:56 +0300310
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +0200311 if (this.state.otherInputDisabled === false) {
312 selectedValues.shift();
313 } else if (selectedValues.includes(i18n(other.OTHER))) {
314 selectedValues = [i18n(other.OTHER)];
315 }
AviZi280f8012017-06-09 02:39:56 +0300316
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +0200317 this.setState({
318 otherInputDisabled:
319 !Boolean(onOtherChange) ||
320 !selectedValues.includes(i18n(other.OTHER))
321 });
322 onEnumChange(selectedValues);
323 }
AviZi280f8012017-06-09 02:39:56 +0300324
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +0200325 changedOtherInput() {
326 let { onOtherChange } = this.props;
327 onOtherChange(this.otherValue.value);
328 }
AviZi280f8012017-06-09 02:39:56 +0300329
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +0200330 componentDidUpdate() {
331 let {
332 otherValue,
333 selectedEnum,
334 onInputChange,
335 multiSelectedEnum
336 } = this.props;
337 if (
338 this.oldProps.otherValue !== otherValue ||
339 this.oldProps.selectedEnum !== selectedEnum ||
340 this.oldProps.multiSelectedEnum !== multiSelectedEnum
341 ) {
342 this.oldProps = {
343 otherValue,
344 selectedEnum,
345 multiSelectedEnum
346 };
347 onInputChange();
348 }
349 }
AviZi280f8012017-06-09 02:39:56 +0300350
Einav Weiss Keidar7fdf7332018-03-20 14:45:40 +0200351 static getTitleByName(values, name) {
352 for (let key of Object.keys(values)) {
353 let option = values[key].find(option => option.enum === name);
354 if (option) {
355 return option.title;
356 }
357 }
358 return name;
359 }
AviZi280f8012017-06-09 02:39:56 +0300360}
361
362export default InputOptions;