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

add optional resultTitle #24

Closed
wants to merge 3 commits into from
Closed
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
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,15 @@ attribute|default|description
caseSensitive|false|Indicates whether comparisons should be case sensitive.
className|null|give a custom class name to the root element
distance|100|Determines how close the match must be to the fuzzy location (specified by location). An exact letter match which is distance characters away from the fuzzy location would score as a complete mismatch. A distance of 0 requires the match be at the exact location specified, a distance of 1000 would require a perfect match to be within 800 characters of the location to be found using a threshold of 0.8.
id|null|The name of the identifier property. If specified, the returned result will be a list of the items' identifiers, otherwise it will be a list of the items.
id|null|The name of the identifier property. If specified, the returned result will be a list of the items' identifiers, otherwise it will be a list of the items. **NOTE:** this is mutually-exclusive with `resultKey`
include|[]|An array of values that should be included from the searcher's output. When this array contains elements, each result in the list will be of the form `{ item: ..., include1: ..., include2: ... }`. Values you can include are score, matches. Eg: `{ include: ['score', 'matches' ] }`
maxPatternLength|32|The maximum length of the pattern. The longer the pattern, the more intensive the search operation will be. Whenever the pattern exceeds the maxPatternLength, an error will be thrown.
onSelect| noop | Function to be executed on selection of any result.
width|430|width of the fuzzy searchbox
keys|all[Array]|List of properties that will be searched. This also supports nested properties.
list|null|Array of properties to be filtered.
placeholder|'Search'|Placeholder of the searchbox
resultKey|'title'|Key to use to display in dropdown divs. **NOTE:** this is mutually-exclusive with `id`. If `id` is given, `resultKey` will be ignored.
resultsTemplate| Func | Template of the dropdown divs
shouldSort| true | Whether to sort the result list, by score.
sortFn|`Array.prototype.sort`|The function that is used for sorting the result list.
Expand Down
18 changes: 14 additions & 4 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,11 @@ const styles = {
function defaultResultsTemplate(props, state, styl, clickHandler) {
return state.results.map((val, i) => {
const style = state.selectedIndex === i ? styl.selectedResultStyle : styl.resultsStyle;
// if ID was given, val is text and not an object, so just use the text
const resultKeyText = props.id ? val : val[props.resultKey];
return (
<div key={i} style={style} onClick={() => clickHandler(i)}>
{val.title}
{resultKeyText}
</div>
);
});
Expand All @@ -76,6 +78,7 @@ export default class FuzzySearch extends Component {
keys: PropTypes.oneOfType([PropTypes.array, PropTypes.string]),
list: PropTypes.array.isRequired,
location: PropTypes.number,
resultKey: PropTypes.string,
placeholder: PropTypes.string,
resultsTemplate: PropTypes.func,
shouldSort: PropTypes.bool,
Expand All @@ -93,6 +96,7 @@ export default class FuzzySearch extends Component {
distance: 100,
include: [],
location: 0,
resultKey: 'title',
width: 430,
placeholder: 'Search',
resultsTemplate: defaultResultsTemplate,
Expand All @@ -113,6 +117,7 @@ export default class FuzzySearch extends Component {
results: [],
selectedIndex: 0,
selectedValue: {},
inputValue: null
};
this.handleChange = this.handleChange.bind(this);
this.handleKeyDown = this.handleKeyDown.bind(this);
Expand All @@ -134,6 +139,7 @@ export default class FuzzySearch extends Component {
distance,
threshold,
location,
resultKey,
options,
} = this.props;

Expand All @@ -150,6 +156,7 @@ export default class FuzzySearch extends Component {
distance,
threshold,
location,
resultKey,
...options,
};
}
Expand Down Expand Up @@ -181,9 +188,12 @@ export default class FuzzySearch extends Component {
});
} else if (e.keyCode === 13) {
if (results[selectedIndex]) {
this.props.onSelect(results[this.state.selectedIndex]);
const result = results[this.state.selectedIndex];
this.props.onSelect(result);
this.setState({
selectedValue: results[this.state.selectedIndex],
selectedValue: result,
// use title for input value if 'title' exists
inputValue: result.title || result
});
}
this.setState({
Expand Down Expand Up @@ -219,7 +229,7 @@ export default class FuzzySearch extends Component {
onChange={this.handleChange}
placeholder={placeholder}
autoFocus={autoFocus}
value={this.state.selectedValue && this.state.selectedValue.title}
value={this.state.inputValue}
/>
</div>
{this.state.results &&
Expand Down
77 changes: 76 additions & 1 deletion src/tests/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@ const list = [
},
];

const triggerDropdown = (wrapper, letter='t') => {
const input = wrapper.find('input');

input.simulate('change', {
target: {
value: letter,
},
});
}

describe('<FuzzySearch />', () => {
it('should set correct placeholder text', () => {
const onSelect = sinon.spy();
Expand Down Expand Up @@ -71,7 +81,7 @@ describe('<FuzzySearch />', () => {
},
});

expect(wrapper.state('results')).to.eql([2, 1]);
expect(wrapper.state('results')).to.eql(['1', '2']);
});

it('should call onChange on selection of result', () => {
Expand Down Expand Up @@ -119,4 +129,69 @@ describe('<FuzzySearch />', () => {
// Each result should have a 'matches' array now with `includeMatches`
expect(wrapper.state('results')[0].matches.length).to.not.equal(0);
});

it('should use resultKey property if given', () => {
const wrapper = mount(
<FuzzySearch
list={list}
resultKey={'author'}
keys={['author', 'title']}
onSelect={sinon.spy()}
/>,
);

triggerDropdown(wrapper);

expect(wrapper.find('div[children="F. Scott Fitzgerald"]')).to.have.length(1);
});


it('should use default title property if resultKey not given', () => {
const wrapper = mount(
<FuzzySearch
list={list}
keys={['author', 'title']}
onSelect={sinon.spy()}
/>,
);

triggerDropdown(wrapper);

expect(wrapper.find('div[children="The Great Gatsby"]')).to.have.length(1);
});

it('should ignore resultKey if id is given', () => {
const wrapper = mount(
<FuzzySearch
list={list}
id={'title'}
resultKey={'author'}
keys={['author', 'title']}
onSelect={sinon.spy()}
/>,
);

triggerDropdown(wrapper);

expect(wrapper.find('div[children="The Great Gatsby"]')).to.have.length(1);
});

it('should set input value even if no title on selectedValue (if ID set)', () => {
const wrapper = mount(
<FuzzySearch
list={list}
id={'title'}
keys={['author', 'title']}
onSelect={sinon.spy()}
/>,
);

triggerDropdown(wrapper);

wrapper.find('.react-fuzzy-search').simulate('keydown', {
keyCode: 13,
});

expect(wrapper.find('input').props().value).to.equal('The Great Gatsby');
});
});