Skip to content

Commit

Permalink
feat(searchFunction): Update API, fix #1924
Browse files Browse the repository at this point in the history
  • Loading branch information
bobylito committed May 16, 2017
1 parent 85f5982 commit c7beb1d
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 36 deletions.
45 changes: 45 additions & 0 deletions docgen/src/guides/migration.md
Expand Up @@ -87,3 +87,48 @@ dropping in this version the support for the react based templates.
As of now, we consider the engine used to build the widgets in InstantSearch.js
as an implementation detail. Since we do not expose it anymore, we'll be able
to change it and use the best solution for each release.

## searchFunction can be used to modify parameters

Introduced in 1.3.0, `searchFunction` was originally meant as a way to modify
the timing of the search. However we realized that it was a good way to
alter the search state before making the actual state.

This is what was required to force the query string:

```javascript
const search = instantsearch({
/* other parameters */
searchFunction: function(helper) {
search.helper.setQuery('fixed query');
helper.search();
}
});
```

And now, it is more straightforward:

```javascript
const search = instantsearch({
/* other parameters */
searchFunction: function(helper) {
helper.setQuery('fixed query').search();
}
});
```

Bear in mind that the helper [still resets the page to 0](https://community.algolia.com/algoliasearch-helper-js/concepts.html#smart-page-behaviour)
when the parameters change. So in order to keep
the previously set page you have to do the following:

```javascript
const search = instantsearch({
/* other parameters */
searchFunction: function(helper) {
const p = helper.getPage();
helper.setQuery('fixed query')
.setPage(p)
.search();
}
});
```
28 changes: 12 additions & 16 deletions src/lib/InstantSearch.js
Expand Up @@ -5,7 +5,6 @@ import algoliasearchHelper from 'algoliasearch-helper';
import forEach from 'lodash/forEach';
import mergeWith from 'lodash/mergeWith';
import union from 'lodash/union';
import clone from 'lodash/clone';
import isPlainObject from 'lodash/isPlainObject';
import {EventEmitter} from 'events';
import urlSyncWidget from './url-sync.js';
Expand Down Expand Up @@ -122,23 +121,20 @@ Usage: instantsearch({
);

if (this._searchFunction) {
this._originalHelperSearch = helper.search.bind(helper);
helper.search = this._wrappedSearch.bind(this);
this.helper = Object.create(helper);
this.helper.search = () => {
helper.setState(this.helper.state);
this._searchFunction(helper);
};
this._init(helper.state, this.helper);
helper.on('result', this._render.bind(this, this.helper));
} else {
this.helper = helper;
this._init(helper.state, this.helper);
this.helper.on('result', this._render.bind(this, this.helper));
}

this.helper = helper;

this._init(helper.state, helper);

helper.on('result', this._render.bind(this, helper));
helper.search();
}

_wrappedSearch() {
const helper = clone(this.helper);
helper.search = this._originalHelperSearch;
this._searchFunction(helper);
return;
this.helper.search();
}

createURL(params) {
Expand Down
35 changes: 15 additions & 20 deletions src/lib/__tests__/InstantSearch-test.js
@@ -1,14 +1,12 @@
import EventEmitter from 'events';

import range from 'lodash/range';
import sinon from 'sinon';

import SearchParameters from 'algoliasearch-helper/src/SearchParameters';
import algoliaSearchHelper from 'algoliasearch-helper';
import InstantSearch from '../InstantSearch';

describe('InstantSearch lifecycle', () => {
let algoliasearch;
let algoliasearchHelper;
let helperStub;
let client;
let helper;
let appId;
Expand All @@ -21,17 +19,11 @@ describe('InstantSearch lifecycle', () => {

beforeEach(() => {
client = {algolia: 'client', addAlgoliaAgent: () => {}};
helper = new EventEmitter();
helper = algoliaSearchHelper(client);

// when using searchFunction, we lose the reference to
// the original helper.search
helperSearchSpy = sinon.spy();
helper.search = helperSearchSpy;
helper.getState = sinon.stub().returns({});
helper.setState = sinon.spy();
helper.state = {
setQueryParameters(params) { return new SearchParameters(params); },
};
helper.search = helperSearchSpy = sinon.spy();

urlSync = {
createURL: sinon.spy(),
Expand All @@ -41,7 +33,7 @@ describe('InstantSearch lifecycle', () => {
};

algoliasearch = sinon.stub().returns(client);
algoliasearchHelper = sinon.stub().returns(helper);
helperStub = sinon.stub().returns(helper);

appId = 'appId';
apiKey = 'apiKey';
Expand All @@ -56,7 +48,7 @@ describe('InstantSearch lifecycle', () => {

InstantSearch.__Rewire__('urlSyncWidget', () => urlSync);
InstantSearch.__Rewire__('algoliasearch', algoliasearch);
InstantSearch.__Rewire__('algoliasearchHelper', algoliasearchHelper);
InstantSearch.__Rewire__('algoliasearchHelper', helperStub);

search = new InstantSearch({
appId,
Expand All @@ -80,7 +72,7 @@ describe('InstantSearch lifecycle', () => {
});

it('does not call algoliasearchHelper', () => {
expect(algoliasearchHelper.notCalled).toBe(true, 'algoliasearchHelper not yet called');
expect(helperStub.notCalled).toBe(true, 'algoliasearchHelper not yet called');
});

describe('when providing a custom client module', () => {
Expand Down Expand Up @@ -134,7 +126,9 @@ describe('InstantSearch lifecycle', () => {
});

it('calls the provided searchFunction when used', () => {
const searchSpy = sinon.spy();
const searchSpy = sinon.spy(h => {
h.setQuery('test').search();
});
search = new InstantSearch({
appId,
apiKey,
Expand All @@ -143,7 +137,8 @@ describe('InstantSearch lifecycle', () => {
});
search.start();
expect(searchSpy.calledOnce).toBe(true);
expect(helperSearchSpy.calledOnce).toBe(false);
expect(helper.state.query).toBe('test');
expect(helperSearchSpy.calledOnce).toBe(true);
});

it('does not fail when passing same references inside multiple searchParameters props', () => {
Expand Down Expand Up @@ -194,8 +189,8 @@ describe('InstantSearch lifecycle', () => {
});

it('calls algoliasearchHelper(client, indexName, searchParameters)', () => {
expect(algoliasearchHelper.calledOnce).toBe(true, 'algoliasearchHelper called once');
expect(algoliasearchHelper.args[0])
expect(helperStub.calledOnce).toBe(true, 'algoliasearchHelper called once');
expect(helperStub.args[0])
.toEqual([
client,
indexName,
Expand Down Expand Up @@ -288,7 +283,7 @@ describe('InstantSearch lifecycle', () => {
});

it('recursively merges searchParameters.values array', () => {
expect(algoliasearchHelper.args[0][2].values).toEqual([-2, -1, 0, 1, 2, 3, 4]);
expect(helperStub.args[0][2].values).toEqual([-2, -1, 0, 1, 2, 3, 4]);
});
});

Expand Down

0 comments on commit c7beb1d

Please sign in to comment.