Skip to content

Commit

Permalink
Add isFilterDropdown prop
Browse files Browse the repository at this point in the history
Allow the searchbox to act as a `filter` dropdown with initial values.
  • Loading branch information
PatrickDesign committed Dec 10, 2020
1 parent 391aa68 commit 381c1a4
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 6 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ caseSensitive|false|Indicates whether comparisons should be case sensitive.
className|null|give a custom class name to the root element
inputStyle|{}|Styles passed directly to the `input` element.
inputWrapperStyle|{}|Styles passed directly to the `input` wrapper `div`.
isFilterDropdown|false|Allow the searchbox to act as a `filter` dropdown with initial values.
listItemStyle|{}|Styles passed to each item in the dropdown list.
listWrapperStyle|{}|Styles passed directly to the dropdown wrapper.
selectedListItemStyle|{}|Styles passed directly to current 'active' item.
Expand Down
51 changes: 45 additions & 6 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ function defaultResultsTemplate(props, state, styl, clickHandler) {
return state.results.map((val, i) => {
const style = state.selectedIndex === i ? {...styl.selectedResultStyle, ...props.selectedListItemStyle} : {...styl.resultsStyle, ...props.listItemStyle };
return (
<div key={i} style={style} onClick={() => clickHandler(i)}>
<div tabIndex="0" key={i} style={style} onClick={() => clickHandler(i)}>
{val[props.keyForDisplayName]}
</div>
);
Expand All @@ -70,6 +70,7 @@ export default class FuzzySearch extends Component {
distance: PropTypes.number,
id: PropTypes.string,
include: PropTypes.array,
isFilterDropdown: PropTypes.bool,
maxPatternLength: PropTypes.number,
onSelect: PropTypes.func.isRequired,
width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
Expand Down Expand Up @@ -98,6 +99,7 @@ export default class FuzzySearch extends Component {
caseSensitive: false,
distance: 100,
include: [],
isFilterDropdown: false,
keyForDisplayName: 'title',
location: 0,
width: 430,
Expand All @@ -122,15 +124,18 @@ export default class FuzzySearch extends Component {
constructor(props) {
super(props);
this.state = {
isOpen: !this.props.isFilterDropdown,
results: [],
selectedIndex: 0,
selectedValue: {},
value: '',
};
this.handleChange = this.handleChange.bind(this);
this.handleKeyDown = this.handleKeyDown.bind(this);
this.handleMouseClick = this.handleMouseClick.bind(this);
this.fuse = new Fuse(props.list, this.getOptions());
this.setDropdownRef = (ref) => {
this.dropdownRef = ref;
};
}

getOptions() {
Expand Down Expand Up @@ -168,8 +173,12 @@ export default class FuzzySearch extends Component {
}

handleChange(e) {
const shouldDisplayAllListItems = this.props.isFilterDropdown && !e.target.value;

this.setState({
results: this.fuse.search(e.target.value).slice(0, this.props.maxResults - 1),
results: shouldDisplayAllListItems
? this.props.list
: this.fuse.search(e.target.value).slice(0, this.props.maxResults - 1),
value: e.target.value,
});
}
Expand Down Expand Up @@ -219,7 +228,15 @@ export default class FuzzySearch extends Component {
}

render() {
const { autoFocus, className, list, placeholder, resultsTemplate, width } = this.props;
const {
autoFocus,
className,
isFilterDropdown,
list,
placeholder,
resultsTemplate,
width,
} = this.props;

// Update the search space list
if (this.fuse.setCollection && list) {
Expand All @@ -229,7 +246,21 @@ export default class FuzzySearch extends Component {
const mainClass = classNames('react-fuzzy-search', className);

return (
<div className={mainClass} style={{ width }} onKeyDown={this.handleKeyDown}>
<div
className={mainClass}
ref={this.setDropdownRef}
style={{ width }}
onBlur={(e) => {
if (this.dropdownRef.contains(e.relatedTarget)) return;

if (isFilterDropdown) {
this.setState({
isOpen: false,
});
}
}}
onKeyDown={this.handleKeyDown}
>
<div style={{...styles.searchBoxWrapper, ...this.props.inputWrapperStyle}}>
<input
autoFocus={autoFocus}
Expand All @@ -238,9 +269,17 @@ export default class FuzzySearch extends Component {
style={{...styles.searchBoxStyle, ...this.props.inputStyle}}
type="text"
value={this.state.value}
onFocus={() => {
if (isFilterDropdown) {
this.setState({
isOpen: true,
results: this.state.value ? this.state.results : list,
});
}
}}
/>
</div>
{this.state.results && this.state.results.length > 0 && (
{this.state.isOpen && this.state.results && this.state.results.length > 0 && (
<div style={{...styles.resultsWrapperStyle, ...this.props.listWrapperStyle}}>
{resultsTemplate(this.props, this.state, styles, this.handleMouseClick)}
</div>
Expand Down
9 changes: 9 additions & 0 deletions src/stories/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,15 @@ storiesOf('SearchBox', module)
/>
);
})
.add('FilterDropdown', () => (
<FuzzySearch
isFilterDropdown
list={list}
keys={['author', 'title']}
width={430}
onSelect={action('selected')}
/>
))
.add('Passthrough Options', () => {
const template = (props, state, styles, click) =>
state.results.map(({ item, matches }, i) => {
Expand Down
20 changes: 20 additions & 0 deletions src/tests/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,4 +170,24 @@ describe('<FuzzySearch />', () => {

expect(wrapper.find('input')).to.exist;
})

it('should display all options onFocus when isFilterDropdown passed in', () => {
const onChange = sinon.spy();
const wrapper = mount(
<FuzzySearch
keyForDisplayName="author"
list={list}
onSelect={onChange}
keys={['author', 'title']}
options={{ includeMatches: true }}
isFilterDropdown
/>,
);

const input = wrapper.find('input');

input.simulate('focus');

expect(wrapper.state('results').length).to.not.equal(0);
});
});

0 comments on commit 381c1a4

Please sign in to comment.