Skip to content

Commit adce212

Browse files
Haroenvfrancoischalifour
andauthoredMay 5, 2021
feat(core): add getUiState function (#4750)
* feat(core): add getUiState function This is useful for if you want to read the ui state externally from InstantSearch, like for example in algolia/autocomplete#550 TODO: tests * write which tests need to be written * write tests * Update src/lib/__tests__/InstantSearch-test.js Co-authored-by: François Chalifour <francoischalifour@users.noreply.github.com> Co-authored-by: François Chalifour <francoischalifour@users.noreply.github.com>
1 parent 2e7ccc9 commit adce212

File tree

3 files changed

+208
-4
lines changed

3 files changed

+208
-4
lines changed
 

‎src/lib/InstantSearch.ts

+9
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,15 @@ See ${createDocumentationLink({
596596
this.scheduleSearch();
597597
}
598598

599+
public getUiState(): UiState {
600+
if (this.started) {
601+
// We refresh the index UI state to make sure changes from `refine` are taken in account
602+
this.mainIndex.refreshUiState();
603+
}
604+
605+
return this.mainIndex.getWidgetUiState({});
606+
}
607+
599608
public onInternalStateChange = defer(() => {
600609
const nextUiState = this.mainIndex.getWidgetUiState({});
601610

‎src/lib/__tests__/InstantSearch-test.js

+198-4
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,17 @@ import { h, render, createRef } from 'preact';
44
import algoliasearchHelper from 'algoliasearch-helper';
55
import InstantSearch from '../InstantSearch';
66
import version from '../version';
7-
import connectSearchBox from '../../connectors/search-box/connectSearchBox';
8-
import connectPagination from '../../connectors/pagination/connectPagination';
9-
import index from '../../widgets/index/index';
7+
import { connectSearchBox, connectPagination } from '../../connectors';
8+
import { index } from '../../widgets';
109
import { noop, warning } from '../utils';
1110
import {
1211
createSearchClient,
1312
createControlledSearchClient,
1413
} from '../../../test/mock/createSearchClient';
15-
import { createWidget } from '../../../test/mock/createWidget';
14+
import {
15+
createRenderOptions,
16+
createWidget,
17+
} from '../../../test/mock/createWidget';
1618
import { runAllMicroTasks } from '../../../test/utils/runAllMicroTasks';
1719

1820
jest.useFakeTimers();
@@ -101,6 +103,9 @@ See: https://www.algolia.com/doc/guides/building-search-ui/going-further/backend
101103
});
102104

103105
it('throws if insightsClient is not a function', () => {
106+
const warn = jest.spyOn(global.console, 'warn');
107+
warn.mockImplementation(() => {});
108+
104109
expect(() => {
105110
// eslint-disable-next-line no-new
106111
new InstantSearch({
@@ -273,6 +278,8 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/instantsear
273278
const searchClient = createSearchClient({
274279
addAlgoliaAgent: jest.fn(),
275280
});
281+
const warn = jest.spyOn(global.console, 'warn');
282+
warn.mockImplementation(() => {});
276283

277284
expect(() => {
278285
// eslint-disable-next-line no-new
@@ -333,6 +340,8 @@ describe('InstantSearch', () => {
333340

334341
it('warns deprecated usage of `searchParameters`', () => {
335342
warning.cache = {};
343+
const warn = jest.spyOn(global.console, 'warn');
344+
warn.mockImplementation(() => {});
336345

337346
expect(() => {
338347
// eslint-disable-next-line no-new
@@ -435,6 +444,9 @@ describe('addWidget(s)', () => {
435444
});
436445

437446
it('forwards the call of `addWidget` to the main index', () => {
447+
const warn = jest.spyOn(global.console, 'warn');
448+
warn.mockImplementation(() => {});
449+
438450
const searchClient = createSearchClient();
439451
const search = new InstantSearch({
440452
indexName: 'indexName',
@@ -465,6 +477,9 @@ describe('addWidget(s)', () => {
465477
});
466478

467479
it('returns the search instance when calling `addWidget`', () => {
480+
const warn = jest.spyOn(global.console, 'warn');
481+
warn.mockImplementation(() => {});
482+
468483
const searchClient = createSearchClient();
469484
const search = new InstantSearch({
470485
indexName: 'indexName',
@@ -499,6 +514,9 @@ describe('removeWidget(s)', () => {
499514
});
500515

501516
it('forwards the call to `removeWidget` to the main index', () => {
517+
const warn = jest.spyOn(global.console, 'warn');
518+
warn.mockImplementation(() => {});
519+
502520
const searchClient = createSearchClient();
503521
const search = new InstantSearch({
504522
indexName: 'indexName',
@@ -537,6 +555,9 @@ describe('removeWidget(s)', () => {
537555
});
538556

539557
it('returns the search instance when calling `removeWidget`', () => {
558+
const warn = jest.spyOn(global.console, 'warn');
559+
warn.mockImplementation(() => {});
560+
540561
const searchClient = createSearchClient();
541562
const search = new InstantSearch({
542563
indexName: 'indexName',
@@ -1974,6 +1995,9 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/instantsear
19741995
});
19751996

19761997
it('warns if UI state contains unmounted widgets in development mode', () => {
1998+
const warn = jest.spyOn(global.console, 'warn');
1999+
warn.mockImplementation(() => {});
2000+
19772001
const searchClient = createSearchClient();
19782002
const search = new InstantSearch({
19792003
indexName: 'indexName',
@@ -2017,6 +2041,176 @@ See https://www.algolia.com/doc/guides/building-search-ui/widgets/customize-an-e
20172041
});
20182042
});
20192043

2044+
describe('getUiState', () => {
2045+
test('retrieves empty UI state without widgets', () => {
2046+
const indexName = 'indexName';
2047+
const searchClient = createSearchClient();
2048+
const search = new InstantSearch({
2049+
indexName,
2050+
searchClient,
2051+
});
2052+
2053+
expect(search.getUiState()).toEqual({
2054+
[indexName]: {},
2055+
});
2056+
});
2057+
2058+
test('retrieves the ui state without refinements', () => {
2059+
const indexName = 'indexName';
2060+
const searchClient = createSearchClient();
2061+
const search = new InstantSearch({
2062+
indexName,
2063+
searchClient,
2064+
});
2065+
2066+
search.addWidgets([
2067+
connectSearchBox(() => {})(),
2068+
connectPagination(() => {})(),
2069+
]);
2070+
2071+
expect(search.getUiState()).toEqual({
2072+
[indexName]: {},
2073+
});
2074+
});
2075+
2076+
test('retrieves the ui state without refinements (multi-index)', () => {
2077+
const indexName = 'indexName';
2078+
const secondIndexName = 'indexName2';
2079+
const searchClient = createSearchClient();
2080+
const search = new InstantSearch({
2081+
indexName,
2082+
searchClient,
2083+
});
2084+
2085+
search.addWidgets([
2086+
connectSearchBox(() => {})(),
2087+
connectPagination(() => {})(),
2088+
index({ indexName: secondIndexName }).addWidgets([
2089+
connectSearchBox(() => {})(),
2090+
]),
2091+
]);
2092+
2093+
expect(search.getUiState()).toEqual({
2094+
[indexName]: {},
2095+
[secondIndexName]: {},
2096+
});
2097+
});
2098+
2099+
test('retrieves the correct ui state after one refinement', () => {
2100+
const indexName = 'indexName';
2101+
const searchClient = createSearchClient();
2102+
const search = new InstantSearch({
2103+
indexName,
2104+
searchClient,
2105+
});
2106+
2107+
search.addWidgets([
2108+
connectSearchBox(() => {})(),
2109+
connectPagination(() => {})(),
2110+
]);
2111+
2112+
search.start();
2113+
2114+
search.mainIndex
2115+
.getWidgets()[0]
2116+
.getWidgetRenderState(createRenderOptions())
2117+
.refine('test');
2118+
2119+
expect(search.getUiState()).toEqual({
2120+
[indexName]: {
2121+
query: 'test',
2122+
},
2123+
});
2124+
});
2125+
2126+
test('retrieves the correct ui state after multiple refinements', () => {
2127+
const indexName = 'indexName';
2128+
const searchClient = createSearchClient();
2129+
const search = new InstantSearch({
2130+
indexName,
2131+
searchClient,
2132+
});
2133+
2134+
search.addWidgets([
2135+
connectSearchBox(() => {})(),
2136+
connectPagination(() => {})(),
2137+
]);
2138+
2139+
search.start();
2140+
2141+
search.mainIndex
2142+
.getWidgets()[0]
2143+
.getWidgetRenderState(createRenderOptions())
2144+
.refine('test');
2145+
2146+
search.mainIndex
2147+
.getWidgets()[1]
2148+
.getWidgetRenderState(createRenderOptions())
2149+
.refine(3);
2150+
2151+
expect(search.getUiState()).toEqual({
2152+
[indexName]: {
2153+
query: 'test',
2154+
page: 4,
2155+
},
2156+
});
2157+
});
2158+
2159+
test('retrieves the correct ui state after multiple refinements (multi-index)', () => {
2160+
const indexName = 'indexName';
2161+
const secondIndexName = 'indexName2';
2162+
const searchClient = createSearchClient();
2163+
const search = new InstantSearch({
2164+
indexName,
2165+
searchClient,
2166+
});
2167+
2168+
search.addWidgets([
2169+
connectSearchBox(() => {})(),
2170+
connectPagination(() => {})(),
2171+
index({ indexName: secondIndexName }).addWidgets([
2172+
connectSearchBox(() => {})(),
2173+
connectPagination(() => {})(),
2174+
]),
2175+
]);
2176+
2177+
search.start();
2178+
2179+
search.mainIndex
2180+
.getWidgets()[0]
2181+
.getWidgetRenderState(createRenderOptions())
2182+
.refine('test');
2183+
2184+
search.mainIndex
2185+
.getWidgets()[1]
2186+
.getWidgetRenderState(createRenderOptions())
2187+
.refine(3);
2188+
2189+
search.mainIndex
2190+
.getWidgets()[2]
2191+
.getWidgets()[0]
2192+
.getWidgetRenderState(createRenderOptions())
2193+
.refine('test2');
2194+
2195+
search.mainIndex
2196+
.getWidgets()[2]
2197+
.getWidgets()[1]
2198+
.getWidgetRenderState(createRenderOptions())
2199+
.refine(39);
2200+
2201+
expect(search.getUiState()).toEqual({
2202+
[indexName]: {
2203+
query: 'test',
2204+
page: 4,
2205+
},
2206+
[secondIndexName]: {
2207+
query: 'test2',
2208+
page: 40,
2209+
},
2210+
});
2211+
});
2212+
});
2213+
20202214
describe('onStateChange', () => {
20212215
test('does not trigger an internal state change', () => {
20222216
const searchClient = createSearchClient();

‎test/mock/createInstantSearch.ts

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export const createInstantSearch = (
3939
_createURL: jest.fn(() => '#'),
4040
onStateChange: null,
4141
setUiState: jest.fn(),
42+
getUiState: jest.fn(() => ({})),
4243
// Since we defer `onInternalStateChange` with our `defer` util which
4344
// creates a scoped deferred function, we're not able to spy that method.
4445
// We'll therefore need to override it when calling `createInstantSearch`.

0 commit comments

Comments
 (0)
Please sign in to comment.