@databiosphere/findable-ui 43.0.0 → 44.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 (96) hide show
  1. package/.release-please-manifest.json +1 -1
  2. package/CHANGELOG.md +14 -0
  3. package/lib/common/filters/sort/config/types.d.ts +13 -0
  4. package/lib/common/filters/sort/config/types.js +8 -0
  5. package/lib/common/filters/sort/config/utils.d.ts +8 -0
  6. package/lib/common/filters/sort/config/utils.js +9 -0
  7. package/lib/common/filters/sort/models/utils.d.ts +23 -0
  8. package/lib/common/filters/sort/models/utils.js +41 -0
  9. package/lib/components/Filter/components/adapters/tanstack/ColumnFiltersAdapter/columnFiltersAdapter.js +6 -1
  10. package/lib/components/Filter/components/adapters/tanstack/ColumnFiltersAdapter/hooks/UseUpdateFilterSort/hook.d.ts +3 -0
  11. package/lib/components/Filter/components/adapters/tanstack/ColumnFiltersAdapter/hooks/UseUpdateFilterSort/hook.js +8 -0
  12. package/lib/components/Filter/components/adapters/tanstack/ColumnFiltersAdapter/hooks/UseUpdateFilterSort/types.d.ts +6 -0
  13. package/lib/components/Filter/components/adapters/tanstack/ColumnFiltersAdapter/hooks/UseUpdateFilterSort/types.js +1 -0
  14. package/lib/components/Filter/components/adapters/tanstack/ColumnFiltersAdapter/hooks/UseUpdateFilterSort/utils.d.ts +14 -0
  15. package/lib/components/Filter/components/adapters/tanstack/ColumnFiltersAdapter/hooks/UseUpdateFilterSort/utils.js +23 -0
  16. package/lib/components/Filter/components/adapters/tanstack/ColumnFiltersAdapter/types.d.ts +2 -0
  17. package/lib/components/Filter/components/adapters/tanstack/ColumnFiltersAdapter/utils.d.ts +3 -1
  18. package/lib/components/Filter/components/adapters/tanstack/ColumnFiltersAdapter/utils.js +24 -16
  19. package/lib/components/Filter/components/controls/Controls/components/FilterSort/constants.d.ts +5 -0
  20. package/lib/components/Filter/components/controls/Controls/components/FilterSort/constants.js +17 -0
  21. package/lib/components/Filter/components/controls/Controls/components/FilterSort/filterSort.d.ts +2 -0
  22. package/lib/components/Filter/components/controls/Controls/components/FilterSort/filterSort.js +28 -0
  23. package/lib/components/Filter/components/controls/Controls/components/FilterSort/filterSort.styles.d.ts +9 -0
  24. package/lib/components/Filter/components/controls/Controls/components/FilterSort/filterSort.styles.js +25 -0
  25. package/lib/components/Filter/components/controls/Controls/components/FilterSort/stories/filterSort.stories.d.ts +6 -0
  26. package/lib/components/Filter/components/controls/Controls/components/FilterSort/stories/filterSort.stories.js +9 -0
  27. package/lib/components/Filter/components/controls/Controls/components/FilterSort/types.d.ts +6 -0
  28. package/lib/components/Filter/components/controls/Controls/components/FilterSort/types.js +1 -0
  29. package/lib/components/Filter/components/controls/Controls/controls.d.ts +2 -3
  30. package/lib/components/Filter/components/controls/Controls/controls.js +4 -2
  31. package/lib/components/Filter/components/controls/Controls/controls.styles.js +2 -1
  32. package/lib/components/Filter/components/controls/Controls/types.d.ts +6 -0
  33. package/lib/components/Filter/components/controls/Controls/types.js +1 -0
  34. package/lib/components/Filter/components/surfaces/types.d.ts +3 -1
  35. package/lib/components/Index/components/EntityView/components/views/ChartView/components/Chart/hooks/UsePlotOptions/hook.js +2 -2
  36. package/lib/components/Index/components/EntityView/components/views/ChartView/components/Chart/utils.d.ts +0 -7
  37. package/lib/components/Index/components/EntityView/components/views/ChartView/components/Chart/utils.js +0 -11
  38. package/lib/config/entities.d.ts +2 -0
  39. package/lib/hooks/useCategoryFilter.d.ts +4 -9
  40. package/lib/hooks/useCategoryFilter.js +5 -15
  41. package/lib/providers/exploreState/actions/updateFilterSort/action.d.ts +10 -0
  42. package/lib/providers/exploreState/actions/updateFilterSort/action.js +21 -0
  43. package/lib/providers/exploreState/actions/updateFilterSort/dispatch.d.ts +7 -0
  44. package/lib/providers/exploreState/actions/updateFilterSort/dispatch.js +12 -0
  45. package/lib/providers/exploreState/actions/updateFilterSort/types.d.ts +7 -0
  46. package/lib/providers/exploreState/actions/updateFilterSort/types.js +1 -0
  47. package/lib/providers/exploreState/actions/updateFilterSort/utils.d.ts +10 -0
  48. package/lib/providers/exploreState/actions/updateFilterSort/utils.js +22 -0
  49. package/lib/providers/exploreState/initializer/constants.js +2 -0
  50. package/lib/providers/exploreState/initializer/utils.js +2 -0
  51. package/lib/providers/exploreState.d.ts +5 -1
  52. package/lib/providers/exploreState.js +9 -1
  53. package/lib/tests/testIds.d.ts +2 -0
  54. package/lib/tests/testIds.js +2 -0
  55. package/lib/views/ExploreView/exploreView.js +7 -1
  56. package/lib/views/ExploreView/hooks/UseUpdateFilterSort/hook.d.ts +2 -0
  57. package/lib/views/ExploreView/hooks/UseUpdateFilterSort/hook.js +12 -0
  58. package/lib/views/ExploreView/hooks/UseUpdateFilterSort/types.d.ts +6 -0
  59. package/lib/views/ExploreView/hooks/UseUpdateFilterSort/types.js +1 -0
  60. package/package.json +1 -1
  61. package/src/common/filters/sort/config/types.ts +14 -0
  62. package/src/common/filters/sort/config/utils.ts +11 -0
  63. package/src/common/filters/sort/models/utils.ts +57 -0
  64. package/src/components/Filter/components/adapters/tanstack/ColumnFiltersAdapter/columnFiltersAdapter.tsx +11 -1
  65. package/src/components/Filter/components/adapters/tanstack/ColumnFiltersAdapter/hooks/UseUpdateFilterSort/hook.ts +22 -0
  66. package/src/components/Filter/components/adapters/tanstack/ColumnFiltersAdapter/hooks/UseUpdateFilterSort/types.ts +7 -0
  67. package/src/components/Filter/components/adapters/tanstack/ColumnFiltersAdapter/hooks/UseUpdateFilterSort/utils.ts +33 -0
  68. package/src/components/Filter/components/adapters/tanstack/ColumnFiltersAdapter/types.ts +2 -0
  69. package/src/components/Filter/components/adapters/tanstack/ColumnFiltersAdapter/utils.ts +36 -13
  70. package/src/components/Filter/components/controls/Controls/components/FilterSort/constants.ts +22 -0
  71. package/src/components/Filter/components/controls/Controls/components/FilterSort/filterSort.styles.ts +34 -0
  72. package/src/components/Filter/components/controls/Controls/components/FilterSort/filterSort.tsx +73 -0
  73. package/src/components/Filter/components/controls/Controls/components/FilterSort/stories/filterSort.stories.tsx +15 -0
  74. package/src/components/Filter/components/controls/Controls/components/FilterSort/types.ts +7 -0
  75. package/src/components/Filter/components/controls/Controls/controls.styles.ts +2 -1
  76. package/src/components/Filter/components/controls/Controls/controls.tsx +11 -3
  77. package/src/components/Filter/components/controls/Controls/types.ts +10 -0
  78. package/src/components/Filter/components/surfaces/types.ts +3 -1
  79. package/src/components/Index/components/EntityView/components/views/ChartView/components/Chart/hooks/UsePlotOptions/hook.ts +2 -2
  80. package/src/components/Index/components/EntityView/components/views/ChartView/components/Chart/utils.ts +0 -15
  81. package/src/config/entities.ts +2 -0
  82. package/src/hooks/useCategoryFilter.ts +8 -19
  83. package/src/providers/exploreState/actions/updateFilterSort/action.ts +30 -0
  84. package/src/providers/exploreState/actions/updateFilterSort/dispatch.ts +16 -0
  85. package/src/providers/exploreState/actions/updateFilterSort/types.ts +9 -0
  86. package/src/providers/exploreState/actions/updateFilterSort/utils.ts +30 -0
  87. package/src/providers/exploreState/initializer/constants.ts +2 -0
  88. package/src/providers/exploreState/initializer/utils.ts +2 -0
  89. package/src/providers/exploreState.tsx +14 -1
  90. package/src/tests/testIds.ts +2 -0
  91. package/src/views/ExploreView/exploreView.tsx +16 -1
  92. package/src/views/ExploreView/hooks/UseUpdateFilterSort/hook.ts +20 -0
  93. package/src/views/ExploreView/hooks/UseUpdateFilterSort/types.ts +7 -0
  94. package/tests/buildCategoryViews.test.ts +282 -0
  95. package/tests/filterSortUtils.test.ts +180 -0
  96. package/tests/getFilterSortType.test.ts +45 -0
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "43.0.0"
2
+ ".": "44.0.0"
3
3
  }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [44.0.0](https://github.com/DataBiosphere/findable-ui/compare/v43.0.0...v44.0.0) (2025-08-28)
4
+
5
+
6
+ ### ⚠ BREAKING CHANGES
7
+
8
+ * configure default filter sort ([#640](https://github.com/DataBiosphere/findable-ui/issues/640)) (#643)
9
+
10
+ ### Features
11
+
12
+ * configure default filter sort ([#640](https://github.com/DataBiosphere/findable-ui/issues/640)) ([#643](https://github.com/DataBiosphere/findable-ui/issues/643)) ([63be8d1](https://github.com/DataBiosphere/findable-ui/commit/63be8d1bb87e3a473385bd45f5196d1125cfb5ed))
13
+ * configure filter sort for tanstack column filtering ([#647](https://github.com/DataBiosphere/findable-ui/issues/647)) ([#648](https://github.com/DataBiosphere/findable-ui/issues/648)) ([c653a19](https://github.com/DataBiosphere/findable-ui/commit/c653a19ec4abd19a7aceafee6414d46f381c7544))
14
+ * create filter sort component ([#641](https://github.com/DataBiosphere/findable-ui/issues/641)) ([#644](https://github.com/DataBiosphere/findable-ui/issues/644)) ([7f80b7c](https://github.com/DataBiosphere/findable-ui/commit/7f80b7c2b76224cc3d6da7a435b678567bbb41c9))
15
+ * update filter sort ([#642](https://github.com/DataBiosphere/findable-ui/issues/642)) ([#645](https://github.com/DataBiosphere/findable-ui/issues/645)) ([e7470b5](https://github.com/DataBiosphere/findable-ui/commit/e7470b50d2c19e674c73e1de533559e2032516e6))
16
+
3
17
  ## [43.0.0](https://github.com/DataBiosphere/findable-ui/compare/v42.1.0...v43.0.0) (2025-08-19)
4
18
 
5
19
 
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Filter sort options.
3
+ */
4
+ export declare enum FILTER_SORT {
5
+ ALPHA = "ALPHA",
6
+ COUNT = "COUNT"
7
+ }
8
+ /**
9
+ * Filter sort configuration for site config.
10
+ */
11
+ export interface FilterSortConfig {
12
+ sortBy?: FILTER_SORT;
13
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Filter sort options.
3
+ */
4
+ export var FILTER_SORT;
5
+ (function (FILTER_SORT) {
6
+ FILTER_SORT["ALPHA"] = "ALPHA";
7
+ FILTER_SORT["COUNT"] = "COUNT";
8
+ })(FILTER_SORT || (FILTER_SORT = {}));
@@ -0,0 +1,8 @@
1
+ import { SiteConfig } from "../../../../config/entities";
2
+ import { FILTER_SORT } from "./types";
3
+ /**
4
+ * Returns the default filter sort type "ALPHA" or "COUNT" from config or ALPHA as fallback.
5
+ * @param config - Filter sort configuration.
6
+ * @returns default filter sort type.
7
+ */
8
+ export declare function getFilterSortType(config?: SiteConfig): FILTER_SORT;
@@ -0,0 +1,9 @@
1
+ import { FILTER_SORT } from "./types";
2
+ /**
3
+ * Returns the default filter sort type "ALPHA" or "COUNT" from config or ALPHA as fallback.
4
+ * @param config - Filter sort configuration.
5
+ * @returns default filter sort type.
6
+ */
7
+ export function getFilterSortType(config) {
8
+ return config?.filterSort?.sortBy ?? FILTER_SORT.ALPHA;
9
+ }
@@ -0,0 +1,23 @@
1
+ import { SelectCategoryValueView } from "../../../entities";
2
+ import { FILTER_SORT } from "../config/types";
3
+ /**
4
+ * Sort category value views based on filter sort configuration.
5
+ * Uses function selection pattern for efficiency.
6
+ * @param categoryValueViews - Array of category value views to sort.
7
+ * @param filterSort - Sort configuration (ALPHA or COUNT).
8
+ */
9
+ export declare function sortCategoryValueViews(categoryValueViews: SelectCategoryValueView[], filterSort: FILTER_SORT): void;
10
+ /**
11
+ * Sort category value views alphabetically.
12
+ * @param cvv0 - First category value view to compare.
13
+ * @param cvv1 - Second category value view to compare.
14
+ * @returns Number indicating sort precedence of cvv0 vs cvv1.
15
+ */
16
+ export declare function sortCategoryValueViewsAlpha(cvv0: SelectCategoryValueView, cvv1: SelectCategoryValueView): number;
17
+ /**
18
+ * Sort category value views by count (descending), then alphabetically.
19
+ * @param cvv0 - First category value view to compare.
20
+ * @param cvv1 - Second category value view to compare.
21
+ * @returns Number indicating sort precedence of cvv0 vs cvv1.
22
+ */
23
+ export declare function sortCategoryValueViewsCount(cvv0: SelectCategoryValueView, cvv1: SelectCategoryValueView): number;
@@ -0,0 +1,41 @@
1
+ import { COLLATOR_CASE_INSENSITIVE } from "../../../constants";
2
+ import { FILTER_SORT } from "../config/types";
3
+ /**
4
+ * Sort category value views based on filter sort configuration.
5
+ * Uses function selection pattern for efficiency.
6
+ * @param categoryValueViews - Array of category value views to sort.
7
+ * @param filterSort - Sort configuration (ALPHA or COUNT).
8
+ */
9
+ export function sortCategoryValueViews(categoryValueViews, filterSort) {
10
+ const sortFn = filterSort === FILTER_SORT.ALPHA
11
+ ? sortCategoryValueViewsAlpha
12
+ : sortCategoryValueViewsCount;
13
+ categoryValueViews.sort(sortFn);
14
+ }
15
+ /**
16
+ * Sort category value views alphabetically.
17
+ * @param cvv0 - First category value view to compare.
18
+ * @param cvv1 - Second category value view to compare.
19
+ * @returns Number indicating sort precedence of cvv0 vs cvv1.
20
+ */
21
+ export function sortCategoryValueViewsAlpha(cvv0, cvv1) {
22
+ // Handle empty labels.
23
+ if (!cvv0.label)
24
+ return 1;
25
+ if (!cvv1.label)
26
+ return -1;
27
+ return COLLATOR_CASE_INSENSITIVE.compare(cvv0.label, cvv1.label);
28
+ }
29
+ /**
30
+ * Sort category value views by count (descending), then alphabetically.
31
+ * @param cvv0 - First category value view to compare.
32
+ * @param cvv1 - Second category value view to compare.
33
+ * @returns Number indicating sort precedence of cvv0 vs cvv1.
34
+ */
35
+ export function sortCategoryValueViewsCount(cvv0, cvv1) {
36
+ // Sort by count descending, then alphabetically.
37
+ const countDiff = cvv1.count - cvv0.count;
38
+ if (countDiff !== 0)
39
+ return countDiff;
40
+ return sortCategoryValueViewsAlpha(cvv0, cvv1);
41
+ }
@@ -2,9 +2,11 @@ import { useCallback } from "react";
2
2
  import { VIEW_KIND } from "../../../../../../common/categories/views/types";
3
3
  import { CLEAR_ALL, } from "../../../../../../common/entities";
4
4
  import { updater } from "../../../../../Table/components/TableFeatures/ColumnFilter/utils";
5
+ import { useUpdateFilterSort } from "./hooks/UseUpdateFilterSort/hook";
5
6
  import { buildColumnFilters, getColumnFiltersCount } from "./utils";
6
7
  export const ColumnFiltersAdapter = ({ renderSurface, table, }) => {
7
- const categoryFilters = buildColumnFilters(table);
8
+ const { enabled: filterSortEnabled, filterSort, onFilterSortChange, } = useUpdateFilterSort(table);
9
+ const categoryFilters = buildColumnFilters(table, filterSort);
8
10
  const count = getColumnFiltersCount(table);
9
11
  const onFilter = useCallback((categoryKey, selectedCategoryValue,
10
12
  // eslint-disable-next-line @typescript-eslint/no-unused-vars -- `selected` is not required by TanStack adapter.
@@ -28,6 +30,9 @@ export const ColumnFiltersAdapter = ({ renderSurface, table, }) => {
28
30
  return renderSurface({
29
31
  categoryFilters,
30
32
  count,
33
+ filterSort,
34
+ filterSortEnabled,
31
35
  onFilter,
36
+ onFilterSortChange,
32
37
  });
33
38
  };
@@ -0,0 +1,3 @@
1
+ import { RowData, Table } from "@tanstack/react-table";
2
+ import { UseUpdateFilterSort } from "./types";
3
+ export declare const useUpdateFilterSort: <T extends RowData>(table: Table<T>) => UseUpdateFilterSort;
@@ -0,0 +1,8 @@
1
+ import { useCallback, useMemo, useState } from "react";
2
+ import { initFilterSort, isFilterSortEnabled } from "./utils";
3
+ export const useUpdateFilterSort = (table) => {
4
+ const [filterSort, setFilterSort] = useState(initFilterSort(table));
5
+ const enabled = useMemo(() => isFilterSortEnabled(table), [table]);
6
+ const onFilterSortChange = useCallback((value) => setFilterSort(value), []);
7
+ return { enabled, filterSort, onFilterSortChange };
8
+ };
@@ -0,0 +1,6 @@
1
+ import { FILTER_SORT } from "../../../../../../../../common/filters/sort/config/types";
2
+ export interface UseUpdateFilterSort {
3
+ enabled: boolean;
4
+ filterSort: FILTER_SORT;
5
+ onFilterSortChange: (filterSort: FILTER_SORT) => void;
6
+ }
@@ -0,0 +1,14 @@
1
+ import { RowData, Table } from "@tanstack/react-table";
2
+ import { FILTER_SORT } from "../../../../../../../../common/filters/sort/config/types";
3
+ /**
4
+ * Returns the default filter sort type "ALPHA" or "COUNT" from table meta or ALPHA as fallback.
5
+ * @param table - Table.
6
+ * @returns default filter sort type.
7
+ */
8
+ export declare function initFilterSort<T extends RowData>(table: Table<T>): FILTER_SORT;
9
+ /**
10
+ * Returns true if filter sort is enabled.
11
+ * @param table - Table.
12
+ * @returns true if filter sort is enabled.
13
+ */
14
+ export declare function isFilterSortEnabled<T extends RowData>(table: Table<T>): boolean;
@@ -0,0 +1,23 @@
1
+ import { FILTER_SORT } from "../../../../../../../../common/filters/sort/config/types";
2
+ /**
3
+ * Returns the default filter sort type "ALPHA" or "COUNT" from table meta or ALPHA as fallback.
4
+ * @param table - Table.
5
+ * @returns default filter sort type.
6
+ */
7
+ export function initFilterSort(table) {
8
+ const { options } = table;
9
+ const { meta = {} } = options;
10
+ const { filterSort } = meta;
11
+ return filterSort || FILTER_SORT.ALPHA;
12
+ }
13
+ /**
14
+ * Returns true if filter sort is enabled.
15
+ * @param table - Table.
16
+ * @returns true if filter sort is enabled.
17
+ */
18
+ export function isFilterSortEnabled(table) {
19
+ const { options } = table;
20
+ const { meta = {} } = options;
21
+ const { filterSort } = meta;
22
+ return Boolean(filterSort);
23
+ }
@@ -1,4 +1,5 @@
1
1
  import { RowData, Table, TableMeta as TanStackTableMeta } from "@tanstack/react-table";
2
+ import { FILTER_SORT } from "../../../../../../common/filters/sort/config/types";
2
3
  import { CategoryGroup } from "../../../../../../config/entities";
3
4
  import { SurfaceProps } from "../../../surfaces/types";
4
5
  export interface ColumnFiltersAdapterProps<T extends RowData> {
@@ -7,4 +8,5 @@ export interface ColumnFiltersAdapterProps<T extends RowData> {
7
8
  }
8
9
  export interface ColumnFiltersTableMeta<T extends RowData> extends TanStackTableMeta<T> {
9
10
  categoryGroups?: CategoryGroup[];
11
+ filterSort?: FILTER_SORT;
10
12
  }
@@ -1,11 +1,13 @@
1
1
  import { RowData, Table } from "@tanstack/react-table";
2
+ import { FILTER_SORT } from "../../../../../../common/filters/sort/config/types";
2
3
  import { SurfaceProps } from "../../../surfaces/types";
3
4
  /**
4
5
  * Adapter for TanStack table column filters to category filters.
5
6
  * @param table - Table.
7
+ * @param filterSort - Filter sort.
6
8
  * @returns Category filters.
7
9
  */
8
- export declare function buildColumnFilters<T extends RowData>(table: Table<T>): SurfaceProps["categoryFilters"];
10
+ export declare function buildColumnFilters<T extends RowData>(table: Table<T>, filterSort?: FILTER_SORT): SurfaceProps["categoryFilters"];
9
11
  /**
10
12
  * Adapter for TanStack table column filters to selected count.
11
13
  * @param table - Table.
@@ -1,6 +1,7 @@
1
1
  import { isRangeCategoryConfig } from "../../../../../../common/categories/config/range/typeGuards";
2
+ import { FILTER_SORT } from "../../../../../../common/filters/sort/config/types";
3
+ import { sortCategoryValueViews } from "../../../../../../common/filters/sort/models/utils";
2
4
  import { getColumnHeader } from "../../../../../Table/common/utils";
3
- import { getSortedFacetedValues } from "../../../../../Table/featureOptions/facetedColumn/utils";
4
5
  /**
5
6
  * Adapter for TanStack table to category configs.
6
7
  * @param table - Table.
@@ -15,12 +16,13 @@ function buildCategoryConfigs(table) {
15
16
  /**
16
17
  * Adapter for TanStack table to category filters.
17
18
  * @param table - Table.
19
+ * @param filterSort - Filter sort.
18
20
  * @param categoryGroups - Category groups.
19
21
  * @returns Category filters.
20
22
  */
21
- function buildCategoryFilters(table, categoryGroups) {
23
+ function buildCategoryFilters(table, filterSort, categoryGroups) {
22
24
  return categoryGroups.reduce((acc, categoryGroup) => {
23
- const categoryFilter = mapCategoryFilter(table, categoryGroup);
25
+ const categoryFilter = mapCategoryFilter(table, filterSort, categoryGroup);
24
26
  if (categoryFilter)
25
27
  acc.push(categoryFilter);
26
28
  return acc;
@@ -29,9 +31,10 @@ function buildCategoryFilters(table, categoryGroups) {
29
31
  /**
30
32
  * Adapter for TanStack table column filters to category filters.
31
33
  * @param table - Table.
34
+ * @param filterSort - Filter sort.
32
35
  * @returns Category filters.
33
36
  */
34
- export function buildColumnFilters(table) {
37
+ export function buildColumnFilters(table, filterSort = FILTER_SORT.ALPHA) {
35
38
  const { options } = table;
36
39
  const { meta = {} } = options;
37
40
  const { categoryGroups } = meta;
@@ -39,10 +42,12 @@ export function buildColumnFilters(table) {
39
42
  // Build single category group with all (filterable) columns.
40
43
  const categoryConfigs = buildCategoryConfigs(table);
41
44
  // Build category filters from single category group.
42
- return buildCategoryFilters(table, [{ categoryConfigs, label: "" }]);
45
+ return buildCategoryFilters(table, filterSort, [
46
+ { categoryConfigs, label: "" },
47
+ ]);
43
48
  }
44
49
  // Build category filters from category groups.
45
- return buildCategoryFilters(table, categoryGroups);
50
+ return buildCategoryFilters(table, filterSort, categoryGroups);
46
51
  }
47
52
  /**
48
53
  * Adapter for TanStack table column filters to selected count.
@@ -68,10 +73,11 @@ function mapCategoryConfig(column) {
68
73
  /**
69
74
  * Adapter for TanStack table to category filter.
70
75
  * @param table - Table.
76
+ * @param filterSort - Filter sort.
71
77
  * @param categoryGroup - Category group.
72
78
  * @returns Category filter.
73
79
  */
74
- function mapCategoryFilter(table, categoryGroup) {
80
+ function mapCategoryFilter(table, filterSort, categoryGroup) {
75
81
  const { categoryConfigs, label } = categoryGroup;
76
82
  const categoryViews = categoryConfigs.reduce((acc, categoryConfig) => {
77
83
  const column = table.getColumn(categoryConfig.key);
@@ -86,7 +92,7 @@ function mapCategoryFilter(table, categoryGroup) {
86
92
  }
87
93
  else {
88
94
  // Build select category view.
89
- categoryView = mapColumnToSelectCategoryView(column, categoryConfig);
95
+ categoryView = mapColumnToSelectCategoryView(column, filterSort, categoryConfig);
90
96
  }
91
97
  return [...acc, categoryView];
92
98
  }, []);
@@ -120,13 +126,14 @@ function mapColumnToRangeCategoryView(column, categoryConfig) {
120
126
  /**
121
127
  * Adapter for TanStack column to select category view.
122
128
  * @param column - Column.
129
+ * @param filterSort - Filter sort.
123
130
  * @param categoryConfig - Category config.
124
131
  * @returns Select category view.
125
132
  */
126
- function mapColumnToSelectCategoryView(column, categoryConfig) {
133
+ function mapColumnToSelectCategoryView(column, filterSort, categoryConfig) {
127
134
  const facetedUniqueValues = column.getFacetedUniqueValues();
128
135
  const isDisabled = facetedUniqueValues.size === 0;
129
- const values = mapColumnToSelectCategoryValueView(column);
136
+ const values = mapColumnToSelectCategoryValueView(column, filterSort);
130
137
  return {
131
138
  annotation: undefined,
132
139
  enableChartView: false,
@@ -140,23 +147,24 @@ function mapColumnToSelectCategoryView(column, categoryConfig) {
140
147
  /**
141
148
  * Adapter for TanStack column to select category value view.
142
149
  * @param column - Column.
150
+ * @param filterSort - Filter sort.
143
151
  * @returns Select category value view.
144
152
  */
145
- function mapColumnToSelectCategoryValueView(column) {
153
+ function mapColumnToSelectCategoryValueView(column, filterSort) {
146
154
  // Get the faceted unique values and sort them.
147
155
  const facetedUniqueValues = column.getFacetedUniqueValues();
148
- const sortedFacetsValues = getSortedFacetedValues(facetedUniqueValues);
149
156
  // Selected values for the column.
150
157
  const filterValue = (column.getFilterValue() || []);
151
158
  // Build the select category values.
152
- const selectCategoryValues = [];
153
- for (const [label, count] of sortedFacetsValues) {
154
- selectCategoryValues.push({
159
+ const categoryValueViews = [];
160
+ for (const [label, count] of [...facetedUniqueValues]) {
161
+ categoryValueViews.push({
155
162
  count,
156
163
  key: String(label),
157
164
  label: String(label),
158
165
  selected: filterValue.includes(label),
159
166
  });
160
167
  }
161
- return selectCategoryValues;
168
+ sortCategoryValueViews(categoryValueViews, filterSort);
169
+ return categoryValueViews;
162
170
  }
@@ -0,0 +1,5 @@
1
+ import { ListItemTextProps, MenuProps, SvgIconProps } from "@mui/material";
2
+ export declare const LIST_ITEM_BUTTON_TEXT_PROPS: ListItemTextProps;
3
+ export declare const LIST_ITEM_TEXT_PROPS: ListItemTextProps;
4
+ export declare const MENU_PROPS: Omit<MenuProps, "anchorEl" | "onClose" | "open">;
5
+ export declare const SVG_ICON_PROPS: SvgIconProps;
@@ -0,0 +1,17 @@
1
+ import { SVG_ICON_PROPS as MUI_SVG_ICON_PROPS } from "../../../../../../../styles/common/mui/svgIcon";
2
+ import { TYPOGRAPHY_PROPS } from "../../../../../../../styles/common/mui/typography";
3
+ export const LIST_ITEM_BUTTON_TEXT_PROPS = {
4
+ slotProps: { primary: { variant: TYPOGRAPHY_PROPS.VARIANT.BODY_400 } },
5
+ };
6
+ export const LIST_ITEM_TEXT_PROPS = {
7
+ slotProps: { primary: { variant: TYPOGRAPHY_PROPS.VARIANT.BODY_500 } },
8
+ };
9
+ export const MENU_PROPS = {
10
+ anchorOrigin: { horizontal: "left", vertical: "bottom" },
11
+ marginThreshold: 8,
12
+ slotProps: { paper: { variant: "menu" } },
13
+ transformOrigin: { horizontal: "left", vertical: "top" },
14
+ };
15
+ export const SVG_ICON_PROPS = {
16
+ fontSize: MUI_SVG_ICON_PROPS.FONT_SIZE.SMALL,
17
+ };
@@ -0,0 +1,2 @@
1
+ import { FilterSortProps } from "./types";
2
+ export declare const FilterSort: ({ enabled, filterSort, onFilterSortChange, }: FilterSortProps) => JSX.Element | null;
@@ -0,0 +1,28 @@
1
+ import { SettingsOutlined } from "@mui/icons-material";
2
+ import { ListItem, ListItemButton, ListItemText, Radio } from "@mui/material";
3
+ import React, { Fragment } from "react";
4
+ import { FILTER_SORT } from "../../../../../../../common/filters/sort/config/types";
5
+ import { ICON_BUTTON_PROPS } from "../../../../../../../styles/common/mui/iconButton";
6
+ import { TEST_IDS } from "../../../../../../../tests/testIds";
7
+ import { RadioCheckedIcon } from "../../../../../../common/CustomIcon/components/RadioCheckedIcon/radioCheckedIcon";
8
+ import { RadioUncheckedIcon } from "../../../../../../common/CustomIcon/components/RadioUncheckedIcon/radioUncheckedIcon";
9
+ import { useMenu } from "../../../../../../common/Menu/hooks/useMenu";
10
+ import { LIST_ITEM_BUTTON_TEXT_PROPS, LIST_ITEM_TEXT_PROPS, MENU_PROPS, SVG_ICON_PROPS, } from "./constants";
11
+ import { StyledIconButton, StyledMenu } from "./filterSort.styles";
12
+ export const FilterSort = ({ enabled = false, filterSort = FILTER_SORT.ALPHA, onFilterSortChange, }) => {
13
+ const { anchorEl, onClose, onOpen, open } = useMenu();
14
+ if (!enabled || !onFilterSortChange)
15
+ return null;
16
+ return (React.createElement(Fragment, null,
17
+ React.createElement(StyledIconButton, { color: ICON_BUTTON_PROPS.COLOR.INK_LIGHT, "data-testid": TEST_IDS.FILTER_SORT_BUTTON, onClick: onOpen, open: open },
18
+ React.createElement(SettingsOutlined, { ...SVG_ICON_PROPS })),
19
+ React.createElement(StyledMenu, { ...MENU_PROPS, anchorEl: anchorEl, "data-testid": TEST_IDS.FILTER_SORT_MENU, onClose: onClose, open: open },
20
+ React.createElement(ListItem, null,
21
+ React.createElement(ListItemText, { ...LIST_ITEM_TEXT_PROPS }, "Sort Filter Values By")),
22
+ React.createElement(ListItemButton, { onClick: () => onFilterSortChange(FILTER_SORT.ALPHA) },
23
+ React.createElement(Radio, { checked: filterSort === FILTER_SORT.ALPHA, checkedIcon: React.createElement(RadioCheckedIcon, { ...SVG_ICON_PROPS }), icon: React.createElement(RadioUncheckedIcon, { ...SVG_ICON_PROPS }) }),
24
+ React.createElement(ListItemText, { ...LIST_ITEM_BUTTON_TEXT_PROPS }, "Alphabetical")),
25
+ React.createElement(ListItemButton, { onClick: () => onFilterSortChange(FILTER_SORT.COUNT) },
26
+ React.createElement(Radio, { checked: filterSort === FILTER_SORT.COUNT, checkedIcon: React.createElement(RadioCheckedIcon, { ...SVG_ICON_PROPS }), icon: React.createElement(RadioUncheckedIcon, { ...SVG_ICON_PROPS }) }),
27
+ React.createElement(ListItemText, { ...LIST_ITEM_BUTTON_TEXT_PROPS }, "By Count (Descending)")))));
28
+ };
@@ -0,0 +1,9 @@
1
+ import { MenuProps } from "@mui/material";
2
+ export declare const StyledIconButton: import("@emotion/styled").StyledComponent<import("@mui/material").IconButtonOwnProps & Omit<import("@mui/material").ButtonBaseOwnProps, "classes"> & import("@mui/material/OverridableComponent").CommonProps & Omit<Omit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref"> & {
3
+ ref?: ((instance: HTMLButtonElement | null) => void | import("react").DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES[keyof import("react").DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES]) | import("react").RefObject<HTMLButtonElement> | null | undefined;
4
+ }, "style" | "size" | "className" | "classes" | "action" | "centerRipple" | "children" | "disabled" | "disableRipple" | "disableTouchRipple" | "focusRipple" | "focusVisibleClassName" | "LinkComponent" | "onFocusVisible" | "sx" | "tabIndex" | "TouchRippleProps" | "touchRippleRef" | "color" | "disableFocusRipple" | "edge" | "loading" | "loadingIndicator"> & {
5
+ theme?: import("@emotion/react").Theme;
6
+ } & Pick<MenuProps, "open">, {}, {}>;
7
+ export declare const StyledMenu: import("@emotion/styled").StyledComponent<MenuProps & {
8
+ theme?: import("@emotion/react").Theme;
9
+ }, {}, {}>;
@@ -0,0 +1,25 @@
1
+ import { css } from "@emotion/react";
2
+ import styled from "@emotion/styled";
3
+ import { IconButton, Menu, menuClasses, paperClasses, } from "@mui/material";
4
+ import { PALETTE } from "../../../../../../../styles/common/constants/palette";
5
+ import { MuiListItemButtonRoot } from "../../../../FilterList/filterList.styles";
6
+ export const StyledIconButton = styled(IconButton) `
7
+ align-self: center;
8
+ padding: 0;
9
+
10
+ ${({ open }) => open &&
11
+ css `
12
+ color: ${PALETTE.INK_MAIN};
13
+ `}
14
+ `;
15
+ export const StyledMenu = styled(Menu) `
16
+ .${paperClasses.root} {
17
+ margin: 4px 0;
18
+ max-width: 300px;
19
+ width: 100%;
20
+
21
+ .${menuClasses.list} {
22
+ ${MuiListItemButtonRoot}
23
+ }
24
+ }
25
+ `;
@@ -0,0 +1,6 @@
1
+ import { Meta, StoryObj } from "@storybook/react";
2
+ import { FilterSort } from "../filterSort";
3
+ declare const meta: Meta<typeof FilterSort>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof meta>;
6
+ export declare const DEFAULT: Story;
@@ -0,0 +1,9 @@
1
+ import { FILTER_SORT } from "../../../../../../../../common/filters/sort/config/types";
2
+ import { FilterSort } from "../filterSort";
3
+ const meta = {
4
+ component: FilterSort,
5
+ };
6
+ export default meta;
7
+ export const DEFAULT = {
8
+ args: { enabled: true, filterSort: FILTER_SORT.ALPHA },
9
+ };
@@ -0,0 +1,6 @@
1
+ import { FILTER_SORT } from "../../../../../../../common/filters/sort/config/types";
2
+ export type FilterSortProps = {
3
+ enabled?: boolean;
4
+ filterSort?: FILTER_SORT;
5
+ onFilterSortChange?: (filterSort: FILTER_SORT) => void;
6
+ };
@@ -1,6 +1,5 @@
1
- import { BaseComponentProps } from "../../../../types";
2
- import { SurfaceProps } from "../../surfaces/types";
1
+ import { ControlsProps } from "./types";
3
2
  /**
4
3
  * Renders filter title and "Clear All" button.
5
4
  */
6
- export declare const Controls: ({ className, onFilter, }: BaseComponentProps & Pick<SurfaceProps, "onFilter">) => JSX.Element | null;
5
+ export declare const Controls: ({ className, filterSort, filterSortEnabled, onFilter, onFilterSortChange, }: ControlsProps) => JSX.Element | null;
@@ -3,12 +3,14 @@ import React from "react";
3
3
  import { CLEAR_ALL } from "../../../../../common/entities";
4
4
  import { TYPOGRAPHY_PROPS } from "../../../../../styles/common/mui/typography";
5
5
  import { BUTTON_PROPS } from "../../../../common/Button/constants";
6
+ import { FilterSort } from "./components/FilterSort/filterSort";
6
7
  import { StyledGrid } from "./controls.styles";
7
8
  /**
8
9
  * Renders filter title and "Clear All" button.
9
10
  */
10
- export const Controls = ({ className, onFilter, }) => {
11
+ export const Controls = ({ className, filterSort, filterSortEnabled = false, onFilter, onFilterSortChange, }) => {
11
12
  return (React.createElement(StyledGrid, { className: className, container: true },
12
13
  React.createElement(Typography, { variant: TYPOGRAPHY_PROPS.VARIANT.BODY_LARGE_500 }, "Filters"),
13
- React.createElement(MButton, { ...BUTTON_PROPS.PRIMARY_TEXT, onClick: () => onFilter(CLEAR_ALL, undefined, false) }, "Clear All")));
14
+ React.createElement(MButton, { ...BUTTON_PROPS.PRIMARY_TEXT, onClick: () => onFilter(CLEAR_ALL, undefined, false) }, "Clear All"),
15
+ React.createElement(FilterSort, { enabled: filterSortEnabled, filterSort: filterSort, onFilterSortChange: onFilterSortChange })));
14
16
  };
@@ -2,7 +2,8 @@ import styled from "@emotion/styled";
2
2
  import { Grid } from "@mui/material";
3
3
  export const StyledGrid = styled(Grid) `
4
4
  display: grid;
5
- gap: 8px 0;
5
+ gap: 8px 16px;
6
+ grid-auto-flow: column;
6
7
  grid-template-columns: 1fr auto;
7
8
  margin: 8px 0;
8
9
  padding: 0 16px;
@@ -0,0 +1,6 @@
1
+ import { BaseComponentProps } from "../../../../types";
2
+ import { SurfaceProps } from "../../surfaces/types";
3
+ import { FilterSortProps } from "./components/FilterSort/types";
4
+ export interface ControlsProps extends BaseComponentProps, Pick<SurfaceProps, "onFilter">, Omit<FilterSortProps, "enabled"> {
5
+ filterSortEnabled?: boolean;
6
+ }
@@ -1,8 +1,10 @@
1
1
  import { OnFilterFn } from "../../../../hooks/useCategoryFilter";
2
2
  import { CategoryFilter } from "../Filters/filters";
3
- export interface SurfaceProps {
3
+ import { FilterSortProps } from "../controls/Controls/components/FilterSort/types";
4
+ export interface SurfaceProps extends Omit<FilterSortProps, "enabled"> {
4
5
  categoryFilters: CategoryFilter[];
5
6
  count?: number;
7
+ filterSortEnabled: boolean;
6
8
  onFilter: OnFilterFn;
7
9
  }
8
10
  export declare enum SURFACE_TYPE {
@@ -1,12 +1,12 @@
1
1
  import { useMemo } from "react";
2
+ import { sortCategoryValueViewsCount } from "../../../../../../../../../../../common/filters/sort/models/utils";
2
3
  import { getPlotOptions } from "../../barX/plot";
3
4
  import { getCategoryTotalCount } from "../../barX/utils";
4
- import { sortByCountThenLabel } from "../../utils";
5
5
  export const usePlotOptions = (selectCategoryValueViews, width, barCount) => {
6
6
  // Organise the select category value views (sort and slice) for chart display.
7
7
  const data = selectCategoryValueViews
8
8
  // Sort the category values by count and label.
9
- .sort(sortByCountThenLabel)
9
+ .sort(sortCategoryValueViewsCount)
10
10
  // Slice the category values to the number of bars to display.
11
11
  .slice(0, barCount);
12
12
  // Build the plot options.
@@ -6,10 +6,3 @@ import { SelectCategoryValueView } from "../../../../../../../../../common/entit
6
6
  * @returns Button text.
7
7
  */
8
8
  export declare function renderButtonText(maxBarCount: number | undefined, selectCategoryValueViews: SelectCategoryValueView[]): string;
9
- /**
10
- * Sorts category value views by count in descending order, then label in ascending order.
11
- * @param a - First category value view.
12
- * @param b - Second category value view.
13
- * @returns Sorted category value views.
14
- */
15
- export declare function sortByCountThenLabel(a: SelectCategoryValueView, b: SelectCategoryValueView): number;
@@ -1,4 +1,3 @@
1
- import { sortCategoryValueViews } from "../../../../../../../../../hooks/useCategoryFilter";
2
1
  /**
3
2
  * Renders the button text for the chart.
4
3
  * @param maxBarCount - Maximum number of bars to display.
@@ -13,13 +12,3 @@ export function renderButtonText(maxBarCount, selectCategoryValueViews) {
13
12
  const count = totalBars - maxBarCount;
14
13
  return `Show ${count} additional results`;
15
14
  }
16
- /**
17
- * Sorts category value views by count in descending order, then label in ascending order.
18
- * @param a - First category value view.
19
- * @param b - Second category value view.
20
- * @returns Sorted category value views.
21
- */
22
- export function sortByCountThenLabel(a, b) {
23
- const compare = b.count - a.count;
24
- return compare === 0 ? sortCategoryValueViews(a, b) : compare;
25
- }