From a4ee1d00e587d1c45b0b01c5649bda872bbaa404 Mon Sep 17 00:00:00 2001 From: Patrick Wees Date: Sat, 3 Oct 2020 12:46:45 -0700 Subject: [PATCH] Add `shouldFillInputWithSelection` prop Co-authored-by: master-elodin --- README.md | 1 + src/index.js | 47 +++++++++++++----------- src/tests/index.js | 91 ++++++++++++++++++++++++++++++---------------- 3 files changed, 85 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index ebf3bef..ed768e9 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,7 @@ keys|all[Array]|List of properties that will be searched. This also supports nes list|null|Array of properties to be filtered. placeholder|'Search'|Placeholder of the searchbox resultsTemplate| Func | Template of the dropdown divs +shouldFillInputWithSelection| false | Determines whether or not to sync the input value with the current selection. shouldSort| true | Whether to sort the result list, by score. sortFn|`Array.prototype.sort`|The function that is used for sorting the result list. threshold|0.6|At what point does the match algorithm give up. A threshold of `0.0` requires a perfect match (of both letters and location), a threshold of `1.0` would match anything. diff --git a/src/index.js b/src/index.js index 36afbc2..6e8bc17 100644 --- a/src/index.js +++ b/src/index.js @@ -79,6 +79,7 @@ export default class FuzzySearch extends Component { location: PropTypes.number, placeholder: PropTypes.string, resultsTemplate: PropTypes.func, + shouldFillInputWithSelection: PropTypes.bool, shouldSort: PropTypes.bool, sortFn: PropTypes.func, threshold: PropTypes.number, @@ -98,6 +99,7 @@ export default class FuzzySearch extends Component { width: 430, placeholder: 'Search', resultsTemplate: defaultResultsTemplate, + shouldFillInputWithSelection: false, shouldSort: true, sortFn(a, b) { return a.score - b.score; @@ -114,7 +116,6 @@ export default class FuzzySearch extends Component { this.state = { results: [], selectedIndex: 0, - selectedValue: {}, value: '', }; this.handleChange = this.handleChange.bind(this); @@ -164,6 +165,27 @@ export default class FuzzySearch extends Component { }); } + selectItem(index) { + const { results } = this.state; + const { keyForDisplayName, onSelect, shouldFillInputWithSelection } = this.props + + const selectedIndex = index || this.state.selectedIndex; + const result = results[selectedIndex]; + if (result) { + // send result to onSelectMethod + onSelect(result); + // and set it as input value + this.setState({ + value: shouldFillInputWithSelection ? (result[keyForDisplayName] || result) : '' + }); + } + // hide dropdown + this.setState({ + results: [], + selectedIndex: 0, + }); + } + handleKeyDown(e) { const { results, selectedIndex } = this.state; @@ -181,31 +203,12 @@ export default class FuzzySearch extends Component { // Handle ENTER } else if (e.keyCode === 13) { - if (results[selectedIndex]) { - this.props.onSelect(results[this.state.selectedIndex]); - this.setState({ - selectedValue: results[this.state.selectedIndex], - }); - } - this.setState({ - results: [], - selectedIndex: 0, - value: results[this.state.selectedIndex].item ? results[this.state.selectedIndex].item.value : '', - }); + this.selectItem(); } } handleMouseClick(clickedIndex) { - const { results } = this.state; - - if (results[clickedIndex]) { - this.props.onSelect(results[clickedIndex]); - } - this.setState({ - results: [], - selectedIndex: 0, - value: results[this.state.selectedIndex].item ? results[this.state.selectedIndex].item.value : '', - }); + this.selectItem(clickedIndex); } render() { diff --git a/src/tests/index.js b/src/tests/index.js index 57d336d..eb39113 100644 --- a/src/tests/index.js +++ b/src/tests/index.js @@ -25,6 +25,16 @@ const list = [ }, ]; +const triggerDropdown = (wrapper, letter='t') => { + const input = wrapper.find('input'); + + input.simulate('change', { + target: { + value: letter, + }, + }); +} + describe('', () => { it('should set correct placeholder text', () => { const onSelect = sinon.spy(); @@ -46,14 +56,9 @@ describe('', () => { , ); - const input = wrapper.find('input'); expect(wrapper.state('results').length).to.equal(0); - input.simulate('change', { - target: { - value: 't', - }, - }); + triggerDropdown(wrapper); expect(wrapper.state('results').length).to.not.equal(0); }); @@ -64,28 +69,18 @@ describe('', () => { , ); - const input = wrapper.find('input'); - input.simulate('change', { - target: { - value: 't', - }, - }); + triggerDropdown(wrapper); expect(wrapper.state('results')).to.eql(['1', '2']); }); - it('should call onChange on selection of result', () => { + it('should call onChange on selection of result and input value set', () => { const onChange = sinon.spy(); const wrapper = mount( - , + , ); - const input = wrapper.find('input'); - input.simulate('change', { - target: { - value: 't', - }, - }); + triggerDropdown(wrapper); expect(wrapper.state('results').length).to.not.equal(0); @@ -96,6 +91,28 @@ describe('', () => { }); expect(onChange.calledOnce).to.equal(true); + + // results should be hidden + expect(wrapper.state('results').length).to.equal(0); + // input value should be set + expect(wrapper.find('input').props().value).to.equal('The Great Gatsby'); + }); + + it('should call onSelect on selection of result if mouse clicked', () => { + const onChange = sinon.spy(); + const wrapper = mount( + , + ); + + triggerDropdown(wrapper); + + expect(wrapper.state('results').length).to.not.equal(0); + + wrapper.find('div[children="The DaVinci Code"]').simulate('click'); + + expect(onChange.calledOnce).to.equal(true); + // results should be hidden + expect(wrapper.state('results').length).to.equal(0); }); it('should overwrite previous props with options passed in', () => { @@ -109,12 +126,7 @@ describe('', () => { />, ); - const input = wrapper.find('input'); - input.simulate('change', { - target: { - value: 't', - }, - }); + triggerDropdown(wrapper); // Each result should have a 'matches' array now with `includeMatches` expect(wrapper.state('results')[0].matches.length).to.not.equal(0); @@ -132,14 +144,29 @@ describe('', () => { />, ); - const input = wrapper.find('input'); - input.simulate('change', { - target: { - value: 'f', - }, - }); + triggerDropdown(wrapper, 'f'); // Each result should have a 'matches' array now with `includeMatches` expect(wrapper.state('results')[0].item.title).to.equal('The Great Gatsby'); }); + + it('should set input value even if no title on selectedValue (if ID set)', () => { + const wrapper = mount( + , + ); + + triggerDropdown(wrapper); + + wrapper.find('.react-fuzzy-search').simulate('keydown', { + keyCode: 13, + }); + + expect(wrapper.find('input').props().value).to.equal('The Great Gatsby'); + }); });