blob: 6e54254eb09c62b40adc04aedfa4cdb5e3a64dcb [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 = '';
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
279export default InputOptions;