Skip to content

Commit

Permalink
feat: update UiState definition (#4075)
Browse files Browse the repository at this point in the history
This PR updates the type definitions to match the new format of the `UiState`. We introduce a new type called `IndexUiState`. The `UiState` is the global structure that represents the InstantSearch instance with each indices. The `IndexUiState` is the previous version of the `UiState`, it represents the state of an "index". Here is their definitions:

```ts
export type IndexUiState = {
  query?: string;
  hitsPerPage?: number;
  // ...
};

export type UiState = {
  [indexId: string]: IndexUiState;
};
```

The introduction of this type mainly impact the `StateMapping` & `RoutingManager`. Both have been updated. The changes applied to `RoutingManager` are there to make the tests pass don't pay too much attention to them since we'll rework this part soon.

**Note**: Name are just a proposition, we could use something else like `WidgetsState`, ... ideas are welcome! We could rename `UiState` for consistency if we find a better name for both.
  • Loading branch information
samouss authored and Haroenv committed Oct 23, 2019
1 parent de00707 commit 9e7d3d8
Show file tree
Hide file tree
Showing 8 changed files with 195 additions and 105 deletions.
28 changes: 17 additions & 11 deletions src/lib/RoutingManager.ts
Expand Up @@ -3,6 +3,7 @@ import { isEqual } from './utils';
import {
InstantSearch,
UiState,
IndexUiState,
Router,
StateMapping,
Widget,
Expand All @@ -21,6 +22,7 @@ class RoutingManager implements Widget {
private readonly stateMapping: StateMapping;

private isFirstRender: boolean = true;
private indexId: string;
private currentUiState: UiState;
private initState?: UiState;
private renderURLFromState?: (event: HelperChangeEvent) => void;
Expand All @@ -33,6 +35,7 @@ class RoutingManager implements Widget {
this.router = router;
this.stateMapping = stateMapping;
this.instantSearchInstance = instantSearchInstance;
this.indexId = this.instantSearchInstance.indexName;
this.currentUiState = this.stateMapping.routeToState(this.router.read());
}

Expand All @@ -44,14 +47,15 @@ class RoutingManager implements Widget {
uiState: UiState;
}): SearchParameters {
const widgets = this.instantSearchInstance.mainIndex.getWidgets();
const indexUiState = uiState[this.indexId] || {};

return widgets.reduce((parameters, widget) => {
if (!widget.getWidgetSearchParameters) {
return parameters;
}

return widget.getWidgetSearchParameters(parameters, {
uiState,
uiState: indexUiState,
});
}, currentSearchParameters);
}
Expand All @@ -64,16 +68,18 @@ class RoutingManager implements Widget {
const widgets = this.instantSearchInstance.mainIndex.getWidgets();
const helper = this.instantSearchInstance.mainIndex.getHelper()!;

return widgets.reduce<UiState>((state, widget) => {
if (!widget.getWidgetState) {
return state;
}

return widget.getWidgetState(state, {
helper,
searchParameters,
});
}, {});
return {
[this.indexId]: widgets.reduce<IndexUiState>((state, widget) => {
if (!widget.getWidgetState) {
return state;
}

return widget.getWidgetState(state, {
helper,
searchParameters,
});
}, {}),
};
}

private setupRouting(state: SearchParameters): void {
Expand Down
10 changes: 7 additions & 3 deletions src/lib/__tests__/InstantSearch-test.js
Expand Up @@ -1029,7 +1029,9 @@ describe('createURL', () => {

expect(search.createURL()).toBe('http://algolia.com');
expect(router.createURL).toHaveBeenCalledWith({
query: 'Apple',
indexName: {
query: 'Apple',
},
});
});

Expand Down Expand Up @@ -1060,8 +1062,10 @@ describe('createURL', () => {

expect(search.createURL({ page: 5 })).toBe('http://algolia.com');
expect(router.createURL).toHaveBeenCalledWith({
query: 'Apple',
page: 5,
indexName: {
query: 'Apple',
page: 5,
},
});
});
});
Expand Down
100 changes: 69 additions & 31 deletions src/lib/__tests__/RoutingManager-test.ts
Expand Up @@ -4,7 +4,7 @@ import qs from 'qs';
import { createSearchClient } from '../../../test/mock/createSearchClient';
import { createWidget } from '../../../test/mock/createWidget';
import { runAllMicroTasks } from '../../../test/utils/runAllMicroTasks';
import { Router, Widget, StateMapping, RouteState } from '../../types';
import { Router, Widget, UiState, StateMapping, RouteState } from '../../types';
import historyRouter from '../routers/history';
import RoutingManager from '../RoutingManager';
import instantsearch from '../main';
Expand Down Expand Up @@ -119,7 +119,7 @@ describe('RoutingManager', () => {
test('reads the state of widgets with a getWidgetState implementation', () => {
const searchClient = createSearchClient();
const search = instantsearch({
indexName: '',
indexName: 'indexName',
searchClient,
});

Expand All @@ -135,7 +135,9 @@ describe('RoutingManager', () => {
search.addWidget(widget);

const actualInitialState = {
query: 'query',
indexName: {
query: 'query',
},
};

search.start();
Expand All @@ -156,7 +158,9 @@ describe('RoutingManager', () => {
searchParameters: search.mainIndex.getHelper()!.state,
});

expect(uiStates).toEqual(widgetState);
expect(uiStates).toEqual({
indexName: widgetState,
});

expect(widget.getWidgetState).toHaveBeenCalledTimes(1);
expect(widget.getWidgetState).toHaveBeenCalledWith(
Expand All @@ -171,7 +175,7 @@ describe('RoutingManager', () => {
test('Does not read UI state from widgets without an implementation of getWidgetState', () => {
const searchClient = createSearchClient();
const search = instantsearch({
indexName: '',
indexName: 'indexName',
searchClient,
});

Expand All @@ -182,7 +186,9 @@ describe('RoutingManager', () => {
search.start();

const actualInitialState = {
query: 'query',
indexName: {
query: 'query',
},
};

const router = new RoutingManager({
Expand All @@ -201,15 +207,17 @@ describe('RoutingManager', () => {
searchParameters: search.mainIndex.getHelper()!.state,
});

expect(uiStates).toEqual({});
expect(uiStates).toEqual({
indexName: {},
});
});
});

describe('getAllSearchParameters', () => {
test('should get searchParameters from widget that implements getWidgetSearchParameters', () => {
const searchClient = createSearchClient();
const search = instantsearch({
indexName: '',
indexName: 'indexName',
searchClient,
});

Expand All @@ -221,7 +229,9 @@ describe('RoutingManager', () => {
search.addWidget(widget);

const actualInitialState = {
query: 'query',
indexName: {
query: 'query',
},
};

search.start();
Expand Down Expand Up @@ -299,7 +309,7 @@ describe('RoutingManager', () => {
});

const search = instantsearch({
indexName: 'instant_search',
indexName: 'indexName',
searchClient,
routing: {
router,
Expand Down Expand Up @@ -330,7 +340,9 @@ describe('RoutingManager', () => {

expect(router.write).toHaveBeenCalledTimes(1);
expect(router.write).toHaveBeenCalledWith({
q: 'q',
indexName: {
q: 'q',
},
});

done();
Expand All @@ -340,15 +352,15 @@ describe('RoutingManager', () => {
test('should update the searchParameters on router state update', done => {
const searchClient = createSearchClient();

let onRouterUpdateCallback: (args: object) => void;
let onRouterUpdateCallback: (args: UiState) => void;
const router = createFakeRouter({
onUpdate: fn => {
onRouterUpdateCallback = fn;
},
});

const search = instantsearch({
indexName: 'instant_search',
indexName: 'indexName',
searchClient,
routing: {
router,
Expand All @@ -358,9 +370,10 @@ describe('RoutingManager', () => {
const widget = {
render: jest.fn(),
getWidgetSearchParameters: jest.fn((searchParameters, { uiState }) =>
searchParameters.setQuery(uiState.q)
searchParameters.setQuery(uiState.query)
),
};

search.addWidget(widget);

search.start();
Expand All @@ -370,9 +383,11 @@ describe('RoutingManager', () => {

expect(search.mainIndex.getHelper()!.state.query).toBeUndefined();

// this simulates a router update with a uiState of {q: 'a'}
// this simulates a router update with a uiState of {query: 'a'}
onRouterUpdateCallback({
q: 'a',
indexName: {
query: 'a',
},
});

search.once('render', () => {
Expand All @@ -393,14 +408,21 @@ describe('RoutingManager', () => {

const stateMapping = createFakeStateMapping({
stateToRoute(uiState) {
return {
query: uiState.query && uiState.query.toUpperCase(),
};
return Object.keys(uiState).reduce((state, indexId) => {
const indexState = uiState[indexId];

return {
...state,
[indexId]: {
query: indexState.query && indexState.query.toUpperCase(),
},
};
}, {});
},
});

const search = instantsearch({
indexName: 'instant_search',
indexName: 'indexName',
searchFunction: helper => {
helper.setQuery('test').search();
},
Expand Down Expand Up @@ -431,7 +453,9 @@ describe('RoutingManager', () => {
expect(search.mainIndex.getHelper()!.state.query).toEqual('test');

expect(router.write).toHaveBeenLastCalledWith({
query: 'TEST',
indexName: {
query: 'TEST',
},
});

done();
Expand All @@ -446,7 +470,7 @@ describe('RoutingManager', () => {
});

const search = instantsearch({
indexName: 'instant_search',
indexName: 'indexName',
searchClient,
routing: {
stateMapping,
Expand All @@ -469,7 +493,9 @@ describe('RoutingManager', () => {

expect(router.write).toHaveBeenCalledTimes(1);
expect(router.write).toHaveBeenLastCalledWith({
query: 'Apple',
indexName: {
query: 'Apple',
},
});

await runAllMicroTasks();
Expand All @@ -481,7 +507,9 @@ describe('RoutingManager', () => {

expect(router.write).toHaveBeenCalledTimes(2);
expect(router.write).toHaveBeenLastCalledWith({
query: 'Apple',
indexName: {
query: 'Apple',
},
});
});

Expand All @@ -493,7 +521,7 @@ describe('RoutingManager', () => {
});

const search = instantsearch({
indexName: 'instant_search',
indexName: 'indexName',
searchFunction(helper) {
// Force the value of the query
helper.setQuery('Apple iPhone').search();
Expand All @@ -518,7 +546,9 @@ describe('RoutingManager', () => {

expect(router.write).toHaveBeenCalledTimes(1);
expect(router.write).toHaveBeenLastCalledWith({
query: 'Apple iPhone',
indexName: {
query: 'Apple iPhone',
},
});

// Trigger getConfiguration
Expand All @@ -528,7 +558,9 @@ describe('RoutingManager', () => {

expect(router.write).toHaveBeenCalledTimes(2);
expect(router.write).toHaveBeenLastCalledWith({
query: 'Apple iPhone',
indexName: {
query: 'Apple iPhone',
},
});
});

Expand All @@ -548,7 +580,7 @@ describe('RoutingManager', () => {
});

const search = instantsearch({
indexName: 'instant_search',
indexName: 'indexName',
searchClient,
routing: {
router,
Expand All @@ -571,15 +603,19 @@ describe('RoutingManager', () => {

expect(router.write).toHaveBeenCalledTimes(1);
expect(router.write).toHaveBeenLastCalledWith({
query: 'Apple',
indexName: {
query: 'Apple',
},
});

// Trigger an update - push a change
fakeSearchBox.refine('Apple iPhone');

expect(router.write).toHaveBeenCalledTimes(2);
expect(router.write).toHaveBeenLastCalledWith({
query: 'Apple iPhone',
indexName: {
query: 'Apple iPhone',
},
});

await runAllMicroTasks();
Expand All @@ -596,7 +632,9 @@ describe('RoutingManager', () => {

expect(router.write).toHaveBeenCalledTimes(3);
expect(router.write).toHaveBeenLastCalledWith({
query: 'Apple',
indexName: {
query: 'Apple',
},
});
});
});
Expand Down

0 comments on commit 9e7d3d8

Please sign in to comment.