blob: 019b6a5c70f042431d8ba25b0526586d1de60a1e [file] [log] [blame]
/*!
* Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import ReactDOM from 'react-dom';
import i18n from 'nfvo-utils/i18n/i18n.js';
import classNames from 'classnames';
import Select from 'nfvo-components/input/SelectInput.jsx';
import Overlay from 'react-bootstrap/lib/Overlay.js';
import Tooltip from 'react-bootstrap/lib/Tooltip.js';
export const other = { OTHER: 'Other' };
class InputOptions extends React.Component {
static propTypes = {
values: PropTypes.arrayOf(
PropTypes.shape({
enum: PropTypes.string,
title: PropTypes.string
})
),
isEnabledOther: PropTypes.bool,
label: PropTypes.string,
selectedValue: PropTypes.string,
multiSelectedEnum: PropTypes.oneOfType([
PropTypes.string,
PropTypes.array
]),
selectedEnum: PropTypes.string,
otherValue: PropTypes.string,
overlayPos: PropTypes.string,
onEnumChange: PropTypes.func,
onOtherChange: PropTypes.func,
onBlur: PropTypes.func,
isRequired: PropTypes.bool,
isMultiSelect: PropTypes.bool,
isValid: PropTypes.bool,
disabled: PropTypes.bool
};
state = {
otherInputDisabled: !this.props.otherValue
};
oldProps = {
selectedEnum: '',
otherValue: '',
multiSelectedEnum: []
};
render() {
let {
label,
isRequired,
values,
otherValue,
onOtherChange,
isMultiSelect,
onBlur,
multiSelectedEnum,
selectedEnum,
isValid,
children,
isReadOnlyMode
} = this.props;
const dataTestId = this.props['data-test-id']
? { 'data-test-id': this.props['data-test-id'] }
: {};
let currentMultiSelectedEnum = [];
let currentSelectedEnum = '';
let otherInputDisabled =
(isMultiSelect &&
(multiSelectedEnum === undefined ||
multiSelectedEnum.length === 0 ||
multiSelectedEnum[0] !== other.OTHER)) ||
(!isMultiSelect &&
(selectedEnum === undefined || selectedEnum !== other.OTHER));
if (isMultiSelect) {
currentMultiSelectedEnum = multiSelectedEnum;
if (!otherInputDisabled) {
currentSelectedEnum = multiSelectedEnum
? multiSelectedEnum.toString()
: undefined;
}
} else if (selectedEnum) {
currentSelectedEnum = selectedEnum;
}
if (!onBlur) {
onBlur = () => {};
}
return (
<div className="validation-input-wrapper">
<div
className={classNames('form-group', {
required: isRequired,
'has-error': !isValid
})}>
{label && <label className="control-label">{label}</label>}
{isMultiSelect && otherInputDisabled ? (
<Select
{...dataTestId}
ref={input => (this.input = input)}
value={currentMultiSelectedEnum}
className="options-input"
clearable={false}
required={isRequired}
disabled={
isReadOnlyMode || Boolean(this.props.disabled)
}
onBlur={() => onBlur()}
onMultiSelectChanged={value =>
this.multiSelectEnumChanged(value)
}
options={this.renderMultiSelectOptions(values)}
multi
/>
) : (
<div
className={classNames('input-options', {
'has-error': !isValid
})}>
<select
{...dataTestId}
ref={input => (this.input = input)}
label={label}
className="form-control input-options-select"
value={currentSelectedEnum}
style={{
width: otherInputDisabled ? '100%' : '100px'
}}
onBlur={() => onBlur()}
disabled={
isReadOnlyMode ||
Boolean(this.props.disabled)
}
onChange={value => this.enumChanged(value)}
type="select">
{children ||
(values &&
values.length &&
values.map((val, index) =>
this.renderOptions(val, index)
))}
{onOtherChange && (
<option key="other" value={other.OTHER}>
{i18n(other.OTHER)}
</option>
)}
</select>
{!otherInputDisabled && (
<div className="input-options-separator" />
)}
<input
className="form-control input-options-other"
placeholder={i18n('other')}
ref={otherValue =>
(this.otherValue = otherValue)
}
style={{
display: otherInputDisabled
? 'none'
: 'block'
}}
disabled={
isReadOnlyMode ||
Boolean(this.props.disabled)
}
value={otherValue || ''}
onBlur={() => onBlur()}
onChange={() => this.changedOtherInput()}
/>
</div>
)}
</div>
{this.renderErrorOverlay()}
</div>
);
}
renderOptions(val, index) {
return (
<option key={index} value={val.enum}>
{val.title}
</option>
);
}
renderMultiSelectOptions(values) {
let { onOtherChange } = this.props;
let optionsList = [];
if (onOtherChange) {
optionsList = values
.map(option => {
return {
label: option.title,
value: option.enum
};
})
.concat([
{
label: i18n(other.OTHER),
value: i18n(other.OTHER)
}
]);
} else {
optionsList = values.map(option => {
return {
label: option.title,
value: option.enum
};
});
}
if (optionsList.length > 0 && optionsList[0].value === '') {
optionsList.shift();
}
return optionsList;
}
renderErrorOverlay() {
let position = 'right';
const { errorText = '', isValid = true, type, overlayPos } = this.props;
if (overlayPos) {
position = overlayPos;
} else if (
type === 'text' ||
type === 'email' ||
type === 'number' ||
type === 'password'
) {
position = 'bottom';
}
return (
<Overlay
show={!isValid}
placement={position}
target={() => {
let { otherInputDisabled } = this.state;
let target = otherInputDisabled
? ReactDOM.findDOMNode(this.input)
: ReactDOM.findDOMNode(this.otherValue);
return target.offsetParent ? target : undefined;
}}
container={this}>
<Tooltip
id={`error-${errorText.replace(' ', '-')}`}
className="validation-error-message">
{errorText}
</Tooltip>
</Overlay>
);
}
getValue() {
let res = '';
let { isMultiSelect } = this.props;
let { otherInputDisabled } = this.state;
if (otherInputDisabled) {
res = isMultiSelect ? this.input.getValue() : this.input.value;
} else {
res = this.otherValue.value;
}
return res;
}
enumChanged() {
let enumValue = this.input.value;
let {
onEnumChange,
onOtherChange,
isMultiSelect,
onChange
} = this.props;
this.setState({
otherInputDisabled:
!Boolean(onOtherChange) || enumValue !== other.OTHER
});
let value = isMultiSelect ? [enumValue] : enumValue;
if (onEnumChange) {
onEnumChange(value);
}
if (onChange) {
onChange(value);
}
}
multiSelectEnumChanged(enumValue) {
let { onEnumChange, onOtherChange } = this.props;
let selectedValues = enumValue.map(enumVal => {
return enumVal.value;
});
if (this.state.otherInputDisabled === false) {
selectedValues.shift();
} else if (selectedValues.includes(i18n(other.OTHER))) {
selectedValues = [i18n(other.OTHER)];
}
this.setState({
otherInputDisabled:
!Boolean(onOtherChange) ||
!selectedValues.includes(i18n(other.OTHER))
});
onEnumChange(selectedValues);
}
changedOtherInput() {
let { onOtherChange } = this.props;
onOtherChange(this.otherValue.value);
}
componentDidUpdate() {
let {
otherValue,
selectedEnum,
onInputChange,
multiSelectedEnum
} = this.props;
if (
this.oldProps.otherValue !== otherValue ||
this.oldProps.selectedEnum !== selectedEnum ||
this.oldProps.multiSelectedEnum !== multiSelectedEnum
) {
this.oldProps = {
otherValue,
selectedEnum,
multiSelectedEnum
};
onInputChange();
}
}
static getTitleByName(values, name) {
for (let key of Object.keys(values)) {
let option = values[key].find(option => option.enum === name);
if (option) {
return option.title;
}
}
return name;
}
}
export default InputOptions;