@bigfishtv/cockpit
Version:
118 lines (106 loc) • 2.87 kB
JavaScript
import PropTypes from 'prop-types'
import React, { Component } from 'react'
import classnames from 'classnames'
import { titleCase } from '../../utils/stringUtils'
function toString(value) {
return value === null ? '' : String(value)
}
function toValue(string, options) {
return string === '' ? null : options.reduce((acc, { value }) => (String(value) === string ? value : acc), string)
}
export default class SelectInput extends Component {
static propTypes = {
options: PropTypes.arrayOf(
PropTypes.oneOfType([
PropTypes.string,
PropTypes.shape({
value: PropTypes.any,
text: PropTypes.node,
}),
])
),
empty: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
value: PropTypes.any,
defaultValue: PropTypes.any,
onChange: PropTypes.func,
coerceValueToOptions: PropTypes.bool,
className: PropTypes.string,
}
static defaultProps = {
options: [],
empty: 'Please select ...',
defaultValue: null,
onChange: () => {},
coerceValueToOptions: true,
}
componentDidMount() {
if (this.props.coerceValueToOptions) {
this.coerceValueToOptions()
}
}
componentDidUpdate() {
if (this.props.coerceValueToOptions) {
this.coerceValueToOptions()
}
}
coerceValueToOptions() {
// coerce value to defaultValue if value not found in options
const { value, defaultValue, empty } = this.props
const options = this.normalisedOptions()
const selectedOption = options.find(option => option.value === value)
if (!selectedOption) {
if (options.length && empty === false) {
// select first item if we have options to choose from
// and we have an "empty" option
this.props.onChange(options[0].value)
} else if (value !== undefined && value !== null && value !== defaultValue) {
// set value to defaultValue
this.props.onChange(defaultValue)
}
}
}
normalisedOptions() {
return this.props.options.map(option =>
typeof option === 'string' ? { value: option, text: titleCase(option) } : option
)
}
renderOptions() {
const empty = this.props.empty !== false && (
<option value={toString(this.props.defaultValue)} key={0}>
{this.props.empty}
</option>
)
const options = this.normalisedOptions().map(({ value, text }, i) => (
<option key={i + 1} value={value}>
{text}
</option>
))
return empty ? [empty, ...options] : options
}
render() {
const {
options,
defaultValue,
empty,
coerceValueToOptions,
readOnly,
onChange,
value,
className,
formValue,
invalid,
...rest
} = this.props
return (
<label className={classnames('select', className)}>
<select
{...rest}
value={toString(value)}
disabled={readOnly}
onChange={event => this.props.onChange(toValue(event.target.value, this.normalisedOptions()))}>
{this.renderOptions()}
</select>
</label>
)
}
}