All files / src/generic-typeahead index.js

100% Statements 23/23
100% Branches 14/14
100% Functions 10/10
100% Lines 20/20
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107                                  3x                                                     12x 12x       9x       2x 1x 1x       10x 10x 10x 10x 10x 10x         20x     36x 36x       1x                   33x 13x 13x                                  
/* Copyright © 2016 Kuali, Inc. - All Rights Reserved
 * You may use and modify this code under the terms of the Kuali, Inc.
 * Pre-Release License Agreement. You may not distribute it.
 *
 * You should have received a copy of the Kuali, Inc. Pre-Release License
 * Agreement with this file. If not, please write to license@kuali.co.
 */
 
import { map, omit, partial, pick } from 'lodash'
import PropTypes from 'prop-types'
import React, { Component } from 'react'
import Autocomplete from 'react-md/lib/Autocompletes'
import Chip from 'react-md/lib/Chips'
 
import styles from './styles.css'
import Fetch from '../fetch'
 
const expectedProps = [
  'ariaLabel',
  'getAll',
  'getById',
  'notFoundMsg',
  'onChange',
  'placeholder',
  'searchByName',
  'value',
]
 
export default class _Typeahead extends Component {
 
  static displayName = 'Typeahead';
 
  static propTypes = {
    ariaLabel: PropTypes.string.isRequired,
    getAll: PropTypes.func.isRequired,
    getById: PropTypes.func.isRequired,
    notFoundMsg: PropTypes.string.isRequired,
    onChange: PropTypes.func.isRequired,
    placeholder: PropTypes.string,
    searchByName: PropTypes.func.isRequired,
    value: PropTypes.string,
  };
 
  constructor(props) {
    super(props)
    this.state = { text: '', dataSource: [] }
  }
 
  componentDidMount() {
    this.handleChange('')
  }
 
  handleSelect = (_name, i, matches) => {
    if (i === -1) return
    this.props.onChange(matches[i].id)
    this.setState({ text: '', dataSource: [] })
  }
 
  handleChange = (text) => {
    this.setState({ text })
    const promise = text ? this.props.searchByName(text) : this.props.getAll()
    return promise.then((items) => {
      const dataSource = map(items, (item) => pick(item, ['id', 'name']))
      this.setState({ dataSource })
      return
    })
  }
 
  renderView() {
    return (
      <Fetch loadFn={partial(this.props.getById, this.props.value)}>
        {(loading, error, item) => {
          const label = loading ? '...' : error ? this.props.notFoundMsg : item.name || '--'
          return (
            <Chip
              className={error && styles.notFound}
              removable
              onClick={() => this.props.onChange(null)}
              label={label}
            />
          )
        }}
      </Fetch>
    )
  }
 
  render() {
    if (this.props.value) return this.renderView()
    const otherProps = omit(this.props, expectedProps)
    return (
      <Autocomplete
        {...otherProps}
        aria-label={this.props.ariaLabel}
        data={this.state.dataSource}
        dataLabel="name"
        dataValue="id"
        filter={null}
        placeholder={this.props.placeholder}
        onChange={this.handleChange}
        onAutocomplete={this.handleSelect}
        value={this.state.text}
      />
    )
  }
 
}