@blaze-cms/react-page-builder 0.131.1 → 0.132.0-admin-updates.1

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.
Files changed (231) hide show
  1. package/CHANGELOG.md +101 -2
  2. package/lib/components/Button.js +2 -1
  3. package/lib/components/Button.js.map +1 -1
  4. package/lib/components/Card/CardsRender.js +13 -6
  5. package/lib/components/Card/CardsRender.js.map +1 -1
  6. package/lib/components/Card/helpers/get-updated-items-to-display.js +55 -0
  7. package/lib/components/Card/helpers/get-updated-items-to-display.js.map +1 -0
  8. package/lib/components/Card/helpers/index.js +7 -0
  9. package/lib/components/Card/helpers/index.js.map +1 -1
  10. package/lib/components/CarouselWrapper/SmoothScrollCarousel.js +2 -2
  11. package/lib/components/CarouselWrapper/SmoothScrollCarousel.js.map +1 -1
  12. package/lib/components/Code/Code.js +7 -3
  13. package/lib/components/Code/Code.js.map +1 -1
  14. package/lib/components/DataSummary/helpers/build-loop-props-content.js +6 -3
  15. package/lib/components/DataSummary/helpers/build-loop-props-content.js.map +1 -1
  16. package/lib/components/DataSummary/helpers/build-props-to-display-with-content.js +3 -2
  17. package/lib/components/DataSummary/helpers/build-props-to-display-with-content.js.map +1 -1
  18. package/lib/components/DataSummary/helpers/get-link-to-published-content.js +2 -0
  19. package/lib/components/DataSummary/helpers/get-link-to-published-content.js.map +1 -1
  20. package/lib/components/List/ListFactory.js +16 -11
  21. package/lib/components/List/ListFactory.js.map +1 -1
  22. package/lib/components/List/helpers/get-list-query.js +43 -0
  23. package/lib/components/List/helpers/get-list-query.js.map +1 -0
  24. package/lib/components/List/helpers/get-list-specific-search-filter.js +21 -0
  25. package/lib/components/List/helpers/get-list-specific-search-filter.js.map +1 -0
  26. package/lib/components/List/helpers/index.js +14 -0
  27. package/lib/components/List/helpers/index.js.map +1 -1
  28. package/lib/components/List/helpers/strip-query-key.js +14 -0
  29. package/lib/components/List/helpers/strip-query-key.js.map +1 -0
  30. package/lib/components/SearchContent/SearchContent.js +8 -8
  31. package/lib/components/SearchContent/SearchContent.js.map +1 -1
  32. package/lib/components/SearchFilter/SearchFilter/SearchFilter.js +17 -13
  33. package/lib/components/SearchFilter/SearchFilter/SearchFilter.js.map +1 -1
  34. package/lib/components/SearchFilter/SearchFilterContainer.js +64 -83
  35. package/lib/components/SearchFilter/SearchFilterContainer.js.map +1 -1
  36. package/lib/components/SearchFilter/helpers/build-filters-query.js +3 -1
  37. package/lib/components/SearchFilter/helpers/build-filters-query.js.map +1 -1
  38. package/lib/components/SearchFilter/helpers/build-new-url.js +17 -0
  39. package/lib/components/SearchFilter/helpers/build-new-url.js.map +1 -0
  40. package/lib/components/SearchFilter/helpers/build-url-query.js +49 -0
  41. package/lib/components/SearchFilter/helpers/build-url-query.js.map +1 -0
  42. package/lib/components/SearchFilter/helpers/get-filter-ranges-and-checkboxes-values.js +32 -0
  43. package/lib/components/SearchFilter/helpers/get-filter-ranges-and-checkboxes-values.js.map +1 -0
  44. package/lib/components/SearchFilter/helpers/get-filter-values.js +31 -0
  45. package/lib/components/SearchFilter/helpers/get-filter-values.js.map +1 -0
  46. package/lib/components/SearchFilter/helpers/index.js +18 -11
  47. package/lib/components/SearchFilter/helpers/index.js.map +1 -1
  48. package/lib/components/SearchFilter/index.js.map +1 -1
  49. package/lib/components/SearchFilter/searchFilterReducer.js +43 -0
  50. package/lib/components/SearchFilter/searchFilterReducer.js.map +1 -0
  51. package/lib/components/SearchFilterSort/SearchFilterSort.js +31 -11
  52. package/lib/components/SearchFilterSort/SearchFilterSort.js.map +1 -1
  53. package/lib/components/SearchFilterSort/helpers/handle-sort-update.js +19 -5
  54. package/lib/components/SearchFilterSort/helpers/handle-sort-update.js.map +1 -1
  55. package/lib/components/SearchFilterSort/helpers/update-sort.js +9 -5
  56. package/lib/components/SearchFilterSort/helpers/update-sort.js.map +1 -1
  57. package/lib/helpers/build-inherited-filters.js +2 -0
  58. package/lib/helpers/build-inherited-filters.js.map +1 -1
  59. package/lib/helpers/build-props-query.js +5 -2
  60. package/lib/helpers/build-props-query.js.map +1 -1
  61. package/lib/helpers/build-query-key.js +16 -0
  62. package/lib/helpers/build-query-key.js.map +1 -0
  63. package/lib/helpers/build-set-filters.js +5 -1
  64. package/lib/helpers/build-set-filters.js.map +1 -1
  65. package/lib/helpers/get-entities-with-banner.js +8 -2
  66. package/lib/helpers/get-entities-with-banner.js.map +1 -1
  67. package/lib/helpers/get-query-filters.js +6 -8
  68. package/lib/helpers/get-query-filters.js.map +1 -1
  69. package/lib/helpers/get-query-props.js +13 -2
  70. package/lib/helpers/get-query-props.js.map +1 -1
  71. package/lib/helpers/get-wrapped-value-with-link.js +2 -3
  72. package/lib/helpers/get-wrapped-value-with-link.js.map +1 -1
  73. package/lib/helpers/index.js +14 -0
  74. package/lib/helpers/index.js.map +1 -1
  75. package/lib/helpers/parse-props-to-display.js +13 -8
  76. package/lib/helpers/parse-props-to-display.js.map +1 -1
  77. package/lib/helpers/process-data-summary-value.js +56 -0
  78. package/lib/helpers/process-data-summary-value.js.map +1 -0
  79. package/lib-es/components/Button.js +2 -1
  80. package/lib-es/components/Button.js.map +1 -1
  81. package/lib-es/components/Card/CardsRender.js +14 -7
  82. package/lib-es/components/Card/CardsRender.js.map +1 -1
  83. package/lib-es/components/Card/helpers/get-updated-items-to-display.js +32 -0
  84. package/lib-es/components/Card/helpers/get-updated-items-to-display.js.map +1 -0
  85. package/lib-es/components/Card/helpers/index.js +1 -0
  86. package/lib-es/components/Card/helpers/index.js.map +1 -1
  87. package/lib-es/components/CarouselWrapper/SmoothScrollCarousel.js +2 -2
  88. package/lib-es/components/CarouselWrapper/SmoothScrollCarousel.js.map +1 -1
  89. package/lib-es/components/Code/Code.js +8 -4
  90. package/lib-es/components/Code/Code.js.map +1 -1
  91. package/lib-es/components/DataSummary/helpers/build-loop-props-content.js +6 -3
  92. package/lib-es/components/DataSummary/helpers/build-loop-props-content.js.map +1 -1
  93. package/lib-es/components/DataSummary/helpers/build-props-to-display-with-content.js +4 -3
  94. package/lib-es/components/DataSummary/helpers/build-props-to-display-with-content.js.map +1 -1
  95. package/lib-es/components/DataSummary/helpers/get-link-to-published-content.js +2 -1
  96. package/lib-es/components/DataSummary/helpers/get-link-to-published-content.js.map +1 -1
  97. package/lib-es/components/List/ListFactory.js +16 -12
  98. package/lib-es/components/List/ListFactory.js.map +1 -1
  99. package/lib-es/components/List/helpers/get-list-query.js +24 -0
  100. package/lib-es/components/List/helpers/get-list-query.js.map +1 -0
  101. package/lib-es/components/List/helpers/get-list-specific-search-filter.js +12 -0
  102. package/lib-es/components/List/helpers/get-list-specific-search-filter.js.map +1 -0
  103. package/lib-es/components/List/helpers/index.js +2 -0
  104. package/lib-es/components/List/helpers/index.js.map +1 -1
  105. package/lib-es/components/List/helpers/strip-query-key.js +6 -0
  106. package/lib-es/components/List/helpers/strip-query-key.js.map +1 -0
  107. package/lib-es/components/SearchContent/SearchContent.js +8 -8
  108. package/lib-es/components/SearchContent/SearchContent.js.map +1 -1
  109. package/lib-es/components/SearchFilter/SearchFilter/SearchFilter.js +18 -13
  110. package/lib-es/components/SearchFilter/SearchFilter/SearchFilter.js.map +1 -1
  111. package/lib-es/components/SearchFilter/SearchFilterContainer.js +48 -70
  112. package/lib-es/components/SearchFilter/SearchFilterContainer.js.map +1 -1
  113. package/lib-es/components/SearchFilter/helpers/build-filters-query.js +2 -0
  114. package/lib-es/components/SearchFilter/helpers/build-filters-query.js.map +1 -1
  115. package/lib-es/components/SearchFilter/helpers/build-new-url.js +8 -0
  116. package/lib-es/components/SearchFilter/helpers/build-new-url.js.map +1 -0
  117. package/lib-es/components/SearchFilter/helpers/build-url-query.js +31 -0
  118. package/lib-es/components/SearchFilter/helpers/build-url-query.js.map +1 -0
  119. package/lib-es/components/SearchFilter/helpers/get-filter-ranges-and-checkboxes-values.js +18 -0
  120. package/lib-es/components/SearchFilter/helpers/get-filter-ranges-and-checkboxes-values.js.map +1 -0
  121. package/lib-es/components/SearchFilter/helpers/get-filter-values.js +19 -0
  122. package/lib-es/components/SearchFilter/helpers/get-filter-values.js.map +1 -0
  123. package/lib-es/components/SearchFilter/helpers/index.js +5 -4
  124. package/lib-es/components/SearchFilter/helpers/index.js.map +1 -1
  125. package/lib-es/components/SearchFilter/index.js +2 -2
  126. package/lib-es/components/SearchFilter/index.js.map +1 -1
  127. package/lib-es/components/SearchFilter/searchFilterReducer.js +26 -0
  128. package/lib-es/components/SearchFilter/searchFilterReducer.js.map +1 -0
  129. package/lib-es/components/SearchFilterSort/SearchFilterSort.js +26 -10
  130. package/lib-es/components/SearchFilterSort/SearchFilterSort.js.map +1 -1
  131. package/lib-es/components/SearchFilterSort/helpers/handle-sort-update.js +21 -4
  132. package/lib-es/components/SearchFilterSort/helpers/handle-sort-update.js.map +1 -1
  133. package/lib-es/components/SearchFilterSort/helpers/update-sort.js +8 -4
  134. package/lib-es/components/SearchFilterSort/helpers/update-sort.js.map +1 -1
  135. package/lib-es/helpers/build-inherited-filters.js +2 -0
  136. package/lib-es/helpers/build-inherited-filters.js.map +1 -1
  137. package/lib-es/helpers/build-props-query.js +4 -2
  138. package/lib-es/helpers/build-props-query.js.map +1 -1
  139. package/lib-es/helpers/build-query-key.js +7 -0
  140. package/lib-es/helpers/build-query-key.js.map +1 -0
  141. package/lib-es/helpers/build-set-filters.js +5 -1
  142. package/lib-es/helpers/build-set-filters.js.map +1 -1
  143. package/lib-es/helpers/get-entities-with-banner.js +9 -2
  144. package/lib-es/helpers/get-entities-with-banner.js.map +1 -1
  145. package/lib-es/helpers/get-query-filters.js +6 -8
  146. package/lib-es/helpers/get-query-filters.js.map +1 -1
  147. package/lib-es/helpers/get-query-props.js +10 -2
  148. package/lib-es/helpers/get-query-props.js.map +1 -1
  149. package/lib-es/helpers/get-wrapped-value-with-link.js +2 -2
  150. package/lib-es/helpers/get-wrapped-value-with-link.js.map +1 -1
  151. package/lib-es/helpers/index.js +2 -0
  152. package/lib-es/helpers/index.js.map +1 -1
  153. package/lib-es/helpers/parse-props-to-display.js +12 -9
  154. package/lib-es/helpers/parse-props-to-display.js.map +1 -1
  155. package/lib-es/helpers/process-data-summary-value.js +36 -0
  156. package/lib-es/helpers/process-data-summary-value.js.map +1 -0
  157. package/package.json +10 -10
  158. package/src/components/Button.js +2 -1
  159. package/src/components/Card/CardsRender.js +27 -7
  160. package/src/components/Card/helpers/get-updated-items-to-display.js +32 -0
  161. package/src/components/Card/helpers/index.js +1 -0
  162. package/src/components/CarouselWrapper/SmoothScrollCarousel.js +2 -2
  163. package/src/components/Code/Code.js +7 -3
  164. package/src/components/DataSummary/helpers/build-loop-props-content.js +7 -3
  165. package/src/components/DataSummary/helpers/build-props-to-display-with-content.js +6 -3
  166. package/src/components/DataSummary/helpers/get-link-to-published-content.js +4 -1
  167. package/src/components/List/ListFactory.js +25 -15
  168. package/src/components/List/helpers/get-list-query.js +28 -0
  169. package/src/components/List/helpers/get-list-specific-search-filter.js +10 -0
  170. package/src/components/List/helpers/index.js +2 -0
  171. package/src/components/List/helpers/strip-query-key.js +6 -0
  172. package/src/components/SearchContent/SearchContent.js +8 -8
  173. package/src/components/SearchFilter/SearchFilter/SearchFilter.js +21 -18
  174. package/src/components/SearchFilter/SearchFilterContainer.js +59 -67
  175. package/src/components/SearchFilter/helpers/build-filters-query.js +9 -1
  176. package/src/components/SearchFilter/helpers/build-new-url.js +8 -0
  177. package/src/components/SearchFilter/helpers/build-url-query.js +33 -0
  178. package/src/components/SearchFilter/helpers/get-filter-ranges-and-checkboxes-values.js +17 -0
  179. package/src/components/SearchFilter/helpers/get-filter-values.js +21 -0
  180. package/src/components/SearchFilter/helpers/index.js +9 -7
  181. package/src/components/SearchFilter/index.js +2 -2
  182. package/src/components/SearchFilter/searchFilterReducer.js +15 -0
  183. package/src/components/SearchFilterSort/SearchFilterSort.js +18 -8
  184. package/src/components/SearchFilterSort/helpers/handle-sort-update.js +14 -5
  185. package/src/components/SearchFilterSort/helpers/update-sort.js +7 -3
  186. package/src/helpers/build-inherited-filters.js +3 -1
  187. package/src/helpers/build-props-query.js +4 -2
  188. package/src/helpers/build-query-key.js +7 -0
  189. package/src/helpers/build-set-filters.js +2 -1
  190. package/src/helpers/get-entities-with-banner.js +3 -2
  191. package/src/helpers/get-query-filters.js +4 -4
  192. package/src/helpers/get-query-props.js +12 -4
  193. package/src/helpers/get-wrapped-value-with-link.js +6 -3
  194. package/src/helpers/index.js +2 -0
  195. package/src/helpers/parse-props-to-display.js +25 -21
  196. package/src/helpers/process-data-summary-value.js +22 -0
  197. package/tests/helpers/mocks.js +3 -5
  198. package/tests/unit/src/components/Card/helpers/get-updated-items-to-display.test.js +72 -0
  199. package/tests/unit/src/components/Code/Code.test.js +5 -0
  200. package/tests/unit/src/components/Code/__snapshots__/Code.test.js.snap +8 -0
  201. package/tests/unit/src/components/DataSummary/helpers/build-loop-props-content.test.js +50 -0
  202. package/tests/unit/src/components/DataSummary/helpers/get-link-to-published-content.test.js +21 -0
  203. package/tests/unit/src/components/PlaceholderIcon/__snapshots__/index.test.js.snap +72 -0
  204. package/tests/unit/src/components/PlaceholderIcon/index.test.js +20 -0
  205. package/tests/unit/src/components/SearchFilter/__snapshots__/SearchFilterContainer.test.js.snap +1 -7
  206. package/tests/unit/src/components/SearchFilter/helpers/build-new-query.test.js +10 -10
  207. package/tests/unit/src/components/SearchFilter/helpers/build-query.test.js +24 -7
  208. package/tests/unit/src/components/SearchFilter/helpers/get-initial-filter-values.test.js +10 -10
  209. package/tests/unit/src/components/SearchFilterSort/SearchFilterSort.test.js +4 -1
  210. package/tests/unit/src/components/SearchFilterSort/helpers/handle-sort-update.test.js +26 -19
  211. package/tests/unit/src/components/SearchFilterSort/helpers/update-sort.test.js +10 -1
  212. package/tests/unit/src/helpers/__snapshots__/get-wrapped-value-with-link.test.js.snap +18 -0
  213. package/tests/unit/src/helpers/build-props-query.test.js +25 -0
  214. package/tests/unit/src/helpers/get-wrapped-value-with-link.test.js +2 -2
  215. package/tests/unit/src/helpers/parse-props-to-display.test.js +4 -0
  216. package/tests/unit/src/helpers/prcoess-data-summary-value.test.js +52 -0
  217. package/lib/components/SearchFilter/helpers/build-new-query.js +0 -15
  218. package/lib/components/SearchFilter/helpers/build-new-query.js.map +0 -1
  219. package/lib/components/SearchFilter/helpers/build-query.js +0 -37
  220. package/lib/components/SearchFilter/helpers/build-query.js.map +0 -1
  221. package/lib/components/SearchFilter/helpers/get-initial-filter-values.js +0 -43
  222. package/lib/components/SearchFilter/helpers/get-initial-filter-values.js.map +0 -1
  223. package/lib-es/components/SearchFilter/helpers/build-new-query.js +0 -6
  224. package/lib-es/components/SearchFilter/helpers/build-new-query.js.map +0 -1
  225. package/lib-es/components/SearchFilter/helpers/build-query.js +0 -23
  226. package/lib-es/components/SearchFilter/helpers/build-query.js.map +0 -1
  227. package/lib-es/components/SearchFilter/helpers/get-initial-filter-values.js +0 -26
  228. package/lib-es/components/SearchFilter/helpers/get-initial-filter-values.js.map +0 -1
  229. package/src/components/SearchFilter/helpers/build-new-query.js +0 -6
  230. package/src/components/SearchFilter/helpers/build-query.js +0 -26
  231. package/src/components/SearchFilter/helpers/get-initial-filter-values.js +0 -30
@@ -2,33 +2,37 @@ import React, { useState, useRef, useReducer, useEffect } from 'react';
2
2
  import { useRouter } from 'next/router';
3
3
  import { useQuery } from '@apollo/client';
4
4
  import PropTypes from 'prop-types';
5
- import { parseUrl, stringify } from 'query-string';
5
+ import { parseUrl } from 'query-string';
6
6
  import SearchFilter from './SearchFilter';
7
7
  import { withTitle } from '../../HOC';
8
8
  import { getSearchPublishedContent } from '../../application/query';
9
9
  import {
10
- buildNewQuery,
10
+ buildNewUrl,
11
11
  buildRawQueryStringified,
12
- getInitialFilterValues,
13
- buildFiltersQuery
12
+ getFilterValues,
13
+ buildFiltersQuery,
14
+ getFilterRangesAndCheckboxesValues
14
15
  } from './helpers';
15
16
  import { getEntityData } from '../../helpers';
16
- import { RAW_RESULTS, RANGE, CHECKBOX_SELECT } from './constants';
17
+ import { RAW_RESULTS } from './constants';
17
18
  import { SCROLL_OFFSET } from '../../constants';
19
+ import searchFilterReducer from './searchFilterReducer';
20
+
21
+ const getFiltersUrlQuery = (query, listName) => {
22
+ if (!listName) return query;
23
+ const updatedQuery = {};
24
+ const listKey = `pb[${listName}]`;
25
+ Object.keys(query).forEach(queryKey => {
26
+ const isListKey = queryKey.indexOf(`${listName}`) !== -1;
27
+ if (!isListKey) return;
28
+ const decodedQueryValue = Array.isArray(query[queryKey])
29
+ ? query[queryKey].map(value => decodeURIComponent(value))
30
+ : decodeURIComponent(query[queryKey]);
31
+ const strippedKey = queryKey.substring(listKey.length + 1, queryKey.length - 1);
32
+ if (isListKey) updatedQuery[strippedKey] = decodedQueryValue;
33
+ });
18
34
 
19
- const reducer = (state, action) => {
20
- const { newValues, type, shouldSearch = true } = action;
21
-
22
- switch (type) {
23
- case 'update':
24
- return { ...state, ...newValues, shouldSearch };
25
- case 'resetSearch':
26
- return { ...state, shouldSearch: false };
27
- case 'reset':
28
- return { ...newValues, shouldSearch: false };
29
- default:
30
- throw new Error();
31
- }
35
+ return updatedQuery;
32
36
  };
33
37
 
34
38
  const SearchFilterContainer = ({
@@ -41,7 +45,8 @@ const SearchFilterContainer = ({
41
45
  groupAfterDesktop,
42
46
  filterBy,
43
47
  filterByProperty,
44
- shouldAddFilters
48
+ shouldAddFilters,
49
+ listComponentName
45
50
  }) => {
46
51
  const router = useRouter();
47
52
  const { asPath } = router;
@@ -49,15 +54,10 @@ const SearchFilterContainer = ({
49
54
  const [key, setKey] = useState(`filter-${name}`);
50
55
  const [displaySearchFilter, setDisplaySearchFilter] = useState(false);
51
56
  const [urlPath, setUrlPath] = useState(asPath); // used as asPath can take too long to update
52
- const {
53
- url: currentUrl,
54
- query: { sort, sortby },
55
- query
56
- } = parseUrl(urlPath);
57
- const [filterValues, dispatch] = useReducer(
58
- reducer,
59
- getInitialFilterValues(null, filters, query)
60
- );
57
+ const { url: currentUrl, query } = parseUrl(urlPath);
58
+ const updatedQuery = getFiltersUrlQuery(query, listComponentName);
59
+
60
+ const [filterValues, dispatch] = useReducer(searchFilterReducer, null);
61
61
 
62
62
  useEffect(
63
63
  () => {
@@ -66,34 +66,19 @@ const SearchFilterContainer = ({
66
66
  [asPath]
67
67
  );
68
68
 
69
- const hasUrl = !!url;
70
-
71
- const sortValues = sort && sortby ? stringify({ sort, sortby }) : '';
72
- const baseQuery = sortValues ? `${currentUrl}?${sortValues}` : currentUrl;
73
-
74
69
  const action = getSearchPublishedContent(RAW_RESULTS);
75
- const checkboxSelectValues = [];
76
- const rangeValues = [];
77
-
78
- filters.forEach(({ type, propsToDisplay }) => {
79
- if (CHECKBOX_SELECT.includes(type)) {
80
- checkboxSelectValues.push(propsToDisplay[0]);
81
- } else if (type === RANGE) {
82
- rangeValues.push(...propsToDisplay);
83
- }
84
- });
70
+ const [checkboxSelectValues, rangeValues] = getFilterRangesAndCheckboxesValues(filters);
85
71
 
86
72
  const { docType } = getEntityData(entity);
87
73
 
88
- const filtersQuery = shouldAddFilters
89
- ? buildFiltersQuery({
90
- query: filterValues,
91
- filterBy,
92
- filterByProperty,
93
- rangeValues,
94
- queryKeys: Object.keys(query)
95
- })
96
- : [];
74
+ const filtersQuery = buildFiltersQuery({
75
+ shouldAddFilters,
76
+ query: filterValues || {},
77
+ filterBy,
78
+ filterByProperty,
79
+ rangeValues,
80
+ queryKeys: Object.keys(updatedQuery)
81
+ });
97
82
 
98
83
  const rawQueryStringified = buildRawQueryStringified(
99
84
  checkboxSelectValues,
@@ -102,7 +87,7 @@ const SearchFilterContainer = ({
102
87
  filtersQuery
103
88
  );
104
89
 
105
- const { data, error } = useQuery(action, {
90
+ const { data, error, loading } = useQuery(action, {
106
91
  variables: { rawQueryStringified, limit: 0 }, // we only want aggs so limit=0 for no search results
107
92
  skip: !rawQueryStringified
108
93
  });
@@ -113,30 +98,32 @@ const SearchFilterContainer = ({
113
98
  const { searchPublishedContent: { rawResults: { aggregations: filterData } = {} } = {} } =
114
99
  data || {};
115
100
 
116
- if (filterValues.dataNotSet && filterData) {
117
- const initialFilterValues = getInitialFilterValues(filterData, filters, query);
118
- dispatch({ newValues: initialFilterValues, shouldSearch: false, type: 'update' });
101
+ if (!filterValues && !loading && (filterData || !rawQueryStringified)) {
102
+ const newValues = getFilterValues(filterData, filters, updatedQuery);
103
+ dispatch({ newValues, shouldSearch: false, type: 'update' });
104
+ return null;
119
105
  }
120
106
 
121
107
  const handleSearch = newQuery => {
122
108
  setDisplaySearchFilter(false);
123
-
109
+ const hashBit = asPath.split('#')[1] || '';
110
+ const parsedHashBit = hashBit ? `#${hashBit}` : '';
111
+ scrollToFirstList();
124
112
  if (!newQuery) {
125
- scrollToFirstList();
113
+ const baseQuery = `${currentUrl}${parsedHashBit}`;
126
114
  setUrlPath(baseQuery);
127
- return router.push('/Resolver', baseQuery, { shallow: !hasUrl, scroll: false }).then(() => {
115
+ return router.push('/Resolver', baseQuery, { shallow: !url, scroll: false }).then(() => {
128
116
  setKey(`filter-${name}:${Date.now()}`); // remove after range component update
129
117
  });
130
118
  }
131
- const newUrl = buildNewQuery(url, currentUrl, newQuery, sortValues);
132
- scrollToFirstList();
119
+ const newUrl = buildNewUrl(url, currentUrl, newQuery, hashBit);
133
120
  setUrlPath(newUrl);
134
- return router.push('/Resolver', newUrl, { shallow: !hasUrl, scroll: false });
121
+ return router.push('/Resolver', newUrl, { shallow: !url, scroll: false });
135
122
  };
136
123
 
137
124
  const scrollToFirstList = () => {
138
125
  const [list] = document.getElementsByClassName('list-top');
139
- const shouldScrollToFirstList = !hasUrl && list;
126
+ const shouldScrollToFirstList = !url && list;
140
127
 
141
128
  if (shouldScrollToFirstList) {
142
129
  window.scrollTo({
@@ -147,16 +134,19 @@ const SearchFilterContainer = ({
147
134
  window.scrollTo(0, 0);
148
135
  }
149
136
  };
150
- if (!filterValues) return '';
137
+
138
+ if (!filterValues) return null;
151
139
 
152
140
  return (
153
141
  <SearchFilter
154
142
  key={key}
155
143
  setAppliedFilters={dispatch}
144
+ query={updatedQuery}
145
+ listComponentName={listComponentName}
156
146
  searchFilterRef={searchFilterRef}
157
147
  data={filterData}
158
148
  filters={filters}
159
- hasUrl={hasUrl}
149
+ hasUrl={!!url}
160
150
  entity={entity}
161
151
  handleSearch={handleSearch}
162
152
  filterValues={filterValues}
@@ -180,7 +170,8 @@ SearchFilterContainer.propTypes = {
180
170
  groupAfterDesktop: PropTypes.number,
181
171
  filterBy: PropTypes.array,
182
172
  filterByProperty: PropTypes.array,
183
- shouldAddFilters: PropTypes.bool
173
+ shouldAddFilters: PropTypes.bool,
174
+ listComponentName: PropTypes.string
184
175
  };
185
176
 
186
177
  SearchFilterContainer.defaultProps = {
@@ -192,7 +183,8 @@ SearchFilterContainer.defaultProps = {
192
183
  groupAfterDesktop: 0,
193
184
  filterBy: [],
194
185
  filterByProperty: [],
195
- shouldAddFilters: false
186
+ shouldAddFilters: false,
187
+ listComponentName: ''
196
188
  };
197
189
 
198
190
  export default withTitle(SearchFilterContainer);
@@ -29,7 +29,15 @@ const builFilterObject = ({ queryKey, value, filters, isRange, isInQuery }) => {
29
29
  filters.push({ match: { [key]: filterValue } });
30
30
  });
31
31
  };
32
- const buildFiltersQuery = ({ query, filterBy, filterByProperty, rangeValues, queryKeys }) => {
32
+ const buildFiltersQuery = ({
33
+ shouldAddFilters,
34
+ query,
35
+ filterBy,
36
+ filterByProperty,
37
+ rangeValues,
38
+ queryKeys
39
+ }) => {
40
+ if (!shouldAddFilters) return [];
33
41
  const mustFilters = [];
34
42
  Object.keys(query).forEach(queryKey => {
35
43
  if (QUERY_KEYS_TO_IGNORE.includes(queryKey)) return;
@@ -0,0 +1,8 @@
1
+ const buildNewUrl = (url, currentUrl, queryParams, hashBit) => {
2
+ const encodedQuery = encodeURI(queryParams);
3
+ const baseUrl = url || currentUrl;
4
+ const newUrl = encodedQuery ? `${baseUrl}?${encodedQuery}` : `${baseUrl}`;
5
+ return hashBit ? `${newUrl}#${hashBit}` : newUrl;
6
+ };
7
+
8
+ export default buildNewUrl;
@@ -0,0 +1,33 @@
1
+ import { SEARCH_TERM, TEXT_SEARCH } from '../constants';
2
+ import parseFilterValue from './parse-filter-value';
3
+ import { buildQueryKey } from '../../../helpers';
4
+
5
+ const buildUrlQuery = (filterValues, filters, listComponentName = '', query = {}) => {
6
+ if (!filterValues) return '';
7
+
8
+ const newQuery = [];
9
+ const queryKeys = [];
10
+ const searchValue = filterValues[SEARCH_TERM];
11
+ const searchKey = buildQueryKey(SEARCH_TERM, listComponentName);
12
+ queryKeys.push(searchKey);
13
+ if (searchValue) newQuery.push(`${searchKey}=${searchValue}`);
14
+
15
+ filters.forEach(({ propsToDisplay, type }) => {
16
+ if (type === TEXT_SEARCH) return;
17
+ propsToDisplay.forEach(prop => {
18
+ const value = filterValues[prop];
19
+ const filterKey = buildQueryKey(prop, listComponentName);
20
+ queryKeys.push(filterKey);
21
+ const parsedValue = parseFilterValue(filterKey, type, value, newQuery);
22
+ if (parsedValue && !newQuery.includes(parsedValue)) newQuery.push(parsedValue);
23
+ });
24
+ });
25
+
26
+ Object.keys(query).forEach(key => {
27
+ if (queryKeys.indexOf(key) === -1) newQuery.push(`${key}=${query[key]}`);
28
+ });
29
+
30
+ return newQuery.join('&');
31
+ };
32
+
33
+ export default buildUrlQuery;
@@ -0,0 +1,17 @@
1
+ import { RANGE, CHECKBOX_SELECT } from '../constants';
2
+
3
+ const getFilterRangesAndCheckboxesValues = filters => {
4
+ const checkboxSelectValues = [];
5
+ const rangeValues = [];
6
+
7
+ filters.forEach(({ type, propsToDisplay }) => {
8
+ if (CHECKBOX_SELECT.includes(type)) {
9
+ checkboxSelectValues.push(propsToDisplay[0]);
10
+ } else if (type === RANGE) {
11
+ rangeValues.push(...propsToDisplay);
12
+ }
13
+ });
14
+ return [checkboxSelectValues, rangeValues];
15
+ };
16
+
17
+ export default getFilterRangesAndCheckboxesValues;
@@ -0,0 +1,21 @@
1
+ import { SEARCH_TERM, TEXT_SEARCH } from '../constants';
2
+ import getFilterValueFromQuery from './get-filter-value-from-query';
3
+
4
+ const getFilterValues = (filterData, filters, query) => {
5
+ const filterValues = {};
6
+ filterValues[SEARCH_TERM] = query[SEARCH_TERM] || '';
7
+
8
+ filters.forEach(filterProps => {
9
+ const {
10
+ propsToDisplay: [key],
11
+ type
12
+ } = filterProps;
13
+ if (type === TEXT_SEARCH) return;
14
+ const data = filterData && filterData[key] ? filterData[key] : {};
15
+ filterValues[key] = getFilterValueFromQuery(key, filterProps, data, query);
16
+ });
17
+ filterValues.dataNotSet = !filterData;
18
+ return filterValues;
19
+ };
20
+
21
+ export default getFilterValues;
@@ -1,4 +1,4 @@
1
- import buildNewQuery from './build-new-query';
1
+ import buildNewUrl from './build-new-url';
2
2
  import buildRawQueryStringified from './build-raw-query-stringified';
3
3
  import { decodeValue, encodeValue } from './decode-encode';
4
4
  import checkIfRangeUpdated from './check-if-range-updated';
@@ -10,14 +10,15 @@ import getIntersectedProp from './get-intersected-prop';
10
10
  import getRangeValue from './get-range-value';
11
11
  import getSelectOptions from './get-select-options';
12
12
  import isDeviceDesktop from './is-device-desktop';
13
- import getInitialFilterValues from './get-initial-filter-values';
13
+ import getFilterValues from './get-filter-values';
14
14
  import getFilterValueFromQuery from './get-filter-value-from-query';
15
- import buildQuery from './build-query';
15
+ import buildUrlQuery from './build-url-query';
16
16
  import buildFiltersQuery from './build-filters-query';
17
+ import getFilterRangesAndCheckboxesValues from './get-filter-ranges-and-checkboxes-values';
17
18
 
18
19
  export {
19
- buildQuery,
20
- buildNewQuery,
20
+ buildUrlQuery,
21
+ buildNewUrl,
21
22
  buildRawQueryStringified,
22
23
  decodeValue,
23
24
  encodeValue,
@@ -30,7 +31,8 @@ export {
30
31
  getRangeValue,
31
32
  getSelectOptions,
32
33
  isDeviceDesktop,
33
- getInitialFilterValues,
34
+ getFilterValues,
34
35
  getFilterValueFromQuery,
35
- buildFiltersQuery
36
+ buildFiltersQuery,
37
+ getFilterRangesAndCheckboxesValues
36
38
  };
@@ -1,3 +1,3 @@
1
- import SearchFilterContainer from './SearchFilterContainer';
1
+ import SearchFilter from './SearchFilterContainer';
2
2
 
3
- export default SearchFilterContainer;
3
+ export default SearchFilter;
@@ -0,0 +1,15 @@
1
+ const searchFilterReducer = (state, action) => {
2
+ const { newValues, type, shouldSearch = true } = action;
3
+ switch (type) {
4
+ case 'update':
5
+ return { ...state, ...newValues, shouldSearch };
6
+ case 'resetSearch':
7
+ return { ...state, shouldSearch: false };
8
+ case 'reset':
9
+ return null;
10
+ default:
11
+ throw new Error();
12
+ }
13
+ };
14
+
15
+ export default searchFilterReducer;
@@ -1,15 +1,23 @@
1
1
  import React from 'react';
2
2
  import Select from '@blaze-react/select';
3
- import { withRouter } from 'next/router';
3
+ import { useRouter } from 'next/router';
4
4
  import { parseUrl } from 'query-string';
5
5
  import PropTypes from 'prop-types';
6
6
  import { DEFAULT_SORT } from './constants';
7
7
  import { handleSortUpdate } from './helpers';
8
8
  import { withTitle } from '../../HOC';
9
9
 
10
- const SearchFilterSort = ({ router, filters, elementLabel, selectLabel, id }) => {
10
+ const getSortFromQuery = listComponentName => {
11
+ const listNameKey = listComponentName ? `pb[${listComponentName}]` : '';
12
+ const sortKey = `${listNameKey}${listNameKey ? '[sort]' : 'sort'}`;
13
+ const sortByKey = `${listNameKey}${listNameKey ? '[sortby]' : 'sortby'}`;
14
+ return [sortKey, sortByKey];
15
+ };
16
+ const SearchFilterSort = ({ filters, elementLabel, selectLabel, id, listComponentName }) => {
17
+ const router = useRouter();
11
18
  const { asPath } = router;
12
- const { url, query, query: { sort, sortby } = {} } = parseUrl(asPath);
19
+ const [sortKey, sortByKey] = getSortFromQuery(listComponentName);
20
+ const { url, query, query: { [sortKey]: sort, [sortByKey]: sortby } = {} } = parseUrl(asPath);
13
21
  const selectedValue = `${sortby}:${sort}` || '';
14
22
  const defaultTextValue = selectLabel || DEFAULT_SORT;
15
23
  const options = filters.map(({ label: optionLabel, propsToDisplay, sort: sortValue }) => [
@@ -17,7 +25,8 @@ const SearchFilterSort = ({ router, filters, elementLabel, selectLabel, id }) =>
17
25
  optionLabel
18
26
  ]);
19
27
 
20
- const setSortValue = ({ value }) => handleSortUpdate(router, value, query, url, selectLabel);
28
+ const setSortValue = ({ value }) =>
29
+ handleSortUpdate({ router, value, query, url, selectLabel, listComponentName });
21
30
 
22
31
  return (
23
32
  <div className="sort-by">
@@ -34,18 +43,19 @@ const SearchFilterSort = ({ router, filters, elementLabel, selectLabel, id }) =>
34
43
  };
35
44
 
36
45
  SearchFilterSort.propTypes = {
37
- router: PropTypes.object.isRequired,
38
46
  filters: PropTypes.array,
39
47
  elementLabel: PropTypes.string,
40
48
  selectLabel: PropTypes.string,
41
- id: PropTypes.string
49
+ id: PropTypes.string,
50
+ listComponentName: PropTypes.string
42
51
  };
43
52
 
44
53
  SearchFilterSort.defaultProps = {
45
54
  filters: [],
46
55
  elementLabel: '',
47
56
  selectLabel: '',
48
- id: ''
57
+ id: '',
58
+ listComponentName: ''
49
59
  };
50
60
 
51
- export default withRouter(withTitle(SearchFilterSort));
61
+ export default withTitle(SearchFilterSort);
@@ -2,21 +2,30 @@ import { stringify } from 'query-string';
2
2
  import { DEFAULT_SORT, SORT, SORTBY, PAGINATION_QUERY } from '../constants';
3
3
  import updateSort from './update-sort';
4
4
 
5
- const handleSortUpdate = (router, value, query, url, selectLabel) => {
5
+ const handleSortUpdate = ({ router, value, query, url, selectLabel, listComponentName }) => {
6
6
  const queryWithoutSort = {};
7
7
  const routerOptions = { shallow: true, scroll: false };
8
+ const { asPath } = router;
9
+ const hashBit = asPath.split('#')[1];
10
+ const parsedHash = hashBit ? `#${hashBit}` : '';
11
+ const listNameKey = listComponentName ? `pb[${listComponentName}]` : '';
12
+ const sortString = listNameKey ? `[${SORT}]` : SORT;
13
+ const sortByString = listNameKey ? `[${SORTBY}]` : SORTBY;
14
+ const sortKey = `${listNameKey}${sortString}`;
15
+ const sortByKey = `${listNameKey}${sortByString}`;
8
16
  Object.keys(query).forEach(key => {
9
17
  if (key.indexOf(PAGINATION_QUERY) !== -1) return;
10
- if (key !== SORTBY && key !== SORT) queryWithoutSort[key] = query[key];
18
+ if (key !== sortByKey && key !== sortKey) queryWithoutSort[key] = query[key];
11
19
  });
12
20
  if (value === DEFAULT_SORT || value === selectLabel) {
13
21
  const stringifiedQuery = stringify(queryWithoutSort);
14
22
 
15
23
  return stringifiedQuery
16
- ? router.push(`/Resolver`, `${url}?${stringifiedQuery}`, routerOptions)
17
- : router.push(`/Resolver`, url, routerOptions);
24
+ ? router.push(`/Resolver`, `${url}?${stringifiedQuery}${parsedHash}`, routerOptions)
25
+ : router.push(`/Resolver`, `${url}${parsedHash}`, routerOptions);
18
26
  }
19
- const updatedSortUrl = updateSort(value, queryWithoutSort, url);
27
+
28
+ const updatedSortUrl = updateSort(value, queryWithoutSort, url, parsedHash, listComponentName);
20
29
  return router.push('/Resolver', updatedSortUrl, routerOptions);
21
30
  };
22
31
 
@@ -1,10 +1,14 @@
1
1
  import { stringify } from 'query-string';
2
+ import { buildQueryKey } from '../../../helpers';
3
+ import { SORT, SORTBY } from '../constants';
2
4
 
3
- const updateSort = (value, query, url) => {
5
+ const updateSort = (value, query, url, hashBit = '', listComponentName = '') => {
6
+ const sortKey = buildQueryKey(SORT, listComponentName);
7
+ const sortByKey = buildQueryKey(SORTBY, listComponentName);
4
8
  const [newSortBy, newSort] = value.split(':');
5
- const updatedQuery = { ...query, sortby: newSortBy, sort: newSort };
9
+ const updatedQuery = { ...query, [sortByKey]: newSortBy, [sortKey]: newSort };
6
10
  const parsedQuery = stringify(updatedQuery);
7
- return `${url}?${parsedQuery}`;
11
+ return `${url}?${parsedQuery}${hashBit}`;
8
12
  };
9
13
 
10
14
  export default updateSort;
@@ -1,6 +1,7 @@
1
1
  import flatten from 'lodash.flatten';
2
2
  import isFilterEntitysId from './is-filter-entitys-id';
3
3
  import getFilterProps from './get-filter-props';
4
+ import { ID } from '../constants';
4
5
 
5
6
  const getRelationData = (entityData, relationName, currentRelationNames) => {
6
7
  if (!entityData) return null;
@@ -49,8 +50,9 @@ const buildInheritedFilters = (
49
50
  shouldIgnoreFilter
50
51
  } = getFilterProps(filter, currentSchema, filterEntitySchema);
51
52
 
52
- const relationData = getRelationData(entityData, relationName, relationForeignKeys);
53
+ if (filterName === ID) return;
53
54
 
55
+ const relationData = getRelationData(entityData, relationName, relationForeignKeys);
54
56
  if (
55
57
  (currentEntityId === entityName && isFilterEntitysId(filterName, currentEntityId)) ||
56
58
  shouldIgnoreFilter
@@ -44,19 +44,21 @@ const buildPropsQuery = (entitySchema, extraProps = [], cardOptions = null, link
44
44
  const buildComplexProps = (
45
45
  shouldAddCategoryProps,
46
46
  props,
47
- { relations = [], dynamicProperties = {} },
47
+ { relations = [], dynamicProperties = {}, properties = {} },
48
48
  linkProps
49
49
  ) =>
50
50
  Object.keys(props).map(base => {
51
51
  const nestedProps = props[base];
52
52
  const hasLink = !!linkProps.find(linkProp => linkProp.includes(base));
53
+ const matchingProperty = !!properties[base];
53
54
  const matchingRelation = relations.find(({ localField }) => localField === base);
54
55
  const matchingDynamicProp = Object.keys(dynamicProperties).find(
55
56
  dynamicKey => dynamicKey === base
56
57
  );
57
58
 
58
59
  if (matchingRelation && !nestedProps.includes('id')) nestedProps.push('id');
59
- if (hasLink && (!!matchingRelation || !!matchingDynamicProp)) nestedProps.push('url');
60
+ if (hasLink && (!!matchingProperty || !!matchingRelation || !!matchingDynamicProp))
61
+ nestedProps.push('url');
60
62
  const jointNestedProps = nestedProps.join(',');
61
63
  if (base === 'category' && shouldAddCategoryProps)
62
64
  return `${base}{${jointNestedProps}, ${categoryProps}}`;
@@ -0,0 +1,7 @@
1
+ const buildQueryKey = (key, listName) => {
2
+ const listNameKey = listName ? `pb[${listName}]` : '';
3
+ const wrappedSearchKey = listNameKey ? `[${key}]` : key;
4
+ return `${listNameKey}${wrappedSearchKey}`;
5
+ };
6
+
7
+ export default buildQueryKey;
@@ -31,8 +31,9 @@ const buildSetFilters = ({
31
31
  }
32
32
 
33
33
  const shouldApplyFilterValues = filterBy.length && filterByProperty.length;
34
- const listFilterValues = shouldApplyFilterValues ? getFilterValues(filterBy) : {};
34
+ if (!shouldApplyFilterValues) return { checkboxFilters, listFilterValues: {} };
35
35
 
36
+ const listFilterValues = getFilterValues(filterBy);
36
37
  return { checkboxFilters, listFilterValues };
37
38
  };
38
39
 
@@ -4,8 +4,9 @@ export default function getEntitiesWithBanner(entities, options, hasNewBannerSet
4
4
  const { hasBanner } = options;
5
5
  if (hasNewBannerSettings) {
6
6
  return entities.map(currentEntity => {
7
- const { parent, transform, limit } = options;
8
- return { ...currentEntity, parent, limit, transform };
7
+ const { parent, transform, limit, banner } = options;
8
+ const { adunit, baseAdunit } = banner || {};
9
+ return { ...currentEntity, parent, limit, transform, adunit, baseAdunit };
9
10
  });
10
11
  }
11
12
  if (!hasBanner) return entities;
@@ -2,10 +2,9 @@ import getSearchFilterType from './get-search-filter-type';
2
2
  import buildSearchValuesCheckboxSelect from './build-search-values-checkbox-select';
3
3
  import { RANGE, CHECKBOX, SELECT } from '../constants';
4
4
 
5
- const getQueryFilters = (searchFilter, query) => {
6
- if (!searchFilter.settings || !searchFilter.settings.filters) {
5
+ const getQueryFilters = (searchFilter, query, listName) => {
6
+ if (!searchFilter.settings || !searchFilter.settings.filters)
7
7
  return { valuesAnd: [], valuesOr: [] };
8
- }
9
8
 
10
9
  const rangeFilters = getSearchFilterType(searchFilter, RANGE);
11
10
  const checkboxFilters = getSearchFilterType(searchFilter, CHECKBOX);
@@ -15,7 +14,8 @@ const getQueryFilters = (searchFilter, query) => {
15
14
  query,
16
15
  rangeFilters,
17
16
  checkboxFilters,
18
- selectFilters
17
+ selectFilters,
18
+ listName
19
19
  );
20
20
  return { valuesAnd, valuesOr };
21
21
  };
@@ -2,10 +2,11 @@ import isFilterEntitysId from './is-filter-entitys-id';
2
2
  import getFilterProps from './get-filter-props';
3
3
  import { ID } from '../constants';
4
4
 
5
- const getQueryProps = (inheritedFilters, currentSchema, filterEntitySchema) => {
5
+ const getQueryProps = (inheritedFilters, currentSchema, filterEntitySchema = {}) => {
6
6
  if (!currentSchema || !inheritedFilters.length) return ID;
7
7
 
8
- const { identifier: currentEntityId } = currentSchema;
8
+ const { identifier: currentEntityId, properties } = currentSchema;
9
+ const { identifier: filterEntityId } = filterEntitySchema;
9
10
 
10
11
  return inheritedFilters.reduce((acc, filter) => {
11
12
  const {
@@ -17,12 +18,19 @@ const getQueryProps = (inheritedFilters, currentSchema, filterEntitySchema) => {
17
18
  relationForeignKeys,
18
19
  shouldIgnoreFilter
19
20
  } = getFilterProps(filter, currentSchema, filterEntitySchema);
20
-
21
21
  if (shouldIgnoreFilter) return acc;
22
22
  if (relationForeignKeys && relationForeignKeys.length) {
23
23
  return `${acc} ${relationForeignKeys.join(' ')}`;
24
24
  }
25
-
25
+ if (filterName === ID) {
26
+ const queryValue = Object.keys(properties).find(
27
+ propKey =>
28
+ properties[propKey] &&
29
+ properties[propKey].relation &&
30
+ properties[propKey].relation.entityIdentifier === filterEntityId
31
+ );
32
+ if (queryValue) return `${acc} ${queryValue} `;
33
+ }
26
34
  const queryProp = relationEntityName ? `${relationName} { ${relationProp} }` : filterName;
27
35
  const queryPropFinalValue =
28
36
  isEntityItself && isFilterEntitysId(filterName, currentEntityId) ? ID : queryProp;
@@ -6,13 +6,16 @@ const getWrappedValueWithLink = (value, link, keyPrefix = '') => {
6
6
  const arrayResults = value.map((arrValue, index) => {
7
7
  const { keyField, url } = arrValue;
8
8
  const keyValue = arrValue[keyField];
9
+
9
10
  const isLastItem = index === value.length - 1;
10
- const keyFieldValue = isLastItem ? keyValue : keyValue.concat(',');
11
+ const keyFieldValue = isLastItem ? keyValue : <>{keyValue}, </>;
11
12
 
12
13
  return url ? (
13
- <BlazeLink key={[keyPrefix, index].join('-')} href={url}>{`${keyFieldValue} `}</BlazeLink>
14
+ <BlazeLink key={[keyPrefix, index].join('-')} href={url}>
15
+ {<>{keyFieldValue} </>}
16
+ </BlazeLink>
14
17
  ) : (
15
- `${keyFieldValue} `
18
+ <>{keyFieldValue} </>
16
19
  );
17
20
  });
18
21