fbi-cde/crime-data-frontend

View on GitHub
src/components/agency/AgencySearch.js

Summary

Maintainability
A
2 hrs
Test Coverage
import PropTypes from 'prop-types'
import React, { Component } from 'react'

import AgencySearchResults from './AgencySearchResults'
import OnEscape from '../OnEscape'
import { oriToState } from '../../util/agencies'

class AgencySearch extends Component {
  constructor(props) {
    super(props)

    const search = props.agency
    const hasSelection = !!search
    this.state = {
      search,
      hasSelection,
      showResults: props.initialShowResults
    }
  }

  componentDidMount() {
    document.addEventListener('click', this.handleClick)
  }

  componentWillReceiveProps({ agency, initialShowResults }) {
    if (initialShowResults !== this.props.initialShowResults) {
      this.setState({ showResults: initialShowResults })
    }

    this.setState({
      search: agency,
      hasSelection: agency !== ''
    })
  }

  componentWillUnmount() {
    document.removeEventListener('click', this.handleClick)
  }

  handleChange = e => {
    this.setState({
      search: e.target.value,
      hasSelection: false,
      showResults: true
    })
  }

  handleClick = e => {
    if (this.state.showResults && !e.target.closest('.agency-search')) {
      this.setState({ showResults: false })
    }
    return e
  }

  handleEscape = e => {
    if (this.state.showResults) this.setState({ showResults: false })
    return e
  }

  handleResultsClick = d => e => {
    e.preventDefault()
    this.setState({ search: d.agency_name, hasSelection: true })
    this.props.onChange({ place: d.ori, placeType: 'agency' })
  }

  handleStateClick = usState => {
    this.setState({ showResults: false })
    this.props.onChange({ place: usState, placeType: 'state' })
  }

  clearInput = () => {
    this.setState({ search: '', hasSelection: false, showResults: true })
  }

  toggleResults = () => {
    this.setState(prevState => ({ showResults: !prevState.showResults }))
  }

  render() {
    const { data } = this.props
    const { search, hasSelection, showResults } = this.state

    // get unique set of counties (for result grouping)
    const counties = {}
    data.forEach(d => (counties[d.county_name || 'N/A'] = true))
    const searchUpper = search.toUpperCase()
    const dataFiltered =
      searchUpper === ''
        ? data
        : data.filter(d => {
            const words = `${d.ori} ${d.agency_name}`.toUpperCase()
            return words.includes(searchUpper)
          })

    return (
      <div className="agency-search mt2">
        <div className="relative">
          <div className="relative">
            <input
              id="agencySearchInput"
              type="text"
              className="col-12 pr5 field field-sm fs-14 bold bg-white border-blue rounded-none placeholder-blue placeholder-fw-100 truncate border-blue"
              placeholder="Search for an agency"
              value={search}
              onChange={this.handleChange}
            />
            <button
              id="agencySearchBtn"
              className="absolute right-0 btn line-height-1 border-none"
              style={{
                padding: '.5rem .75rem',
                borderLeft: '1px solid #284152'
              }}
              onClick={hasSelection ? this.clearInput : this.toggleResults}
            >
              {hasSelection ? (
                <img src="/img/x-navy.svg" alt="close" width="10" height="10" />
              ) : (
                <img
                  src="/img/chevron-down-navy.svg"
                  alt="see results"
                  width="12"
                  height="12"
                />
              )}
            </button>
          </div>
          {!hasSelection &&
            showResults &&
            dataFiltered.length > 0 && (
              <OnEscape handler={this.handleEscape}>
                <AgencySearchResults
                  data={dataFiltered.sort(
                    (a, b) => a.agency_name > b.agency_name
                  )}
                  groupKey="county_name"
                  groupValues={Object.keys(counties).sort()}
                  onResultsClick={this.handleResultsClick}
                  onStateClick={this.handleStateClick}
                  usState={oriToState(data[0].ori)}
                />
              </OnEscape>
            )}
        </div>
      </div>
    )
  }
}

AgencySearch.propTypes = {
  agency: PropTypes.string.isRequired,
  data: PropTypes.arrayOf(PropTypes.object).isRequired,
  initialShowResults: PropTypes.bool
}

AgencySearch.defaultProps = {
  initialShowResults: true
}

export default AgencySearch