@databiosphere/findable-ui 33.0.0 → 34.1.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.
Files changed (213) hide show
  1. package/.release-please-manifest.json +1 -1
  2. package/CHANGELOG.md +18 -0
  3. package/jest.config.js +4 -0
  4. package/lib/common/entities.d.ts +3 -1
  5. package/lib/components/DataDictionary/components/Entities/entities.d.ts +1 -1
  6. package/lib/components/DataDictionary/components/Entities/entities.js +3 -2
  7. package/lib/components/DataDictionary/components/Entities/types.d.ts +3 -4
  8. package/lib/components/DataDictionary/components/Entity/entity.d.ts +1 -1
  9. package/lib/components/DataDictionary/components/Entity/entity.js +12 -6
  10. package/lib/components/DataDictionary/components/Entity/entity.styles.js +7 -1
  11. package/lib/components/DataDictionary/components/Entity/types.d.ts +4 -4
  12. package/lib/components/DataDictionary/components/Entity/utils.d.ts +9 -0
  13. package/lib/components/DataDictionary/components/Entity/utils.js +18 -0
  14. package/lib/components/DataDictionary/components/Filters/components/ColumnFilters/columnFilters.d.ts +4 -0
  15. package/lib/components/DataDictionary/components/Filters/components/ColumnFilters/columnFilters.js +9 -0
  16. package/lib/components/DataDictionary/components/Filters/components/ColumnFilters/types.d.ts +5 -0
  17. package/lib/components/DataDictionary/components/Filters/components/ColumnFilters/types.js +1 -0
  18. package/lib/components/DataDictionary/components/Filters/filters.d.ts +4 -0
  19. package/lib/components/DataDictionary/components/Filters/filters.js +7 -0
  20. package/lib/components/DataDictionary/components/Filters/filters.styles.d.ts +7 -0
  21. package/lib/components/DataDictionary/components/Filters/filters.styles.js +13 -0
  22. package/lib/components/DataDictionary/components/Filters/stories/constants.d.ts +4 -0
  23. package/lib/components/DataDictionary/components/Filters/stories/constants.js +25 -0
  24. package/lib/components/DataDictionary/components/Filters/stories/filters.stories.d.ts +6 -0
  25. package/lib/components/DataDictionary/components/Filters/stories/filters.stories.js +31 -0
  26. package/lib/components/DataDictionary/components/Filters/stories/hook.d.ts +5 -0
  27. package/lib/components/DataDictionary/components/Filters/stories/hook.js +5 -0
  28. package/lib/components/DataDictionary/components/Filters/stories/types.d.ts +4 -0
  29. package/lib/components/DataDictionary/components/Filters/stories/types.js +1 -0
  30. package/lib/components/DataDictionary/components/Filters/types.d.ts +5 -0
  31. package/lib/components/DataDictionary/components/Filters/types.js +1 -0
  32. package/lib/components/DataDictionary/components/Layout/components/EntitiesLayout/entitiesLayout.styles.js +3 -1
  33. package/lib/components/DataDictionary/components/Layout/components/FiltersLayout/filtersLayout.d.ts +2 -0
  34. package/lib/components/DataDictionary/components/Layout/components/FiltersLayout/filtersLayout.js +5 -0
  35. package/lib/components/DataDictionary/components/Layout/components/FiltersLayout/filtersLayout.styles.d.ts +5 -0
  36. package/lib/components/DataDictionary/components/Layout/components/FiltersLayout/filtersLayout.styles.js +24 -0
  37. package/lib/components/DataDictionary/components/Layout/components/FiltersLayout/types.d.ts +5 -0
  38. package/lib/components/DataDictionary/components/Layout/components/FiltersLayout/types.js +1 -0
  39. package/lib/components/DataDictionary/components/Layout/components/OutlineLayout/outlineLayout.styles.js +1 -1
  40. package/lib/components/DataDictionary/components/Layout/components/TitleLayout/titleLayout.styles.js +1 -0
  41. package/lib/components/DataDictionary/components/Layout/constants.d.ts +4 -0
  42. package/lib/components/DataDictionary/components/Layout/constants.js +4 -0
  43. package/lib/components/DataDictionary/components/Outline/utils.d.ts +5 -5
  44. package/lib/components/DataDictionary/components/Outline/utils.js +23 -6
  45. package/lib/components/DataDictionary/components/Table/hook.d.ts +3 -3
  46. package/lib/components/DataDictionary/components/Table/hook.js +11 -3
  47. package/lib/components/DataDictionary/components/Table/options/columnFilters/constants.d.ts +2 -0
  48. package/lib/components/DataDictionary/components/Table/options/columnFilters/constants.js +8 -0
  49. package/lib/components/DataDictionary/components/Table/options/columnFilters/hook.d.ts +6 -0
  50. package/lib/components/DataDictionary/components/Table/options/columnFilters/hook.js +14 -0
  51. package/lib/components/DataDictionary/components/Table/options/expanded/constants.d.ts +2 -0
  52. package/lib/components/DataDictionary/components/Table/options/expanded/constants.js +5 -0
  53. package/lib/components/DataDictionary/components/Table/options/faceted/constants.d.ts +2 -0
  54. package/lib/components/DataDictionary/components/Table/options/faceted/constants.js +6 -0
  55. package/lib/components/DataDictionary/components/Table/options/grouping/constants.d.ts +2 -0
  56. package/lib/components/DataDictionary/components/Table/options/grouping/constants.js +5 -0
  57. package/lib/components/DataDictionary/components/Table/options/hook.d.ts +1 -1
  58. package/lib/components/DataDictionary/components/Table/options/hook.js +17 -0
  59. package/lib/components/DataDictionary/components/Table/options/visibility/constants.d.ts +2 -0
  60. package/lib/components/DataDictionary/components/Table/options/visibility/constants.js +3 -0
  61. package/lib/components/DataDictionary/components/Table/table.d.ts +1 -1
  62. package/lib/components/DataDictionary/components/Table/table.js +2 -2
  63. package/lib/components/DataDictionary/components/Table/types.d.ts +4 -1
  64. package/lib/components/DataDictionary/components/Table/utils.d.ts +18 -0
  65. package/lib/components/DataDictionary/components/Table/utils.js +27 -0
  66. package/lib/components/DataDictionary/dataDictionary.d.ts +1 -1
  67. package/lib/components/DataDictionary/dataDictionary.js +8 -6
  68. package/lib/components/DataDictionary/hooks/UseDataDictionary/hook.js +16 -5
  69. package/lib/components/DataDictionary/hooks/UseDataDictionary/types.d.ts +5 -4
  70. package/lib/components/DataDictionary/types.d.ts +1 -0
  71. package/lib/components/Detail/components/Table/components/TableBody/tableBody.d.ts +8 -2
  72. package/lib/components/Detail/components/Table/components/TableBody/tableBody.js +2 -2
  73. package/lib/components/Detail/components/Table/components/TableRows/components/CollapsableRows/collapsableRows.d.ts +8 -2
  74. package/lib/components/Detail/components/Table/components/TableRows/components/CollapsableRows/collapsableRows.js +2 -2
  75. package/lib/components/Detail/components/Table/components/TableRows/tableRows.d.ts +8 -2
  76. package/lib/components/Detail/components/Table/components/TableRows/tableRows.js +2 -2
  77. package/lib/components/Filter/components/FilterLabel/filterLabel.styles.d.ts +1 -1
  78. package/lib/components/Filter/components/FilterList/filterList.styles.d.ts +2 -0
  79. package/lib/components/Filter/components/FilterList/filterList.styles.js +28 -15
  80. package/lib/components/Filter/components/SearchAllFiltersSearch/searchAllFiltersSearch.styles.d.ts +1 -1
  81. package/lib/components/Layout/components/Header/components/Content/components/Actions/components/Authentication/components/Button/button.styles.d.ts +1 -1
  82. package/lib/components/Layout/components/Header/components/Content/components/Actions/components/Search/components/Button/button.styles.d.ts +1 -1
  83. package/lib/components/Layout/components/Outline/components/ContentsTab/contentsTab.styles.d.ts +1 -1
  84. package/lib/components/Layout/components/Outline/outline.styles.d.ts +1 -1
  85. package/lib/components/Login/components/Button/button.styles.d.ts +1 -1
  86. package/lib/components/Table/common/utils.d.ts +6 -0
  87. package/lib/components/Table/common/utils.js +14 -0
  88. package/lib/components/Table/components/TableFeatures/ColumnFilter/columnFilter.d.ts +7 -0
  89. package/lib/components/Table/components/TableFeatures/ColumnFilter/columnFilter.js +33 -0
  90. package/lib/components/Table/components/TableFeatures/ColumnFilter/columnFilter.styles.d.ts +3 -0
  91. package/lib/components/Table/components/TableFeatures/ColumnFilter/columnFilter.styles.js +19 -0
  92. package/lib/components/Table/components/TableFeatures/ColumnFilter/constants.d.ts +2 -0
  93. package/lib/components/Table/components/TableFeatures/ColumnFilter/constants.js +14 -0
  94. package/lib/components/Table/components/TableFeatures/ColumnFilter/types.d.ts +7 -0
  95. package/lib/components/Table/components/TableFeatures/ColumnFilter/types.js +1 -0
  96. package/lib/components/Table/components/TableFeatures/ColumnFilter/utils.d.ts +6 -0
  97. package/lib/components/Table/components/TableFeatures/ColumnFilter/utils.js +23 -0
  98. package/lib/components/Table/featureOptions/facetedColumn/utils.d.ts +6 -0
  99. package/lib/components/Table/featureOptions/facetedColumn/utils.js +9 -0
  100. package/lib/components/common/ButtonGroup/constants.d.ts +2 -0
  101. package/lib/components/common/ButtonGroup/constants.js +11 -0
  102. package/lib/components/common/Tabs/tabs.styles.d.ts +1 -1
  103. package/lib/mocks/@storybook/addon-actions.d.ts +9 -0
  104. package/lib/mocks/@storybook/addon-actions.js +9 -0
  105. package/lib/providers/exploreState/entities.d.ts +1 -1
  106. package/lib/providers/exploreState.js +10 -10
  107. package/lib/providers/exploreStateSync/hooks/UseMetaCommands/handlers.d.ts +10 -0
  108. package/lib/providers/exploreStateSync/hooks/UseMetaCommands/handlers.js +18 -0
  109. package/lib/providers/exploreStateSync/hooks/UseMetaCommands/hook.d.ts +1 -0
  110. package/lib/providers/exploreStateSync/hooks/UseMetaCommands/hook.js +33 -0
  111. package/lib/providers/exploreStateSync/hooks/UseMetaCommands/types.d.ts +11 -0
  112. package/lib/providers/exploreStateSync/hooks/UseMetaCommands/types.js +5 -0
  113. package/lib/providers/exploreStateSync/hooks/UseMetaCommands/utils.d.ts +34 -0
  114. package/lib/providers/exploreStateSync/hooks/UseMetaCommands/utils.js +59 -0
  115. package/lib/providers/exploreStateSync/provider.d.ts +19 -0
  116. package/lib/providers/exploreStateSync/provider.js +22 -0
  117. package/lib/styles/common/mui/buttonGroup.d.ts +13 -0
  118. package/lib/styles/common/mui/buttonGroup.js +34 -0
  119. package/lib/styles/common/mui/typography.js +2 -0
  120. package/lib/tests/mui/constants.d.ts +1 -0
  121. package/lib/tests/mui/constants.js +1 -0
  122. package/lib/tests/utils.d.ts +6 -0
  123. package/lib/tests/utils.js +8 -0
  124. package/lib/theme/common/components.d.ts +0 -6
  125. package/lib/theme/common/components.js +17 -31
  126. package/lib/theme/components/index.d.ts +1 -0
  127. package/lib/theme/components/index.js +1 -0
  128. package/lib/theme/components/muiButtonGroup.d.ts +2 -0
  129. package/lib/theme/components/muiButtonGroup.js +76 -0
  130. package/lib/theme/theme.js +1 -1
  131. package/lib/views/ExploreView/exploreView.js +2 -1
  132. package/package.json +1 -1
  133. package/src/common/entities.ts +3 -1
  134. package/src/components/DataDictionary/components/Entities/entities.tsx +5 -9
  135. package/src/components/DataDictionary/components/Entities/types.ts +3 -4
  136. package/src/components/DataDictionary/components/Entity/entity.styles.ts +9 -1
  137. package/src/components/DataDictionary/components/Entity/entity.tsx +18 -8
  138. package/src/components/DataDictionary/components/Entity/types.ts +4 -4
  139. package/src/components/DataDictionary/components/Entity/utils.ts +25 -0
  140. package/src/components/DataDictionary/components/Filters/components/ColumnFilters/columnFilters.tsx +21 -0
  141. package/src/components/DataDictionary/components/Filters/components/ColumnFilters/types.ts +6 -0
  142. package/src/components/DataDictionary/components/Filters/filters.styles.ts +14 -0
  143. package/src/components/DataDictionary/components/Filters/filters.tsx +16 -0
  144. package/src/components/DataDictionary/components/Filters/stories/constants.ts +31 -0
  145. package/src/components/DataDictionary/components/Filters/stories/filters.stories.tsx +42 -0
  146. package/src/components/DataDictionary/components/Filters/stories/hook.ts +9 -0
  147. package/src/components/DataDictionary/components/Filters/stories/types.ts +3 -0
  148. package/src/components/DataDictionary/components/Filters/types.ts +6 -0
  149. package/src/components/DataDictionary/components/Layout/components/EntitiesLayout/entitiesLayout.styles.ts +4 -1
  150. package/src/components/DataDictionary/components/Layout/components/FiltersLayout/filtersLayout.styles.ts +27 -0
  151. package/src/components/DataDictionary/components/Layout/components/FiltersLayout/filtersLayout.tsx +10 -0
  152. package/src/components/DataDictionary/components/Layout/components/FiltersLayout/types.ts +6 -0
  153. package/src/components/DataDictionary/components/Layout/components/OutlineLayout/outlineLayout.styles.ts +1 -1
  154. package/src/components/DataDictionary/components/Layout/components/TitleLayout/titleLayout.styles.ts +1 -0
  155. package/src/components/DataDictionary/components/Layout/constants.ts +4 -0
  156. package/src/components/DataDictionary/components/Outline/utils.ts +35 -13
  157. package/src/components/DataDictionary/components/Table/hook.ts +17 -5
  158. package/src/components/DataDictionary/components/Table/options/columnFilters/constants.ts +16 -0
  159. package/src/components/DataDictionary/components/Table/options/columnFilters/hook.ts +32 -0
  160. package/src/components/DataDictionary/components/Table/options/expanded/constants.ts +13 -0
  161. package/src/components/DataDictionary/components/Table/options/faceted/constants.ts +14 -0
  162. package/src/components/DataDictionary/components/Table/options/grouping/constants.ts +9 -0
  163. package/src/components/DataDictionary/components/Table/options/hook.ts +26 -3
  164. package/src/components/DataDictionary/components/Table/options/visibility/constants.ts +5 -0
  165. package/src/components/DataDictionary/components/Table/table.tsx +2 -0
  166. package/src/components/DataDictionary/components/Table/types.ts +8 -1
  167. package/src/components/DataDictionary/components/Table/utils.ts +40 -0
  168. package/src/components/DataDictionary/dataDictionary.tsx +9 -5
  169. package/src/components/DataDictionary/hooks/UseDataDictionary/hook.ts +19 -5
  170. package/src/components/DataDictionary/hooks/UseDataDictionary/types.ts +5 -4
  171. package/src/components/DataDictionary/types.ts +1 -0
  172. package/src/components/Detail/components/Table/components/TableBody/tableBody.tsx +14 -3
  173. package/src/components/Detail/components/Table/components/TableRows/components/CollapsableRows/collapsableRows.tsx +9 -2
  174. package/src/components/Detail/components/Table/components/TableRows/tableRows.tsx +9 -2
  175. package/src/components/Filter/components/FilterList/filterList.styles.ts +34 -15
  176. package/src/components/Table/common/utils.ts +16 -0
  177. package/src/components/Table/components/TableFeatures/ColumnFilter/columnFilter.styles.ts +23 -0
  178. package/src/components/Table/components/TableFeatures/ColumnFilter/columnFilter.tsx +98 -0
  179. package/src/components/Table/components/TableFeatures/ColumnFilter/constants.ts +16 -0
  180. package/src/components/Table/components/TableFeatures/ColumnFilter/types.ts +10 -0
  181. package/src/components/Table/components/TableFeatures/ColumnFilter/utils.ts +27 -0
  182. package/src/components/Table/featureOptions/facetedColumn/utils.ts +14 -0
  183. package/src/components/common/ButtonGroup/constants.ts +13 -0
  184. package/src/mocks/@storybook/addon-actions.ts +10 -0
  185. package/src/providers/exploreState/entities.ts +1 -1
  186. package/src/providers/exploreState.tsx +10 -11
  187. package/src/providers/exploreStateSync/hooks/UseMetaCommands/handlers.ts +25 -0
  188. package/src/providers/exploreStateSync/hooks/UseMetaCommands/hook.ts +38 -0
  189. package/src/providers/exploreStateSync/hooks/UseMetaCommands/types.ts +13 -0
  190. package/src/providers/exploreStateSync/hooks/UseMetaCommands/utils.ts +69 -0
  191. package/src/providers/exploreStateSync/provider.tsx +29 -0
  192. package/src/styles/common/mui/buttonGroup.ts +46 -0
  193. package/src/styles/common/mui/typography.ts +2 -0
  194. package/src/tests/mui/constants.ts +1 -0
  195. package/src/tests/utils.ts +9 -0
  196. package/src/theme/common/components.ts +17 -32
  197. package/src/theme/components/index.ts +1 -0
  198. package/src/theme/components/muiButtonGroup.ts +79 -0
  199. package/src/theme/theme.ts +1 -1
  200. package/src/views/ExploreView/exploreView.tsx +3 -2
  201. package/tests/dataDictionaryColumnFilters.test.tsx +101 -0
  202. package/lib/providers/exploreState/hooks/UseMetaCommands/actions.d.ts +0 -13
  203. package/lib/providers/exploreState/hooks/UseMetaCommands/actions.js +0 -24
  204. package/lib/providers/exploreState/hooks/UseMetaCommands/types.d.ts +0 -4
  205. package/lib/providers/exploreState/hooks/UseMetaCommands/types.js +0 -5
  206. package/lib/providers/exploreState/hooks/UseMetaCommands/useMetaCommands.d.ts +0 -2
  207. package/lib/providers/exploreState/hooks/UseMetaCommands/useMetaCommands.js +0 -21
  208. package/lib/providers/exploreState/hooks/UseMetaCommands/utils.d.ts +0 -9
  209. package/lib/providers/exploreState/hooks/UseMetaCommands/utils.js +0 -25
  210. package/src/providers/exploreState/hooks/UseMetaCommands/actions.ts +0 -29
  211. package/src/providers/exploreState/hooks/UseMetaCommands/types.ts +0 -4
  212. package/src/providers/exploreState/hooks/UseMetaCommands/useMetaCommands.ts +0 -27
  213. package/src/providers/exploreState/hooks/UseMetaCommands/utils.ts +0 -33
@@ -0,0 +1,10 @@
1
+ import { Button, MenuProps } from "@mui/material";
2
+ import { Column, RowData } from "@tanstack/react-table";
3
+ import { BaseComponentProps } from "../../../../types";
4
+
5
+ export interface ColumnFilterProps<T extends RowData>
6
+ extends BaseComponentProps,
7
+ Omit<MenuProps, "anchorEl" | "onClose" | "open"> {
8
+ Button?: typeof Button;
9
+ column: Column<T>;
10
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Returns an updater function for column filter.
3
+ * @param value - Value.
4
+ * @returns An updater function that returns the new filter value.
5
+ */
6
+ export function updater(
7
+ value: unknown
8
+ ): (old: unknown[] | undefined) => unknown[] | undefined {
9
+ return (old: unknown[] | undefined) => {
10
+ // If no old value, return new value.
11
+ if (!old) return [value];
12
+
13
+ // If value already exists, remove it.
14
+ if (old.includes(value)) {
15
+ // Filter out the value.
16
+ const next = old.filter((v) => v !== value);
17
+
18
+ // If no values remain, return undefined.
19
+ if (next.length === 0) return undefined;
20
+
21
+ return next;
22
+ }
23
+
24
+ // Otherwise, add the value.
25
+ return [...old, value];
26
+ };
27
+ }
@@ -0,0 +1,14 @@
1
+ import { COLLATOR_CASE_INSENSITIVE } from "../../../../common/constants";
2
+
3
+ /**
4
+ * Sorts Map entries by key.
5
+ * @param facetedValues - Map of faceted values.
6
+ * @returns Sorted array of [key, value] entries.
7
+ */
8
+ export function getSortedFacetedValues(
9
+ facetedValues: Map<unknown, number>
10
+ ): [unknown, number][] {
11
+ return [...facetedValues].sort((a, b) =>
12
+ COLLATOR_CASE_INSENSITIVE.compare(String(a), String(b))
13
+ );
14
+ }
@@ -0,0 +1,13 @@
1
+ import { ButtonGroupProps } from "@mui/material";
2
+ import { BUTTON_GROUP_PROPS as MUI_BUTTON_GROUP_PROPS } from "../../../styles/common/mui/buttonGroup";
3
+
4
+ export const BUTTON_GROUP_PROPS: Record<string, Partial<ButtonGroupProps>> = {
5
+ PRIMARY_CONTAINED: {
6
+ color: MUI_BUTTON_GROUP_PROPS.COLOR.PRIMARY,
7
+ variant: MUI_BUTTON_GROUP_PROPS.VARIANT.CONTAINED,
8
+ },
9
+ SECONDARY_OUTLINED: {
10
+ color: MUI_BUTTON_GROUP_PROPS.COLOR.SECONDARY,
11
+ variant: MUI_BUTTON_GROUP_PROPS.VARIANT.OUTLINED,
12
+ },
13
+ };
@@ -0,0 +1,10 @@
1
+ import { jest } from "@jest/globals";
2
+
3
+ /**
4
+ * Mock for Storybook's @storybook/addon-actions function.
5
+ * The mock keeps the same function signature as the real Storybook action,
6
+ * does not trigger any Storybook logic, and simply returns a Jest mock function
7
+ * for use in tests.
8
+ * @returns A Jest mock function.
9
+ */
10
+ export const action = (): jest.Mock => jest.fn();
@@ -18,7 +18,7 @@ import {
18
18
  EntityPath,
19
19
  SavedFilter,
20
20
  } from "../../config/entities";
21
- import { META_COMMAND } from "./hooks/UseMetaCommands/types";
21
+ import { META_COMMAND } from "../exploreStateSync/hooks/UseMetaCommands/types";
22
22
 
23
23
  export interface EntityPageState {
24
24
  categoryGroupConfigKey: CategoryGroupConfigKey;
@@ -35,8 +35,6 @@ import {
35
35
  Meta,
36
36
  } from "./exploreState/entities";
37
37
  import { useBeforePopState } from "./exploreState/hooks/UseBeforePopState/useBeforePopState";
38
- import { META_COMMAND } from "./exploreState/hooks/UseMetaCommands/types";
39
- import { useMetaCommands } from "./exploreState/hooks/UseMetaCommands/useMetaCommands";
40
38
  import {
41
39
  DEFAULT_PAGINATION_STATE,
42
40
  INITIAL_STATE,
@@ -70,6 +68,7 @@ import {
70
68
  updateEntityStateByCategoryGroupConfigKey,
71
69
  updateSelectColumnVisibility,
72
70
  } from "./exploreState/utils";
71
+ import { META_COMMAND } from "./exploreStateSync/hooks/UseMetaCommands/types";
73
72
 
74
73
  export type CatalogState = string | undefined;
75
74
 
@@ -215,9 +214,6 @@ export function ExploreStateProvider({
215
214
  });
216
215
  }, [exploreDispatch, token]);
217
216
 
218
- // Meta-command related side effects.
219
- useMetaCommands({ exploreDispatch, exploreState });
220
-
221
217
  // Before pop state related side effects (forward / backward navigation by browser buttons).
222
218
  useBeforePopState({ exploreDispatch, exploreState });
223
219
 
@@ -445,7 +441,7 @@ function exploreReducer(
445
441
  ),
446
442
  filterCount: getFilterCount(filterState),
447
443
  filterState,
448
- meta: { command: META_COMMAND.NAVIGATE_TO_FILTERS },
444
+ meta: { command: META_COMMAND.STATE_TO_URL_PUSH },
449
445
  paginationState: resetPage(state.paginationState),
450
446
  rowPreview,
451
447
  };
@@ -471,7 +467,7 @@ function exploreReducer(
471
467
  ),
472
468
  filterCount,
473
469
  filterState,
474
- meta: { command: META_COMMAND.NAVIGATE_TO_FILTERS },
470
+ meta: { command: META_COMMAND.STATE_TO_URL_PUSH },
475
471
  paginationState: resetPage(state.paginationState),
476
472
  rowPreview,
477
473
  };
@@ -578,8 +574,11 @@ function exploreReducer(
578
574
  **/
579
575
  case ExploreActionKind.SelectEntityType: {
580
576
  if (payload === state.tabValue) {
581
- // Update meta to match command "REPLACE_TO_FILTERS" - facilitates navigation to filters on return back to entity from elsewhere.
582
- return { ...state, meta: { command: META_COMMAND.REPLACE_TO_FILTERS } };
577
+ // Update meta to match command "STATE_TO_URL_REPLACE" - facilitates navigation to filters on return back to entity from elsewhere.
578
+ return {
579
+ ...state,
580
+ meta: { command: META_COMMAND.STATE_TO_URL_REPLACE },
581
+ };
583
582
  }
584
583
  const entityState = getEntityState(
585
584
  state,
@@ -594,7 +593,7 @@ function exploreReducer(
594
593
  filterState: entityState.filterState,
595
594
  listItems: [],
596
595
  loading: true,
597
- meta: { command: META_COMMAND.REPLACE_TO_FILTERS },
596
+ meta: { command: META_COMMAND.STATE_TO_URL_REPLACE },
598
597
  paginationState: { ...resetPage(state.paginationState), rows: 0 },
599
598
  rowPreview,
600
599
  tabValue: payload,
@@ -699,7 +698,7 @@ function exploreReducer(
699
698
  ),
700
699
  filterCount: getFilterCount(filterState),
701
700
  filterState,
702
- meta: { command: META_COMMAND.NAVIGATE_TO_FILTERS },
701
+ meta: { command: META_COMMAND.STATE_TO_URL_PUSH },
703
702
  paginationState: resetPage(state.paginationState),
704
703
  rowPreview,
705
704
  };
@@ -0,0 +1,25 @@
1
+ import Router, { NextRouter } from "next/router";
2
+ import { ExploreQueryState } from "./types";
3
+ import { buildQuery, stringifyQuery } from "./utils";
4
+
5
+ /**
6
+ * Updates the URL query parameters based on state.
7
+ * Pushes or replaces the query to the router.
8
+ * @param state - State -- partial explore state.
9
+ * @param currentQuery - Query -- current.
10
+ * @param method - "push" or "replace".
11
+ */
12
+ export function updateUrlFromState(
13
+ state: ExploreQueryState,
14
+ currentQuery: NextRouter["query"],
15
+ method: "push" | "replace" = "push"
16
+ ): void {
17
+ // Build the next query.
18
+ const query = buildQuery(state);
19
+
20
+ // Do nothing if the next query is the same as the current query.
21
+ if (stringifyQuery(query) === stringifyQuery(currentQuery)) return;
22
+
23
+ // Push or replace the query to the router.
24
+ Router[method]({ query }, undefined, { shallow: true });
25
+ }
@@ -0,0 +1,38 @@
1
+ import { useRouter } from "next/router";
2
+ import { useEffect, useMemo } from "react";
3
+ import { useExploreState } from "../../../../hooks/useExploreState";
4
+ import { clearMeta } from "../../../exploreState/actions/clearMeta/dispatch";
5
+ import { updateUrlFromState } from "./handlers";
6
+ import { META_COMMAND } from "./types";
7
+ import { getQueryState } from "./utils";
8
+
9
+ export const useMetaCommands = (): void => {
10
+ const { exploreDispatch, exploreState } = useExploreState();
11
+
12
+ // Router must be ready before executing any meta-command side effects.
13
+ const { isReady, query } = useRouter();
14
+
15
+ // Command.
16
+ const command = exploreState.meta?.command;
17
+
18
+ // Extract relevant state to update URL.
19
+ const state = useMemo(() => getQueryState(exploreState), [exploreState]);
20
+
21
+ useEffect(() => {
22
+ // Do nothing if the router is not ready.
23
+ if (!isReady) return;
24
+
25
+ switch (command) {
26
+ case META_COMMAND.STATE_TO_URL_PUSH:
27
+ updateUrlFromState(state, query);
28
+ exploreDispatch(clearMeta());
29
+ break;
30
+ case META_COMMAND.STATE_TO_URL_REPLACE:
31
+ updateUrlFromState(state, query, "replace");
32
+ exploreDispatch(clearMeta());
33
+ break;
34
+ default:
35
+ break;
36
+ }
37
+ }, [command, exploreDispatch, isReady, query, state]);
38
+ };
@@ -0,0 +1,13 @@
1
+ import { ExploreState } from "../../../exploreState";
2
+
3
+ export interface ExploreQueryState {
4
+ catalog: ExploreState["catalogState"];
5
+ entityListType: ExploreState["tabValue"];
6
+ ff: ExploreState["featureFlagState"];
7
+ filter: ExploreState["filterState"];
8
+ }
9
+
10
+ export enum META_COMMAND {
11
+ STATE_TO_URL_PUSH = "STATE_TO_URL_PUSH",
12
+ STATE_TO_URL_REPLACE = "STATE_TO_URL_REPLACE",
13
+ }
@@ -0,0 +1,69 @@
1
+ import { NextRouter } from "next/router";
2
+ import { ExploreState } from "../../../exploreState";
3
+ import { ExploreQueryState } from "./types";
4
+
5
+ /**
6
+ * Builds a query object from state.
7
+ * State values are expected to be undefined, string, or an array.
8
+ * Undefined values and empty arrays are not included in the query.
9
+ * @param state - State -- partial explore state.
10
+ * @returns A query object.
11
+ */
12
+ export function buildQuery(state: ExploreQueryState): NextRouter["query"] {
13
+ const query: NextRouter["query"] = {};
14
+
15
+ for (const [key, value] of Object.entries(state)) {
16
+ // Handle the undefined case.
17
+ if (value === undefined) continue;
18
+
19
+ // Handle the string case.
20
+ if (typeof value === "string") {
21
+ query[key] = value;
22
+ continue;
23
+ }
24
+
25
+ // Handle the array case.
26
+ if (value.length === 0) continue;
27
+ query[key] = JSON.stringify(value);
28
+ }
29
+
30
+ return query;
31
+ }
32
+
33
+ /**
34
+ * Extracts URL-relevant values from the ExploreState for query parameter synchronization.
35
+ *
36
+ * This function maps specific properties from the full ExploreState to the
37
+ * ExploreQueryState interface, which contains only the subset of state that
38
+ * should be synchronized with the URL.
39
+ *
40
+ * The extracted properties are:
41
+ * - catalog: Current catalog selection (string | undefined)
42
+ * - entityListType: Current active tab value (string)
43
+ * - ff: Feature flag state (string | undefined)
44
+ * - filter: Applied filters (SelectedFilter[])
45
+ *
46
+ * @param exploreState - Explore state.
47
+ * @returns Subset of state used for URL query parameters.
48
+ */
49
+ export function getQueryState(exploreState: ExploreState): ExploreQueryState {
50
+ return {
51
+ catalog: exploreState.catalogState,
52
+ entityListType: exploreState.tabValue,
53
+ ff: exploreState.featureFlagState,
54
+ filter: exploreState.filterState,
55
+ };
56
+ }
57
+
58
+ /**
59
+ * Returns a sorted string representation of a query object.
60
+ * @param query - Query object.
61
+ * @returns Sorted string representation of the query object.
62
+ */
63
+ export function stringifyQuery(query: NextRouter["query"]): string {
64
+ return JSON.stringify(
65
+ Object.keys(query)
66
+ .sort()
67
+ .reduce((acc, key) => ({ ...acc, [key]: query[key] }), {})
68
+ );
69
+ }
@@ -0,0 +1,29 @@
1
+ import React, { ReactNode } from "react";
2
+ import { useMetaCommands } from "./hooks/UseMetaCommands/hook";
3
+
4
+ /**
5
+ * Synchronizes the ExploreView component's state with the Next.js URL.
6
+ *
7
+ * Listens for reducer meta-commands (`STATE_TO_URL_PUSH`/`STATE_TO_URL_REPLACE`)
8
+ * and updates the URL accordingly using router methods.
9
+ *
10
+ * Usage:
11
+ * ```tsx
12
+ * <ExploreStateProvider>
13
+ * <ExploreStateSyncProvider>
14
+ * <ExploreView />
15
+ * </ExploreStateSyncProvider>
16
+ * </ExploreStateProvider>
17
+ * ```
18
+ */
19
+
20
+ export function ExploreStateSyncProvider({
21
+ children,
22
+ }: {
23
+ children: ReactNode;
24
+ }): JSX.Element {
25
+ // Meta-command related side effects.
26
+ useMetaCommands();
27
+
28
+ return <>{children}</>;
29
+ }
@@ -0,0 +1,46 @@
1
+ import { buttonGroupClasses, ButtonGroupProps } from "@mui/material";
2
+
3
+ type ButtonGroupPropsOptions = {
4
+ CLASSES: typeof CLASSES;
5
+ COLOR: typeof COLOR;
6
+ SIZE: typeof SIZE;
7
+ VARIANT: typeof VARIANT;
8
+ };
9
+
10
+ const CLASSES: Record<string, ButtonGroupProps["classes"]> = {
11
+ COLOR_PRIMARY: buttonGroupClasses.colorPrimary,
12
+ COLOR_SECONDARY: buttonGroupClasses.colorSecondary,
13
+ CONTAINED: buttonGroupClasses.contained,
14
+ GROUPED: buttonGroupClasses.grouped,
15
+ OUTLINED: buttonGroupClasses.outlined,
16
+ ROOT: buttonGroupClasses.root,
17
+ };
18
+
19
+ const COLOR: Record<string, ButtonGroupProps["color"]> = {
20
+ ERROR: "error",
21
+ INFO: "info",
22
+ INHERIT: "inherit",
23
+ PRIMARY: "primary",
24
+ SECONDARY: "secondary",
25
+ SUCCESS: "success",
26
+ WARNING: "warning",
27
+ };
28
+
29
+ const SIZE: Record<string, ButtonGroupProps["size"]> = {
30
+ LARGE: "large",
31
+ MEDIUM: "medium",
32
+ SMALL: "small",
33
+ };
34
+
35
+ const VARIANT: Record<string, ButtonGroupProps["variant"]> = {
36
+ CONTAINED: "contained",
37
+ OUTLINED: "outlined",
38
+ TEXT: "text",
39
+ };
40
+
41
+ export const BUTTON_GROUP_PROPS: ButtonGroupPropsOptions = {
42
+ CLASSES,
43
+ COLOR,
44
+ SIZE,
45
+ VARIANT,
46
+ };
@@ -10,12 +10,14 @@ const COLOR: Record<string, TypographyOwnProps["color"]> = {
10
10
  INHERIT: "inherit",
11
11
  INK_LIGHT: "ink.light",
12
12
  INK_MAIN: "ink.main",
13
+ PRIMARY: "primary",
13
14
  };
14
15
 
15
16
  const VARIANT: Record<string, TypographyOwnProps["variant"]> = {
16
17
  INHERIT: "inherit",
17
18
  TEXT_BODY_400: "text-body-400",
18
19
  TEXT_BODY_400_2_LINES: "text-body-400-2lines",
20
+ TEXT_BODY_500: "text-body-500",
19
21
  TEXT_BODY_SMALL_400: "text-body-small-400",
20
22
  TEXT_HEADING_LARGE: "text-heading-large",
21
23
  TEXT_HEADING_SMALL: "text-heading-small",
@@ -1,5 +1,6 @@
1
1
  export const MUI_CLASSES = {
2
2
  ACTIVE: "Mui-active",
3
3
  COMPLETED: "Mui-completed",
4
+ DISABLED: "Mui-disabled",
4
5
  SELECTED: "Mui-selected",
5
6
  };
@@ -32,6 +32,15 @@ export function getStartsWithRegex(text: string): RegExp {
32
32
  return new RegExp(`^${escapeRegExp(text)}`);
33
33
  }
34
34
 
35
+ /**
36
+ * Retrieves an element by its role.
37
+ * @param role - The role of the element.
38
+ * @returns The element.
39
+ */
40
+ export function getRole<T extends HTMLElement = HTMLElement>(role: string): T {
41
+ return screen.getByRole(role);
42
+ }
43
+
35
44
  /**
36
45
  * Retrieves an element by its text content.
37
46
  * @param text - The text content of the element.
@@ -3,6 +3,7 @@ import { DropDownIcon } from "../../components/common/Form/components/Select/com
3
3
  import { COLOR_MIXES } from "../../styles/common/constants/colorMixes";
4
4
  import { PALETTE } from "../../styles/common/constants/palette";
5
5
  import { SHADOWS } from "../../styles/common/constants/shadows";
6
+ import { BUTTON_PROPS } from "../../styles/common/mui/button";
6
7
  import { CHIP_PROPS } from "../../styles/common/mui/chip";
7
8
  import { desktopUp, mobileUp, tabletUp } from "./breakpoints";
8
9
  import {
@@ -200,6 +201,22 @@ export const MuiButton = (theme: Theme): Components["MuiButton"] => {
200
201
  },
201
202
  endIcon: {
202
203
  margin: 0,
204
+ variants: [
205
+ {
206
+ props: { size: BUTTON_PROPS.SIZE.MEDIUM },
207
+ style: {
208
+ marginLeft: -6,
209
+ marginRight: -6,
210
+ },
211
+ },
212
+ {
213
+ props: { size: BUTTON_PROPS.SIZE.SMALL },
214
+ style: {
215
+ marginLeft: -6,
216
+ marginRight: -6,
217
+ },
218
+ },
219
+ ],
203
220
  },
204
221
  outlinedSecondary: {
205
222
  backgroundColor: "transparent",
@@ -317,38 +334,6 @@ export const MuiButtonBase = (theme: Theme): Components["MuiButtonBase"] => {
317
334
  };
318
335
  };
319
336
 
320
- /**
321
- * MuiButtonGroup Component
322
- * @param theme - Theme.
323
- * @returns MuiButtonGroup component theme styles.
324
- */
325
- export const MuiButtonGroup = (theme: Theme): Components["MuiButtonGroup"] => {
326
- return {
327
- defaultProps: {
328
- disableElevation: true,
329
- disableRipple: true,
330
- },
331
- styleOverrides: {
332
- grouped: {
333
- minWidth: 0,
334
- padding: "6px 8px",
335
- },
336
- groupedContainedPrimary: {
337
- borderColor: theme.palette.primary.dark,
338
- boxShadow: `0 1px 0 0 ${theme.palette.primary.dark}`,
339
- // eslint-disable-next-line sort-keys -- disabling key order for readability
340
- "&:hover": {
341
- boxShadow: `0 1px 0 0 ${theme.palette.primary.dark}`,
342
- },
343
- // eslint-disable-next-line sort-keys -- disabling key order for readability
344
- "&:active": {
345
- boxShadow: "none",
346
- },
347
- },
348
- },
349
- };
350
- };
351
-
352
337
  /**
353
338
  * MuiCard Component
354
339
  */
@@ -1,3 +1,4 @@
1
1
  export { MuiAlert } from "./muiAlert";
2
2
  export { MuiAlertTitle } from "./muiAlertTitle";
3
+ export { MuiButtonGroup } from "./muiButtonGroup";
3
4
  export { MuiTableCell } from "./muiTableCell";
@@ -0,0 +1,79 @@
1
+ import { Components } from "@mui/material";
2
+ import { COLOR_MIXES } from "../../styles/common/constants/colorMixes";
3
+ import { PALETTE } from "../../styles/common/constants/palette";
4
+ import { BUTTON_GROUP_PROPS } from "../../styles/common/mui/buttonGroup";
5
+
6
+ const SELECTORS = {
7
+ GROUPED: `.${BUTTON_GROUP_PROPS.CLASSES.GROUPED}`,
8
+ };
9
+
10
+ export const MuiButtonGroup: Components["MuiButtonGroup"] = {
11
+ defaultProps: {
12
+ disableElevation: true,
13
+ disableRipple: true,
14
+ },
15
+ styleOverrides: {
16
+ root: {
17
+ variants: [
18
+ /* PRIMARY CONTAINED */
19
+ {
20
+ props: {
21
+ color: BUTTON_GROUP_PROPS.COLOR.PRIMARY,
22
+ variant: BUTTON_GROUP_PROPS.VARIANT.CONTAINED,
23
+ },
24
+ style: {
25
+ [SELECTORS.GROUPED]: {
26
+ borderColor: PALETTE.PRIMARY_DARK,
27
+ boxShadow: `0 1px 0 0 ${PALETTE.PRIMARY_DARK}`,
28
+ minWidth: 0,
29
+ // eslint-disable-next-line sort-keys -- disabling key order for readability
30
+ "&:hover": {
31
+ boxShadow: `0 1px 0 0 ${PALETTE.PRIMARY_DARK}`,
32
+ },
33
+ // eslint-disable-next-line sort-keys -- disabling key order for readability
34
+ "&:active": {
35
+ boxShadow: "none",
36
+ },
37
+ },
38
+ },
39
+ },
40
+ /* SECONDARY OUTLINED */
41
+ {
42
+ props: {
43
+ color: BUTTON_GROUP_PROPS.COLOR.SECONDARY,
44
+ variant: BUTTON_GROUP_PROPS.VARIANT.OUTLINED,
45
+ },
46
+ style: {
47
+ [SELECTORS.GROUPED]: {
48
+ backgroundColor: PALETTE.COMMON_WHITE,
49
+ boxShadow: `inset 0 0 0 1px ${PALETTE.SMOKE_DARK}, 0 1px 0 0 ${COLOR_MIXES.COMMON_BLACK_08}`,
50
+ color: PALETTE.INK_MAIN,
51
+ minWidth: 0,
52
+ // eslint-disable-next-line sort-keys -- disabling key order for readability
53
+ "&:hover": {
54
+ backgroundColor: PALETTE.SMOKE_LIGHTEST,
55
+ boxShadow: `inset 0 0 0 1px ${PALETTE.SMOKE_DARK}, 0 1px 0 0 ${COLOR_MIXES.COMMON_BLACK_08}`,
56
+ },
57
+ // eslint-disable-next-line sort-keys -- disabling key order for readability
58
+ "&:active": {
59
+ backgroundColor: PALETTE.SMOKE_LIGHTEST,
60
+ boxShadow: `inset 0 0 0 1px ${PALETTE.SMOKE_DARK}`,
61
+ },
62
+ },
63
+ },
64
+ },
65
+ /* SMALL */
66
+ {
67
+ props: {
68
+ size: BUTTON_GROUP_PROPS.SIZE.SMALL,
69
+ },
70
+ style: {
71
+ [SELECTORS.GROUPED]: {
72
+ padding: "6px 8px",
73
+ },
74
+ },
75
+ },
76
+ ],
77
+ },
78
+ },
79
+ };
@@ -80,7 +80,7 @@ export function createAppTheme(customOptions: ThemeOptions = {}): Theme {
80
80
  MuiBreadcrumbs: C.MuiBreadcrumbs(theme),
81
81
  MuiButton: C.MuiButton(theme),
82
82
  MuiButtonBase: C.MuiButtonBase(theme),
83
- MuiButtonGroup: C.MuiButtonGroup(theme),
83
+ MuiButtonGroup: M.MuiButtonGroup,
84
84
  MuiCard: C.MuiCard,
85
85
  MuiCheckbox: C.MuiCheckbox(theme),
86
86
  MuiChip: C.MuiChip(theme),
@@ -35,6 +35,7 @@ import { useExploreState } from "../../hooks/useExploreState";
35
35
  import { useSummary } from "../../hooks/useSummary";
36
36
  import { ExploreActionKind } from "../../providers/exploreState";
37
37
  import { SELECT_CATEGORY_KEY } from "../../providers/exploreState/constants";
38
+ import { ExploreStateSyncProvider } from "../../providers/exploreStateSync/provider";
38
39
  import { TEST_IDS } from "../../tests/testIds";
39
40
  import { DESKTOP_SM } from "../../theme/common/breakpoints";
40
41
 
@@ -141,7 +142,7 @@ export const ExploreView = (props: ExploreViewProps): JSX.Element => {
141
142
  }, [entityListType, exploreDispatch]);
142
143
 
143
144
  return (
144
- <>
145
+ <ExploreStateSyncProvider>
145
146
  {categoryViews && !!categoryViews.length && (
146
147
  <Sidebar drawerOpen={isDrawerOpen} onDrawerClose={onCloseDrawer}>
147
148
  <SidebarTools data-testid={TEST_IDS.FILTER_CONTROLS}>
@@ -182,7 +183,7 @@ export const ExploreView = (props: ExploreViewProps): JSX.Element => {
182
183
  Tabs={<Tabs />}
183
184
  title={entityConfig.explorerTitle || explorerTitle}
184
185
  />
185
- </>
186
+ </ExploreStateSyncProvider>
186
187
  );
187
188
  };
188
189