@askdialog/dialog-sdk 2.10.0 → 2.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/__tests__/searchController.spec.js +80 -1
- package/dist/searchController.d.ts +1 -1
- package/dist/searchController.d.ts.map +1 -1
- package/dist/searchController.js +23 -10
- package/dist/types/searchController.d.ts +7 -1
- package/dist/types/searchController.d.ts.map +1 -1
- package/dist/utils/searchSections.d.ts +5 -0
- package/dist/utils/searchSections.d.ts.map +1 -0
- package/dist/utils/searchSections.js +21 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -261,7 +261,7 @@ A non-2xx answer rejects with `DialogSearchError` — stable `name`, HTTP `statu
|
|
|
261
261
|
|
|
262
262
|
- Search controller
|
|
263
263
|
|
|
264
|
-
`createSearchController()` wraps the stateless transport with the stateful behavior every search UI needs — debounce (immediate on explicit submission), cancellation of the in-flight request, stale-response protection (a late response never replaces newer results, even if the transport ignores the abort), pagination that resets on a new query, `idle` / `loading` / `success` / `empty` / `error` states, retry, and the
|
|
264
|
+
`createSearchController()` wraps the stateless transport with the stateful behavior every search UI needs — debounce (immediate on explicit submission), cancellation of the in-flight request, stale-response protection (a late response never replaces newer results, even if the transport ignores the abort), pagination that resets on a new query, `idle` / `loading` / `success` / `empty` / `error` states, retry, and the attribution events (`view_search_results` viewport impressions, `select_search_result` clicks). It searches the products index (`products_<locale>`) and exposes the products result entry as `state.response`; optional `sections` add other indices (`collections`, …) to the same request, exposed under `state.sections`. It has no framework or rendering dependency: raw JavaScript, React, Vue and Shopify integrations are rendering-and-routing adapters around it.
|
|
265
265
|
|
|
266
266
|
```typescript
|
|
267
267
|
import { createSearchController, Dialog, SearchStatus } from '@askdialog/dialog-sdk';
|
|
@@ -279,6 +279,7 @@ const controller = createSearchController({
|
|
|
279
279
|
navigate: (url) => router.push(url), // optional platform routing adapter
|
|
280
280
|
debounceMs: 250, // optional (default 250)
|
|
281
281
|
hitsPerPage: 12, // optional (default 12)
|
|
282
|
+
sections: [{ index: 'collections', hitsPerPage: 5 }], // optional, first page only; a function is resolved per request
|
|
282
283
|
});
|
|
283
284
|
|
|
284
285
|
const unsubscribe = controller.subscribe((state) => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* eslint max-lines: ["error",
|
|
1
|
+
/* eslint max-lines: ["error", 460] */
|
|
2
2
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
3
|
import { createSearchController } from "../searchController";
|
|
4
4
|
import { SearchStatus } from "../types/searchController";
|
|
@@ -288,4 +288,83 @@ describe("createSearchController", () => {
|
|
|
288
288
|
expect(search).toHaveBeenCalledTimes(1);
|
|
289
289
|
expect(controller.getState().status).toBe(SearchStatus.LOADING);
|
|
290
290
|
});
|
|
291
|
+
it("requests the sections on their first page with the products and exposes their entries", async () => {
|
|
292
|
+
const controller = createSearchController({
|
|
293
|
+
search,
|
|
294
|
+
analytics: {
|
|
295
|
+
surface: "search_page",
|
|
296
|
+
trackViewSearchResults,
|
|
297
|
+
trackSelectSearchResult,
|
|
298
|
+
},
|
|
299
|
+
locale: "fr",
|
|
300
|
+
sections: [{ index: "collections", hitsPerPage: 5 }],
|
|
301
|
+
});
|
|
302
|
+
const collections = {
|
|
303
|
+
...response().results[0],
|
|
304
|
+
index: "collections_fr",
|
|
305
|
+
hits: [{ objectID: "c1" }],
|
|
306
|
+
nbHits: 1,
|
|
307
|
+
};
|
|
308
|
+
search.mockResolvedValueOnce({
|
|
309
|
+
results: [...response().results, collections],
|
|
310
|
+
});
|
|
311
|
+
controller.submit("shoes");
|
|
312
|
+
await settle();
|
|
313
|
+
expect(search.mock.calls[0][0].requests).toEqual([
|
|
314
|
+
{ indexName: "products_fr", query: "shoes", page: 0, hitsPerPage: 12 },
|
|
315
|
+
{ indexName: "collections_fr", query: "shoes", page: 0, hitsPerPage: 5 },
|
|
316
|
+
]);
|
|
317
|
+
expect(controller.getState().response?.index).toBe("products_fr");
|
|
318
|
+
expect(controller.getState().sections).toEqual({ collections });
|
|
319
|
+
});
|
|
320
|
+
it("resolves a sections function per request and leaves a missing entry out", async () => {
|
|
321
|
+
let enabled = false;
|
|
322
|
+
const controller = createSearchController({
|
|
323
|
+
search,
|
|
324
|
+
analytics: {
|
|
325
|
+
surface: "search_page",
|
|
326
|
+
trackViewSearchResults,
|
|
327
|
+
trackSelectSearchResult,
|
|
328
|
+
},
|
|
329
|
+
locale: "fr",
|
|
330
|
+
sections: () => (enabled ? [{ index: "collections" }] : []),
|
|
331
|
+
});
|
|
332
|
+
search.mockResolvedValue(response());
|
|
333
|
+
controller.submit("shoes");
|
|
334
|
+
await settle();
|
|
335
|
+
enabled = true;
|
|
336
|
+
controller.submit("boots");
|
|
337
|
+
await settle();
|
|
338
|
+
expect(search.mock.calls[0][0].requests).toHaveLength(1);
|
|
339
|
+
expect(search.mock.calls[1][0].requests).toHaveLength(2);
|
|
340
|
+
expect(search.mock.calls[1][0].requests[1]).toMatchObject({
|
|
341
|
+
indexName: "collections_fr",
|
|
342
|
+
hitsPerPage: 12,
|
|
343
|
+
});
|
|
344
|
+
expect(controller.getState().sections).toEqual({});
|
|
345
|
+
});
|
|
346
|
+
it("enters the error state when the sections resolver throws", async () => {
|
|
347
|
+
const failure = new Error("entitlement lookup failed");
|
|
348
|
+
const controller = createSearchController({
|
|
349
|
+
search,
|
|
350
|
+
analytics: {
|
|
351
|
+
surface: "search_page",
|
|
352
|
+
trackViewSearchResults,
|
|
353
|
+
trackSelectSearchResult,
|
|
354
|
+
},
|
|
355
|
+
locale: "fr",
|
|
356
|
+
sections: () => {
|
|
357
|
+
throw failure;
|
|
358
|
+
},
|
|
359
|
+
});
|
|
360
|
+
controller.submit("shoes");
|
|
361
|
+
await settle();
|
|
362
|
+
expect(search).not.toHaveBeenCalled();
|
|
363
|
+
expect(controller.getState()).toMatchObject({
|
|
364
|
+
status: SearchStatus.ERROR,
|
|
365
|
+
error: failure,
|
|
366
|
+
response: undefined,
|
|
367
|
+
sections: undefined,
|
|
368
|
+
});
|
|
369
|
+
});
|
|
291
370
|
});
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { SearchController, SearchControllerOptions } from "./types/searchController";
|
|
2
|
-
export declare function createSearchController({ search, analytics, navigate, debounceMs, hitsPerPage, locale, }: SearchControllerOptions): SearchController;
|
|
2
|
+
export declare function createSearchController({ search, analytics, navigate, debounceMs, hitsPerPage, locale, sections, }: SearchControllerOptions): SearchController;
|
|
3
3
|
//# sourceMappingURL=searchController.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"searchController.d.ts","sourceRoot":"","sources":["../src/searchController.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"searchController.d.ts","sourceRoot":"","sources":["../src/searchController.ts"],"names":[],"mappings":"AAOA,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EAIxB,MAAM,0BAA0B,CAAC;AAqBlC,wBAAgB,sBAAsB,CAAC,EACrC,MAAM,EACN,SAAS,EACT,QAAQ,EACR,UAAgC,EAChC,WAAmC,EACnC,MAAM,EACN,QAAa,GACd,EAAE,uBAAuB,GAAG,gBAAgB,CA6L5C"}
|
package/dist/searchController.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
// This controller is one cohesive unit
|
|
2
|
-
// navigate adapter handled the transition
|
|
3
|
-
//
|
|
4
|
-
|
|
1
|
+
// This controller is one cohesive unit: `selectResult` reports whether the
|
|
2
|
+
// navigate adapter handled the transition and the response is split into the
|
|
3
|
+
// products entry plus its sections, pushing the file past the default 200-line
|
|
4
|
+
// cap — raised modestly rather than split artificially.
|
|
5
|
+
/* eslint max-lines: ["error", 240] */
|
|
5
6
|
import { searchIndexName } from "./services/search";
|
|
6
7
|
import { SearchStatus, } from "./types/searchController";
|
|
7
8
|
import { createControllerAnalytics } from "./utils/searchControllerAnalytics";
|
|
9
|
+
import { buildSectionQueries, pickSectionResults, } from "./utils/searchSections";
|
|
8
10
|
const DEFAULT_DEBOUNCE_MS = 250;
|
|
9
11
|
const DEFAULT_HITS_PER_PAGE = 12;
|
|
10
12
|
// `SearchRequest` rejects queries under two visible characters (code points).
|
|
@@ -14,10 +16,12 @@ const INITIAL_STATE = {
|
|
|
14
16
|
query: "",
|
|
15
17
|
page: 0,
|
|
16
18
|
response: undefined,
|
|
19
|
+
sections: undefined,
|
|
17
20
|
error: undefined,
|
|
18
21
|
};
|
|
19
|
-
export function createSearchController({ search, analytics, navigate, debounceMs = DEFAULT_DEBOUNCE_MS, hitsPerPage = DEFAULT_HITS_PER_PAGE, locale, }) {
|
|
22
|
+
export function createSearchController({ search, analytics, navigate, debounceMs = DEFAULT_DEBOUNCE_MS, hitsPerPage = DEFAULT_HITS_PER_PAGE, locale, sections = [], }) {
|
|
20
23
|
const indexName = searchIndexName("products", locale);
|
|
24
|
+
const resolveSections = () => typeof sections === "function" ? sections() : sections;
|
|
21
25
|
let state = INITIAL_STATE;
|
|
22
26
|
const listeners = new Set();
|
|
23
27
|
const controllerAnalytics = createControllerAnalytics(analytics);
|
|
@@ -40,17 +44,20 @@ export function createSearchController({ search, analytics, navigate, debounceMs
|
|
|
40
44
|
abortController?.abort();
|
|
41
45
|
abortController = undefined;
|
|
42
46
|
};
|
|
43
|
-
const buildRequest = (query, page) => ({
|
|
44
|
-
requests: [
|
|
47
|
+
const buildRequest = (query, page, requested) => ({
|
|
48
|
+
requests: [
|
|
49
|
+
{ indexName, query, page, hitsPerPage },
|
|
50
|
+
...buildSectionQueries(requested, query, locale, hitsPerPage),
|
|
51
|
+
],
|
|
45
52
|
});
|
|
46
53
|
const run = async (query, page) => {
|
|
47
54
|
cancelInFlight();
|
|
48
55
|
abortController = new AbortController();
|
|
49
56
|
const id = ++requestId;
|
|
50
|
-
const request = buildRequest(query, page);
|
|
51
57
|
setState({ status: SearchStatus.LOADING, query, page });
|
|
52
58
|
try {
|
|
53
|
-
const
|
|
59
|
+
const requested = resolveSections();
|
|
60
|
+
const response = await search(buildRequest(query, page, requested), {
|
|
54
61
|
signal: abortController.signal,
|
|
55
62
|
});
|
|
56
63
|
if (id !== requestId) {
|
|
@@ -64,6 +71,7 @@ export function createSearchController({ search, analytics, navigate, debounceMs
|
|
|
64
71
|
setState({
|
|
65
72
|
status: result.nbHits === 0 ? SearchStatus.EMPTY : SearchStatus.SUCCESS,
|
|
66
73
|
response: result,
|
|
74
|
+
sections: pickSectionResults(response, requested, locale),
|
|
67
75
|
error: undefined,
|
|
68
76
|
});
|
|
69
77
|
}
|
|
@@ -74,7 +82,12 @@ export function createSearchController({ search, analytics, navigate, debounceMs
|
|
|
74
82
|
if (id !== requestId) {
|
|
75
83
|
return;
|
|
76
84
|
}
|
|
77
|
-
setState({
|
|
85
|
+
setState({
|
|
86
|
+
status: SearchStatus.ERROR,
|
|
87
|
+
error,
|
|
88
|
+
response: undefined,
|
|
89
|
+
sections: undefined,
|
|
90
|
+
});
|
|
78
91
|
}
|
|
79
92
|
};
|
|
80
93
|
// Shared by `setQuery`/`submit`: undefined = reset to idle (query too short).
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SearchHit, SearchOptions, SearchRequest, SearchResponse, SearchResult } from "./search";
|
|
1
|
+
import { SearchHit, SearchIndex, SearchOptions, SearchRequest, SearchResponse, SearchResult } from "./search";
|
|
2
2
|
import { SearchSurface, SelectSearchResultParams, ViewSearchResultsParams } from "./searchAnalytics";
|
|
3
3
|
export declare const SearchStatus: {
|
|
4
4
|
readonly IDLE: "idle";
|
|
@@ -9,6 +9,10 @@ export declare const SearchStatus: {
|
|
|
9
9
|
readonly ERROR: "error";
|
|
10
10
|
};
|
|
11
11
|
export type SearchStatus = (typeof SearchStatus)[keyof typeof SearchStatus];
|
|
12
|
+
export interface SearchSection {
|
|
13
|
+
index: Exclude<SearchIndex, "products">;
|
|
14
|
+
hitsPerPage?: number;
|
|
15
|
+
}
|
|
12
16
|
export interface SearchControllerState {
|
|
13
17
|
status: SearchStatus;
|
|
14
18
|
/** Last committed (run) query — not the text currently being typed. */
|
|
@@ -20,6 +24,7 @@ export interface SearchControllerState {
|
|
|
20
24
|
* loads, cleared on error. Hits are flat records (`objectID`, `title`, …).
|
|
21
25
|
*/
|
|
22
26
|
response?: SearchResult;
|
|
27
|
+
sections?: Partial<Record<SearchIndex, SearchResult>>;
|
|
23
28
|
error?: unknown;
|
|
24
29
|
}
|
|
25
30
|
export type SearchFunction = (request: SearchRequest, options?: SearchOptions) => Promise<SearchResponse>;
|
|
@@ -48,6 +53,7 @@ export interface SearchControllerOptions {
|
|
|
48
53
|
debounceMs?: number;
|
|
49
54
|
hitsPerPage?: number;
|
|
50
55
|
locale: string;
|
|
56
|
+
sections?: readonly SearchSection[] | (() => readonly SearchSection[]);
|
|
51
57
|
}
|
|
52
58
|
/**
|
|
53
59
|
* Framework-agnostic stateful search behavior around the stateless
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"searchController.d.ts","sourceRoot":"","sources":["../../src/types/searchController.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,aAAa,EACb,aAAa,EACb,cAAc,EACd,YAAY,EACb,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,aAAa,EACb,wBAAwB,EACxB,uBAAuB,EACxB,MAAM,mBAAmB,CAAC;AAE3B,eAAO,MAAM,YAAY;;;;IAIvB,yGAAyG;;;CAGjG,CAAC;AAEX,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,OAAO,YAAY,CAAC,CAAC;AAE5E,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,YAAY,CAAC;IACrB,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,MAAM,cAAc,GAAG,CAC3B,OAAO,EAAE,aAAa,EACtB,OAAO,CAAC,EAAE,aAAa,KACpB,OAAO,CAAC,cAAc,CAAC,CAAC;AAE7B;;;;;GAKG;AACH,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,aAAa,CAAC;IACvB,sBAAsB,EAAE,CAAC,MAAM,EAAE,uBAAuB,KAAK,IAAI,CAAC;IAClE,uBAAuB,EAAE,CAAC,MAAM,EAAE,wBAAwB,KAAK,IAAI,CAAC;CACrE;AAED,MAAM,WAAW,uBAAuB;IACtC,0CAA0C;IAC1C,MAAM,EAAE,cAAc,CAAC;IACvB,SAAS,EAAE,yBAAyB,CAAC;IACrC;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,KAAK,IAAI,CAAC;IACjD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"searchController.d.ts","sourceRoot":"","sources":["../../src/types/searchController.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,WAAW,EACX,aAAa,EACb,aAAa,EACb,cAAc,EACd,YAAY,EACb,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,aAAa,EACb,wBAAwB,EACxB,uBAAuB,EACxB,MAAM,mBAAmB,CAAC;AAE3B,eAAO,MAAM,YAAY;;;;IAIvB,yGAAyG;;;CAGjG,CAAC;AAEX,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,OAAO,YAAY,CAAC,CAAC;AAE5E,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,OAAO,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;IACxC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,YAAY,CAAC;IACrB,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC,CAAC;IACtD,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,MAAM,cAAc,GAAG,CAC3B,OAAO,EAAE,aAAa,EACtB,OAAO,CAAC,EAAE,aAAa,KACpB,OAAO,CAAC,cAAc,CAAC,CAAC;AAE7B;;;;;GAKG;AACH,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,aAAa,CAAC;IACvB,sBAAsB,EAAE,CAAC,MAAM,EAAE,uBAAuB,KAAK,IAAI,CAAC;IAClE,uBAAuB,EAAE,CAAC,MAAM,EAAE,wBAAwB,KAAK,IAAI,CAAC;CACrE;AAED,MAAM,WAAW,uBAAuB;IACtC,0CAA0C;IAC1C,MAAM,EAAE,cAAc,CAAC;IACvB,SAAS,EAAE,yBAAyB,CAAC;IACrC;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,KAAK,IAAI,CAAC;IACjD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,SAAS,aAAa,EAAE,GAAG,CAAC,MAAM,SAAS,aAAa,EAAE,CAAC,CAAC;CACxE;AAED;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,4CAA4C;IAC5C,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,2EAA2E;IAC3E,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,kEAAkE;IAClE,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,oEAAoE;IACpE,KAAK,IAAI,IAAI,CAAC;IACd;;;OAGG;IACH,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrD;;;;;;;;;;;;;;;OAeG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC;IACvE,mEAAmE;IACnE,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACxE,QAAQ,IAAI,qBAAqB,CAAC;IAClC,kEAAkE;IAClE,OAAO,IAAI,IAAI,CAAC;CACjB"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { SearchIndex, SearchQuery, SearchResponse, SearchResult } from "../types/search";
|
|
2
|
+
import { SearchSection } from "../types/searchController";
|
|
3
|
+
export declare const buildSectionQueries: (sections: readonly SearchSection[], query: string, locale: string, hitsPerPage: number) => SearchQuery[];
|
|
4
|
+
export declare const pickSectionResults: (response: SearchResponse, sections: readonly SearchSection[], locale: string) => Partial<Record<SearchIndex, SearchResult>> | undefined;
|
|
5
|
+
//# sourceMappingURL=searchSections.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"searchSections.d.ts","sourceRoot":"","sources":["../../src/utils/searchSections.ts"],"names":[],"mappings":"AACA,OAAO,EACL,WAAW,EACX,WAAW,EACX,cAAc,EACd,YAAY,EACb,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE1D,eAAO,MAAM,mBAAmB,GAC9B,UAAU,SAAS,aAAa,EAAE,EAClC,OAAO,MAAM,EACb,QAAQ,MAAM,EACd,aAAa,MAAM,KAClB,WAAW,EAMT,CAAC;AAEN,eAAO,MAAM,kBAAkB,GAC7B,UAAU,cAAc,EACxB,UAAU,SAAS,aAAa,EAAE,EAClC,QAAQ,MAAM,KACb,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC,GAAG,SAgB/C,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { searchIndexName } from "../services/search";
|
|
2
|
+
export const buildSectionQueries = (sections, query, locale, hitsPerPage) => sections.map((section) => ({
|
|
3
|
+
indexName: searchIndexName(section.index, locale),
|
|
4
|
+
query,
|
|
5
|
+
page: 0,
|
|
6
|
+
hitsPerPage: section.hitsPerPage ?? hitsPerPage,
|
|
7
|
+
}));
|
|
8
|
+
export const pickSectionResults = (response, sections, locale) => {
|
|
9
|
+
if (sections.length === 0) {
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
const picked = {};
|
|
13
|
+
for (const section of sections) {
|
|
14
|
+
const sectionIndexName = searchIndexName(section.index, locale);
|
|
15
|
+
const entry = response.results.find((candidate) => candidate.index === sectionIndexName);
|
|
16
|
+
if (entry !== undefined) {
|
|
17
|
+
picked[section.index] = entry;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return picked;
|
|
21
|
+
};
|