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