@databiosphere/findable-ui 29.0.2 → 30.0.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 (205) hide show
  1. package/.release-please-manifest.json +1 -1
  2. package/CHANGELOG.md +22 -0
  3. package/lib/common/categories/config/types.d.ts +28 -0
  4. package/lib/common/categories/config/utils.d.ts +31 -0
  5. package/lib/common/categories/config/utils.js +29 -0
  6. package/lib/common/categories/models/range/typeGuards.d.ts +14 -0
  7. package/lib/common/categories/models/range/typeGuards.js +18 -0
  8. package/lib/common/categories/models/range/types.d.ts +15 -0
  9. package/lib/common/categories/models/range/types.js +1 -0
  10. package/lib/common/categories/models/range/utils.d.ts +23 -0
  11. package/lib/common/categories/models/range/utils.js +41 -0
  12. package/lib/common/categories/models/select/utils.d.ts +8 -0
  13. package/lib/common/categories/models/select/utils.js +16 -0
  14. package/lib/common/categories/models/types.d.ts +6 -0
  15. package/lib/common/categories/models/types.js +1 -0
  16. package/lib/common/categories/views/common/types.d.ts +10 -0
  17. package/lib/common/categories/views/common/types.js +1 -0
  18. package/lib/common/categories/views/range/typeGuards.d.ts +8 -0
  19. package/lib/common/categories/views/range/typeGuards.js +8 -0
  20. package/lib/common/categories/views/range/types.d.ts +19 -0
  21. package/lib/common/categories/views/range/types.js +1 -0
  22. package/lib/common/categories/views/range/utils.d.ts +12 -0
  23. package/lib/common/categories/views/range/utils.js +24 -0
  24. package/lib/common/categories/views/select/typeGuards.d.ts +8 -0
  25. package/lib/common/categories/views/select/typeGuards.js +8 -0
  26. package/lib/common/categories/views/select/types.d.ts +7 -0
  27. package/lib/common/categories/views/select/types.js +1 -0
  28. package/lib/common/categories/views/types.d.ts +13 -0
  29. package/lib/common/categories/views/types.js +8 -0
  30. package/lib/common/entities.d.ts +5 -2
  31. package/lib/components/DataDictionary/components/Table/components/BasicCell/basicCell.d.ts +3 -2
  32. package/lib/components/DataDictionary/components/Table/components/BasicCell/basicCell.js +6 -2
  33. package/lib/components/DataDictionary/components/Table/components/BasicCell/utils.d.ts +9 -0
  34. package/lib/components/DataDictionary/components/Table/components/BasicCell/utils.js +12 -0
  35. package/lib/components/DataDictionary/dataDictionary.styles.js +2 -3
  36. package/lib/components/Filter/components/Filter/filter.d.ts +2 -2
  37. package/lib/components/Filter/components/Filter/filter.js +11 -3
  38. package/lib/components/Filter/components/Filter/stories/args.d.ts +5 -0
  39. package/lib/components/Filter/components/Filter/stories/args.js +19 -0
  40. package/lib/components/Filter/components/Filter/stories/filter.stories.d.ts +8 -0
  41. package/lib/components/Filter/components/Filter/stories/filter.stories.js +21 -0
  42. package/lib/components/Filter/components/FilterMenu/filterMenu.js +2 -2
  43. package/lib/components/Filter/components/FilterMenu/filterMenu.styles.d.ts +1 -1
  44. package/lib/components/Filter/components/FilterMenu/filterMenu.styles.js +1 -1
  45. package/lib/components/Filter/components/FilterRange/constants.d.ts +0 -2
  46. package/lib/components/Filter/components/FilterRange/constants.js +0 -5
  47. package/lib/components/Filter/components/FilterRange/filterRange.d.ts +1 -1
  48. package/lib/components/Filter/components/FilterRange/filterRange.js +50 -21
  49. package/lib/components/Filter/components/FilterRange/filterRange.styles.js +58 -10
  50. package/lib/components/Filter/components/FilterRange/hooks/UseFilterRange/constants.d.ts +5 -0
  51. package/lib/components/Filter/components/FilterRange/hooks/UseFilterRange/constants.js +5 -0
  52. package/lib/components/Filter/components/FilterRange/hooks/UseFilterRange/hook.d.ts +2 -2
  53. package/lib/components/Filter/components/FilterRange/hooks/UseFilterRange/hook.js +32 -7
  54. package/lib/components/Filter/components/FilterRange/hooks/UseFilterRange/schema.d.ts +6 -0
  55. package/lib/components/Filter/components/FilterRange/hooks/UseFilterRange/schema.js +50 -0
  56. package/lib/components/Filter/components/FilterRange/hooks/UseFilterRange/types.d.ts +26 -3
  57. package/lib/components/Filter/components/FilterRange/hooks/UseFilterRange/types.js +6 -1
  58. package/lib/components/Filter/components/FilterRange/hooks/UseFilterRange/utils.d.ts +15 -0
  59. package/lib/components/Filter/components/FilterRange/hooks/UseFilterRange/utils.js +25 -0
  60. package/lib/components/Filter/components/FilterRange/stories/args.d.ts +3 -0
  61. package/lib/components/Filter/components/FilterRange/stories/args.js +13 -0
  62. package/lib/components/Filter/components/FilterRange/stories/filterRange.stories.js +2 -2
  63. package/lib/components/Filter/components/FilterRange/types.d.ts +10 -6
  64. package/lib/components/Filter/components/FilterRange/types.js +1 -6
  65. package/lib/components/Filter/components/FilterRange/utils.d.ts +8 -0
  66. package/lib/components/Filter/components/FilterRange/utils.js +15 -0
  67. package/lib/components/Filter/components/FilterTag/stories/args.d.ts +5 -0
  68. package/lib/components/Filter/components/FilterTag/stories/args.js +17 -0
  69. package/lib/components/Filter/components/FilterTag/stories/filterTag.stories.d.ts +8 -0
  70. package/lib/components/Filter/components/FilterTag/stories/filterTag.stories.js +21 -0
  71. package/lib/components/Filter/components/FilterTag/utils.d.ts +10 -0
  72. package/lib/components/Filter/components/FilterTag/utils.js +40 -0
  73. package/lib/components/Filter/components/Filters/filters.d.ts +2 -2
  74. package/lib/components/Filter/components/Filters/filters.js +15 -8
  75. package/lib/components/Filter/components/Filters/stories/args.d.ts +3 -0
  76. package/lib/components/Filter/components/Filters/stories/args.js +15 -0
  77. package/lib/components/Filter/components/Filters/stories/constants.d.ts +22 -0
  78. package/lib/components/Filter/components/Filters/stories/constants.js +134 -0
  79. package/lib/components/Filter/components/Filters/stories/filters.stories.d.ts +6 -0
  80. package/lib/components/Filter/components/Filters/stories/filters.stories.js +15 -0
  81. package/lib/components/Filter/components/SearchAllFilters/components/VariableSizeList/VariableSizeList.d.ts +1 -1
  82. package/lib/components/Filter/components/SearchAllFilters/components/VariableSizeList/VariableSizeList.js +5 -5
  83. package/lib/components/Filter/components/SearchAllFilters/components/VariableSizeListItem/variableSizeListItem.js +2 -1
  84. package/lib/components/Filter/components/SearchAllFilters/searchAllFilters.d.ts +3 -2
  85. package/lib/components/Filter/components/SearchAllFilters/searchAllFilters.js +6 -4
  86. package/lib/components/Filter/components/VariableSizeListItem/variableSizeListItem.js +2 -1
  87. package/lib/components/Index/components/EntitiesView/components/ChartView/utils.js +2 -0
  88. package/lib/components/Index/table/hook.js +4 -0
  89. package/lib/components/Table/columnDef/accessorFn/typeGuards.d.ts +9 -0
  90. package/lib/components/Table/columnDef/accessorFn/typeGuards.js +10 -0
  91. package/lib/components/Table/common/utils.d.ts +2 -2
  92. package/lib/components/Table/common/utils.js +28 -13
  93. package/lib/components/Table/components/TableCell/components/ChipCell/chipCell.d.ts +3 -0
  94. package/lib/components/Table/components/TableCell/components/ChipCell/chipCell.js +8 -0
  95. package/lib/components/Table/components/TableCell/components/LinkCell/linkCell.d.ts +4 -0
  96. package/lib/components/Table/components/TableCell/components/LinkCell/linkCell.js +21 -0
  97. package/lib/components/Table/components/TableCell/components/LinkCell/stories/args.d.ts +6 -0
  98. package/lib/components/Table/components/TableCell/components/LinkCell/stories/args.js +27 -0
  99. package/lib/components/Table/components/TableCell/components/LinkCell/stories/linkCell.stories.d.ts +9 -0
  100. package/lib/components/Table/components/TableCell/components/LinkCell/stories/linkCell.stories.js +18 -0
  101. package/lib/components/Table/components/TableCell/components/LinkCell/stories/types.d.ts +3 -0
  102. package/lib/components/Table/components/TableCell/components/LinkCell/stories/types.js +1 -0
  103. package/lib/components/Table/components/TableCell/components/LinkCell/utils.d.ts +22 -0
  104. package/lib/components/Table/components/TableCell/components/LinkCell/utils.js +45 -0
  105. package/lib/components/Table/featureOptions/facetedColumn/getFacetedMinMaxValues.d.ts +8 -0
  106. package/lib/components/Table/featureOptions/facetedColumn/getFacetedMinMaxValues.js +46 -0
  107. package/lib/components/common/Link/typeGuards.d.ts +13 -0
  108. package/lib/components/common/Link/typeGuards.js +21 -0
  109. package/lib/config/entities.d.ts +2 -11
  110. package/lib/hooks/useCategoryFilter.d.ts +8 -13
  111. package/lib/hooks/useCategoryFilter.js +31 -28
  112. package/lib/providers/exploreState/entities.d.ts +5 -3
  113. package/lib/providers/exploreState/payloads/entities.d.ts +6 -2
  114. package/lib/providers/exploreState.d.ts +3 -2
  115. package/lib/providers/exploreState.js +1 -1
  116. package/lib/tests/utils.d.ts +24 -0
  117. package/lib/tests/utils.js +34 -0
  118. package/lib/theme/common/components.js +19 -1
  119. package/lib/views/ExploreView/exploreView.js +10 -8
  120. package/package.json +2 -1
  121. package/src/common/categories/config/types.ts +42 -0
  122. package/src/common/categories/config/utils.ts +47 -0
  123. package/src/common/categories/models/range/typeGuards.ts +24 -0
  124. package/src/common/categories/models/range/types.ts +17 -0
  125. package/src/common/categories/models/range/utils.ts +51 -0
  126. package/src/common/categories/models/select/utils.ts +23 -0
  127. package/src/common/categories/models/types.ts +7 -0
  128. package/src/common/categories/views/common/types.ts +11 -0
  129. package/src/common/categories/views/range/typeGuards.ts +13 -0
  130. package/src/common/categories/views/range/types.ts +21 -0
  131. package/src/common/categories/views/range/utils.ts +35 -0
  132. package/src/common/categories/views/select/typeGuards.ts +13 -0
  133. package/src/common/categories/views/select/types.ts +8 -0
  134. package/src/common/categories/views/types.ts +15 -0
  135. package/src/common/entities.ts +10 -5
  136. package/src/components/DataDictionary/components/Table/components/BasicCell/basicCell.tsx +12 -4
  137. package/src/components/DataDictionary/components/Table/components/BasicCell/utils.ts +13 -0
  138. package/src/components/DataDictionary/dataDictionary.styles.ts +2 -3
  139. package/src/components/Filter/components/Filter/filter.tsx +38 -13
  140. package/src/components/Filter/components/Filter/stories/args.ts +24 -0
  141. package/src/components/Filter/components/Filter/stories/filter.stories.tsx +32 -0
  142. package/src/components/Filter/components/FilterMenu/filterMenu.styles.ts +1 -1
  143. package/src/components/Filter/components/FilterMenu/filterMenu.tsx +7 -3
  144. package/src/components/Filter/components/FilterRange/constants.ts +0 -7
  145. package/src/components/Filter/components/FilterRange/filterRange.styles.ts +58 -14
  146. package/src/components/Filter/components/FilterRange/filterRange.tsx +112 -40
  147. package/src/components/Filter/components/FilterRange/hooks/UseFilterRange/constants.ts +5 -0
  148. package/src/components/Filter/components/FilterRange/hooks/UseFilterRange/hook.ts +51 -10
  149. package/src/components/Filter/components/FilterRange/hooks/UseFilterRange/schema.ts +60 -0
  150. package/src/components/Filter/components/FilterRange/hooks/UseFilterRange/types.ts +34 -3
  151. package/src/components/Filter/components/FilterRange/hooks/UseFilterRange/utils.ts +32 -0
  152. package/src/components/Filter/components/FilterRange/stories/args.ts +16 -0
  153. package/src/components/Filter/components/FilterRange/stories/filterRange.stories.tsx +2 -2
  154. package/src/components/Filter/components/FilterRange/types.ts +12 -6
  155. package/src/components/Filter/components/FilterRange/utils.ts +16 -0
  156. package/src/components/Filter/components/FilterTag/stories/args.ts +22 -0
  157. package/src/components/Filter/components/FilterTag/stories/filterTag.stories.tsx +32 -0
  158. package/src/components/Filter/components/FilterTag/utils.ts +57 -0
  159. package/src/components/Filter/components/Filters/filters.tsx +21 -12
  160. package/src/components/Filter/components/Filters/stories/args.ts +24 -0
  161. package/src/components/Filter/components/Filters/stories/constants.ts +151 -0
  162. package/src/components/Filter/components/Filters/stories/filters.stories.tsx +24 -0
  163. package/src/components/Filter/components/SearchAllFilters/components/VariableSizeList/VariableSizeList.tsx +32 -29
  164. package/src/components/Filter/components/SearchAllFilters/components/VariableSizeListItem/variableSizeListItem.tsx +9 -1
  165. package/src/components/Filter/components/SearchAllFilters/searchAllFilters.tsx +12 -6
  166. package/src/components/Filter/components/VariableSizeListItem/variableSizeListItem.tsx +2 -1
  167. package/src/components/Index/components/EntitiesView/components/ChartView/utils.ts +2 -0
  168. package/src/components/Index/table/hook.ts +4 -0
  169. package/src/components/Table/columnDef/accessorFn/typeGuards.ts +15 -0
  170. package/src/components/Table/common/utils.ts +37 -16
  171. package/src/components/Table/components/TableCell/components/ChipCell/chipCell.tsx +14 -0
  172. package/src/components/Table/components/TableCell/components/LinkCell/linkCell.tsx +64 -0
  173. package/src/components/Table/components/TableCell/components/LinkCell/stories/args.ts +35 -0
  174. package/src/components/Table/components/TableCell/components/LinkCell/stories/linkCell.stories.tsx +32 -0
  175. package/src/components/Table/components/TableCell/components/LinkCell/stories/types.ts +4 -0
  176. package/src/components/Table/components/TableCell/components/LinkCell/utils.ts +59 -0
  177. package/src/components/Table/featureOptions/facetedColumn/getFacetedMinMaxValues.ts +64 -0
  178. package/src/components/common/Link/typeGuards.ts +35 -0
  179. package/src/config/entities.ts +1 -14
  180. package/src/hooks/useCategoryFilter.ts +56 -53
  181. package/src/providers/exploreState/entities.ts +3 -3
  182. package/src/providers/exploreState/initializer/utils.ts +1 -1
  183. package/src/providers/exploreState/payloads/entities.ts +5 -2
  184. package/src/providers/exploreState.tsx +5 -3
  185. package/src/tests/utils.ts +44 -0
  186. package/src/theme/common/components.ts +19 -1
  187. package/src/views/ExploreView/exploreView.tsx +17 -22
  188. package/tests/filter.test.tsx +100 -0
  189. package/tests/filterRange.test.tsx +331 -46
  190. package/tests/filters.test.tsx +61 -0
  191. package/tests/getFacetedMinMaxValues.test.ts +166 -0
  192. package/tests/linkCell.test.tsx +89 -0
  193. package/lib/components/DataDictionary/components/Table/components/BasicCell/types.d.ts +0 -3
  194. package/lib/components/Filter/components/Filter/filter.stories.d.ts +0 -25
  195. package/lib/components/Filter/components/Filter/filter.stories.js +0 -42
  196. package/lib/components/Filter/components/FilterTag/filterTag.stories.d.ts +0 -16
  197. package/lib/components/Filter/components/FilterTag/filterTag.stories.js +0 -17
  198. package/lib/components/Filter/components/Filters/filters.stories.d.ts +0 -6
  199. package/lib/components/Filter/components/Filters/filters.stories.js +0 -91
  200. package/src/components/DataDictionary/components/Table/components/BasicCell/types.ts +0 -7
  201. package/src/components/Filter/components/Filter/filter.stories.tsx +0 -52
  202. package/src/components/Filter/components/FilterTag/filterTag.stories.tsx +0 -23
  203. package/src/components/Filter/components/Filters/filters.stories.tsx +0 -101
  204. package/tests/filterRangeMock.test.tsx +0 -38
  205. /package/lib/{components/DataDictionary/components/Table/components/BasicCell → common/categories/config}/types.js +0 -0
@@ -0,0 +1,32 @@
1
+ import { Box } from "@mui/material";
2
+ import { Meta, StoryObj } from "@storybook/react";
3
+ import React from "react";
4
+ import { FilterTag } from "../filterTag";
5
+ import { DEFAULT_ARGS, WITH_ELLIPSIS_ARGS, WITH_RANGE_ARGS } from "./args";
6
+
7
+ const meta: Meta<typeof FilterTag> = {
8
+ component: FilterTag,
9
+ decorators: [
10
+ (Story): JSX.Element => (
11
+ <Box sx={{ width: 232 }}>
12
+ <Story />
13
+ </Box>
14
+ ),
15
+ ],
16
+ };
17
+
18
+ export default meta;
19
+
20
+ type Story = StoryObj<typeof meta>;
21
+
22
+ export const Default: Story = {
23
+ args: DEFAULT_ARGS,
24
+ };
25
+
26
+ export const WithEllipsis: Story = {
27
+ args: WITH_ELLIPSIS_ARGS,
28
+ };
29
+
30
+ export const WithRange: Story = {
31
+ args: WITH_RANGE_ARGS,
32
+ };
@@ -0,0 +1,57 @@
1
+ import { RangeCategoryView } from "../../../../common/categories/views/range/types";
2
+ import { VIEW_KIND } from "../../../../common/categories/views/types";
3
+ import { CategoryTag } from "../../../../common/entities";
4
+ import { OnFilterFn } from "../../../../hooks/useCategoryFilter";
5
+ import { RANGE_OPERATOR } from "../FilterRange/hooks/UseFilterRange/types";
6
+ import { getRangeOperator } from "../FilterRange/utils";
7
+
8
+ /**
9
+ * Returns set of filter tags with tag label (the selected range values) and corresponding Tag onRemove function.
10
+ * @param categoryView - View model of range category.
11
+ * @param onFilter - Function to execute on selection or removal of category value.
12
+ * @returns Array of selected filter tags.
13
+ */
14
+ export function buildRangeTag(
15
+ categoryView: RangeCategoryView,
16
+ onFilter: OnFilterFn
17
+ ): CategoryTag[] {
18
+ const rangeOperator = getRangeOperator(categoryView);
19
+ if (!rangeOperator) return [];
20
+ return [
21
+ {
22
+ label: buildRangeTagLabel(categoryView, rangeOperator),
23
+ onRemove: () =>
24
+ onFilter(
25
+ categoryView.key,
26
+ undefined,
27
+ false,
28
+ undefined,
29
+ VIEW_KIND.RANGE
30
+ ),
31
+ superseded: false,
32
+ },
33
+ ];
34
+ }
35
+
36
+ /**
37
+ * Returns the label for the range tag based on the selected values.
38
+ * @param categoryView - View model of range category.
39
+ * @param rangeOperator - The range operator.
40
+ * @returns The label for the range tag.
41
+ */
42
+ function buildRangeTagLabel(
43
+ categoryView: RangeCategoryView,
44
+ rangeOperator: RANGE_OPERATOR
45
+ ): string {
46
+ const { selectedMax, selectedMin } = categoryView;
47
+ switch (rangeOperator) {
48
+ case RANGE_OPERATOR.BETWEEN:
49
+ return `${selectedMin} - ${selectedMax}`;
50
+ case RANGE_OPERATOR.GREATER_THAN:
51
+ return `> ${selectedMin}`;
52
+ case RANGE_OPERATOR.LESS_THAN:
53
+ return `< ${selectedMax}`;
54
+ default:
55
+ return "";
56
+ }
57
+ }
@@ -1,6 +1,8 @@
1
1
  import { Divider } from "@mui/material";
2
2
  import React, { Fragment, useEffect, useRef, useState } from "react";
3
- import { CategoryTag, SelectCategoryView } from "../../../../common/entities";
3
+ import { isRangeCategoryView } from "../../../../common/categories/views/range/typeGuards";
4
+ import { CategoryView } from "../../../../common/categories/views/types";
5
+ import { CategoryTag } from "../../../../common/entities";
4
6
  import { TrackFilterOpenedFunction } from "../../../../config/entities";
5
7
  import {
6
8
  BREAKPOINT_FN_NAME,
@@ -11,11 +13,12 @@ import { useWindowResize } from "../../../../hooks/useWindowResize";
11
13
  import { TEST_IDS } from "../../../../tests/testIds";
12
14
  import { DESKTOP_SM } from "../../../../theme/common/breakpoints";
13
15
  import { Filter } from "../Filter/filter";
16
+ import { buildRangeTag } from "../FilterTag/utils";
14
17
  import { FilterTags } from "../FilterTags/filterTags";
15
18
  import { CategoryViewsLabel, Filters as FilterList } from "./filters.styles";
16
19
 
17
20
  export interface CategoryFilter {
18
- categoryViews: SelectCategoryView[];
21
+ categoryViews: CategoryView[];
19
22
  label?: string;
20
23
  }
21
24
 
@@ -28,15 +31,21 @@ export interface FiltersProps {
28
31
  }
29
32
 
30
33
  /**
31
- * Returns set of selected category tags with tag label (the selected metadata label) and corresponding Tag onRemove function.
34
+ * Returns filter tags for the given category view.
32
35
  * @param categoryView - View model of category to display.
33
- * @param onFilter - Function to execute on select of category value or remove of selected category value.
34
- * @returns Array of selected category tags.
36
+ * @param onFilter - Function to execute on selection or removal of category value.
37
+ * @returns Array of filter tags.
35
38
  */
36
- function buildSelectCategoryTags(
37
- categoryView: SelectCategoryView,
39
+ function buildFilterTags(
40
+ categoryView: CategoryView,
38
41
  onFilter: OnFilterFn
39
42
  ): CategoryTag[] {
43
+ // Handle range category views
44
+ if (isRangeCategoryView(categoryView)) {
45
+ return buildRangeTag(categoryView, onFilter);
46
+ }
47
+
48
+ // Handle select category views.
40
49
  const { key: categoryKey, values } = categoryView;
41
50
  return values
42
51
  .filter(({ selected }) => selected)
@@ -50,16 +59,16 @@ function buildSelectCategoryTags(
50
59
  }
51
60
 
52
61
  /**
53
- * Build selected filter tags element for the given category type.
62
+ * Returns filter tags element for the given category view.
54
63
  * @param categoryView - View model of category to display.
55
- * @param onFilter - Function to execute on select of category value or remove of selected category value.
56
- * @returns Filter tags element displaying selected category values.
64
+ * @param onFilter - Function to execute on selection or removal of category value.
65
+ * @returns Filter tags element.
57
66
  */
58
67
  function renderFilterTags(
59
- categoryView: SelectCategoryView,
68
+ categoryView: CategoryView,
60
69
  onFilter: OnFilterFn
61
70
  ): JSX.Element {
62
- const tags = buildSelectCategoryTags(categoryView, onFilter);
71
+ const tags = buildFilterTags(categoryView, onFilter);
63
72
  return <FilterTags tags={tags} />;
64
73
  }
65
74
 
@@ -0,0 +1,24 @@
1
+ import { fn } from "@storybook/test";
2
+ import { ComponentProps } from "react";
3
+ import { Filters } from "../filters";
4
+ import {
5
+ BIOLOGICAL_SEX,
6
+ DONOR_COUNT,
7
+ FILE_FORMAT,
8
+ FILE_TYPE,
9
+ GENUS_SPECIES,
10
+ } from "./constants";
11
+
12
+ export const DEFAULT_ARGS: ComponentProps<typeof Filters> = {
13
+ categoryFilters: [
14
+ {
15
+ categoryViews: [BIOLOGICAL_SEX, GENUS_SPECIES, DONOR_COUNT],
16
+ label: "Donor",
17
+ },
18
+ {
19
+ categoryViews: [FILE_FORMAT, FILE_TYPE],
20
+ label: "File",
21
+ },
22
+ ],
23
+ onFilter: fn(),
24
+ };
@@ -0,0 +1,151 @@
1
+ import { RangeCategoryView } from "../../../../../common/categories/views/range/types";
2
+ import { SelectCategoryView } from "../../../../../common/entities";
3
+
4
+ /**
5
+ * Biological Sex
6
+ */
7
+ const MALE = {
8
+ count: 14,
9
+ key: "male",
10
+ label: "Male",
11
+ selected: false,
12
+ };
13
+
14
+ /**
15
+ * Biological Sex
16
+ */
17
+ const FEMALE = {
18
+ count: 12,
19
+ key: "female",
20
+ label: "Female",
21
+ selected: false,
22
+ };
23
+
24
+ /**
25
+ * Genus Species
26
+ */
27
+ const HOMO_SAPIENS = {
28
+ count: 471,
29
+ key: "Homo sapiens",
30
+ label: "Homo sapiens",
31
+ selected: false,
32
+ };
33
+
34
+ /**
35
+ * Genus Species
36
+ */
37
+ const MUS_MUSCLES = {
38
+ count: 55,
39
+ key: "Mus musculus",
40
+ label: "Mus musculus",
41
+ selected: false,
42
+ };
43
+
44
+ /**
45
+ * File Format
46
+ */
47
+ const BAM = {
48
+ count: 7,
49
+ key: "bam",
50
+ label: "bam",
51
+ selected: false,
52
+ };
53
+
54
+ /**
55
+ * File Format
56
+ */
57
+ const CSV = {
58
+ count: 5,
59
+ key: "csv",
60
+ label: "csv",
61
+ selected: false,
62
+ };
63
+
64
+ /**
65
+ * File Format
66
+ */
67
+ const FASTQ = {
68
+ count: 38,
69
+ key: "fastq",
70
+ label: "fastq",
71
+ selected: false,
72
+ };
73
+
74
+ /**
75
+ * File Format
76
+ */
77
+ const TSV = {
78
+ count: 3,
79
+ key: "tsv",
80
+ label: "tsv",
81
+ selected: false,
82
+ };
83
+
84
+ /**
85
+ * File Type
86
+ */
87
+ const RAW = {
88
+ count: 1,
89
+ key: "raw",
90
+ label: "raw",
91
+ selected: false,
92
+ };
93
+
94
+ /**
95
+ * File Type
96
+ */
97
+ const PROCESSED = {
98
+ count: 1,
99
+ key: "processed",
100
+ label: "processed",
101
+ selected: false,
102
+ };
103
+
104
+ /**
105
+ * Biological Sex select category view
106
+ */
107
+ export const BIOLOGICAL_SEX: SelectCategoryView = {
108
+ key: "biologicalSex",
109
+ label: "Biological Sex",
110
+ values: [MALE, FEMALE],
111
+ };
112
+
113
+ /**
114
+ * Genus Species select category view
115
+ */
116
+ export const GENUS_SPECIES: SelectCategoryView = {
117
+ key: "genusSpecies",
118
+ label: "Genus Species",
119
+ values: [HOMO_SAPIENS, MUS_MUSCLES],
120
+ };
121
+
122
+ /**
123
+ * Donor Count range category view
124
+ */
125
+ export const DONOR_COUNT: RangeCategoryView = {
126
+ key: "Donor Count",
127
+ label: "Donor Count",
128
+ max: 200,
129
+ min: 10,
130
+ selectedMax: null,
131
+ selectedMin: null,
132
+ };
133
+
134
+ /**
135
+ * File Format select category view
136
+ */
137
+ export const FILE_FORMAT: SelectCategoryView = {
138
+ key: "fileFormat",
139
+ label: "File Format",
140
+ values: [BAM, CSV, FASTQ, TSV],
141
+ };
142
+
143
+ /**
144
+ * File Type select category view
145
+ */
146
+ export const FILE_TYPE: SelectCategoryView = {
147
+ isDisabled: true,
148
+ key: "fileType",
149
+ label: "File Type",
150
+ values: [RAW, PROCESSED],
151
+ };
@@ -0,0 +1,24 @@
1
+ import { Box } from "@mui/material";
2
+ import { Meta, StoryObj } from "@storybook/react";
3
+ import React from "react";
4
+ import { Filters } from "../filters";
5
+ import { DEFAULT_ARGS } from "./args";
6
+
7
+ const meta: Meta<typeof Filters> = {
8
+ component: Filters,
9
+ decorators: [
10
+ (Story): JSX.Element => (
11
+ <Box sx={{ width: 264 }}>
12
+ <Story />
13
+ </Box>
14
+ ),
15
+ ],
16
+ };
17
+
18
+ export default meta;
19
+
20
+ type Story = StoryObj<typeof Filters>;
21
+
22
+ export const Default: Story = {
23
+ args: DEFAULT_ARGS,
24
+ };
@@ -46,12 +46,12 @@ export type ItemSizeByItemKey = Map<string, number>;
46
46
 
47
47
  export interface VariableSizeListProps {
48
48
  autocompleteListProps: Omit<MListProps, "children">;
49
- categoryViews: SelectCategoryView[];
50
49
  height?: number; // Height of list; vertical list must be a number.
51
50
  itemSize?: number; // Default item size.
52
51
  onFilter: OnFilterFn;
53
52
  overscanCount?: ListProps["overscanCount"];
54
53
  searchTerm: string;
54
+ selectCategoryViews: SelectCategoryView[];
55
55
  width?: ListProps["width"]; // Width of list; default to 100% width of parent element.
56
56
  }
57
57
 
@@ -120,17 +120,17 @@ export const VariableSizeList = forwardRef<
120
120
  >(function VariableSizeList(
121
121
  {
122
122
  autocompleteListProps,
123
- categoryViews,
124
123
  height: initHeight = MAX_LIST_HEIGHT_PX,
125
124
  itemSize = LIST_ITEM_HEIGHT,
126
125
  onFilter,
127
126
  overscanCount = MAX_DISPLAYABLE_LIST_ITEMS * 2,
128
127
  searchTerm,
128
+ selectCategoryViews,
129
129
  width = "100%",
130
130
  }: VariableSizeListProps,
131
131
  autocompleteListRef
132
132
  ): JSX.Element {
133
- const filteredItems = applyMenuFilter(categoryViews, searchTerm);
133
+ const filteredItems = applyMenuFilter(selectCategoryViews, searchTerm);
134
134
  let resizeRequired = true;
135
135
  const desktopSmDown = useBreakpointHelper(
136
136
  BREAKPOINT_FN_NAME.DOWN,
@@ -223,40 +223,43 @@ export const VariableSizeList = forwardRef<
223
223
 
224
224
  /**
225
225
  * Filter categories' values by a search term and return model of list items
226
- * @param categoryViews - Select category views
226
+ * @param selectCategoryViews - Select category views
227
227
  * @param inputValue - Search term
228
228
  * @returns array of objects representing list items to be rendered
229
229
  */
230
230
  function applyMenuFilter(
231
- categoryViews: SelectCategoryView[],
231
+ selectCategoryViews: SelectCategoryView[],
232
232
  inputValue: string
233
233
  ): SearchAllFiltersItem[] {
234
234
  const sortMatches = getSortMatchesFn(inputValue);
235
- const filteredItems = categoryViews.reduce((filteredItems, category) => {
236
- if (!category.isDisabled) {
237
- const categoryValueKeyPrefix =
238
- "value_" + category.key.replaceAll(";", ";;") + ";_"; // Terminating the category key with a semicolon (and escaping preceding semicolons) ensures a unique prefix
239
- const filteredCategoryValues = sortMatches(category.values).map(
240
- (match): ValueItem => ({
241
- categoryKey: category.key,
242
- key: categoryValueKeyPrefix + match.value.key,
243
- matchRanges: match.labelRanges,
244
- type: ITEM_TYPE.VALUE,
245
- value: match.value,
246
- })
247
- );
248
- if (filteredCategoryValues.length) {
249
- if (filteredItems.length) filteredItems.push(DIVIDER_ITEM);
250
- filteredItems.push({
251
- categoryLabel: category.label,
252
- key: "category_" + category.key,
253
- type: ITEM_TYPE.CATEGORY,
254
- });
255
- filteredItems.push(...filteredCategoryValues);
235
+ const filteredItems = selectCategoryViews.reduce(
236
+ (filteredItems, category) => {
237
+ if (!category.isDisabled) {
238
+ const categoryValueKeyPrefix =
239
+ "value_" + category.key.replaceAll(";", ";;") + ";_"; // Terminating the category key with a semicolon (and escaping preceding semicolons) ensures a unique prefix
240
+ const filteredCategoryValues = sortMatches(category.values).map(
241
+ (match): ValueItem => ({
242
+ categoryKey: category.key,
243
+ key: categoryValueKeyPrefix + match.value.key,
244
+ matchRanges: match.labelRanges,
245
+ type: ITEM_TYPE.VALUE,
246
+ value: match.value,
247
+ })
248
+ );
249
+ if (filteredCategoryValues.length) {
250
+ if (filteredItems.length) filteredItems.push(DIVIDER_ITEM);
251
+ filteredItems.push({
252
+ categoryLabel: category.label,
253
+ key: "category_" + category.key,
254
+ type: ITEM_TYPE.CATEGORY,
255
+ });
256
+ filteredItems.push(...filteredCategoryValues);
257
+ }
256
258
  }
257
- }
258
- return filteredItems;
259
- }, [] as SearchAllFiltersItem[]);
259
+ return filteredItems;
260
+ },
261
+ [] as SearchAllFiltersItem[]
262
+ );
260
263
  if (filteredItems.length === 0) filteredItems.push(NO_RESULTS_ITEM);
261
264
  return filteredItems;
262
265
  }
@@ -6,6 +6,7 @@ import {
6
6
  Typography,
7
7
  } from "@mui/material";
8
8
  import React, { useEffect, useRef } from "react";
9
+ import { VIEW_KIND } from "../../../../../../common/categories/views/types";
9
10
  import { OnFilterFn } from "../../../../../../hooks/useCategoryFilter";
10
11
  import { TEST_IDS } from "../../../../../../tests/testIds";
11
12
  import { TEXT_BODY_SMALL_400 } from "../../../../../../theme/common/typography";
@@ -54,7 +55,14 @@ export default function VariableSizeListItem({
54
55
  ref={setRef}
55
56
  key={key}
56
57
  onClick={(): void =>
57
- onFilter(categoryKey, valueKey, !selected, undefined, searchTerm)
58
+ onFilter(
59
+ categoryKey,
60
+ valueKey,
61
+ !selected,
62
+ undefined,
63
+ VIEW_KIND.SELECT,
64
+ searchTerm
65
+ )
58
66
  }
59
67
  selected={selected}
60
68
  style={style}
@@ -10,6 +10,8 @@ import React, {
10
10
  useRef,
11
11
  useState,
12
12
  } from "react";
13
+ import { isSelectCategoryView } from "../../../../common/categories/views/select/typeGuards";
14
+ import { CategoryView } from "../../../../common/categories/views/types";
13
15
  import { SelectCategoryView } from "../../../../common/entities";
14
16
  import { SELECTOR } from "../../../../common/selectors";
15
17
  import {
@@ -29,18 +31,18 @@ import { VariableSizeList } from "./components/VariableSizeList/VariableSizeList
29
31
  import { Autocomplete } from "./searchAllFilters.styles";
30
32
 
31
33
  export interface SearchAllFiltersProps {
32
- categoryViews: SelectCategoryView[];
34
+ categoryViews: CategoryView[];
33
35
  drawerOpen?: boolean;
34
36
  onFilter: OnFilterFn;
35
37
  }
36
38
 
37
39
  interface ListboxContextValue {
38
- categoryViews: SelectCategoryView[];
39
40
  onClearSearch: () => void;
40
41
  onCloseSearch: () => void;
41
42
  onFilter: OnFilterFn;
42
43
  open: boolean;
43
44
  searchTerm: string;
45
+ selectCategoryViews: SelectCategoryView[];
44
46
  }
45
47
 
46
48
  const renderInput = (params: AutocompleteRenderInputParams): JSX.Element => (
@@ -48,12 +50,12 @@ const renderInput = (params: AutocompleteRenderInputParams): JSX.Element => (
48
50
  );
49
51
 
50
52
  export const ListboxContext = createContext<ListboxContextValue>({
51
- categoryViews: [],
52
53
  onClearSearch: (): void => undefined,
53
54
  onCloseSearch: (): void => undefined,
54
55
  onFilter: (): void => undefined,
55
56
  open: false,
56
57
  searchTerm: "",
58
+ selectCategoryViews: [],
57
59
  });
58
60
 
59
61
  const Listbox = React.forwardRef<HTMLUListElement, MListProps>(function Listbox(
@@ -63,14 +65,15 @@ const Listbox = React.forwardRef<HTMLUListElement, MListProps>(function Listbox(
63
65
  props = Object.assign({}, props, {
64
66
  children: undefined, // Content is controlled by VariableSizeList
65
67
  });
66
- const { categoryViews, onFilter, searchTerm } = useContext(ListboxContext);
68
+ const { onFilter, searchTerm, selectCategoryViews } =
69
+ useContext(ListboxContext);
67
70
  return (
68
71
  <VariableSizeList
69
72
  autocompleteListProps={props}
70
- categoryViews={categoryViews}
71
73
  onFilter={onFilter}
72
74
  ref={ref}
73
75
  searchTerm={searchTerm}
76
+ selectCategoryViews={selectCategoryViews}
74
77
  />
75
78
  );
76
79
  });
@@ -84,6 +87,9 @@ export const SearchAllFilters = ({
84
87
  const autocompleteRef = useRef<HTMLDivElement>(null);
85
88
  const [open, setOpen] = useState(false);
86
89
  const [searchTerm, setSearchTerm] = useState("");
90
+ const selectCategoryViews = categoryViews.filter((view) =>
91
+ isSelectCategoryView(view)
92
+ );
87
93
 
88
94
  // Handles background scroll action (desktop only).
89
95
  const handleBackgroundScroll = (overflowStyle: OVERFLOW_STYLE): void => {
@@ -145,12 +151,12 @@ export const SearchAllFilters = ({
145
151
  return (
146
152
  <ListboxContext.Provider
147
153
  value={{
148
- categoryViews,
149
154
  onClearSearch,
150
155
  onCloseSearch,
151
156
  onFilter,
152
157
  open,
153
158
  searchTerm,
159
+ selectCategoryViews,
154
160
  }}
155
161
  >
156
162
  <Autocomplete
@@ -5,6 +5,7 @@ import {
5
5
  Typography,
6
6
  } from "@mui/material";
7
7
  import React, { CSSProperties, useEffect, useRef } from "react";
8
+ import { VIEW_KIND } from "../../../../common/categories/views/types";
8
9
  import { CategoryKey } from "../../../../common/entities";
9
10
  import { OnFilterFn } from "../../../../hooks/useCategoryFilter";
10
11
  import { SELECT_CATEGORY_KEY } from "../../../../providers/exploreState/constants";
@@ -45,7 +46,7 @@ export default function VariableSizeListItem({
45
46
  }, [key, onUpdateItemSizeByItemKey]);
46
47
 
47
48
  const handleItemClicked = (): void => {
48
- onFilter(categoryKey, key, !selected, categorySection);
49
+ onFilter(categoryKey, key, !selected, categorySection, VIEW_KIND.SELECT);
49
50
  };
50
51
 
51
52
  return (
@@ -1,3 +1,4 @@
1
+ import { isSelectCategoryView } from "../../../../../../common/categories/views/select/typeGuards";
1
2
  import { SelectCategoryView } from "../../../../../../common/entities";
2
3
  import { CategoryFilter } from "../../../../../Filter/components/Filters/filters";
3
4
 
@@ -11,6 +12,7 @@ export function getSelectCategoryViews(
11
12
  ): SelectCategoryView[] {
12
13
  return categoryFilters
13
14
  .flatMap(({ categoryViews }) => categoryViews)
15
+ .filter(isSelectCategoryView)
14
16
  .filter(({ enableChartView = true }) => enableChartView)
15
17
  .filter(({ values }) => values.length > 0);
16
18
  }
@@ -33,6 +33,7 @@ import {
33
33
  isClientFilteringEnabled,
34
34
  sortingFn,
35
35
  } from "../../Table/common/utils";
36
+ import { getFacetedMinMaxValues } from "../../Table/featureOptions/facetedColumn/getFacetedMinMaxValues";
36
37
  import { ROW_POSITION } from "../../Table/features/RowPosition/constants";
37
38
  import { ROW_PREVIEW } from "../../Table/features/RowPreview/constants";
38
39
  import { RowPreviewState } from "../../Table/features/RowPreview/entities";
@@ -167,6 +168,9 @@ export const useTable = <T extends RowData>(): UseTable<T> => {
167
168
  enableMultiSort: clientFiltering, // TODO(cc) move to sorting options; default to false and let the table options in config flag this value.
168
169
  filterFns: { arrIncludesSome },
169
170
  getCoreRowModel: getCoreRowModel(),
171
+ getFacetedMinMaxValues: clientFiltering
172
+ ? getFacetedMinMaxValues()
173
+ : undefined,
170
174
  getFacetedRowModel: clientFiltering ? getFacetedRowModel() : undefined,
171
175
  getFacetedUniqueValues: clientFiltering
172
176
  ? getFacetedUniqueValuesWithArrayValues()
@@ -0,0 +1,15 @@
1
+ import { RowData } from "@tanstack/react-table";
2
+
3
+ /**
4
+ * Type guard to check if a row has a specific key.
5
+ * Useful for generic accessor functions.
6
+ * @param row - The row to check.
7
+ * @param key - The key to check.
8
+ * @returns True if the row has the specified key, false otherwise.
9
+ */
10
+ export function rowHasKey<T extends RowData, K extends PropertyKey, TValue>(
11
+ row: T,
12
+ key: K
13
+ ): row is T & Record<K, TValue> {
14
+ return row != null && typeof row === "object" && key in row;
15
+ }