Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

agency search refine #801

Merged
merged 1 commit into from
Jun 1, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 58 additions & 50 deletions src/components/AgencySearch.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,42 @@
import PropTypes from 'prop-types'
import React, { Component } from 'react'

import AgencySearchResults from './AgencySearchResults'

class AgencySearch extends Component {
state = { search: '', showSelection: true }
constructor(props) {
super(props)

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

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

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

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

render() {
const { agencies, ori, state } = this.props
const { showSelection, search } = this.state

const data = Object.keys(agencies[state]).map(id => ({
ori: id,
...agencies[state][id],
}))
toggleResults = () => {
this.setState(prevState => ({ showResults: !prevState.showResults }))
}

const selected = data.find(d => d.ori === ori)
render() {
const { data } = this.props
const { search, hasSelection, showResults } = this.state

// get unique set of counties (for result grouping)
const counties = {}
Expand All @@ -42,48 +50,48 @@ class AgencySearch extends Component {
return words.includes(searchUpper)
})

const showOris = !!search.length && dataFiltered.length > 0

return (
<div className="mt2">
{selected && showSelection
? <div className="mb2 relative">
<input
type="text"
className="col-12 field field-sm bg-white border-blue pr5 truncate"
defaultValue={selected.agency_name}
<div className="relative">
<div className="relative">
<input
type="text"
className="col-12 field field-sm bold bg-white border-blue rounded-none"
placeholder="Search for an agency..."
value={search}
onChange={this.handleChange}
/>
<button
className="absolute btn p0 line-height-1"
style={{ top: '.5rem', right: '1rem' }}
onClick={hasSelection ? this.clearInput : this.toggleResults}
>
<img
src={`/img/${hasSelection ? 'x-navy' : 'chevron-down-navy'}.svg`}
alt="close"
width="12"
height="12"
/>
<button
className="absolute btn p0 line-height-1"
style={{ top: '.5rem', right: '1rem' }}
onClick={this.removeSelection}
>
<img src="/img/x-navy.svg" alt="close" width="12" height="12" />
</button>
</div>
: <div className="relative">
<div className="relative">
<input
type="text"
className="col-12 field field-sm bg-white border-blue rounded-none"
placeholder="Search for an agency..."
value={search}
onChange={this.handleChange}
/>
</div>
{showOris &&
<AgencySearchResults
data={dataFiltered.sort(
(a, b) => a.agency_name > b.agency_name,
)}
groupKey="primary_county"
groupValues={Object.keys(counties).sort()}
onClick={this.handleClick}
/>}
</div>}
</button>
</div>
{!hasSelection &&
showResults &&
dataFiltered.length > 0 &&
<AgencySearchResults
data={dataFiltered.sort((a, b) => a.agency_name > b.agency_name)}
groupKey="primary_county"
groupValues={Object.keys(counties).sort()}
onClick={this.handleClick}
/>}
</div>
</div>
)
}
}

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

export default AgencySearch
4 changes: 2 additions & 2 deletions src/components/Explorer.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class Explorer extends React.Component {
}

render() {
const { appState, dispatch, params, router } = this.props
const { appState, dispatch, params } = this.props
const { crime } = params
const { place, placeType } = getPlaceInfo(params)

Expand Down Expand Up @@ -91,7 +91,7 @@ class Explorer extends React.Component {
</button>
</div>
</div>
<SidebarContainer onChange={this.handleSidebarChange} router={router} />
<SidebarContainer onChange={this.handleSidebarChange} />
<div className="site-content">
<div className="container-main mx-auto px2 md-py3 lg-px8">
<ExplorerHeader
Expand Down
43 changes: 17 additions & 26 deletions src/components/LocationFilter.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,30 @@ import React from 'react'

import AgencySearch from './AgencySearch'
import LocationSelect from './LocationSelect'
import { oriToState } from '../util/ori'
import { nationalKey } from '../util/usa'

const LocationFilter = ({
agencies,
agency,
agencyData,
onChange,
place,
placeType,
showSearch,
}) => {
const isNational = place === nationalKey
const isAgency = placeType === 'agency'
const selected = isAgency ? oriToState(place) : place

return (
<div id="location" className="mb4">
<div className="mb3 fs-22 bold border-bottom">Location</div>
<LocationSelect onChange={onChange} selected={startCase(selected)} />
{showSearch &&
selected &&
!isNational &&
<AgencySearch
agencies={agencies.data}
onChange={onChange}
ori={isAgency && place}
state={selected}
/>}
</div>
)
}
usState,
}) => (
<div id="location" className="mb4">
<div className="mb3 fs-22 bold border-bottom">Location</div>
<LocationSelect onChange={onChange} selected={startCase(usState)} />
{showSearch &&
usState !== nationalKey &&
<AgencySearch
onChange={onChange}
agency={(agency || {}).agency_name || ''}
data={agencyData}
/>}
</div>
)

LocationFilter.defaultProps = {
selected: '',
usState: '',
showSearch: false,
}

Expand Down
107 changes: 64 additions & 43 deletions src/components/SidebarContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,59 +6,80 @@ import CrimeTypeFilter from './CrimeTypeFilter'
import LocationFilter from './LocationFilter'
import TimePeriodFilter from './TimePeriodFilter'
import { hideSidebar } from '../actions/sidebar'
import { getAgency, oriToState } from '../util/ori'

const SidebarContainer = ({
agencies,
dispatch,
agency,
agencyData,
crime,
hide,
filters,
isOpen,
onChange,
}) => {
const { crime, place, placeType } = filters
const hide = () => dispatch(hideSidebar())

return (
<nav className={`site-sidebar bg-white ${isOpen ? 'open' : ''}`}>
<div className="p2 bg-red-bright line-height-1 md-hide lg-hide">
<button
type="button"
className="right btn p0 fs-12 caps line-height-4 black"
onClick={hide}
>
Close
</button>
<img
className="align-middle"
width="22"
height="20"
src="/img/filters.svg"
alt="filters"
/>
</div>
<div className="p6 sm-p3 md-p4">
<LocationFilter
agencies={agencies}
onChange={onChange}
place={place}
placeType={placeType}
showSearch={filters.agencySearch === 'true'}
/>
<TimePeriodFilter onChange={onChange} {...filters} />
<CrimeTypeFilter onChange={onChange} selected={crime} />
</div>
</nav>
)
}
showSearch,
usState,
}) => (
<nav className={`site-sidebar bg-white ${isOpen ? 'open' : ''}`}>
<div className="p2 bg-red-bright line-height-1 md-hide lg-hide">
<button
type="button"
className="right btn p0 fs-12 caps line-height-4 black"
onClick={hide}
>
Close
</button>
<img
className="align-middle"
width="22"
height="20"
src="/img/filters.svg"
alt="filters"
/>
</div>
<div className="p6 sm-p3 md-p4">
<LocationFilter
agency={agency}
agencyData={agencyData}
onChange={onChange}
showSearch={showSearch}
usState={usState}
/>
<TimePeriodFilter onChange={onChange} {...filters} />
<CrimeTypeFilter onChange={onChange} selected={crime} />
</div>
</nav>
)

SidebarContainer.propTypes = {
onChange: PropTypes.func,
}

const mapStateToProps = ({ agencies, filters, sidebar }) => ({
agencies,
filters,
isOpen: sidebar.isOpen,
const formatAgencyData = (agencies, state) =>
Object.keys(agencies[state]).map(id => ({
ori: id,
...agencies[state][id],
}))

const mapStateToProps = ({ agencies, filters, sidebar }) => {
const { agencySearch, crime, place, placeType } = filters

const isAgency = placeType === 'agency'
const usState = isAgency ? oriToState(place) : place
const agency = isAgency && getAgency(agencies, place)
const agencyData = usState && formatAgencyData(agencies.data, usState)

return {
agency,
agencyData,
crime,
filters,
isOpen: sidebar.isOpen,
showSearch: agencySearch === 'true',
usState,
}
}
const mapDispatchToProps = dispatch => ({
hide: () => dispatch(hideSidebar()),
})
const mapDispatchToProps = dispatch => ({ dispatch })

export default connect(mapStateToProps, mapDispatchToProps)(SidebarContainer)