Skip to content

Commit

Permalink
feat(routing): add a "single index" compatibility mode (#4087)
Browse files Browse the repository at this point in the history
* feat(routing): enable "compatibility" mode by default

This mode has as goal to

For Vue InstantSearch, which doesn't provide a default value for routing, users import this `compat` state mapping, and need to give the static index name as a property.

This required some maybe less than ideal changes to the `Routing` type, because it assumed you could only pass the generic once, maybe someone has a better idea than what i did here?

https://algolia.atlassian.net/browse/IFW-941

* do not enable by default

* chore: add default values

* rename
  • Loading branch information
Haroenv committed Oct 23, 2019
1 parent 2e47318 commit 842eb0f
Show file tree
Hide file tree
Showing 5 changed files with 246 additions and 9 deletions.
16 changes: 8 additions & 8 deletions src/lib/InstantSearch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Client as AlgoliaSearchClient } from 'algoliasearch';
import EventEmitter from 'events';
import index, { Index } from '../widgets/index/index';
import RoutingManager from './RoutingManager';
import simpleMapping from './stateMappings/simple';
import simpleStateMapping from './stateMappings/simple';
import historyRouter from './routers/history';
import version from './version';
import createHelpers from './createHelpers';
Expand All @@ -28,11 +28,6 @@ const withUsage = createDocumentationMessageGenerator({
name: 'instantsearch',
});

const ROUTING_DEFAULT_OPTIONS = {
stateMapping: simpleMapping(),
router: historyRouter(),
};

function defaultCreateURL() {
return '#';
}
Expand Down Expand Up @@ -237,11 +232,16 @@ See ${createDocumentationLink({
this._searchFunction = searchFunction;
}

const defaultRoutingOptions = {
stateMapping: simpleStateMapping(),
router: historyRouter(),
};

if (routing === true) {
this.routing = ROUTING_DEFAULT_OPTIONS;
this.routing = defaultRoutingOptions;
} else if (isPlainObject(routing)) {
this.routing = {
...ROUTING_DEFAULT_OPTIONS,
...defaultRoutingOptions,
...routing,
};
}
Expand Down
215 changes: 215 additions & 0 deletions src/lib/stateMappings/__tests__/singleIndex-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
import singleIndexStateMapping from '../singleIndex';

describe('singleIndexStateMapping', () => {
describe('stateToRoute', () => {
it('passes normal state through', () => {
const stateMapping = singleIndexStateMapping('indexName');
const actual = stateMapping.stateToRoute({
indexName: {
query: 'zamboni',
refinementList: {
color: ['red'],
},
},
});

expect(actual).toEqual({
query: 'zamboni',
refinementList: {
color: ['red'],
},
});
});

it('removes configure', () => {
const stateMapping = singleIndexStateMapping('indexName');
const actual = stateMapping.stateToRoute({
indexName: {
query: 'zamboni',
refinementList: {
color: ['red'],
},
configure: {
advancedSyntax: false,
},
},
});

expect(actual).toEqual({
query: 'zamboni',
refinementList: {
color: ['red'],
},
});
});

it('passes non-UiState through', () => {
const stateMapping = singleIndexStateMapping('indexName');
const actual = stateMapping.stateToRoute({
indexName: {
query: 'zamboni',
refinementList: {
color: ['red'],
},
// @ts-ignore
spy: ['stealing', 'all', 'your', 'searches'],
},
});

expect(actual).toEqual({
query: 'zamboni',
refinementList: {
color: ['red'],
},
spy: ['stealing', 'all', 'your', 'searches'],
});
});

it('picks the correct index', () => {
const stateMapping = singleIndexStateMapping('indexName');
const actual = stateMapping.stateToRoute({
indexName: {
query: 'zamboni',
refinementList: {
color: ['red'],
},
},
anotherIndex: {
// @ts-ignore
totally: 'ignored',
refinementList: {
color: ['blue'],
},
},
});

expect(actual).toEqual({
query: 'zamboni',
refinementList: {
color: ['red'],
},
});
});

it('empty object if there is no matching index', () => {
const stateMapping = singleIndexStateMapping('magicIndex');
const actual = stateMapping.stateToRoute({
indexName: {
query: 'zamboni',
refinementList: {
color: ['red'],
},
},
anotherIndex: {
// @ts-ignore
totally: 'ignored',
refinementList: {
color: ['blue'],
},
},
});

expect(actual).toEqual({});
});
});

describe('routeToState', () => {
it('passes normal state through', () => {
const stateMapping = singleIndexStateMapping('indexName');
const actual = stateMapping.routeToState({
query: 'zamboni',
refinementList: {
color: ['red'],
},
});

expect(actual).toEqual({
indexName: {
query: 'zamboni',
refinementList: {
color: ['red'],
},
},
});
});

it('removes configure', () => {
const stateMapping = singleIndexStateMapping('indexName');
const actual = stateMapping.routeToState({
query: 'zamboni',
refinementList: {
color: ['red'],
},
configure: {
advancedSyntax: false,
},
});

expect(actual).toEqual({
indexName: {
query: 'zamboni',
refinementList: {
color: ['red'],
},
},
});
});

it('passes non-UiState through', () => {
const stateMapping = singleIndexStateMapping('indexName');
const actual = stateMapping.routeToState({
query: 'zamboni',
refinementList: {
color: ['red'],
},
// @ts-ignore
spy: ['stealing', 'all', 'your', 'searches'],
});

expect(actual).toEqual({
indexName: {
query: 'zamboni',
refinementList: {
color: ['red'],
},
spy: ['stealing', 'all', 'your', 'searches'],
},
});
});

it('returns wrong data if used with nested state', () => {
const stateMapping = singleIndexStateMapping('indexName');
const actual = stateMapping.routeToState({
// @ts-ignore (we are passing wrong data)
indexName: {
query: 'zamboni',
refinementList: {
color: ['red'],
},
},
anotherIndex: {},
});

expect(actual).toEqual({
indexName: {
indexName: {
query: 'zamboni',
refinementList: {
color: ['red'],
},
},
anotherIndex: {},
},
});
});

it('keeps empty index empty', () => {
const stateMapping = singleIndexStateMapping('indexName');
const actual = stateMapping.routeToState({});

expect(actual).toEqual({
indexName: {},
});
});
});
});
1 change: 1 addition & 0 deletions src/lib/stateMappings/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { default as simple } from './simple';
export { default as singleIndex } from './singleIndex';
2 changes: 1 addition & 1 deletion src/lib/stateMappings/simple.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export default function simpleStateMapping(): StateMapping<UiState> {
);
},

routeToState(routeState) {
routeToState(routeState = {}) {
return Object.keys(routeState).reduce<UiState>(
(state, indexId) => ({
...state,
Expand Down
21 changes: 21 additions & 0 deletions src/lib/stateMappings/singleIndex.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { StateMapping, IndexUiState } from '../../types';

function getIndexStateWithoutConfigure(uiState: IndexUiState): IndexUiState {
const { configure, ...trackedUiState } = uiState;
return trackedUiState;
}

export default function singleIndexStateMapping(
indexName: string
): StateMapping<IndexUiState> {
return {
stateToRoute(uiState) {
return getIndexStateWithoutConfigure(uiState[indexName] || {});
},
routeToState(routeState = {}) {
return {
[indexName]: getIndexStateWithoutConfigure(routeState),
};
},
};
}

0 comments on commit 842eb0f

Please sign in to comment.