Skip to content

Commit

Permalink
FIX (searchFn)[#101] searchFn callback should only call once when typ…
Browse files Browse the repository at this point in the history
…ing in the search bar, #134, #133 (#136)

* Working searchFn example.

* Remove debug code.

* Undo indentation

* Restore changes.

* Remove debug code.

* Restore return.

* Add test for a custom function.

* Add test coverage for custom search function.

* Apply code review suggestions.

* Reset searchResults on blur.

* Clear search results on dropdown; update search results on options change.
  • Loading branch information
Pattie Reaves authored Nov 17, 2020
1 parent eccff4c commit 64a737a
Show file tree
Hide file tree
Showing 9 changed files with 247 additions and 26 deletions.
143 changes: 143 additions & 0 deletions __tests__/__snapshots__/index.spec.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,149 @@ exports[`<Select/> component <Select/> renders with clearable 1`] = `
</div>
`;

exports[`<Select/> component <Select/> renders with custom search function 1`] = `
.emotion-6 {
box-sizing: border-box;
position: relative;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
border: 1px solid #ccc;
width: 100%;
border-radius: 2px;
padding: 2px 5px;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
direction: ltr;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
cursor: pointer;
min-height: 36px;
pointer-events: all;
}
.emotion-6:hover,
.emotion-6:focus-within {
border-color: #0074D9;
}
.emotion-6:focus,
.emotion-6:focus-within {
outline: 0;
box-shadow: 0 0 0 3px rgba(0,116,217,0.2);
}
.emotion-6 * {
box-sizing: border-box;
}
.emotion-2 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex: 1;
-ms-flex: 1;
flex: 1;
-webkit-flex-wrap: wrap;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
}
.emotion-0 {
line-height: inherit;
border: none;
margin-left: 5px;
background: transparent;
font-size: smaller;
}
.emotion-0:focus {
outline: none;
}
.emotion-4 {
text-align: center;
pointer-events: none;
margin: 0 0 0 5px;
-webkit-transform: rotate(180deg);
-ms-transform: rotate(180deg);
transform: rotate(180deg);
cursor: pointer;
}
.emotion-4 svg {
width: 16px;
height: 16px;
}
.emotion-4:hover path {
stroke: #0074D9;
}
.emotion-4:focus {
outline: none;
}
.emotion-4:focus path {
stroke: #0074D9;
}
<div>
<div
className="react-dropdown-select undefined emotion-6 emotion-7"
color="#0074D9"
direction="ltr"
disabled={false}
onClick={[Function]}
onKeyDown={[Function]}
tabIndex="0"
>
<div
className="react-dropdown-select-content react-dropdown-select-type-single emotion-2 emotion-3"
onClick={[Function]}
>
<input
className="react-dropdown-select-input emotion-0 emotion-1"
disabled={false}
onBlur={[Function]}
onChange={[Function]}
onClick={[Function]}
onFocus={[Function]}
onKeyPress={[Function]}
placeholder="Select..."
readOnly={false}
size={3}
tabIndex="-1"
value="Zer"
/>
</div>
<div
className="react-dropdown-select-dropdown-handle emotion-4 emotion-5"
color="#0074D9"
onClick={[Function]}
onKeyDown={[Function]}
onKeyPress={[Function]}
rotate={1}
tabIndex="-1"
>
<svg
fill="currentColor"
viewBox="0 0 40 40"
>
<path
d="M31 26.4q0 .3-.2.5l-1.1 1.2q-.3.2-.6.2t-.5-.2l-8.7-8.8-8.8 8.8q-.2.2-.5.2t-.5-.2l-1.2-1.2q-.2-.2-.2-.5t.2-.5l10.4-10.4q.3-.2.6-.2t.5.2l10.4 10.4q.2.2.2.5z"
/>
</svg>
</div>
</div>
</div>
`;

exports[`<Select/> component <Select/> renders with loading 1`] = `
.emotion-8 {
box-sizing: border-box;
Expand Down
3 changes: 2 additions & 1 deletion __tests__/components/Dropdown.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ const props = {
dropdownHeight: '300px'
},
state: {
selectBounds: {}
selectBounds: {},
searchResults: [],
},
methods: {
searchResults: () => [],
Expand Down
25 changes: 24 additions & 1 deletion __tests__/index.spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import TestRenderer from 'react-test-renderer';
import { LIB_NAME } from '../src/constants';

import Select from '../src/index';

Expand Down Expand Up @@ -59,9 +60,31 @@ describe('<Select/> component', () => {
expect(tree).toMatchSnapshot();
});

it('<Select/> renders with custom search function', () => {
const options = [
{ id: 0, name: 'Zero' },
{ id: 1, name: 'One' },
{ id: 2, name: 'Two' },
];

const searchFn = ({ props, state }) => {
return props.options.filter(({ name }) => new RegExp(state.search).test(name) );
};

const component = selectWithProps(<Select {...props({ searchFn, options })} />);

const input = component.root.find(element => element.props.className === `${LIB_NAME}-input`);

TestRenderer.act(() => input.props.onChange({ target: { value: 'Zer' } }));

expect(component.toTree().instance.state.search).toBe('Zer');
expect(component.toTree().instance.state.searchResults).toStrictEqual([{ id: 0, name: 'Zero'}])
expect(component.toJSON()).toMatchSnapshot();
});

it('<Select/> is disabled', () => {
const tree = selectWithProps(<Select {...props({ disabled: true })} />).toJSON();

expect(tree).toMatchSnapshot();
})
});
});
37 changes: 37 additions & 0 deletions docs/src/examples/WithSearchFn.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from 'react';
import { Heading } from './components/Heading';
import Select from '../../../src';

import {
getByPath,
} from '../../../src/util';

const WithSearchFn = ({ options, title }) => {
const onSearch = ({ props, state, methods }) => {
console.log({ props, state, methods });

const regexp = new RegExp(methods.safeString(state.search), 'i');
return methods
.sortBy()
.filter((item) =>
regexp.test(getByPath(item, props.searchBy) || getByPath(item, props.valueField))
);
};

return (
<React.Fragment>
<Heading
title={title}
source="https://github.com/sanusart/react-dropdown-select/tree/master/docs/src/examples/WithSearchFn.js"
/>
<Select
options={options}
searchFn={onSearch}
/>
</React.Fragment>
);
};

WithSearchFn.propTypes = {};

export default WithSearchFn;
5 changes: 5 additions & 0 deletions docs/src/pages/examples.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import ExternalClear from '../examples/ExternalClear';
import AccessDataByPath from '../examples/AccessDataByPath';
import CustomDropdownHandle from '../examples/CustomDropdownHandle';
import WithAnimation from '../examples/WithAnimation';
import WithSearchFn from '../examples/WithSearchFn';

const demoOptions = options.map((option) => ({
...option,
Expand Down Expand Up @@ -116,6 +117,10 @@ const Examples = () => (
<WithAnimation title={`With animations`} options={demoOptions} />
</Wrapper>

<Wrapper>
<WithSearchFn title={`With a custom search function`} options={demoOptions} />
</Wrapper>

<br />
<br />
<br />
Expand Down
2 changes: 2 additions & 0 deletions docs/src/pages/state.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ Access to current state of &lt;Select/&gt; component
| dropdown | boolean | check if dropdown is open |
| values | array | selected value(s) |
| search | string | current search string |
| searchResults| string | Filtered search results
|
| selectBounds | object | current `getBoundingClientRect()` of &lt;Select/&gt; |
9 changes: 6 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 13 additions & 14 deletions src/components/Dropdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,27 +49,26 @@ const Dropdown = ({ props, state, methods }) => (
{props.createNewLabel.replace('{search}', `"${state.search}"`)}
</AddNew>
)}
{methods.searchResults().length === 0 ? (
{state.searchResults.length === 0 ? (
<NoData
className={`${LIB_NAME}-no-data`}
state={state}
props={props}
methods={methods}
/>
) : (
methods
.searchResults()
.map((item, itemIndex) => (
<Item
key={item[props.valueField]}
item={item}
itemIndex={itemIndex}
state={state}
props={props}
methods={methods}
/>
))
)}
state.searchResults
.map((item, itemIndex) => (
<Item
key={item[props.valueField]}
item={item}
itemIndex={itemIndex}
state={state}
props={props}
methods={methods}
/>
))
)}
</React.Fragment>
)}
</DropDown>
Expand Down
Loading

0 comments on commit 64a737a

Please sign in to comment.