@sparkpost/matchbox
Version: 
A React UI component library
120 lines (104 loc) • 3.06 kB
JavaScript
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Label } from '../Label';
import { Error } from '../Error';
import { ArrowDropDown } from '@sparkpost/matchbox-icons';
import classnames from 'classnames';
import styles from './Select.module.scss';
const Option = ({ option }) => {
  if (typeof option === 'object') {
    const { value, label = value, ...rest } = option;
    return <option value={value} {...rest}>{label}</option>;
  } else if (typeof option === 'string' || typeof option === 'number') {
    return <option value={option}>{option}</option>;
  }
};
class Select extends Component {
  static displayName = 'Select';
  static propTypes = {
    id: PropTypes.string,
    /**
     * Select options -
     * Array of Objects with { value, label }, Strings, or Numbers
     */
    options: PropTypes.arrayOf(
      PropTypes.oneOfType([
        PropTypes.shape({
          value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
          label: PropTypes.string.isRequired
        }),
        PropTypes.string.isRequired,
        PropTypes.number.isRequired
      ])
    ).isRequired,
    disabled: PropTypes.bool,
    required: PropTypes.bool,
    label: PropTypes.string,
    helpText: PropTypes.node,
    error: PropTypes.string,
    errorInLabel: PropTypes.bool,
    onChange: PropTypes.func,
    onFocus: PropTypes.func,
    onBlur: PropTypes.func
  };
  render() {
    const {
      id,
      options,
      label,
      helpText,
      placeholder,
      placeholderValue,
      disabled,
      required,
      error,
      errorInLabel,
      ...rest
    } = this.props;
    const setClasses = classnames(
      styles.Select,
      error && styles.error
    );
    const inputClasses = classnames(
      styles.Input,
      disabled && styles.disabled
    );
    const dropdownClasses = classnames(
      styles.Dropdown,
      !label && styles.labelHidden
    );
    let combined = options;
    if (placeholder) {
      combined = [ { label: placeholder, value: placeholderValue, disabled: true }, ...combined ];
    }
    const optionMarkup = combined && combined.length
      ? combined.map((option, key) => <Option option={option} key={key} />)
      : null;
    const requiredIndicator = required
      ? ' *'
      : '';
    const labelMarkup = (
      <Label id={id} label={`${label}${requiredIndicator}`}>
        {error && errorInLabel && <Error className={styles.InlineError} wrapper='span' error={error} />}
      </Label>
    );
    const helpMarkup = helpText
      ? <div className={styles.HelpText}>{helpText}</div>
      : null;
    return (
      <fieldset className={setClasses}>
        {label && labelMarkup}
        <select
          className={inputClasses}
          disabled={disabled}
          {...rest} >
          {optionMarkup}
        </select>
        <ArrowDropDown className={dropdownClasses} />
        {error && !errorInLabel && <Error error={error} />}
        {helpMarkup}
      </fieldset>
    );
  }
}
export default Select;