Skip to content
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

core(instantsearch-manager): provide a fn for setting SearchParameters #1471

Merged
merged 3 commits into from
Oct 26, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions packages/react-instantsearch/src/core/InstantSearch.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ function validateNextProps(props, nextProps) {
* @propType {string} appId - The Algolia application id.
* @propType {string} apiKey - Your Algolia Search-Only API key.
* @propType {string} indexName - The index in which to search.
* @propType {object} [searchParameters] - Object containing query parameters to be sent to Algolia.
* It will be overriden by the search parameters resolved via the widgets. Typical use case:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the "Typical use case"

* setting the distinct setting is done by providing an object like: `{distinct: 1}`. For more information
* about the kind of object that can be provided on the [official API documentation](https://www.algolia.com/doc/rest-api/search#full-text-search-parameters).
* @propType {bool=true} urlSync - Enables automatic synchronization of widgets state to the URL. See [URL Synchronization](#url-synchronization).
* @propType {object} history - A custom [history](https://github.com/ReactTraining/history) to push location to. Useful for quick integration with [React Router](https://github.com/reactjs/react-router). Takes precedence over urlSync. See [Custom History](#custom-history).
* @propType {number=700} threshold - Threshold in milliseconds above which new locations will be pushed to the history, instead of replacing the previous one. See [Location Debouncing](#location-debouncing).
Expand Down Expand Up @@ -118,6 +122,7 @@ class InstantSearch extends Component {
appId: props.appId,
apiKey: props.apiKey,
indexName: props.indexName,
searchParameters: props.searchParameters,

initialState,
});
Expand Down Expand Up @@ -208,6 +213,8 @@ InstantSearch.propTypes = {
apiKey: PropTypes.string.isRequired,
indexName: PropTypes.string.isRequired,

searchParameters: PropTypes.object,

history: PropTypes.object,
urlSync: PropTypes.bool,
threshold: PropTypes.number,
Expand Down
13 changes: 13 additions & 0 deletions packages/react-instantsearch/src/core/createConnector.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,19 @@ import {omit, has} from 'lodash';

import {shallowEqual, getDisplayName} from './utils';

/**
* @typedef {object} ConnectorDescription
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am unsure on the context of this addition, can you detail it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I started exploring the possibility of writing the widget, and didn't have a proper documentation for the object that could be passed to createConnector. This is its documentation, and I thought maybe we could keep it since it was already done.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unsure, if it currently does not have any output in our documentation website I would say we do not need it for now.

* @property {string} displayName - the displayName used by the wrapper
* @property {function} refine - a function to filter the local state
* @property {function} getSearchParameters - function transforming the local state to a SearchParameters
* @property {function} getMetadata - metadata of the widget
* @property {function} transitionState - hook after the state has changed
* @property {function} getProps - transform the state into props passed to the wrapped component.
* Receives (props, widgetStates, searchState, metadata) and returns the local state.
* @property {object} propTypes - PropTypes forwarded to the wrapped component.
* @property {object} defaultProps - default values for the props
*/

/**
* Connectors are the HOC used to transform React components
* into InstantSearch widgets.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,22 @@ import algoliasearchHelper, {SearchParameters} from 'algoliasearch-helper';
import createWidgetsManager from './createWidgetsManager';
import createStore from './createStore';

/**
* Creates a new instance of the InstantSearchManager which controls the widgets and
* trigger the search when the widgets are updated.
* @param {string} appId - the application ID
* @param {string} apiKey - the api key
* @param {string} indexName - the main index name
* @param {object} initialState - initial widget state
* @param {object} SearchParameters - optional additional parameters to send to the algolia API
* @return {InstantSearchManager} a new instance of InstantSearchManager
*/
export default function createInstantSearchManager({
appId,
apiKey,
indexName,
initialState,
searchParameters = {},
}) {
const client = algoliasearch(appId, apiKey);
const helper = algoliasearchHelper(client);
Expand All @@ -29,23 +40,24 @@ export default function createInstantSearchManager({
.map(widget => widget.getMetadata(state));
}

function getSearchParameters(searchParameters) {
function getSearchParameters(initialSearchParameters) {
return widgetsManager.getWidgets()
.filter(widget => Boolean(widget.getSearchParameters))
.reduce(
(res, widget) => widget.getSearchParameters(res),
searchParameters
initialSearchParameters
);
}

function search() {
const baseSP = new SearchParameters({index: indexName});
// @TODO: Provide a way to configure base SearchParameters.
// We previously had a `configureSearchParameters : SP -> SP` option.
// We could also just have a `baseSearchParameters : SP` option.
const searchParameters = getSearchParameters(baseSP);
const baseSP = new SearchParameters({
...searchParameters,
index: indexName,
});
const widgetSearchParameters = getSearchParameters(baseSP);

helper.searchOnce(searchParameters)
helper
.searchOnce(widgetSearchParameters)
.then(({content}) => {
store.setState({
...store.getState(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ describe('createInstantSearchManager', () => {
let initialState;
let ism;

function init() {
function init(otherISMParameters = {}) {
createHrefForState = jest.fn(a => a);
onInternalStateUpdate = jest.fn();
initialState = {
Expand All @@ -46,6 +46,7 @@ describe('createInstantSearchManager', () => {
initialState,
createHrefForState,
onInternalStateUpdate,
...otherISMParameters,
});
}

Expand Down Expand Up @@ -133,7 +134,7 @@ describe('createInstantSearchManager', () => {
});
});

function testSearch(promise) {
function testSearch(promise, searchParameters = undefined) {
const helper = {
searchOnce: jest.fn(() => promise),
};
Expand All @@ -153,7 +154,7 @@ describe('createInstantSearchManager', () => {
},
],
}));
init();
init({searchParameters});
return helper.searchOnce;
}

Expand Down Expand Up @@ -182,6 +183,16 @@ describe('createInstantSearchManager', () => {
expect(params.query).toBe('hello');
expect(params.page).toBe(20);
});

it('when searching it adds the searchParameters if any', () => {
const searchOnce = testSearch(new Promise(() => null), {distinct: 1});
const onUpdate = createWidgetsManager.mock.calls[0][0];
onUpdate();
const params = searchOnce.mock.calls[0][0];
expect(params.query).toBe('hello');
expect(params.page).toBe(20);
expect(params.distinct).toBe(1);
});
});

describe('onExternalStateUpdate', () => {
Expand Down