@truedat/core 8.4.2 → 8.4.3

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.
@@ -0,0 +1,62 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`<DomainSearchFilter /> matches the latest snapshot 1`] = `
4
+ <div>
5
+ <div
6
+ aria-expanded="true"
7
+ class="ui active visible floating item dropdown domain-search-filter"
8
+ name="domainSearchFilter"
9
+ role="listbox"
10
+ tabindex="0"
11
+ >
12
+ <div
13
+ class="ui label"
14
+ >
15
+ taxonomy
16
+ <i
17
+ aria-hidden="true"
18
+ class="delete icon"
19
+ />
20
+ </div>
21
+ <div
22
+ class="menu transition dimmable visible"
23
+ >
24
+ <div
25
+ class="ui left icon input search"
26
+ >
27
+ <input
28
+ type="text"
29
+ />
30
+ <i
31
+ aria-hidden="true"
32
+ class="search icon"
33
+ />
34
+ </div>
35
+ <div
36
+ class="scrolling menu transition"
37
+ >
38
+ <div>
39
+ <span>
40
+ Domain 1
41
+ </span>
42
+ <button
43
+ type="button"
44
+ >
45
+ open-1
46
+ </button>
47
+ <button
48
+ type="button"
49
+ >
50
+ select-1
51
+ </button>
52
+ <button
53
+ type="button"
54
+ >
55
+ toggle-1
56
+ </button>
57
+ </div>
58
+ </div>
59
+ </div>
60
+ </div>
61
+ </div>
62
+ `;
@@ -0,0 +1,130 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`<DomainSearchFilterItem /> matches snapshot when partial selected is true 1`] = `
4
+ <div>
5
+ <div
6
+ class="item"
7
+ role="option"
8
+ >
9
+ <div
10
+ class="item-content"
11
+ style="margin-left: 0px; padding-left: 5px;"
12
+ >
13
+ <i
14
+ aria-hidden="true"
15
+ class="chevron right icon"
16
+ />
17
+ <i
18
+ aria-hidden="true"
19
+ class="minus square outline icon"
20
+ />
21
+ <span
22
+ style="opacity: 1;"
23
+ title="Domain 1"
24
+ >
25
+ Domain 1
26
+ </span>
27
+ <button
28
+ class="toggle-children"
29
+ type="button"
30
+ >
31
+ <span
32
+ class="toggle-children-content"
33
+ title=""
34
+ >
35
+ <i
36
+ aria-hidden="true"
37
+ class="toggle off icon"
38
+ />
39
+ </span>
40
+ </button>
41
+ </div>
42
+ </div>
43
+ </div>
44
+ `;
45
+
46
+ exports[`<DomainSearchFilterItem /> matches snapshot when selected is true 1`] = `
47
+ <div>
48
+ <div
49
+ class="item"
50
+ role="option"
51
+ >
52
+ <div
53
+ class="item-content"
54
+ style="margin-left: 0px; padding-left: 5px;"
55
+ >
56
+ <i
57
+ aria-hidden="true"
58
+ class="chevron right icon"
59
+ />
60
+ <i
61
+ aria-hidden="true"
62
+ class="check square outline icon"
63
+ />
64
+ <span
65
+ style="opacity: 1;"
66
+ title="Domain 1"
67
+ >
68
+ Domain 1
69
+ </span>
70
+ <button
71
+ class="toggle-children"
72
+ type="button"
73
+ >
74
+ <span
75
+ class="toggle-children-content"
76
+ title=""
77
+ >
78
+ <i
79
+ aria-hidden="true"
80
+ class="toggle off icon"
81
+ />
82
+ </span>
83
+ </button>
84
+ </div>
85
+ </div>
86
+ </div>
87
+ `;
88
+
89
+ exports[`<DomainSearchFilterItem /> matches the latest snapshot 1`] = `
90
+ <div>
91
+ <div
92
+ class="item"
93
+ role="option"
94
+ >
95
+ <div
96
+ class="item-content"
97
+ style="margin-left: 0px; padding-left: 5px;"
98
+ >
99
+ <i
100
+ aria-hidden="true"
101
+ class="chevron right icon"
102
+ />
103
+ <i
104
+ aria-hidden="true"
105
+ class="square outline icon"
106
+ />
107
+ <span
108
+ style="opacity: 1;"
109
+ title="Domain 1"
110
+ >
111
+ Domain 1
112
+ </span>
113
+ <button
114
+ class="toggle-children"
115
+ type="button"
116
+ >
117
+ <span
118
+ class="toggle-children-content"
119
+ title=""
120
+ >
121
+ <i
122
+ aria-hidden="true"
123
+ class="toggle off icon"
124
+ />
125
+ </span>
126
+ </button>
127
+ </div>
128
+ </div>
129
+ </div>
130
+ `;
@@ -17,6 +17,7 @@ import DateFilter from "./DateFilter";
17
17
  import DateRangeFilter from "./DateRangeFilter";
18
18
  import DateTime from "./DateTime";
19
19
  import DescriptionInput from "./DescriptionInput";
20
+ import DomainSearchFilter from "./DomainSearchFilter";
20
21
  import DomainSelector from "./DomainSelector";
21
22
  import DropdownMenuItem from "./DropdownMenuItem";
22
23
  import ErrorBoundary from "./ErrorBoundary";
@@ -81,6 +82,7 @@ export {
81
82
  DateRangeFilter,
82
83
  DateTime,
83
84
  DescriptionInput,
85
+ DomainSearchFilter,
84
86
  DomainSelector,
85
87
  DropdownMenuItem,
86
88
  ErrorBoundary,
@@ -30,6 +30,7 @@ export const SearchContextProvider = (props) => {
30
30
  const pageSize = _.propOr(7, "pageSize")(props);
31
31
  const userFiltersType = _.prop("userFiltersType")(props);
32
32
  const userFilterScope = _.prop("userFilterScope")(props);
33
+ const useDomainSearchFilter = _.propOr(false, "useDomainSearchFilter")(props);
33
34
  const omitFilters = _.propOr([], "omitFilters")(props);
34
35
  const translations = _.propOr(() => ({}), "translations")(props);
35
36
  const filtersGroup = _.propOr([], "filtersGroup")(props);
@@ -257,13 +258,22 @@ export const SearchContextProvider = (props) => {
257
258
  enrichSearchPayload,
258
259
  ]);
259
260
 
261
+ const mapDomainFilterKey = (key) => {
262
+ if (!useDomainSearchFilter) return key;
263
+ return key === "taxonomy" ? "domain_ids" : key;
264
+ };
265
+
260
266
  const getMustFilters = (filters) =>
261
- _.pickBy((value, key) => !key.startsWith("mustnot."))(filters);
267
+ _.flow(
268
+ _.pickBy((value, key) => !key.startsWith("mustnot.")),
269
+ _.mapKeys(mapDomainFilterKey)
270
+ )(filters);
262
271
 
263
272
  const getMustNotFilters = (filters) =>
264
273
  _.flow(
265
274
  _.pickBy((value, key) => key.startsWith("mustnot.")),
266
- _.mapKeys((key) => key.replace("mustnot.", ""))
275
+ _.mapKeys((key) => key.replace("mustnot.", "")),
276
+ _.mapKeys(mapDomainFilterKey)
267
277
  )(filters);
268
278
 
269
279
  const makeFiltersGroup = (filters, groups) =>
@@ -377,6 +387,7 @@ export const SearchContextProvider = (props) => {
377
387
 
378
388
  userFiltersType,
379
389
  userFilterScope,
390
+ useDomainSearchFilter,
380
391
 
381
392
  setOnSearchChange,
382
393
  };
@@ -7,6 +7,7 @@ import UserFilters from "./UserFilters";
7
7
  import FilterDropdown from "./FilterDropdown";
8
8
  import FilterQueryDropdown from "./FilterQueryDropdown";
9
9
  import FilterMultilevelDropdown from "./FilterMultilevelDropdown";
10
+ import DomainSearchFilter from "../components/DomainSearchFilter";
10
11
  import HierarchyFilterDropdown from "./HierarchyFilterDropdown";
11
12
  import SearchContext, { useSearchContext } from "./SearchContext";
12
13
 
@@ -21,6 +22,7 @@ export default function SearchSelectedFilters() {
21
22
  activeFilterName,
22
23
  activeFilterValues,
23
24
  userFiltersType,
25
+ useDomainSearchFilter,
24
26
  } = context;
25
27
  const [selectedUserFilter, setSelectedUserFilter] = useState();
26
28
  const { pathname } = useLocation();
@@ -56,7 +58,21 @@ export default function SearchSelectedFilters() {
56
58
  key={filter}
57
59
  >
58
60
  {filterType === "domain" ? (
59
- <FilterMultilevelDropdown />
61
+ useDomainSearchFilter ? (
62
+ <DomainSearchFilter
63
+ name={context.name}
64
+ activeValues={context.activeFilterSelectedValues}
65
+ closeFilter={context.closeFilter}
66
+ filter={filter}
67
+ loading={context.loadingFilters}
68
+ openFilter={context.openFilter}
69
+ options={options}
70
+ removeFilter={context.removeFilter}
71
+ toggleFilterValue={context.toggleFilterValue}
72
+ />
73
+ ) : (
74
+ <FilterMultilevelDropdown />
75
+ )
60
76
  ) : filterType === "hierarchy" ? (
61
77
  <HierarchyFilterDropdown />
62
78
  ) : _.size(options) >= MIN_LENGTH_FOR_QUERY_DROPDOWN ? (
@@ -1,5 +1,10 @@
1
+ import { waitFor } from "@testing-library/react";
2
+ import userEvent from "@testing-library/user-event";
1
3
  import { render } from "@truedat/test/render";
2
- import { SearchContextProvider } from "@truedat/core/search/SearchContext";
4
+ import {
5
+ SearchContextProvider,
6
+ useSearchContext,
7
+ } from "@truedat/core/search/SearchContext";
3
8
 
4
9
  jest.mock("react-router", () => ({
5
10
  ...jest.requireActual("react-router"),
@@ -57,6 +62,82 @@ const renderOpts = {
57
62
 
58
63
  describe("<SearchContextProvider />", () => {
59
64
  const ChildComponent = () => <div>Child Component</div>;
65
+ const ContextConsumer = ({ onSearchChangeSpy }) => {
66
+ const {
67
+ availableGroupedFilters,
68
+ availableFilters,
69
+ allActiveFilters,
70
+ hiddenFilters,
71
+ handleSortSelection,
72
+ removeFilter,
73
+ removeHiddenFilter,
74
+ sortColumn,
75
+ sortDirection,
76
+ toggleFilterValue,
77
+ toggleHiddenFilterValue,
78
+ setQuery,
79
+ query,
80
+ setOnSearchChange,
81
+ } = useSearchContext();
82
+
83
+ return (
84
+ <div>
85
+ <button onClick={() => setOnSearchChange(onSearchChangeSpy)}>
86
+ set-callback
87
+ </button>
88
+ <button
89
+ onClick={() =>
90
+ toggleFilterValue({ filter: "status", value: "draft" })
91
+ }
92
+ >
93
+ toggle-filter-draft
94
+ </button>
95
+ <button
96
+ onClick={() =>
97
+ toggleFilterValue({
98
+ filter: "status",
99
+ value: ["draft", "published"],
100
+ })
101
+ }
102
+ >
103
+ replace-filter-array
104
+ </button>
105
+ <button onClick={() => removeFilter({ filter: "status" })}>
106
+ remove-filter-status
107
+ </button>
108
+ <button
109
+ onClick={() =>
110
+ toggleHiddenFilterValue({ filter: "taxonomy", value: "10" })
111
+ }
112
+ >
113
+ toggle-hidden-taxonomy
114
+ </button>
115
+ <button
116
+ onClick={() =>
117
+ toggleHiddenFilterValue({ filter: "taxonomy", value: ["10", "20"] })
118
+ }
119
+ >
120
+ replace-hidden-array
121
+ </button>
122
+ <button onClick={() => removeHiddenFilter({ filter: "taxonomy" })}>
123
+ remove-hidden-taxonomy
124
+ </button>
125
+ <button onClick={() => handleSortSelection()}>sort-empty</button>
126
+ <button onClick={() => handleSortSelection("status")}>
127
+ sort-status
128
+ </button>
129
+ <button onClick={() => setQuery("abc")}>set-query</button>
130
+ <button onClick={() => setQuery("")}>clean-query</button>
131
+ <div>{`sort-column:${sortColumn}`}</div>
132
+ <div>{`sort-direction:${sortDirection}`}</div>
133
+ <div>{`query:${query}`}</div>
134
+ <div>{`available:${JSON.stringify(availableFilters)}`}</div>
135
+ <div>{`active:${JSON.stringify(allActiveFilters)}`}</div>
136
+ <div>{`hidden:${JSON.stringify(hiddenFilters)}`}</div>
137
+ <div>{`grouped:${JSON.stringify(availableGroupedFilters)}`}</div>
138
+ </div>
139
+ );
140
+ };
60
141
 
61
142
  it(`matches the latest snapshot`, async () => {
62
143
  jest
@@ -102,4 +183,272 @@ describe("<SearchContextProvider />", () => {
102
183
  );
103
184
  jest.useRealTimers();
104
185
  });
186
+
187
+ it("passes enrichSearchPayload to search trigger", async () => {
188
+ jest.useFakeTimers();
189
+ const triggerSearchSpy = jest.fn().mockReturnValue({
190
+ then: (callback) =>
191
+ callback({
192
+ data,
193
+ headers: {},
194
+ }),
195
+ });
196
+ const useSearchSpy = () => ({ trigger: triggerSearchSpy });
197
+
198
+ const enrichSearchPayload = { link_structures: true };
199
+ const props = {
200
+ ...searchProps,
201
+ useSearch: useSearchSpy,
202
+ enrichSearchPayload,
203
+ };
204
+
205
+ render(
206
+ <SearchContextProvider {...props}>
207
+ <ChildComponent />
208
+ </SearchContextProvider>,
209
+ renderOpts
210
+ );
211
+
212
+ jest.runAllTimers();
213
+
214
+ expect(triggerSearchSpy).toHaveBeenCalledWith(
215
+ expect.objectContaining({
216
+ link_structures: true,
217
+ })
218
+ );
219
+ jest.useRealTimers();
220
+ });
221
+
222
+ it("maps taxonomy into domain_ids when useDomainSearchFilter is enabled", async () => {
223
+ jest.useFakeTimers();
224
+ const triggerSearchSpy = jest.fn().mockReturnValue({
225
+ then: (callback) =>
226
+ callback({
227
+ data,
228
+ headers: {},
229
+ }),
230
+ });
231
+ const triggerFiltersSpy = jest.fn().mockReturnValue({
232
+ then: (callback) => callback({ data: [] }),
233
+ });
234
+ const useSearchSpy = () => ({ trigger: triggerSearchSpy });
235
+ const useFiltersSpy = () => ({ trigger: triggerFiltersSpy });
236
+
237
+ render(
238
+ <SearchContextProvider
239
+ {...searchProps}
240
+ useSearch={useSearchSpy}
241
+ useFilters={useFiltersSpy}
242
+ useDomainSearchFilter
243
+ defaultFilters={{ taxonomy: ["100"], "mustnot.taxonomy": ["200"] }}
244
+ >
245
+ <ChildComponent />
246
+ </SearchContextProvider>,
247
+ renderOpts
248
+ );
249
+
250
+ jest.runAllTimers();
251
+
252
+ expect(triggerSearchSpy).toHaveBeenCalledWith(
253
+ expect.objectContaining({
254
+ must: expect.objectContaining({ domain_ids: ["100"] }),
255
+ must_not: expect.objectContaining({ domain_ids: ["200"] }),
256
+ })
257
+ );
258
+
259
+ expect(triggerFiltersSpy).toHaveBeenCalledWith(
260
+ expect.objectContaining({
261
+ must: expect.objectContaining({ domain_ids: ["100"] }),
262
+ must_not: expect.objectContaining({ domain_ids: ["200"] }),
263
+ })
264
+ );
265
+ jest.useRealTimers();
266
+ });
267
+
268
+ it("groups filters and removes internal prefixed groups", async () => {
269
+ jest.useFakeTimers();
270
+ const triggerFiltersSpy = jest.fn().mockReturnValue({
271
+ then: (callback) =>
272
+ callback({
273
+ data: {
274
+ data: {
275
+ status: { type: "term", values: ["draft", "published"] },
276
+ taxonomy: { type: "term", values: ["10", "20"] },
277
+ internal: { type: "term", values: ["x", "y"] },
278
+ one_value: { type: "term", values: ["only"] },
279
+ },
280
+ },
281
+ }),
282
+ });
283
+ const useFiltersSpy = () => ({ trigger: triggerFiltersSpy });
284
+
285
+ const rendered = render(
286
+ <SearchContextProvider
287
+ {...searchProps}
288
+ useFilters={useFiltersSpy}
289
+ filtersGroup={[
290
+ ["main", ["status", "taxonomy"]],
291
+ ["_internal", ["internal"]],
292
+ ]}
293
+ >
294
+ <ContextConsumer onSearchChangeSpy={jest.fn()} />
295
+ </SearchContextProvider>,
296
+ renderOpts
297
+ );
298
+
299
+ jest.runAllTimers();
300
+
301
+ await waitFor(() =>
302
+ expect(
303
+ rendered.getByText(/grouped:\[\["main",\["status","taxonomy"\]\]\]/i)
304
+ ).toBeInTheDocument()
305
+ );
306
+ await waitFor(() =>
307
+ expect(
308
+ rendered.getByText(/available:\["status","taxonomy","internal"\]/i)
309
+ ).toBeInTheDocument()
310
+ );
311
+ jest.useRealTimers();
312
+ });
313
+
314
+ it("handles active and hidden filter toggles and removals", async () => {
315
+ jest.useFakeTimers();
316
+ const user = userEvent.setup({
317
+ delay: null,
318
+ advanceTimers: jest.advanceTimersByTime,
319
+ });
320
+ const onSearchChangeSpy = jest.fn();
321
+ const rendered = render(
322
+ <SearchContextProvider {...searchProps}>
323
+ <ContextConsumer onSearchChangeSpy={onSearchChangeSpy} />
324
+ </SearchContextProvider>,
325
+ renderOpts
326
+ );
327
+
328
+ jest.runAllTimers();
329
+
330
+ await user.click(rendered.getByText(/set-callback/i));
331
+
332
+ await user.click(rendered.getByText(/toggle-filter-draft/i));
333
+ await waitFor(() =>
334
+ expect(
335
+ rendered.getByText(/active:\{"status":\["draft"\]\}/i)
336
+ ).toBeInTheDocument()
337
+ );
338
+
339
+ await user.click(rendered.getByText(/toggle-filter-draft/i));
340
+ await waitFor(() =>
341
+ expect(
342
+ rendered.getByText(/active:\{"status":\[\]\}/i)
343
+ ).toBeInTheDocument()
344
+ );
345
+
346
+ await user.click(rendered.getByText(/replace-filter-array/i));
347
+ await waitFor(() =>
348
+ expect(
349
+ rendered.getByText(/active:\{"status":\["draft","published"\]\}/i)
350
+ ).toBeInTheDocument()
351
+ );
352
+
353
+ await user.click(rendered.getByText(/remove-filter-status/i));
354
+ await waitFor(() =>
355
+ expect(rendered.getByText(/active:\{\}/i)).toBeInTheDocument()
356
+ );
357
+
358
+ await user.click(rendered.getByText(/toggle-hidden-taxonomy/i));
359
+ await waitFor(() =>
360
+ expect(
361
+ rendered.getByText(/hidden:\{"taxonomy":\["10"\]\}/i)
362
+ ).toBeInTheDocument()
363
+ );
364
+
365
+ await user.click(rendered.getByText(/toggle-hidden-taxonomy/i));
366
+ await waitFor(() =>
367
+ expect(
368
+ rendered.getByText(/hidden:\{"taxonomy":\[\]\}/i)
369
+ ).toBeInTheDocument()
370
+ );
371
+
372
+ await user.click(rendered.getByText(/replace-hidden-array/i));
373
+ await waitFor(() =>
374
+ expect(
375
+ rendered.getByText(/hidden:\{"taxonomy":\["10","20"\]\}/i)
376
+ ).toBeInTheDocument()
377
+ );
378
+
379
+ await user.click(rendered.getByText(/remove-hidden-taxonomy/i));
380
+ await waitFor(() =>
381
+ expect(rendered.getByText(/hidden:\{\}/i)).toBeInTheDocument()
382
+ );
383
+
384
+ await waitFor(() => expect(onSearchChangeSpy).toHaveBeenCalled());
385
+ jest.useRealTimers();
386
+ });
387
+
388
+ it("handles sort selection transitions and setQuery score sort", async () => {
389
+ jest.useFakeTimers();
390
+ const user = userEvent.setup({
391
+ delay: null,
392
+ advanceTimers: jest.advanceTimersByTime,
393
+ });
394
+ const onSearchChangeSpy = jest.fn();
395
+ const rendered = render(
396
+ <SearchContextProvider {...searchProps}>
397
+ <ContextConsumer onSearchChangeSpy={onSearchChangeSpy} />
398
+ </SearchContextProvider>,
399
+ renderOpts
400
+ );
401
+
402
+ jest.runAllTimers();
403
+
404
+ await user.click(rendered.getByText(/set-callback/i));
405
+ await user.click(rendered.getByText(/sort-empty/i));
406
+ expect(rendered.getByText(/sort-column:name\.raw/i)).toBeInTheDocument();
407
+ expect(rendered.getByText(/sort-direction:ascending/i)).toBeInTheDocument();
408
+
409
+ await user.click(rendered.getByText(/sort-status/i));
410
+ await waitFor(() =>
411
+ expect(rendered.getByText(/sort-column:status/i)).toBeInTheDocument()
412
+ );
413
+ await waitFor(() =>
414
+ expect(
415
+ rendered.getByText(/sort-direction:ascending/i)
416
+ ).toBeInTheDocument()
417
+ );
418
+
419
+ await user.click(rendered.getByText(/sort-status/i));
420
+ await waitFor(() =>
421
+ expect(
422
+ rendered.getByText(/sort-direction:descending/i)
423
+ ).toBeInTheDocument()
424
+ );
425
+
426
+ await user.click(rendered.getByText(/set-query/i));
427
+ await waitFor(() =>
428
+ expect(rendered.getByText(/sort-column:_score/i)).toBeInTheDocument()
429
+ );
430
+ await waitFor(() =>
431
+ expect(
432
+ rendered.getByText(/sort-direction:descending/i)
433
+ ).toBeInTheDocument()
434
+ );
435
+ await waitFor(() =>
436
+ expect(rendered.getByText(/query:abc/i)).toBeInTheDocument()
437
+ );
438
+
439
+ await user.click(rendered.getByText(/clean-query/i));
440
+ await waitFor(() =>
441
+ expect(rendered.getByText(/sort-column:name\.raw/i)).toBeInTheDocument()
442
+ );
443
+ await waitFor(() =>
444
+ expect(
445
+ rendered.getByText(/sort-direction:ascending/i)
446
+ ).toBeInTheDocument()
447
+ );
448
+ await waitFor(() =>
449
+ expect(rendered.getByText(/^query:$/i)).toBeInTheDocument()
450
+ );
451
+ await waitFor(() => expect(onSearchChangeSpy).toHaveBeenCalled());
452
+ jest.useRealTimers();
453
+ });
105
454
  });