@databiosphere/findable-ui 42.1.0 → 43.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 (77) hide show
  1. package/.release-please-manifest.json +1 -1
  2. package/CHANGELOG.md +13 -0
  3. package/lib/common/categories/config/range/typeGuards.d.ts +8 -0
  4. package/lib/common/categories/config/range/typeGuards.js +9 -0
  5. package/lib/components/Filter/components/SearchAllFilters/common/constants.d.ts +3 -13
  6. package/lib/components/Filter/components/SearchAllFilters/common/constants.js +12 -36
  7. package/lib/components/Filter/components/SearchAllFilters/components/OutlinedInput/constants.d.ts +2 -0
  8. package/lib/components/Filter/components/SearchAllFilters/components/OutlinedInput/constants.js +7 -0
  9. package/lib/components/Filter/components/SearchAllFilters/components/OutlinedInput/outlinedInput.d.ts +2 -0
  10. package/lib/components/Filter/components/SearchAllFilters/components/OutlinedInput/outlinedInput.js +13 -0
  11. package/lib/components/Filter/components/SearchAllFilters/components/OutlinedInput/utils.d.ts +11 -0
  12. package/lib/components/Filter/components/SearchAllFilters/components/OutlinedInput/utils.js +17 -0
  13. package/lib/components/Filter/components/SearchAllFilters/components/VariableSizeList/VariableSizeList.d.ts +2 -0
  14. package/lib/components/Filter/components/SearchAllFilters/components/VariableSizeList/VariableSizeList.js +9 -10
  15. package/lib/components/Filter/components/SearchAllFilters/context/context.d.ts +2 -0
  16. package/lib/components/Filter/components/SearchAllFilters/context/context.js +10 -0
  17. package/lib/components/Filter/components/SearchAllFilters/context/hook.d.ts +6 -0
  18. package/lib/components/Filter/components/SearchAllFilters/context/hook.js +9 -0
  19. package/lib/components/Filter/components/SearchAllFilters/context/types.d.ts +11 -0
  20. package/lib/components/Filter/components/SearchAllFilters/context/types.js +1 -0
  21. package/lib/components/Filter/components/SearchAllFilters/searchAllFilters.d.ts +2 -19
  22. package/lib/components/Filter/components/SearchAllFilters/searchAllFilters.js +37 -74
  23. package/lib/components/Filter/components/SearchAllFilters/searchAllFilters.styles.d.ts +2 -2
  24. package/lib/components/Filter/components/SearchAllFilters/searchAllFilters.styles.js +7 -8
  25. package/lib/components/Filter/components/SearchAllFilters/stories/args.d.ts +3 -0
  26. package/lib/components/Filter/components/SearchAllFilters/stories/args.js +8 -0
  27. package/lib/components/Filter/components/SearchAllFilters/stories/searchAllFilters.stories.d.ts +6 -0
  28. package/lib/components/Filter/components/SearchAllFilters/stories/searchAllFilters.stories.js +19 -0
  29. package/lib/components/Filter/components/SearchAllFilters/types.d.ts +10 -0
  30. package/lib/components/Filter/components/SearchAllFilters/types.js +1 -0
  31. package/lib/components/Filter/components/adapters/tanstack/ColumnFiltersAdapter/columnFiltersAdapter.js +10 -1
  32. package/lib/components/Filter/components/adapters/tanstack/ColumnFiltersAdapter/types.d.ts +5 -1
  33. package/lib/components/Filter/components/adapters/tanstack/ColumnFiltersAdapter/utils.js +105 -10
  34. package/lib/components/Filter/components/surfaces/popper/Popper/popper.styles.d.ts +6 -0
  35. package/lib/components/Filter/components/surfaces/popper/Popper/popper.styles.js +18 -0
  36. package/lib/components/Filter/components/surfaces/types.d.ts +3 -1
  37. package/lib/components/Filter/components/surfaces/types.js +2 -0
  38. package/lib/styles/common/mui/textField.d.ts +11 -0
  39. package/lib/styles/common/mui/textField.js +22 -0
  40. package/lib/views/ExploreView/exploreView.js +1 -1
  41. package/package.json +1 -1
  42. package/src/common/categories/config/range/typeGuards.ts +14 -0
  43. package/src/components/Filter/components/SearchAllFilters/common/constants.ts +24 -37
  44. package/src/components/Filter/components/SearchAllFilters/components/OutlinedInput/constants.ts +9 -0
  45. package/src/components/Filter/components/SearchAllFilters/components/OutlinedInput/outlinedInput.tsx +31 -0
  46. package/src/components/Filter/components/SearchAllFilters/components/OutlinedInput/utils.ts +22 -0
  47. package/src/components/Filter/components/SearchAllFilters/components/VariableSizeList/VariableSizeList.tsx +12 -13
  48. package/src/components/Filter/components/SearchAllFilters/context/context.ts +12 -0
  49. package/src/components/Filter/components/SearchAllFilters/context/hook.ts +11 -0
  50. package/src/components/Filter/components/SearchAllFilters/context/types.ts +12 -0
  51. package/src/components/Filter/components/SearchAllFilters/searchAllFilters.styles.ts +8 -9
  52. package/src/components/Filter/components/SearchAllFilters/searchAllFilters.tsx +70 -132
  53. package/src/components/Filter/components/SearchAllFilters/stories/args.ts +15 -0
  54. package/src/components/Filter/components/SearchAllFilters/stories/searchAllFilters.stories.tsx +29 -0
  55. package/src/components/Filter/components/SearchAllFilters/types.ts +16 -0
  56. package/src/components/Filter/components/adapters/tanstack/ColumnFiltersAdapter/columnFiltersAdapter.tsx +12 -1
  57. package/src/components/Filter/components/adapters/tanstack/ColumnFiltersAdapter/types.ts +10 -1
  58. package/src/components/Filter/components/adapters/tanstack/ColumnFiltersAdapter/utils.ts +147 -12
  59. package/src/components/Filter/components/surfaces/popper/Popper/popper.styles.ts +20 -0
  60. package/src/components/Filter/components/surfaces/types.ts +2 -0
  61. package/src/styles/common/mui/textField.ts +33 -0
  62. package/src/views/ExploreView/exploreView.tsx +3 -0
  63. package/lib/components/Filter/components/SearchAllFilters/components/AutocompletePopper/autocompletePopper.styles.d.ts +0 -3
  64. package/lib/components/Filter/components/SearchAllFilters/components/AutocompletePopper/autocompletePopper.styles.js +0 -15
  65. package/lib/components/Filter/components/SearchAllFilters/searchAllFilters.stories.d.ts +0 -6
  66. package/lib/components/Filter/components/SearchAllFilters/searchAllFilters.stories.js +0 -82
  67. package/lib/components/Filter/components/SearchAllFiltersSearch/components/SearchCloseButton/searchCloseButton.d.ts +0 -1
  68. package/lib/components/Filter/components/SearchAllFiltersSearch/components/SearchCloseButton/searchCloseButton.js +0 -13
  69. package/lib/components/Filter/components/SearchAllFiltersSearch/searchAllFiltersSearch.d.ts +0 -2
  70. package/lib/components/Filter/components/SearchAllFiltersSearch/searchAllFiltersSearch.js +0 -12
  71. package/lib/components/Filter/components/SearchAllFiltersSearch/searchAllFiltersSearch.styles.d.ts +0 -5
  72. package/lib/components/Filter/components/SearchAllFiltersSearch/searchAllFiltersSearch.styles.js +0 -34
  73. package/src/components/Filter/components/SearchAllFilters/components/AutocompletePopper/autocompletePopper.styles.ts +0 -16
  74. package/src/components/Filter/components/SearchAllFilters/searchAllFilters.stories.tsx +0 -92
  75. package/src/components/Filter/components/SearchAllFiltersSearch/components/SearchCloseButton/searchCloseButton.tsx +0 -25
  76. package/src/components/Filter/components/SearchAllFiltersSearch/searchAllFiltersSearch.styles.ts +0 -35
  77. package/src/components/Filter/components/SearchAllFiltersSearch/searchAllFiltersSearch.tsx +0 -29
@@ -1,61 +1,25 @@
1
- import {
2
- AutocompleteRenderInputParams,
3
- ListProps as MListProps,
4
- } from "@mui/material";
5
- import React, {
6
- ChangeEvent,
7
- createContext,
8
- useContext,
9
- useEffect,
10
- useRef,
11
- useState,
12
- } from "react";
1
+ import { ListProps as MListProps } from "@mui/material";
2
+ import React, { useCallback, useRef, useState } from "react";
13
3
  import { isSelectCategoryView } from "../../../../common/categories/views/select/typeGuards";
14
- import { CategoryView } from "../../../../common/categories/views/types";
15
- import { SelectCategoryView } from "../../../../common/entities";
16
4
  import { SELECTOR } from "../../../../common/selectors";
17
- import {
18
- BREAKPOINT_FN_NAME,
19
- useBreakpointHelper,
20
- } from "../../../../hooks/useBreakpointHelper";
21
- import { OnFilterFn } from "../../../../hooks/useCategoryFilter";
22
5
  import { TEST_IDS } from "../../../../tests/testIds";
23
- import { useDrawer } from "../../../common/Drawer/provider/hook";
24
- import { SearchCloseButton } from "../SearchAllFiltersSearch/components/SearchCloseButton/searchCloseButton";
25
- import { SearchAllFiltersSearch } from "../SearchAllFiltersSearch/searchAllFiltersSearch";
26
- import { DEFAULT_SLOT_PROPS, DRAWER_SLOT_PROPS } from "./common/constants";
6
+ import {
7
+ StyledPopper,
8
+ StyledPopperDrawer,
9
+ } from "../surfaces/popper/Popper/popper.styles";
10
+ import { SURFACE_TYPE } from "../surfaces/types";
11
+ import {
12
+ POPPER_DRAWER_SLOT_PROPS,
13
+ POPPER_MENU_SLOT_PROPS,
14
+ } from "./common/constants";
27
15
  import { OVERFLOW_STYLE } from "./common/entites";
28
16
  import { setElementsOverflowStyle } from "./common/utils";
29
- import { AutocompletePopper } from "./components/AutocompletePopper/autocompletePopper.styles";
17
+ import { OutlinedInput } from "./components/OutlinedInput/outlinedInput";
30
18
  import { VariableSizeList } from "./components/VariableSizeList/VariableSizeList";
31
- import { Autocomplete } from "./searchAllFilters.styles";
32
-
33
- export interface SearchAllFiltersProps {
34
- categoryViews: CategoryView[];
35
- onFilter: OnFilterFn;
36
- }
37
-
38
- interface ListboxContextValue {
39
- onClearSearch: () => void;
40
- onCloseSearch: () => void;
41
- onFilter: OnFilterFn;
42
- open: boolean;
43
- searchTerm: string;
44
- selectCategoryViews: SelectCategoryView[];
45
- }
46
-
47
- const renderInput = (params: AutocompleteRenderInputParams): JSX.Element => (
48
- <SearchAllFiltersSearch {...params} />
49
- );
50
-
51
- export const ListboxContext = createContext<ListboxContextValue>({
52
- onClearSearch: (): void => undefined,
53
- onCloseSearch: (): void => undefined,
54
- onFilter: (): void => undefined,
55
- open: false,
56
- searchTerm: "",
57
- selectCategoryViews: [],
58
- });
19
+ import { AutocompleteContext } from "./context/context";
20
+ import { useAutocomplete } from "./context/hook";
21
+ import { StyledAutocomplete } from "./searchAllFilters.styles";
22
+ import { SearchAllFiltersProps } from "./types";
59
23
 
60
24
  const Listbox = React.forwardRef<HTMLUListElement, MListProps>(function Listbox(
61
25
  props,
@@ -64,8 +28,8 @@ const Listbox = React.forwardRef<HTMLUListElement, MListProps>(function Listbox(
64
28
  props = Object.assign({}, props, {
65
29
  children: undefined, // Content is controlled by VariableSizeList
66
30
  });
67
- const { onFilter, searchTerm, selectCategoryViews } =
68
- useContext(ListboxContext);
31
+ const { onFilter, searchTerm, selectCategoryViews, surfaceType } =
32
+ useAutocomplete();
69
33
  return (
70
34
  <VariableSizeList
71
35
  autocompleteListProps={props}
@@ -73,120 +37,94 @@ const Listbox = React.forwardRef<HTMLUListElement, MListProps>(function Listbox(
73
37
  ref={ref}
74
38
  searchTerm={searchTerm}
75
39
  selectCategoryViews={selectCategoryViews}
40
+ surfaceType={surfaceType}
76
41
  />
77
42
  );
78
43
  });
79
44
 
80
45
  export const SearchAllFilters = ({
81
46
  categoryViews,
47
+ className,
82
48
  onFilter,
49
+ surfaceType = SURFACE_TYPE.POPPER_MENU,
50
+ ...props /* Mui AutocompleteProps */
83
51
  }: SearchAllFiltersProps): JSX.Element => {
84
- const { open: isDrawerOpen } = useDrawer();
85
- const bpUpMd = useBreakpointHelper(BREAKPOINT_FN_NAME.UP, "md");
86
52
  const autocompleteRef = useRef<HTMLDivElement>(null);
87
53
  const [open, setOpen] = useState(false);
88
54
  const [searchTerm, setSearchTerm] = useState("");
89
- const selectCategoryViews = categoryViews.filter((view) =>
90
- isSelectCategoryView(view)
91
- );
92
-
93
- // Handles background scroll action ("md" and up).
94
- const handleBackgroundScroll = (overflowStyle: OVERFLOW_STYLE): void => {
95
- if (bpUpMd) {
96
- setElementsOverflowStyle(
97
- [
98
- document.querySelector(SELECTOR.BODY),
99
- document.getElementById(SELECTOR.SIDEBAR_POSITIONER),
100
- ],
101
- overflowStyle
102
- );
103
- }
104
- };
55
+ const selectCategoryViews = categoryViews.filter(isSelectCategoryView);
105
56
 
106
- // Callback fired when the value is changed.
107
- const onChange = (event: ChangeEvent<HTMLInputElement>): void => {
108
- setSearchTerm(event.target.value);
109
- };
110
-
111
- // Clear search
112
- const onClearSearch = (): void => {
113
- setSearchTerm("");
114
- };
57
+ // Handles background scroll action.
58
+ const handleBackgroundScroll = useCallback(
59
+ (overflowStyle: OVERFLOW_STYLE): void => {
60
+ if (surfaceType === SURFACE_TYPE.POPPER_MENU) {
61
+ setElementsOverflowStyle(
62
+ [
63
+ document.querySelector(SELECTOR.BODY),
64
+ document.getElementById(SELECTOR.SIDEBAR_POSITIONER),
65
+ ],
66
+ overflowStyle
67
+ );
68
+ }
69
+ },
70
+ [surfaceType]
71
+ );
115
72
 
116
- // Close search.
117
- const onCloseSearch = (): void => {
73
+ const onClose = useCallback((): void => {
118
74
  setSearchTerm("");
119
75
  setOpen(false);
120
76
  handleBackgroundScroll(OVERFLOW_STYLE.NONE);
121
- };
122
-
123
- // Callback fired when the popup requests to be opened.
124
- const onOpen = (): void => {
125
- handleBackgroundScroll(OVERFLOW_STYLE.HIDDEN);
126
- };
77
+ setTimeout(() => {
78
+ autocompleteRef.current?.querySelector("input")?.blur();
79
+ }, 100);
80
+ }, [handleBackgroundScroll]);
127
81
 
128
- // Open search.
129
- const onOpenSearch = (): void => {
130
- if (open) {
131
- return;
132
- }
82
+ const onOpen = useCallback((): void => {
133
83
  setOpen(true);
134
- };
135
-
136
- useEffect(() => {
137
- if (!open) {
138
- autocompleteRef.current?.querySelector("input")?.blur();
139
- }
140
- }, [open]);
84
+ handleBackgroundScroll(OVERFLOW_STYLE.HIDDEN);
85
+ }, [handleBackgroundScroll]);
141
86
 
142
- // Close search when filter drawer is closed.
143
- useEffect(() => {
144
- if (!isDrawerOpen) {
145
- setSearchTerm("");
146
- setOpen(false);
147
- }
148
- }, [isDrawerOpen]);
87
+ const onClear = useCallback((): void => {
88
+ setSearchTerm("");
89
+ if (surfaceType === SURFACE_TYPE.POPPER_DRAWER) onClose();
90
+ }, [onClose, surfaceType]);
149
91
 
150
92
  return (
151
- <ListboxContext.Provider
93
+ <AutocompleteContext.Provider
152
94
  value={{
153
- onClearSearch,
154
- onCloseSearch,
95
+ onClear,
155
96
  onFilter,
156
97
  open,
157
98
  searchTerm,
158
99
  selectCategoryViews,
100
+ surfaceType,
159
101
  }}
160
102
  >
161
- <Autocomplete
162
- clearOnBlur={bpUpMd}
103
+ <StyledAutocomplete
104
+ className={className}
163
105
  data-testid={TEST_IDS.SEARCH_ALL_FILTERS}
164
106
  filterOptions={(options): string[] => options}
165
107
  freeSolo
166
- ListboxComponent={Listbox}
167
- onBlur={bpUpMd ? onCloseSearch : undefined}
168
- onClose={bpUpMd ? onCloseSearch : undefined}
169
- onFocus={onOpenSearch}
108
+ onClose={onClose}
109
+ onInputChange={(_, v = "") => setSearchTerm(v)}
170
110
  onOpen={onOpen}
171
111
  open={open}
172
112
  options={[""]} // Placeholder options, since item rendering is fully controlled by VariableSizeList
173
- PopperComponent={AutocompletePopper}
174
113
  ref={autocompleteRef}
175
- renderInput={(props): JSX.Element =>
176
- renderInput({
177
- ...props,
178
- InputProps: {
179
- ...props.InputProps,
180
- endAdornment: <SearchCloseButton />,
181
- },
182
- inputProps: {
183
- ...props.inputProps,
184
- onChange,
185
- },
186
- })
114
+ renderInput={OutlinedInput}
115
+ slotProps={
116
+ surfaceType === SURFACE_TYPE.POPPER_MENU
117
+ ? POPPER_MENU_SLOT_PROPS
118
+ : POPPER_DRAWER_SLOT_PROPS
119
+ }
120
+ slots={
121
+ surfaceType === SURFACE_TYPE.POPPER_MENU
122
+ ? { listbox: Listbox, popper: StyledPopper }
123
+ : { listbox: Listbox, popper: StyledPopperDrawer }
187
124
  }
188
- slotProps={bpUpMd ? DEFAULT_SLOT_PROPS : DRAWER_SLOT_PROPS}
125
+ value={searchTerm}
126
+ {...props}
189
127
  />
190
- </ListboxContext.Provider>
128
+ </AutocompleteContext.Provider>
191
129
  );
192
130
  };
@@ -0,0 +1,15 @@
1
+ import { fn } from "@storybook/test";
2
+ import { ComponentProps } from "react";
3
+ import {
4
+ BIOLOGICAL_SEX,
5
+ DONOR_COUNT,
6
+ GENUS_SPECIES,
7
+ } from "../../Filters/stories/constants";
8
+ import { SURFACE_TYPE } from "../../surfaces/types";
9
+ import { SearchAllFilters } from "../searchAllFilters";
10
+
11
+ export const DEFAULT_ARGS: ComponentProps<typeof SearchAllFilters> = {
12
+ categoryViews: [BIOLOGICAL_SEX, GENUS_SPECIES, DONOR_COUNT],
13
+ onFilter: fn(),
14
+ surfaceType: SURFACE_TYPE.POPPER_MENU,
15
+ };
@@ -0,0 +1,29 @@
1
+ import { Box } from "@mui/material";
2
+ import { Meta, StoryObj } from "@storybook/react";
3
+ import React from "react";
4
+ import { PALETTE } from "../../../../../styles/common/constants/palette";
5
+ import { SearchAllFilters } from "../searchAllFilters";
6
+ import { DEFAULT_ARGS } from "./args";
7
+
8
+ const meta: Meta<typeof SearchAllFilters> = {
9
+ component: SearchAllFilters,
10
+ decorators: [
11
+ (Story): JSX.Element => (
12
+ <Box sx={{ backgroundColor: PALETTE.COMMON_WHITE, minWidth: 264 }}>
13
+ <Story />
14
+ </Box>
15
+ ),
16
+ ],
17
+ };
18
+
19
+ export default meta;
20
+
21
+ type Story = StoryObj<typeof meta>;
22
+
23
+ const DefaultStory = (): JSX.Element => {
24
+ return <SearchAllFilters {...DEFAULT_ARGS} />;
25
+ };
26
+
27
+ export const Default: Story = {
28
+ render: () => <DefaultStory />,
29
+ };
@@ -0,0 +1,16 @@
1
+ import { AutocompleteProps } from "@mui/material";
2
+ import { CategoryView } from "../../../../common/categories/views/types";
3
+ import { OnFilterFn } from "../../../../hooks/useCategoryFilter";
4
+ import { BaseComponentProps } from "../../../types";
5
+ import { SURFACE_TYPE } from "../surfaces/types";
6
+
7
+ export interface SearchAllFiltersProps
8
+ extends Omit<
9
+ AutocompleteProps<string, false, false, true>,
10
+ "options" | "renderInput"
11
+ >,
12
+ BaseComponentProps {
13
+ categoryViews: CategoryView[];
14
+ onFilter: OnFilterFn;
15
+ surfaceType?: SURFACE_TYPE;
16
+ }
@@ -1,5 +1,6 @@
1
1
  import { RowData } from "@tanstack/react-table";
2
2
  import { useCallback } from "react";
3
+ import { VIEW_KIND } from "../../../../../../common/categories/views/types";
3
4
  import {
4
5
  CategoryKey,
5
6
  CategoryValueKey,
@@ -22,13 +23,23 @@ export const ColumnFiltersAdapter = <T extends RowData>({
22
23
  categoryKey: CategoryKey | ClearAll,
23
24
  selectedCategoryValue: CategoryValueKey,
24
25
  // eslint-disable-next-line @typescript-eslint/no-unused-vars -- `selected` is not required by TanStack adapter.
25
- _selected: boolean
26
+ _selected: boolean,
27
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars -- `categorySection` is not required by TanStack adapter.
28
+ _categorySection?: string,
29
+ viewKind?: VIEW_KIND
26
30
  ) => {
27
31
  if (categoryKey === CLEAR_ALL) {
28
32
  table.resetColumnFilters();
29
33
  return;
30
34
  }
31
35
 
36
+ // Range filters use direct value assignment (e.g., [min, max] tuple for numeric ranges).
37
+ if (viewKind === VIEW_KIND.RANGE) {
38
+ table.getColumn(categoryKey)?.setFilterValue(selectedCategoryValue);
39
+ return;
40
+ }
41
+
42
+ // Select filters use an updater function to toggle individual values in/out of the filter array.
32
43
  table
33
44
  .getColumn(categoryKey)
34
45
  ?.setFilterValue(updater(selectedCategoryValue));
@@ -1,7 +1,16 @@
1
- import { RowData, Table } from "@tanstack/react-table";
1
+ import {
2
+ RowData,
3
+ Table,
4
+ TableMeta as TanStackTableMeta,
5
+ } from "@tanstack/react-table";
6
+ import { CategoryGroup } from "../../../../../../config/entities";
2
7
  import { SurfaceProps } from "../../../surfaces/types";
3
8
 
4
9
  export interface ColumnFiltersAdapterProps<T extends RowData> {
5
10
  renderSurface: (props: SurfaceProps) => JSX.Element | null;
6
11
  table: Table<T>;
7
12
  }
13
+ export interface ColumnFiltersTableMeta<T extends RowData>
14
+ extends TanStackTableMeta<T> {
15
+ categoryGroups?: CategoryGroup[];
16
+ }
@@ -1,9 +1,52 @@
1
1
  import { Column, RowData, Table } from "@tanstack/react-table";
2
+ import { isRangeCategoryConfig } from "../../../../../../common/categories/config/range/typeGuards";
3
+ import { CategoryConfig } from "../../../../../../common/categories/config/types";
4
+ import { RangeCategoryView } from "../../../../../../common/categories/views/range/types";
2
5
  import { CategoryView } from "../../../../../../common/categories/views/types";
3
- import { SelectCategoryValueView } from "../../../../../../common/entities";
6
+ import {
7
+ SelectCategoryValueView,
8
+ SelectCategoryView,
9
+ } from "../../../../../../common/entities";
10
+ import { CategoryGroup } from "../../../../../../config/entities";
4
11
  import { getColumnHeader } from "../../../../../Table/common/utils";
5
12
  import { getSortedFacetedValues } from "../../../../../Table/featureOptions/facetedColumn/utils";
13
+ import { CategoryFilter } from "../../../Filters/filters";
6
14
  import { SurfaceProps } from "../../../surfaces/types";
15
+ import { ColumnFiltersTableMeta } from "./types";
16
+
17
+ /**
18
+ * Adapter for TanStack table to category configs.
19
+ * @param table - Table.
20
+ * @returns Category configs.
21
+ */
22
+ function buildCategoryConfigs<T extends RowData>(
23
+ table: Table<T>
24
+ ): CategoryConfig[] {
25
+ return table
26
+ .getAllColumns()
27
+ .filter((column) => column.getCanFilter())
28
+ .map(mapCategoryConfig);
29
+ }
30
+
31
+ /**
32
+ * Adapter for TanStack table to category filters.
33
+ * @param table - Table.
34
+ * @param categoryGroups - Category groups.
35
+ * @returns Category filters.
36
+ */
37
+ function buildCategoryFilters<T extends RowData>(
38
+ table: Table<T>,
39
+ categoryGroups: CategoryGroup[]
40
+ ): SurfaceProps["categoryFilters"] {
41
+ return categoryGroups.reduce<SurfaceProps["categoryFilters"]>(
42
+ (acc, categoryGroup) => {
43
+ const categoryFilter = mapCategoryFilter(table, categoryGroup);
44
+ if (categoryFilter) acc.push(categoryFilter);
45
+ return acc;
46
+ },
47
+ []
48
+ );
49
+ }
7
50
 
8
51
  /**
9
52
  * Adapter for TanStack table column filters to category filters.
@@ -13,13 +56,19 @@ import { SurfaceProps } from "../../../surfaces/types";
13
56
  export function buildColumnFilters<T extends RowData>(
14
57
  table: Table<T>
15
58
  ): SurfaceProps["categoryFilters"] {
16
- // Build the category views; single category filter.
17
- const categoryViews = table
18
- .getAllColumns()
19
- .filter((column) => column.getCanFilter())
20
- .map(mapColumnToCategoryView);
59
+ const { options } = table;
60
+ const { meta = {} } = options;
61
+ const { categoryGroups } = meta as ColumnFiltersTableMeta<T>;
21
62
 
22
- return [{ categoryViews }];
63
+ if (!categoryGroups) {
64
+ // Build single category group with all (filterable) columns.
65
+ const categoryConfigs: CategoryConfig[] = buildCategoryConfigs(table);
66
+ // Build category filters from single category group.
67
+ return buildCategoryFilters(table, [{ categoryConfigs, label: "" }]);
68
+ }
69
+
70
+ // Build category filters from category groups.
71
+ return buildCategoryFilters(table, categoryGroups);
23
72
  }
24
73
 
25
74
  /**
@@ -39,14 +88,99 @@ export function getColumnFiltersCount<T extends RowData>(
39
88
  }
40
89
 
41
90
  /**
42
- * Adapter for TanStack column to category view.
43
- * Currently supports only select category views.
91
+ * Adapter for TanStack column to category config.
44
92
  * @param column - Column.
45
- * @returns Category view.
93
+ * @returns Category config.
46
94
  */
47
- function mapColumnToCategoryView<T extends RowData>(
95
+ function mapCategoryConfig<T extends RowData>(
48
96
  column: Column<T>
49
- ): CategoryView {
97
+ ): CategoryConfig {
98
+ return {
99
+ key: column.id,
100
+ label: getColumnHeader(column),
101
+ };
102
+ }
103
+
104
+ /**
105
+ * Adapter for TanStack table to category filter.
106
+ * @param table - Table.
107
+ * @param categoryGroup - Category group.
108
+ * @returns Category filter.
109
+ */
110
+ function mapCategoryFilter<T extends RowData>(
111
+ table: Table<T>,
112
+ categoryGroup: CategoryGroup
113
+ ): CategoryFilter | undefined {
114
+ const { categoryConfigs, label } = categoryGroup;
115
+
116
+ const categoryViews = categoryConfigs.reduce<CategoryView[]>(
117
+ (acc, categoryConfig) => {
118
+ const column = table.getColumn(categoryConfig.key);
119
+ if (!column) return acc;
120
+ if (!column.getCanFilter()) return acc;
121
+
122
+ let categoryView: CategoryView;
123
+
124
+ if (isRangeCategoryConfig(categoryConfig)) {
125
+ // Build range category view.
126
+ categoryView = mapColumnToRangeCategoryView(column, categoryConfig);
127
+ } else {
128
+ // Build select category view.
129
+ categoryView = mapColumnToSelectCategoryView(column, categoryConfig);
130
+ }
131
+
132
+ return [...acc, categoryView];
133
+ },
134
+ []
135
+ );
136
+
137
+ if (categoryViews.length === 0) return;
138
+
139
+ return { categoryViews, label };
140
+ }
141
+
142
+ /**
143
+ * Adapter for TanStack column to range category view.
144
+ * @param column - Column.
145
+ * @param categoryConfig - Category config.
146
+ * @returns Range category view.
147
+ */
148
+ function mapColumnToRangeCategoryView<T extends RowData>(
149
+ column: Column<T>,
150
+ categoryConfig?: CategoryConfig
151
+ ): RangeCategoryView {
152
+ const minMax = column.getFacetedMinMaxValues();
153
+ const isDisabled = !minMax;
154
+
155
+ // Selected values for the column.
156
+ const filterValue = (column.getFilterValue() || [null, null]) as [
157
+ number | null,
158
+ number | null
159
+ ];
160
+
161
+ return {
162
+ annotation: undefined,
163
+ isDisabled,
164
+ key: column.id,
165
+ label: getColumnHeader(column),
166
+ max: minMax?.[1] || Infinity,
167
+ min: minMax?.[0] || -Infinity,
168
+ selectedMax: filterValue[1],
169
+ selectedMin: filterValue[0],
170
+ ...categoryConfig,
171
+ };
172
+ }
173
+
174
+ /**
175
+ * Adapter for TanStack column to select category view.
176
+ * @param column - Column.
177
+ * @param categoryConfig - Category config.
178
+ * @returns Select category view.
179
+ */
180
+ function mapColumnToSelectCategoryView<T extends RowData>(
181
+ column: Column<T>,
182
+ categoryConfig?: CategoryConfig
183
+ ): SelectCategoryView {
50
184
  const facetedUniqueValues = column.getFacetedUniqueValues();
51
185
  const isDisabled = facetedUniqueValues.size === 0;
52
186
  const values = mapColumnToSelectCategoryValueView(column);
@@ -57,6 +191,7 @@ function mapColumnToCategoryView<T extends RowData>(
57
191
  key: column.id,
58
192
  label: getColumnHeader(column),
59
193
  values,
194
+ ...categoryConfig,
60
195
  };
61
196
  }
62
197
 
@@ -0,0 +1,20 @@
1
+ import styled from "@emotion/styled";
2
+ import { Popper } from "@mui/material";
3
+ import { PALETTE } from "../../../../../../styles/common/constants/palette";
4
+
5
+ export const StyledPopper = styled(Popper)`
6
+ .MuiPaper-root {
7
+ width: 368px;
8
+ }
9
+ `;
10
+
11
+ export const StyledPopperDrawer = styled(Popper)`
12
+ .MuiPaper-root {
13
+ background-color: ${PALETTE.SMOKE_LIGHT};
14
+ width: 312px;
15
+
16
+ .MuiList-root {
17
+ margin: 0;
18
+ }
19
+ }
20
+ `;
@@ -10,4 +10,6 @@ export interface SurfaceProps {
10
10
  export enum SURFACE_TYPE {
11
11
  DRAWER = "DRAWER",
12
12
  MENU = "MENU",
13
+ POPPER_DRAWER = "POPPER_DRAWER",
14
+ POPPER_MENU = "POPPER_MENU",
13
15
  }
@@ -0,0 +1,33 @@
1
+ import { TextFieldProps } from "@mui/material";
2
+
3
+ type TextFieldPropsOptions = {
4
+ COLOR: typeof COLOR;
5
+ SIZE: typeof SIZE;
6
+ VARIANT: typeof VARIANT;
7
+ };
8
+
9
+ const COLOR: Record<string, TextFieldProps["color"]> = {
10
+ ERROR: "error",
11
+ INFO: "info",
12
+ PRIMARY: "primary",
13
+ SECONDARY: "secondary",
14
+ SUCCESS: "success",
15
+ WARNING: "warning",
16
+ } as const;
17
+
18
+ const SIZE: Record<string, TextFieldProps["size"]> = {
19
+ MEDIUM: "medium",
20
+ SMALL: "small",
21
+ } as const;
22
+
23
+ const VARIANT: Record<string, TextFieldProps["variant"]> = {
24
+ FILLED: "filled",
25
+ OUTLINED: "outlined",
26
+ STANDARD: "standard",
27
+ } as const;
28
+
29
+ export const TEXT_FIELD_PROPS: TextFieldPropsOptions = {
30
+ COLOR,
31
+ SIZE,
32
+ VARIANT,
33
+ };
@@ -135,6 +135,9 @@ export const ExploreView = (props: ExploreViewProps): JSX.Element => {
135
135
  <SearchAllFilters
136
136
  categoryViews={categoryViews}
137
137
  onFilter={onFilterChange.bind(null, true)}
138
+ surfaceType={
139
+ mdDown ? SURFACE_TYPE.POPPER_DRAWER : SURFACE_TYPE.POPPER_MENU
140
+ }
138
141
  />
139
142
  </SidebarTools>
140
143
  <Filters
@@ -1,3 +0,0 @@
1
- export declare const AutocompletePopper: import("@emotion/styled").StyledComponent<import("@mui/material").PopperProps & import("react").RefAttributes<HTMLDivElement> & {
2
- theme?: import("@emotion/react").Theme;
3
- }, {}, {}>;
@@ -1,15 +0,0 @@
1
- import styled from "@emotion/styled";
2
- import { Popper as MPopper } from "@mui/material";
3
- import { PALETTE } from "../../../../../../styles/common/constants/palette";
4
- import { bpDownMd } from "../../../../../../styles/common/mixins/breakpoints";
5
- export const AutocompletePopper = styled(MPopper) `
6
- ${bpDownMd} {
7
- .MuiPaper-root {
8
- background-color: ${PALETTE.SMOKE_LIGHT};
9
-
10
- .MuiList-root {
11
- margin: 0;
12
- }
13
- }
14
- }
15
- `;
@@ -1,6 +0,0 @@
1
- import { Meta, StoryObj } from "@storybook/react";
2
- import { SearchAllFilters } from "./searchAllFilters";
3
- declare const _default: Meta<typeof SearchAllFilters>;
4
- export default _default;
5
- type Story = StoryObj<typeof SearchAllFilters>;
6
- export declare const SearchAllFiltersStory: Story;