@@ -76,7 +76,7 @@ An input field that controls the state used to render the items in `FilterResult
### onChange
-`onChange` is an optional callback function that is called BEFORE the value in the input field changes via an `onchange` event. If it returns `false`, the new value will not be propagated to the shared state. (returning nothing or any other return will propagate the state).
+`onChange` is an optional callback function that is called BEFORE the value in the input field changes via an `onchange` event. It can optionally return a string, which will then be passed directly to `FilterResults` rather than the original string. This can be used to filter out special inputs (eg: `author:jdlehman`) from fuzzy searching. These special inputs could then be used to change the `items` being passed to `FilterResults`.
# FilterResults
@@ -105,11 +105,6 @@ Collection of fuzzy filtered items (filtered by the `InputFilter`'s value), each
`classPrefix` is a string that is used to prefix the class names in the component. It defaults to `react-fuzzy-filter`. (`react-fuzzy-filter__results-container`)
-
-### initialSearch
-
-`initialSearch` is a string that can override the initial search state when the component is created. It defaults to `''`.
-
### wrapper
`wrapper` is an optional component that will wrap the results if defined. This will be used as the wrapper around the items INSTEAD of `react-fuzzy-filter__results-container`.
diff --git a/dist/react-fuzzy-filter.min.js b/dist/react-fuzzy-filter.min.js
index a011454..7d8e26f 100644
--- a/dist/react-fuzzy-filter.min.js
+++ b/dist/react-fuzzy-filter.min.js
@@ -1 +1 @@
-!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e(require("react")):"function"==typeof define&&define.amd?define(["react"],e):"object"==typeof exports?exports.ReactFuzzyFilter=e(require("react")):t.ReactFuzzyFilter=e(t.React)}(this,function(t){return function(t){function e(n){if(r[n])return r[n].exports;var s=r[n]={exports:{},id:n,loaded:!1};return t[n].call(s.exports,s,s.exports,e),s.loaded=!0,s.exports}var r={};return e.m=t,e.c=r,e.p="",e(0)}([function(t,e,r){"use strict";function n(t){return t&&t.__esModule?t:{"default":t}}function s(){var t=new i.Subject;return{InputFilter:(0,c["default"])(t),FilterResults:(0,a["default"])(t)}}Object.defineProperty(e,"__esModule",{value:!0}),e["default"]=s;var i=r(13),o=r(9),c=n(o),u=r(8),a=n(u)},function(t,e,r){"use strict";var n=r(20),s=r(21),i=r(5),o=r(23),c=r(4),u=r(19),a=function(){function t(t){this.isUnsubscribed=!1,t&&(this._unsubscribe=t)}return t.prototype.unsubscribe=function(){var t,e=!1;if(!this.isUnsubscribed){this.isUnsubscribed=!0;var r=this,a=r._unsubscribe,p=r._subscriptions;if(this._subscriptions=null,i.isFunction(a)){var h=o.tryCatch(a).call(this);h===c.errorObject&&(e=!0,(t=t||[]).push(c.errorObject.e))}if(n.isArray(p))for(var f=-1,l=p.length;++f1)throw new Error("Key weight has to be > 0 and <= 1");t=t.name}else this._keyMap[t]={weight:1};this._analyze(t,i(p,t,[]),p,r)}},n.prototype._analyze=function(t,e,n,s){var o,c,u,a,p,h,f,l,b,y,d,v,m,_=this.options,w=!1;if(void 0!==e&&null!==e)if(c=[],"string"==typeof e){if(o=e.split(_.tokenSeparator),_.verbose&&r("---------\nKey:",t),_.verbose&&r("Record:",o),this.options.tokenize){for(v=0;v0){if(e={item:o.item},p.indexOf("matches")!==-1)for(n=o.output,e.matches=[],r=0;rP.maxPatternLength){if(v=t.match(new RegExp(this.pattern.replace(P.tokenSeparator,"|"))),m=!!v)for(w=[],e=0,g=v.length;e=p;r--)if(b=this.patternAlphabet[t.charAt(r-1)],b&&(_[r-1]=1),0===e?f[r]=(f[r+1]<<1|1)&b:f[r]=(f[r+1]<<1|1)&b|((l[r+1]|l[r])<<1|1)|l[r+1],f[r]&this.matchmask&&(y=this._bitapScore(e,r-1),y<=i)){if(i=y,o=r-1,d.push(o),!(o>s))break;p=Math.max(1,2*s-o)}if(this._bitapScore(e+1,s)>i)break;l=f}return w=this._getMatchedIndices(_),{isMatch:o>=0,score:0===y?.001:y,matchedIndices:w}},o.prototype._getMatchedIndices=function(t){for(var e,r=[],n=-1,s=-1,i=0,o=t.length;i1)throw new Error("Key weight has to be > 0 and <= 1");t=t.name}else this._keyMap[t]={weight:1};this._analyze(t,o(p,t,[]),p,r)}},n.prototype._analyze=function(t,e,n,s){var i,c,u,a,p,h,f,l,b,y,d,v,_,m=this.options,w=!1;if(void 0!==e&&null!==e)if(c=[],"string"==typeof e){if(i=e.split(m.tokenSeparator),m.verbose&&r("---------\nKey:",t),m.verbose&&r("Record:",i),this.options.tokenize){for(v=0;v0){if(e={item:i.item},p.indexOf("matches")!==-1)for(n=i.output,e.matches=[],r=0;rP.maxPatternLength){if(v=t.match(new RegExp(this.pattern.replace(P.tokenSeparator,"|"))),_=!!v)for(w=[],e=0,g=v.length;e=p;r--)if(b=this.patternAlphabet[t.charAt(r-1)],b&&(m[r-1]=1),0===e?f[r]=(f[r+1]<<1|1)&b:f[r]=(f[r+1]<<1|1)&b|((l[r+1]|l[r])<<1|1)|l[r+1],f[r]&this.matchmask&&(y=this._bitapScore(e,r-1),y<=o)){if(o=y,i=r-1,d.push(i),!(i>s))break;p=Math.max(1,2*s-i)}if(this._bitapScore(e+1,s)>o)break;l=f}return w=this._getMatchedIndices(m),{isMatch:i>=0,score:0===y?.001:y,matchedIndices:w}},i.prototype._getMatchedIndices=function(t){for(var e,r=[],n=-1,s=-1,o=0,i=t.length;o this.setState({search}));
}
- componentWillReceiveProps({items, fuseConfig}) {
- this.setState({fuse: new Fuse(items, fuseConfig)});
- }
-
componentWillUnmount() {
this.subscription.unsubscribe();
}
- renderItems() {
- let items;
+ filterItems() {
if (!this.state.search || this.state.search.trim() === '') {
- items = this.props.defaultAllItems ? this.props.items : [];
+ return this.props.defaultAllItems ? this.props.items : [];
} else {
- items = this.state.fuse.search(this.state.search);
+ const fuse = new Fuse(this.props.items, this.props.fuseConfig);
+ return fuse.search(this.state.search);
}
- return items.map((item, i) => this.props.renderItem(item, i));
+ }
+
+ renderItems() {
+ return this.filterItems().map((item, i) => this.props.renderItem(item, i));
}
render() {
diff --git a/src/InputFilter.js b/src/InputFilter.js
index 22b5c69..45e2e29 100644
--- a/src/InputFilter.js
+++ b/src/InputFilter.js
@@ -17,16 +17,21 @@ export default function inputFilterFactory(store) {
onChange: function() {}
};
- state = {
- search: this.props.initialSearch
- };
+ componentDidMount() {
+ let value = this.props.initialSearch;
+ const overrideValue = this.props.onChange(value);
+ if (typeof overrideValue === 'string') {
+ value = overrideValue;
+ }
+ store.next(value);
+ }
handleChange = ({target: {value}}) => {
- const continueChange = this.props.onChange(value);
- if (continueChange !== false) {
- this.setState({search: value});
- store.next(value);
+ const overrideValue = this.props.onChange(value);
+ if (typeof overrideValue === 'string') {
+ value = overrideValue;
}
+ store.next(value);
};
render() {
diff --git a/src/index.js b/src/index.js
index fedbbac..f4ae59c 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,9 +1,9 @@
-import {Subject} from 'rxjs/Subject';
+import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import inputFilterFactory from './InputFilter';
import filterResultsFactory from './FilterResults';
export default function fuzzyFilterFactory() {
- const store = new Subject();
+ const store = new BehaviorSubject();
return {
InputFilter: inputFilterFactory(store),
FilterResults: filterResultsFactory(store)
diff --git a/test/FilterResults_test.js b/test/FilterResults_test.js
index 49de4d1..c142892 100644
--- a/test/FilterResults_test.js
+++ b/test/FilterResults_test.js
@@ -162,17 +162,5 @@ describe('FilterResults', () => {
expect(component.find('.my-item').length).toEqual(1);
expect(component.find('.my-item').at(0).text()).toEqual('three');
});
-
- it('can override the initial search value with initialSearch prop', () => {
- const component = shallow(
-
- );
- expect(component.find('.my-item').length).toEqual(2);
- });
});
});
diff --git a/test/InputFilter_test.js b/test/InputFilter_test.js
index e19f646..955678a 100644
--- a/test/InputFilter_test.js
+++ b/test/InputFilter_test.js
@@ -6,8 +6,9 @@ import inputFilterFactory from '../src/InputFilter';
describe('InputFilter', () => {
let InputFilter;
- const store = new Subject();
+ let store;
beforeEach(() => {
+ store = new Subject();
InputFilter = inputFilterFactory(store);
});
@@ -15,7 +16,6 @@ describe('InputFilter', () => {
it('renders with defaults', () => {
const component = shallow();
expect(component.find('.react-fuzzy-filter__input').length).toEqual(1);
- expect(component.state()).toEqual({search: undefined});
});
it('sets classPrefix', () => {
@@ -31,7 +31,7 @@ describe('InputFilter', () => {
it('sets initialSearch', () => {
const component = shallow();
- expect(component.state()).toEqual({search: 'first search'});
+ expect(component.find('input').html()).toEqual('');
});
});
@@ -50,18 +50,6 @@ describe('InputFilter', () => {
expect(spy).toHaveBeenCalledWith('my string');
});
- it('sets the state', () => {
- component.find('input').simulate('change', {
- target: {value: 'first'}
- });
- expect(component.state()).toEqual({search: 'first'});
-
- component.find('input').simulate('change', {
- target: {value: 'second'}
- });
- expect(component.state()).toEqual({search: 'second'});
- });
-
it('passes the value to the store', (done) => {
store.subscribe(data => {
expect(data).toEqual('some input');
@@ -73,12 +61,15 @@ describe('InputFilter', () => {
});
});
- it('does not set state or pass value to the store if onChange returns false', () => {
- component = shallow( false} />);
+ it('overrides search value with any return value to onChange', (done) => {
+ store.subscribe(data => {
+ expect(data).toEqual('hello');
+ done();
+ });
+ component = shallow( 'hello'} />);
component.find('input').simulate('change', {
target: {value: 'some input'}
});
- expect(component.state()).toEqual({search: undefined});
});
});
});
diff --git a/test/index_test.js b/test/index_test.js
index 2e46aec..dd7e862 100644
--- a/test/index_test.js
+++ b/test/index_test.js
@@ -14,6 +14,29 @@ const defaultFuseConfig = {
keys: ['searchData']
};
+function defaultRender({name}, index) {
+ return