Skip to content

Commit

Permalink
feat(instantsearch): allow overriding the helper.search function
Browse files Browse the repository at this point in the history
add a new option to the instantsearch: searchFunction. It allows user
to override the default call to helper.search() by anything they want.

With this, they will be able to implement:
- do not search at first render
- do not search when query < 3 chars
- any conditional we cannot think of
  • Loading branch information
vvo committed Feb 25, 2016
1 parent 026180c commit 9a930e7
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 43 deletions.
9 changes: 5 additions & 4 deletions dev/app.js
Expand Up @@ -302,10 +302,6 @@ search.addWidget(
})
);

search.once('render', function() {
document.querySelector('.search').className = 'row search search--visible';
});

search.addWidget(
instantsearch.widgets.priceRanges({
container: '#price-ranges',
Expand Down Expand Up @@ -338,5 +334,10 @@ search.addWidget(
})
);

search.once('render', function() {
[...document.querySelectorAll('.smooth-search--hidden')]
.forEach(element => element.classList.remove('smooth-search--hidden'));
});

search.start();

58 changes: 30 additions & 28 deletions dev/index.html
Expand Up @@ -19,8 +19,8 @@
<h1><a href="./">Instant search demo</a> <small>using instantsearch.js</small></h1>
</div>

<div class="row search search--hidden">
<div class="col-md-3">
<div class="row">
<div class="col-md-3 smooth-search smooth-search--hidden">
<div class="facet" id="current-refined-values"></div>
<div class="facet" id="hierarchical-categories"></div>
<div class="facet" id="brands"></div>
Expand All @@ -38,40 +38,42 @@ <h1><a href="./">Instant search demo</a> <small>using instantsearch.js</small></
<div class="form-group">
<input id="search-box" class="form-control" />
</div>
<div class="row">
<div class="col-md-3">
<div id="stats"></div>
</div>
<div class="col-md-3 text-right">
<div class="form-inline">
<div class="form-group">
<label for="hits-per-page-select">Show:</label>
<span id="hits-per-page-selector"></span>
<div class="smooth-search smooth-search--hidden">
<div class="row">
<div class="col-md-3">
<div id="stats"></div>
</div>
<div class="col-md-3 text-right">
<div class="form-inline">
<div class="form-group">
<label for="hits-per-page-select">Show:</label>
<span id="hits-per-page-selector"></span>
</div>
</div>
</div>
</div>
<div class="col-md-3 text-right">
<div class="form-inline">
<div class="form-group">
<label for="sort-by-selector-select">Popularity:</label>
<span id="popularity-selector"></span>
<div class="col-md-3 text-right">
<div class="form-inline">
<div class="form-group">
<label for="sort-by-selector-select">Popularity:</label>
<span id="popularity-selector"></span>
</div>
</div>
</div>
</div>
<div class="col-md-3 text-right">
<div class="form-inline">
<div class="form-group">
<label for="sort-by-selector-select">Sort by:</label>
<span id="sort-by-selector"></span>
<div class="col-md-3 text-right">
<div class="form-inline">
<div class="form-group">
<label for="sort-by-selector-select">Sort by:</label>
<span id="sort-by-selector"></span>
</div>
</div>
</div>
</div>
<h3>Results overview</h3>
<div id="hits-table"></div>
<h3>Results</h3>
<div id="hits"></div>
<div id="pagination" class="text-center"></div>
</div>
<h3>Results overview</h3>
<div id="hits-table"></div>
<h3>Results</h3>
<div id="hits"></div>
<div id="pagination" class="text-center"></div>
</div>
</div>
</div>
Expand Down
8 changes: 2 additions & 6 deletions dev/style.css
Expand Up @@ -8,18 +8,14 @@ body {
padding: 2em 0;
}

.search {
.smooth-search {
transition: opacity 200ms;
}

.search--hidden {
.smooth-search--hidden {
opacity: 0;
}

.search--visible {

}

/* Hits widget */
.hit + .hit {
margin-top: 1em;
Expand Down
28 changes: 25 additions & 3 deletions src/lib/InstantSearch.js
Expand Up @@ -4,11 +4,13 @@ import algoliasearchHelper from 'algoliasearch-helper';
import forEach from 'lodash/collection/forEach';
import merge from 'lodash/object/merge';
import union from 'lodash/array/union';
import clone from 'lodash/lang/clone';

import {EventEmitter} from 'events';

import urlSyncWidget from './url-sync.js';
import version from './version.js';
import helpers from './helpers.js';

function defaultCreateURL() { return '#'; }

Expand All @@ -33,6 +35,9 @@ function defaultCreateURL() { return '#'; }
* history API.
* @param {number} [options.urlSync.threshold] Time in ms after which a new
* state is created in the browser history. The default value is 700.
* @param {function} [options.searchFunction] A hook that will be called each time a search needs to be done, with the
* helper as a parameter. It's your responsibility to call helper.search(). This option allows you to avoid doing
* searches at page load for example.
* @return {Object} the instantsearch instance
*/
class InstantSearch extends EventEmitter {
Expand All @@ -42,7 +47,8 @@ class InstantSearch extends EventEmitter {
indexName = null,
numberLocale,
searchParameters = {},
urlSync = null
urlSync = null,
searchFunction
}) {
super();
if (appId === null || apiKey === null || indexName === null) {
Expand All @@ -64,9 +70,14 @@ Usage: instantsearch({
this.searchParameters = {...searchParameters, index: indexName};
this.widgets = [];
this.templatesConfig = {
helpers: require('./helpers.js')({numberLocale}),
helpers: helpers({numberLocale}),
compileOptions: {}
};

if (searchFunction) {
this._searchFunction = searchFunction;
}

this.urlSync = urlSync === true ? {} : urlSync;
}

Expand Down Expand Up @@ -109,14 +120,25 @@ Usage: instantsearch({
this.searchParameters
);

if (this._searchFunction) {
this._originalHelperSearch = helper.search.bind(helper);
helper.search = this._wrappedSearch.bind(this);
}

this.helper = helper;

this._init(helper.state, helper);
helper.on('result', this._render.bind(this, helper));

helper.search();
}

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

createURL(params) {
if (!this._createURL) {
throw new Error('You need to call start() before calling createURL()');
Expand Down
21 changes: 19 additions & 2 deletions src/lib/__tests__/InstantSearch-test.js
Expand Up @@ -22,12 +22,16 @@ describe('InstantSearch lifecycle', () => {
let indexName;
let searchParameters;
let search;
let helperSearchSpy;

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

helper.search = sinon.spy();
// when using searchFunction, we lose the reference to
// the original helper.search
helperSearchSpy = sinon.spy();
helper.search = helperSearchSpy;
helper.getState = sinon.stub().returns({});
helper.state = {
setQueryParameters: function(params) { return new SearchParameters(params); }
Expand Down Expand Up @@ -88,6 +92,19 @@ describe('InstantSearch lifecycle', () => {
});
});

it('calls the provided searchFunction when used', () => {
let searchSpy = sinon.spy();
search = new InstantSearch({
appId: appId,
apiKey: apiKey,
indexName: indexName,
searchFunction: searchSpy
});
search.start();
expect(searchSpy.calledOnce).toBe(true);
expect(helperSearchSpy.calledOnce).toBe(false);
});

context('when adding a widget', () => {
let widget;

Expand Down Expand Up @@ -129,7 +146,7 @@ describe('InstantSearch lifecycle', () => {
});

it('calls helper.search()', () => {
expect(helper.search.calledOnce).toBe(true);
expect(helperSearchSpy.calledOnce).toBe(true);
});

it('calls widget.init(helper.state, helper, templatesConfig)', () => {
Expand Down

0 comments on commit 9a930e7

Please sign in to comment.