-
Notifications
You must be signed in to change notification settings - Fork 516
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
feat(search-client): Add support for Custom Search Clients #2894
Changes from 12 commits
b085d77
a700991
76c242e
113a56c
af50f97
3c4da58
fd78e86
1b50bb4
dae8d23
9fc0eb9
28c8f0e
0bc3cf7
d6ce1c7
462b0c1
de182ab
2de38bb
35cba06
627d7f2
517c109
2b4d47b
837c47e
ae351c8
dca3fab
fa0ca7f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
// Jest Snapshot v1, https://goo.gl/fbAQLP | ||
|
||
exports[`InstantSearch Search Client Lifecycle calls the provided searchFunction when used 1`] = ` | ||
Array [ | ||
Object { | ||
"indexName": "", | ||
"params": Object { | ||
"facets": Array [], | ||
"page": 0, | ||
"query": "test", | ||
"tagFilters": "", | ||
}, | ||
}, | ||
] | ||
`; | ||
|
||
exports[`InstantSearch Search Client Lifecycle gets called on search 1`] = ` | ||
Array [ | ||
Object { | ||
"indexName": "", | ||
"params": Object { | ||
"facets": Array [], | ||
"page": 0, | ||
"query": "", | ||
"tagFilters": "", | ||
}, | ||
}, | ||
] | ||
`; | ||
|
||
exports[`InstantSearch Search Client Properties throws if no \`search()\` method 1`] = `"InstantSearch configuration error: \`searchClient\` must implement a \`search(requests)\` method."`; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
/* eslint no-new: off */ | ||
import InstantSearch from '../InstantSearch'; | ||
|
||
const usage = ` | ||
Usage: instantsearch({ | ||
indexName: 'my_index_name', | ||
searchClient: { | ||
search(requests) {}, | ||
searchForFacetValues(requests) {} | ||
} | ||
});`; | ||
|
||
// THROWAWAY: Test suite to remove once the next major version is released | ||
describe('InstantSearch API collision', () => { | ||
describe('with search client', () => { | ||
const appId = 'appId'; | ||
const apiKey = 'apiKey'; | ||
const indexName = 'indexName'; | ||
const searchClient = { search() {} }; | ||
|
||
it('and indexName', () => { | ||
expect(() => { | ||
new InstantSearch({ | ||
indexName, | ||
searchClient, | ||
}); | ||
}).not.toThrow(); | ||
}); | ||
|
||
it('and nothing else', () => { | ||
expect(() => { | ||
new InstantSearch({ | ||
searchClient, | ||
}); | ||
}).toThrow(usage); | ||
}); | ||
|
||
it('and appId', () => { | ||
expect(() => { | ||
new InstantSearch({ | ||
appId, | ||
searchClient, | ||
}); | ||
}).toThrow(usage); | ||
}); | ||
|
||
it('and apiKey', () => { | ||
expect(() => { | ||
new InstantSearch({ | ||
apiKey, | ||
searchClient, | ||
}); | ||
}).toThrow(usage); | ||
}); | ||
|
||
it('and createAlgoliaClient', () => { | ||
expect(() => { | ||
new InstantSearch({ | ||
createAlgoliaClient: () => {}, | ||
searchClient, | ||
}); | ||
}).toThrow(usage); | ||
}); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import InstantSearch from '../InstantSearch'; | ||
|
||
describe('InstantSearch Search Client', () => { | ||
describe('Properties', () => { | ||
it('throws if no `search()` method', () => { | ||
expect(() => { | ||
// eslint-disable-next-line no-new | ||
new InstantSearch({ | ||
indexName: '', | ||
searchClient: {}, | ||
}); | ||
}).toThrowErrorMatchingSnapshot(); | ||
}); | ||
|
||
it('should have default `addAlgoliaAgent()` and `clearCache()` methods', () => { | ||
const search = new InstantSearch({ | ||
indexName: '', | ||
searchClient: { search() {} }, | ||
}); | ||
|
||
expect(search.client.addAlgoliaAgent).toBeDefined(); | ||
expect(search.client.clearCache).toBeDefined(); | ||
}); | ||
}); | ||
|
||
describe('Lifecycle', () => { | ||
it('gets called on search', () => { | ||
const searchClientSpy = { | ||
search: jest.fn(), | ||
}; | ||
|
||
const search = new InstantSearch({ | ||
indexName: '', | ||
searchClient: searchClientSpy, | ||
}); | ||
|
||
expect(searchClientSpy.search).not.toHaveBeenCalled(); | ||
|
||
search.start(); | ||
|
||
expect(search.helper.state.query).toBe(''); | ||
expect(searchClientSpy.search).toHaveBeenCalledTimes(1); | ||
expect(searchClientSpy.search.mock.calls[0][0]).toMatchSnapshot(); | ||
}); | ||
|
||
it('calls the provided searchFunction when used', () => { | ||
const searchFunctionSpy = jest.fn(h => { | ||
h.setQuery('test').search(); | ||
}); | ||
|
||
const searchClientSpy = { | ||
search: jest.fn(), | ||
}; | ||
|
||
const search = new InstantSearch({ | ||
indexName: '', | ||
searchFunction: searchFunctionSpy, | ||
searchClient: searchClientSpy, | ||
}); | ||
|
||
expect(searchFunctionSpy).not.toHaveBeenCalled(); | ||
expect(searchClientSpy.search).not.toHaveBeenCalled(); | ||
|
||
search.start(); | ||
|
||
expect(searchFunctionSpy).toHaveBeenCalledTimes(1); | ||
expect(search.helper.state.query).toBe('test'); | ||
expect(searchClientSpy.search).toHaveBeenCalledTimes(1); | ||
expect(searchClientSpy.search.mock.calls[0][0]).toMatchSnapshot(); | ||
}); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -79,6 +79,12 @@ import * as stateMappings from './stateMappings/index.js'; | |
* @property {function} dispose cleans up any event listeners. | ||
*/ | ||
|
||
/** | ||
* @typedef {Object} SearchClient | ||
* @property {function} search Performs the requests in the hits. | ||
* @property {function} searchForFacetValues Performs the requests in the facet values. | ||
*/ | ||
|
||
/** | ||
* @typedef {Object} InstantSearchOptions | ||
* @property {string} appId The Algolia application ID | ||
|
@@ -89,10 +95,10 @@ import * as stateMappings from './stateMappings/index.js'; | |
* @property {function} [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. | ||
* @property {function} [createAlgoliaClient] Allows you to provide your own algolia client instead of | ||
* the one instantiated internally by instantsearch.js. Useful in situations where you need | ||
* to setup complex mechanism on the client or if you need to share it easily. | ||
* Usage: | ||
* @property {function} [createAlgoliaClient] _Deprecated in favor of [`searchClient`](instantsearch.html#struct-InstantSearchOptions-searchClient)._<br> | ||
* Allows you to provide your own algolia client instead of the one instantiated internally by instantsearch.js. | ||
* Useful in situations where you need to setup complex mechanism on the client or if you need to share it easily. | ||
* <br>Usage: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you also leave a newline instead of br? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, didn't realize we could. |
||
* ```javascript | ||
* instantsearch({ | ||
* // other parameters | ||
|
@@ -110,6 +116,37 @@ import * as stateMappings from './stateMappings/index.js'; | |
* @property {number} [stalledSearchDelay=200] Time before a search is considered stalled. | ||
* @property {RoutingOptions} [routing] the router configuration used to save the UI State into the URL or | ||
* any client side persistence. | ||
* @property {SearchClient} [searchClient] The search client to plug to instantsearch.js. You should start updating with this | ||
* syntax to ease the migration to InstantSearch 3. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could link to the "Preparing for v3" guide once it's published. |
||
* <br>Usage: | ||
* ```javascript | ||
* // Using the default Algolia client (https://github.com/algolia/algoliasearch-client-javascript) | ||
* // This is the default client used by InstantSearch. Equivalent to: | ||
* // instantsearch({ | ||
* // appId: 'appId', | ||
* // apiKey: 'apiKey', | ||
* // indexName: 'indexName', | ||
* // }); | ||
* instantsearch({ | ||
* indexName: 'indexName', | ||
* searchClient: algoliasearch('appId', 'apiKey') | ||
* }); | ||
* | ||
* // Using a custom search client | ||
* instantsearch({ | ||
* indexName: 'indexName', | ||
* searchClient: { | ||
* search(requests) { | ||
* // fetch response based on requests | ||
* return response; | ||
* }, | ||
* searchForFacetValues(requests) { | ||
* // fetch response based on requests | ||
* return response; | ||
* } | ||
* } | ||
* }); | ||
* ``` | ||
*/ | ||
|
||
/** | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'll get rid of this test once #2903 is merged.