filter-suggest
v3.5.5
Published
Dynamic filter suggestion for advanced search query syntax
Downloads
48
Maintainers
Readme
FilterSuggest
A react component for achieving search-as-you-type functionality on a list of option items. The actual sorting & filtering of items is left up to you, making it easy to combine data from multiple sources (synchronous & asynchronous).
Implemented using downshift and material-components-web-react.
Demo
https://dan-kwiat.github.io/filter-suggest
Installation
With Yarn:
yarn add filter-suggest
Or npm:
npm install --save filter-suggest
You'll need to have the peer dependencies installed too:
{
"prop-types": "15.x",
"react": "16.x"
},
Examples
Sync
A basic synchronous example using match-sorter to sort items:
import React, { useState } from 'react'
import FilterSuggest from 'filter-suggest'
import 'filter-suggest/es/index.css'
import matchSorter from 'match-sorter'
const ITEMS = [
{
id: `movie-1`,
icon: null,
primary: 'movie:The Big Short',
secondary: 'Filter by movie',
},
// add more items here
]
const Demo = () => {
const [inputValue, setInputValue] = useState('')
const sortedItems = inputValue ? matchSorter(
ITEMS,
inputValue,
{ keys: ['primary'] }
) : []
return (
<FilterSuggest
inputValue={inputValue}
label='Start typing...'
onInputValueChange={setInputValue}
onSelect={item => {
// deal with selected item here
}}
items={sortedItems}
/>
)
}
See the demo source code for a more comprehensive synchronous example.
Async
A basic asynchronous example using a dummy GraphQL endpoint to fetch sorted items:
import React, { Component } from 'react'
import debounce from 'lodash.debounce'
import gql from 'graphql-tag'
import { Query } from 'react-apollo'
import FilterSuggest from 'filter-suggest'
import 'filter-suggest/es/index.css'
const DEBOUNCE_TIME = 100
const applyDebounced = debounce((f, x) => f(x), DEBOUNCE_TIME)
const QUERY = gql`
query GET_ITEMS(
$search: String!
) {
getItems(
search: $search
) {
id
primary
secondary
}
}
`
class AsyncDemo extends Component {
state = {
inputValue: '',
variables: {
search: '',
}
}
setInputValue = inputValue => {
this.setState({ inputValue })
}
setVariables = variables => {
this.setState({ variables })
}
onInputValueChange = value => {
this.setInputValue(value)
applyDebounced(this.setVariables, { search: value })
}
render() {
const { inputValue, variables } = this.state
return (
<Query query={QUERY} variables={variables}>
{({ data, loading, error }) => {
return (
<FilterSuggest
inputValue={inputValue}
label='Search async'
loading={loading}
onInputValueChange={this.onInputValueChange}
onSelect={item => {
// handle selected item
}}
items={inputValue && data ? data.getItems : []}
/>
)
}}
</Query>
)
}
}
For a seamless search-as-you-type experience, results should be returned very quickly (say of the order 100ms). You might want to look at Elasticsearch completion suggester or PostgreSQL trigram indices.
See charity-base-search for a real-world asynchronous example.
Props
FilterSuggest accepts the following props:
FilterSuggest.propTypes = {
// Optional class applied to the parent div
className: PropTypes.string,
// Error message to render instead of dropdown
errorMessage: PropTypes.string,
// The current value of the input (you must handle the state yourself)
inputValue: PropTypes.string.isRequired,
// An array of items to render in the dropdown
items: PropTypes.arrayOf(PropTypes.shape({
// A unique item id
id: PropTypes.string.isRequired,
// An optional icon to render on the left
icon: PropTypes.element,
// The main text to display on the item
primary: PropTypes.string.isRequired,
// Secondary text to display below the main text (useful for giving prompts)
secondary: PropTypes.string,
// You may want to provide additional item props here (for use in the onSelect callback)
})).isRequired,
// The input label
label: PropTypes.string,
// Optional icon element to prefix the input
leadingIcon: PropTypes.element,
// Whether or not the items are loading
loading: PropTypes.bool,
// Maximum number of items to render in dropdown list
maxSuggestions: PropTypes.number,
// Optional class applied to the dropdown menu
menuClassName: PropTypes.string,
// Blur event handler
onBlur: PropTypes.func,
// Focus event handler
onFocus: PropTypes.func,
// A callback fired whenever an input value change is detected
onInputValueChange: PropTypes.func.isRequired,
// A callback fired whenever an item is selected
onSelect: PropTypes.func.isRequired,
// Whether or not to render the outlined variant of text field
outlined: PropTypes.bool,
// Optional class applied to the input element's parent
textFieldClassName: PropTypes.string,
}
FilterSuggest.defaultProps = {
label: 'Start typing...',
maxSuggestions: 12,
}
Styles
With CSS:
import 'filter-suggest/es/index.css'
With Sass:
import 'filter-suggest/es/index.scss'
The colour theme can be customised using the following Sass mixin:
$mdc-theme-primary: #00ff00;
For further customisation see MDC Web's mixins for the text field and list.
You may also supply textFieldClassName
and menuClassName
props which will be applied to the appropriate components.