@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
@@ -4,6 +4,7 @@ import { JSXElementConstructor, ReactNode } from "react";
4
4
  import { AzulSummaryResponse } from "../apis/azul/common/entities";
5
5
  import { CategoryConfig } from "../common/categories/config/types";
6
6
  import { DataDictionaryAnnotation, DataDictionaryConfig, SelectedFilter } from "../common/entities";
7
+ import { FilterSortConfig } from "../common/filters/sort/config/types";
7
8
  import { FooterProps } from "../components/Layout/components/Footer/footer";
8
9
  import { HeaderProps } from "../components/Layout/components/Header/header";
9
10
  import { ExploreMode } from "../hooks/useExploreMode/types";
@@ -305,6 +306,7 @@ export interface SiteConfig {
305
306
  export?: ExportConfig;
306
307
  exportsRequireAuth?: boolean;
307
308
  exportToTerraUrl?: string;
309
+ filterSort?: FilterSortConfig;
308
310
  gitHubUrl?: string;
309
311
  layout: {
310
312
  floating?: FloatingConfig;
@@ -1,7 +1,8 @@
1
1
  import { CategoryConfig } from "../common/categories/config/types";
2
2
  import { Category } from "../common/categories/models/types";
3
3
  import { CategoryView, VIEW_KIND } from "../common/categories/views/types";
4
- import { CategoryKey, CategoryValueKey, ClearAll, Filters, SelectCategoryValue, SelectCategoryValueView } from "../common/entities";
4
+ import { CategoryKey, CategoryValueKey, ClearAll, Filters, SelectCategoryValue } from "../common/entities";
5
+ import { FILTER_SORT } from "../common/filters/sort/config/types";
5
6
  /**
6
7
  * State backing filter functionality and calculations. Converted to view model for display.
7
8
  */
@@ -15,9 +16,10 @@ export type OnFilterFn = (categoryKey: CategoryKey | ClearAll, selectedCategoryV
15
16
  * @param categories - Categories, category value and their counts with the current filter applied.
16
17
  * @param categoryConfigs - Category configs indicating accept list as well as label configuration.
17
18
  * @param filterState - Current set of selected category and category values.
19
+ * @param filterSort - Sort configuration (ALPHA or COUNT).
18
20
  * @returns Array of category view objects.
19
21
  */
20
- export declare function buildCategoryViews(categories: Category[], categoryConfigs: CategoryConfig[] | undefined, filterState: FilterState): CategoryView[];
22
+ export declare function buildCategoryViews(categories: Category[], categoryConfigs: CategoryConfig[] | undefined, filterState: FilterState, filterSort: FILTER_SORT): CategoryView[];
21
23
  /**
22
24
  * Build new set of selected filters on de/select of filter.
23
25
  * @param filterState - Current set of selected category and category values.
@@ -34,10 +36,3 @@ export declare function buildNextFilterState(filterState: FilterState, categoryK
34
36
  * @returns original select category value.
35
37
  */
36
38
  export declare function getSelectCategoryValue(selectCategoryValue: SelectCategoryValue): SelectCategoryValue;
37
- /**
38
- * Sort category value views by key, ascending.
39
- * @param cvv0 - First category value view to compare.
40
- * @param cvv1 - Second category value view to compare.
41
- * @returns Number indicating sort precedence of cv0 vs cv1.
42
- */
43
- export declare function sortCategoryValueViews(cvv0: SelectCategoryValueView, cvv1: SelectCategoryValueView): number;
@@ -5,6 +5,7 @@ import { buildNextSelectFilterState } from "../common/categories/models/select/u
5
5
  import { buildRangeCategoryView } from "../common/categories/views/range/utils";
6
6
  import { VIEW_KIND } from "../common/categories/views/types";
7
7
  import { COLLATOR_CASE_INSENSITIVE } from "../common/constants";
8
+ import { sortCategoryValueViews } from "../common/filters/sort/models/utils";
8
9
  /**
9
10
  * Build the view-specific model of the given category value.
10
11
  * @param categoryValue - The category value to build a view model of.
@@ -46,9 +47,10 @@ function buildCategoryView(selectCategory, selectCategoryValueViews, categoryCon
46
47
  * @param categories - Categories, category value and their counts with the current filter applied.
47
48
  * @param categoryConfigs - Category configs indicating accept list as well as label configuration.
48
49
  * @param filterState - Current set of selected category and category values.
50
+ * @param filterSort - Sort configuration (ALPHA or COUNT).
49
51
  * @returns Array of category view objects.
50
52
  */
51
- export function buildCategoryViews(categories, categoryConfigs, filterState) {
53
+ export function buildCategoryViews(categories, categoryConfigs, filterState, filterSort) {
52
54
  if (!categories || !categoryConfigs) {
53
55
  return [];
54
56
  }
@@ -64,7 +66,8 @@ export function buildCategoryViews(categories, categoryConfigs, filterState) {
64
66
  }
65
67
  // Build view model for single or multiselect categories.
66
68
  const categoryValueViews = category.values.map((categoryValue) => buildCategoryValueView(categoryValue, categorySelectedFilter));
67
- categoryValueViews.sort(sortCategoryValueViews);
69
+ // Sort category value views based on filter sort configuration.
70
+ sortCategoryValueViews(categoryValueViews, filterSort);
68
71
  // Build category view model.
69
72
  return buildCategoryView(category, categoryValueViews, categoryConfigs);
70
73
  });
@@ -156,19 +159,6 @@ function isCategoryValueSelected(categoryValueKey, categorySelectedFilter) {
156
159
  function isCategoryAcceptListed(category, categoryConfigs) {
157
160
  return categoryConfigs.some((categoryConfig) => categoryConfig.key === category.key);
158
161
  }
159
- /**
160
- * Sort category value views by key, ascending.
161
- * @param cvv0 - First category value view to compare.
162
- * @param cvv1 - Second category value view to compare.
163
- * @returns Number indicating sort precedence of cv0 vs cv1.
164
- */
165
- export function sortCategoryValueViews(cvv0, cvv1) {
166
- return !cvv0.label
167
- ? 1
168
- : !cvv1.label
169
- ? -1
170
- : COLLATOR_CASE_INSENSITIVE.compare(cvv0.label, cvv1.label);
171
- }
172
162
  /**
173
163
  * Sort category views by display label, ascending.
174
164
  * @param c0 - First category view to compare.
@@ -0,0 +1,10 @@
1
+ import { ExploreState } from "../../../exploreState";
2
+ import { UpdateFilterSortPayload } from "./types";
3
+ /**
4
+ * Reducer function to handle the "update filter sort" action.
5
+ * Updates the filter sort in the state for the current entity.
6
+ * @param state - Explore State.
7
+ * @param payload - Payload.
8
+ * @returns explore state.
9
+ */
10
+ export declare function updateFilterSortAction(state: ExploreState, payload: UpdateFilterSortPayload): ExploreState;
@@ -0,0 +1,21 @@
1
+ import { updateEntityStateByCategoryGroupConfigKey } from "../../utils";
2
+ import { sortCategoryViews } from "./utils";
3
+ /**
4
+ * Reducer function to handle the "update filter sort" action.
5
+ * Updates the filter sort in the state for the current entity.
6
+ * @param state - Explore State.
7
+ * @param payload - Payload.
8
+ * @returns explore state.
9
+ */
10
+ export function updateFilterSortAction(state, payload) {
11
+ const filterSort = payload;
12
+ // Sort the category views based on the new filter sort.
13
+ const categoryViews = sortCategoryViews(state, filterSort);
14
+ // Update entity state by category group config key
15
+ updateEntityStateByCategoryGroupConfigKey(state, { categoryViews });
16
+ return {
17
+ ...state,
18
+ categoryViews,
19
+ filterSort,
20
+ };
21
+ }
@@ -0,0 +1,7 @@
1
+ import { UpdateFilterSortAction, UpdateFilterSortPayload } from "./types";
2
+ /**
3
+ * Action creator for updating filter sort in the state.
4
+ * @param payload - Payload.
5
+ * @returns Action with payload and action type.
6
+ */
7
+ export declare function updateFilterSort(payload: UpdateFilterSortPayload): UpdateFilterSortAction;
@@ -0,0 +1,12 @@
1
+ import { ExploreActionKind } from "../../../exploreState";
2
+ /**
3
+ * Action creator for updating filter sort in the state.
4
+ * @param payload - Payload.
5
+ * @returns Action with payload and action type.
6
+ */
7
+ export function updateFilterSort(payload) {
8
+ return {
9
+ payload,
10
+ type: ExploreActionKind.UpdateFilterSort,
11
+ };
12
+ }
@@ -0,0 +1,7 @@
1
+ import { FILTER_SORT } from "../../../../common/filters/sort/config/types";
2
+ import { ExploreActionKind } from "../../../exploreState";
3
+ export type UpdateFilterSortAction = {
4
+ payload: UpdateFilterSortPayload;
5
+ type: ExploreActionKind.UpdateFilterSort;
6
+ };
7
+ export type UpdateFilterSortPayload = FILTER_SORT;
@@ -0,0 +1,10 @@
1
+ import { CategoryView } from "../../../../common/categories/views/types";
2
+ import { FILTER_SORT } from "../../../../common/filters/sort/config/types";
3
+ import { ExploreState } from "../../../exploreState";
4
+ /**
5
+ * Sorts the category views for the current entity type, based on the filter sort.
6
+ * @param state - Explore state.
7
+ * @param filterSort - Filter sort.
8
+ * @returns Sorted category views.
9
+ */
10
+ export declare function sortCategoryViews(state: ExploreState, filterSort: FILTER_SORT): CategoryView[];
@@ -0,0 +1,22 @@
1
+ import { isRangeCategory } from "../../../../common/categories/models/range/typeGuards";
2
+ import { sortCategoryValueViews } from "../../../../common/filters/sort/models/utils";
3
+ /**
4
+ * Sorts the category views for the current entity type, based on the filter sort.
5
+ * @param state - Explore state.
6
+ * @param filterSort - Filter sort.
7
+ * @returns Sorted category views.
8
+ */
9
+ export function sortCategoryViews(state, filterSort) {
10
+ return [...state.categoryViews].map((categoryView) => {
11
+ // Skip range categories.
12
+ if (isRangeCategory(categoryView))
13
+ return categoryView;
14
+ // Use structural clone for select categories that need sorting.
15
+ const nextCategoryView = {
16
+ ...categoryView,
17
+ values: [...categoryView.values],
18
+ };
19
+ sortCategoryValueViews(nextCategoryView.values, filterSort);
20
+ return nextCategoryView;
21
+ });
22
+ }
@@ -1,3 +1,4 @@
1
+ import { FILTER_SORT } from "../../../common/filters/sort/config/types";
1
2
  import { SELECT_CATEGORY_KEY } from "../constants";
2
3
  export const DEFAULT_CATEGORY_GROUP_SAVED_FILTERS = {
3
4
  categoryConfigs: [
@@ -27,6 +28,7 @@ export const INITIAL_STATE = {
27
28
  entityStateByCategoryGroupConfigKey: new Map(),
28
29
  featureFlagState: undefined,
29
30
  filterCount: 0,
31
+ filterSort: FILTER_SORT.ALPHA,
30
32
  filterState: [],
31
33
  listItems: [],
32
34
  loading: true,
@@ -1,3 +1,4 @@
1
+ import { getFilterSortType } from "../../../common/filters/sort/config/utils";
1
2
  import { getInitialColumnVisibilityState } from "../../../components/TableCreator/options/initialState/columnVisibility";
2
3
  import { SELECT_CATEGORY_KEY } from "../constants";
3
4
  import { buildNextEntities } from "../entities/state";
@@ -237,6 +238,7 @@ export function initReducerArguments(config, entityListType, decodedFilterParam,
237
238
  entityStateByCategoryGroupConfigKey,
238
239
  featureFlagState: decodedFeatureFlagParam,
239
240
  filterCount: getFilterCount(filterState),
241
+ filterSort: getFilterSortType(config),
240
242
  filterState,
241
243
  tabValue: entityListType,
242
244
  };
@@ -2,10 +2,12 @@ import React, { Dispatch, ReactNode } from "react";
2
2
  import { AzulSearchIndex } from "../apis/azul/common/entities";
3
3
  import { CategoryView } from "../common/categories/views/types";
4
4
  import { SelectedFilter } from "../common/entities";
5
+ import { FILTER_SORT } from "../common/filters/sort/config/types";
5
6
  import { RowPreviewState } from "../components/Table/features/RowPreview/entities";
6
7
  import { CategoryGroup, SiteConfig } from "../config/entities";
7
8
  import { ClearMetaAction } from "./exploreState/actions/clearMeta/types";
8
9
  import { StateToUrlAction } from "./exploreState/actions/stateToUrl/types";
10
+ import { UpdateFilterSortAction } from "./exploreState/actions/updateFilterSort/types";
9
11
  import { UpdateGroupingAction } from "./exploreState/actions/updateGrouping/types";
10
12
  import { UpdateColumnVisibilityAction } from "./exploreState/actions/updateVisibility/types";
11
13
  import { UrlToStateAction } from "./exploreState/actions/urlToState/types";
@@ -32,6 +34,7 @@ export type ExploreState = {
32
34
  entityStateByCategoryGroupConfigKey: EntityStateByCategoryGroupConfigKey;
33
35
  featureFlagState: FeatureFlagState;
34
36
  filterCount: number;
37
+ filterSort: FILTER_SORT;
35
38
  filterState: SelectedFilter[];
36
39
  listItems: ListItems;
37
40
  loading: boolean;
@@ -114,6 +117,7 @@ export declare enum ExploreActionKind {
114
117
  UpdateEntityFilters = "UPDATE_ENTITY_FILTERS",
115
118
  UpdateEntityViewAccess = "UPDATE_ENTITY_VIEW_ACCESS",
116
119
  UpdateFilter = "UPDATE_FILTER",
120
+ UpdateFilterSort = "UPDATE_FILTER_SORT",
117
121
  UpdateGrouping = "UPDATE_GROUPING",
118
122
  UpdateRowPreview = "UPDATE_ROW_PREVIEW",
119
123
  UpdateRowSelection = "UPDATE_ROW_SELECTION",
@@ -123,7 +127,7 @@ export declare enum ExploreActionKind {
123
127
  /**
124
128
  * Explore action.
125
129
  */
126
- export type ExploreAction = ApplySavedFilterAction | ClearFiltersAction | ClearMetaAction | PaginateTableAction | PatchExploreResponseAction | ProcessExploreResponseAction | ResetExploreResponseAction | ResetStateAction | SelectEntityTypeAction | StateToUrlAction | UpdateColumnVisibilityAction | UpdateEntityFiltersAction | UpdateEntityViewAccessAction | UpdateFilterAction | UpdateGroupingAction | UpdateRowPreviewAction | UpdateRowSelectionAction | UpdateSortingAction | UrlToStateAction;
130
+ export type ExploreAction = ApplySavedFilterAction | ClearFiltersAction | ClearMetaAction | PaginateTableAction | PatchExploreResponseAction | ProcessExploreResponseAction | ResetExploreResponseAction | ResetStateAction | SelectEntityTypeAction | StateToUrlAction | UpdateColumnVisibilityAction | UpdateEntityFiltersAction | UpdateEntityViewAccessAction | UpdateFilterAction | UpdateFilterSortAction | UpdateGroupingAction | UpdateRowPreviewAction | UpdateRowSelectionAction | UpdateSortingAction | UrlToStateAction;
127
131
  /**
128
132
  * Apply saved filter action.
129
133
  */
@@ -6,6 +6,7 @@ import { useConfig } from "../hooks/useConfig";
6
6
  import { useURLFilterParams } from "../hooks/useURLFilterParams";
7
7
  import { clearMetaAction } from "./exploreState/actions/clearMeta/action";
8
8
  import { stateToUrlAction } from "./exploreState/actions/stateToUrl/action";
9
+ import { updateFilterSortAction } from "./exploreState/actions/updateFilterSort/action";
9
10
  import { updateGroupingAction } from "./exploreState/actions/updateGrouping/action";
10
11
  import { updateColumnVisibilityAction } from "./exploreState/actions/updateVisibility/action";
11
12
  import { urlToStateAction } from "./exploreState/actions/urlToState/action";
@@ -77,6 +78,7 @@ export var ExploreActionKind;
77
78
  ExploreActionKind["UpdateEntityFilters"] = "UPDATE_ENTITY_FILTERS";
78
79
  ExploreActionKind["UpdateEntityViewAccess"] = "UPDATE_ENTITY_VIEW_ACCESS";
79
80
  ExploreActionKind["UpdateFilter"] = "UPDATE_FILTER";
81
+ ExploreActionKind["UpdateFilterSort"] = "UPDATE_FILTER_SORT";
80
82
  ExploreActionKind["UpdateGrouping"] = "UPDATE_GROUPING";
81
83
  ExploreActionKind["UpdateRowPreview"] = "UPDATE_ROW_PREVIEW";
82
84
  ExploreActionKind["UpdateRowSelection"] = "UPDATE_ROW_SELECTION";
@@ -192,7 +194,7 @@ function exploreReducer(state, action, exploreContext) {
192
194
  ? buildCategoryViews([
193
195
  ...payload.selectCategories,
194
196
  ...entityState.savedSelectCategories, // "savedFilter" select categories are built from config at reducer initialization.
195
- ], entityState.categoryConfigs, [...state.filterState, ...entityState.savedFilterState])
197
+ ], entityState.categoryConfigs, [...state.filterState, ...entityState.savedFilterState], state.filterSort)
196
198
  : state.categoryViews;
197
199
  const rowPreview = entityPageState.rowPreview;
198
200
  updateEntityStateByCategoryGroupConfigKey(state, {
@@ -328,6 +330,12 @@ function exploreReducer(state, action, exploreContext) {
328
330
  rowPreview,
329
331
  };
330
332
  }
333
+ /**
334
+ * Update filter sort
335
+ */
336
+ case ExploreActionKind.UpdateFilterSort: {
337
+ return updateFilterSortAction(state, payload);
338
+ }
331
339
  /**
332
340
  * Update grouping
333
341
  **/
@@ -9,6 +9,8 @@ export declare const TEST_IDS: {
9
9
  FILTER_ITEM: string;
10
10
  FILTER_POPOVER: string;
11
11
  FILTER_RANGE: string;
12
+ FILTER_SORT_BUTTON: string;
13
+ FILTER_SORT_MENU: string;
12
14
  FILTER_TERM: string;
13
15
  SEARCH_ALL_FILTERS: string;
14
16
  SIDEBAR: string;
@@ -9,6 +9,8 @@ export const TEST_IDS = {
9
9
  FILTER_ITEM: "filter-item",
10
10
  FILTER_POPOVER: "filter-popover",
11
11
  FILTER_RANGE: "filter-range",
12
+ FILTER_SORT_BUTTON: "filter-sort-button",
13
+ FILTER_SORT_MENU: "filter-sort-menu",
12
14
  FILTER_TERM: "filter-term",
13
15
  SEARCH_ALL_FILTERS: "search-all-filters",
14
16
  SIDEBAR: "sidebar",
@@ -1,8 +1,10 @@
1
+ import { Stack } from "@mui/material";
1
2
  import React, { useEffect, useMemo } from "react";
2
3
  import { track } from "../../common/analytics/analytics";
3
4
  import { EVENT_NAME, EVENT_PARAM } from "../../common/analytics/entities";
4
5
  import { DrawerProvider } from "../../components/common/Drawer/provider/provider";
5
6
  import { ClearAllFilters } from "../../components/Filter/components/ClearAllFilters/clearAllFilters";
7
+ import { FilterSort } from "../../components/Filter/components/controls/Controls/components/FilterSort/filterSort";
6
8
  import { Filters, } from "../../components/Filter/components/Filters/filters";
7
9
  import { SearchAllFilters } from "../../components/Filter/components/SearchAllFilters/searchAllFilters";
8
10
  import { SURFACE_TYPE } from "../../components/Filter/components/surfaces/types";
@@ -21,6 +23,7 @@ import { stateToUrl } from "../../providers/exploreState/actions/stateToUrl/disp
21
23
  import { urlToState } from "../../providers/exploreState/actions/urlToState/dispatch";
22
24
  import { SELECT_CATEGORY_KEY } from "../../providers/exploreState/constants";
23
25
  import { TEST_IDS } from "../../tests/testIds";
26
+ import { useUpdateFilterSort } from "./hooks/UseUpdateFilterSort/hook";
24
27
  import { buildStateSyncManagerContext } from "./utils";
25
28
  export const ExploreView = (props) => {
26
29
  const { mdDown } = useBreakpoint();
@@ -32,6 +35,7 @@ export const ExploreView = (props) => {
32
35
  useEntityList(props); // Fetch entities.
33
36
  const { entityListType } = props;
34
37
  const categoryFilters = useMemo(() => buildCategoryFilters(categoryViews, categoryGroups), [categoryGroups, categoryViews]);
38
+ const { enabled: filterSortEnabled, filterSort, onFilterSortChange, } = useUpdateFilterSort();
35
39
  /**
36
40
  * State sync manager.
37
41
  * Handles state synchronization between the explore state and the URL.
@@ -98,7 +102,9 @@ export const ExploreView = (props) => {
98
102
  categoryViews && !!categoryViews.length && (React.createElement(Sidebar, null,
99
103
  React.createElement(SidebarTools, { "data-testid": TEST_IDS.FILTER_CONTROLS },
100
104
  React.createElement(SidebarLabel, { label: "Filters" }),
101
- React.createElement(ClearAllFilters, null),
105
+ React.createElement(Stack, { direction: "row", gap: 4 },
106
+ React.createElement(ClearAllFilters, null),
107
+ React.createElement(FilterSort, { enabled: filterSortEnabled, filterSort: filterSort, onFilterSortChange: onFilterSortChange })),
102
108
  React.createElement(SearchAllFilters, { categoryViews: categoryViews, onFilter: onFilterChange.bind(null, true), surfaceType: mdDown ? SURFACE_TYPE.POPPER_DRAWER : SURFACE_TYPE.POPPER_MENU })),
103
109
  React.createElement(Filters, { categoryFilters: categoryFilters, onFilter: onFilterChange.bind(null, false), surfaceType: mdDown ? SURFACE_TYPE.DRAWER : SURFACE_TYPE.MENU, trackFilterOpened: trackingConfig?.trackFilterOpened }))),
104
110
  React.createElement(IndexView, { className: props.className, categoryFilters: categoryFilters, entityListType: entityListType, entityName: label, loading: loading })));
@@ -0,0 +1,2 @@
1
+ import { UseUpdateFilterSort } from "./types";
2
+ export declare const useUpdateFilterSort: () => UseUpdateFilterSort;
@@ -0,0 +1,12 @@
1
+ import { useCallback } from "react";
2
+ import { useConfig } from "../../../../hooks/useConfig";
3
+ import { useExploreState } from "../../../../hooks/useExploreState";
4
+ import { updateFilterSort } from "../../../../providers/exploreState/actions/updateFilterSort/dispatch";
5
+ export const useUpdateFilterSort = () => {
6
+ const { config } = useConfig();
7
+ const { exploreDispatch, exploreState } = useExploreState();
8
+ const { filterSort } = exploreState;
9
+ const enabled = Boolean(config.filterSort?.sortBy);
10
+ const onFilterSortChange = useCallback((filterSort) => exploreDispatch(updateFilterSort(filterSort)), [exploreDispatch]);
11
+ return { enabled, filterSort, onFilterSortChange };
12
+ };
@@ -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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@databiosphere/findable-ui",
3
- "version": "43.0.0",
3
+ "version": "44.0.0",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Filter sort options.
3
+ */
4
+ export enum FILTER_SORT {
5
+ ALPHA = "ALPHA",
6
+ COUNT = "COUNT",
7
+ }
8
+
9
+ /**
10
+ * Filter sort configuration for site config.
11
+ */
12
+ export interface FilterSortConfig {
13
+ sortBy?: FILTER_SORT;
14
+ }
@@ -0,0 +1,11 @@
1
+ import { SiteConfig } from "../../../../config/entities";
2
+ import { FILTER_SORT } from "./types";
3
+
4
+ /**
5
+ * Returns the default filter sort type "ALPHA" or "COUNT" from config or ALPHA as fallback.
6
+ * @param config - Filter sort configuration.
7
+ * @returns default filter sort type.
8
+ */
9
+ export function getFilterSortType(config?: SiteConfig): FILTER_SORT {
10
+ return config?.filterSort?.sortBy ?? FILTER_SORT.ALPHA;
11
+ }
@@ -0,0 +1,57 @@
1
+ import { COLLATOR_CASE_INSENSITIVE } from "../../../constants";
2
+ import { SelectCategoryValueView } from "../../../entities";
3
+ import { FILTER_SORT } from "../config/types";
4
+
5
+ /**
6
+ * Sort category value views based on filter sort configuration.
7
+ * Uses function selection pattern for efficiency.
8
+ * @param categoryValueViews - Array of category value views to sort.
9
+ * @param filterSort - Sort configuration (ALPHA or COUNT).
10
+ */
11
+ export function sortCategoryValueViews(
12
+ categoryValueViews: SelectCategoryValueView[],
13
+ filterSort: FILTER_SORT
14
+ ): void {
15
+ const sortFn =
16
+ filterSort === FILTER_SORT.ALPHA
17
+ ? sortCategoryValueViewsAlpha
18
+ : sortCategoryValueViewsCount;
19
+
20
+ categoryValueViews.sort(sortFn);
21
+ }
22
+
23
+ /**
24
+ * Sort category value views alphabetically.
25
+ * @param cvv0 - First category value view to compare.
26
+ * @param cvv1 - Second category value view to compare.
27
+ * @returns Number indicating sort precedence of cvv0 vs cvv1.
28
+ */
29
+
30
+ export function sortCategoryValueViewsAlpha(
31
+ cvv0: SelectCategoryValueView,
32
+ cvv1: SelectCategoryValueView
33
+ ): number {
34
+ // Handle empty labels.
35
+ if (!cvv0.label) return 1;
36
+ if (!cvv1.label) return -1;
37
+
38
+ return COLLATOR_CASE_INSENSITIVE.compare(cvv0.label, cvv1.label);
39
+ }
40
+
41
+ /**
42
+ * Sort category value views by count (descending), then alphabetically.
43
+ * @param cvv0 - First category value view to compare.
44
+ * @param cvv1 - Second category value view to compare.
45
+ * @returns Number indicating sort precedence of cvv0 vs cvv1.
46
+ */
47
+ export function sortCategoryValueViewsCount(
48
+ cvv0: SelectCategoryValueView,
49
+ cvv1: SelectCategoryValueView
50
+ ): number {
51
+ // Sort by count descending, then alphabetically.
52
+ const countDiff = cvv1.count - cvv0.count;
53
+
54
+ if (countDiff !== 0) return countDiff;
55
+
56
+ return sortCategoryValueViewsAlpha(cvv0, cvv1);
57
+ }
@@ -8,6 +8,7 @@ import {
8
8
  ClearAll,
9
9
  } from "../../../../../../common/entities";
10
10
  import { updater } from "../../../../../Table/components/TableFeatures/ColumnFilter/utils";
11
+ import { useUpdateFilterSort } from "./hooks/UseUpdateFilterSort/hook";
11
12
  import { ColumnFiltersAdapterProps } from "./types";
12
13
  import { buildColumnFilters, getColumnFiltersCount } from "./utils";
13
14
 
@@ -15,7 +16,13 @@ export const ColumnFiltersAdapter = <T extends RowData>({
15
16
  renderSurface,
16
17
  table,
17
18
  }: ColumnFiltersAdapterProps<T>): JSX.Element | null => {
18
- const categoryFilters = buildColumnFilters(table);
19
+ const {
20
+ enabled: filterSortEnabled,
21
+ filterSort,
22
+ onFilterSortChange,
23
+ } = useUpdateFilterSort(table);
24
+
25
+ const categoryFilters = buildColumnFilters(table, filterSort);
19
26
  const count = getColumnFiltersCount(table);
20
27
 
21
28
  const onFilter = useCallback(
@@ -50,6 +57,9 @@ export const ColumnFiltersAdapter = <T extends RowData>({
50
57
  return renderSurface({
51
58
  categoryFilters,
52
59
  count,
60
+ filterSort,
61
+ filterSortEnabled,
53
62
  onFilter,
63
+ onFilterSortChange,
54
64
  });
55
65
  };
@@ -0,0 +1,22 @@
1
+ import { RowData, Table } from "@tanstack/react-table";
2
+ import { useCallback, useMemo, useState } from "react";
3
+ import { FILTER_SORT } from "../../../../../../../../common/filters/sort/config/types";
4
+ import { UseUpdateFilterSort } from "./types";
5
+ import { initFilterSort, isFilterSortEnabled } from "./utils";
6
+
7
+ export const useUpdateFilterSort = <T extends RowData>(
8
+ table: Table<T>
9
+ ): UseUpdateFilterSort => {
10
+ const [filterSort, setFilterSort] = useState<FILTER_SORT>(
11
+ initFilterSort(table)
12
+ );
13
+
14
+ const enabled = useMemo(() => isFilterSortEnabled(table), [table]);
15
+
16
+ const onFilterSortChange = useCallback(
17
+ (value: FILTER_SORT) => setFilterSort(value),
18
+ []
19
+ );
20
+
21
+ return { enabled, filterSort, onFilterSortChange };
22
+ };
@@ -0,0 +1,7 @@
1
+ import { FILTER_SORT } from "../../../../../../../../common/filters/sort/config/types";
2
+
3
+ export interface UseUpdateFilterSort {
4
+ enabled: boolean;
5
+ filterSort: FILTER_SORT;
6
+ onFilterSortChange: (filterSort: FILTER_SORT) => void;
7
+ }
@@ -0,0 +1,33 @@
1
+ import { RowData, Table } from "@tanstack/react-table";
2
+ import { FILTER_SORT } from "../../../../../../../../common/filters/sort/config/types";
3
+ import { ColumnFiltersTableMeta } from "../../types";
4
+
5
+ /**
6
+ * Returns the default filter sort type "ALPHA" or "COUNT" from table meta or ALPHA as fallback.
7
+ * @param table - Table.
8
+ * @returns default filter sort type.
9
+ */
10
+ export function initFilterSort<T extends RowData>(
11
+ table: Table<T>
12
+ ): FILTER_SORT {
13
+ const { options } = table;
14
+ const { meta = {} } = options;
15
+ const { filterSort } = meta as ColumnFiltersTableMeta<T>;
16
+
17
+ return filterSort || FILTER_SORT.ALPHA;
18
+ }
19
+
20
+ /**
21
+ * Returns true if filter sort is enabled.
22
+ * @param table - Table.
23
+ * @returns true if filter sort is enabled.
24
+ */
25
+ export function isFilterSortEnabled<T extends RowData>(
26
+ table: Table<T>
27
+ ): boolean {
28
+ const { options } = table;
29
+ const { meta = {} } = options;
30
+ const { filterSort } = meta as ColumnFiltersTableMeta<T>;
31
+
32
+ return Boolean(filterSort);
33
+ }
@@ -3,6 +3,7 @@ import {
3
3
  Table,
4
4
  TableMeta as TanStackTableMeta,
5
5
  } from "@tanstack/react-table";
6
+ import { FILTER_SORT } from "../../../../../../common/filters/sort/config/types";
6
7
  import { CategoryGroup } from "../../../../../../config/entities";
7
8
  import { SurfaceProps } from "../../../surfaces/types";
8
9
 
@@ -13,4 +14,5 @@ export interface ColumnFiltersAdapterProps<T extends RowData> {
13
14
  export interface ColumnFiltersTableMeta<T extends RowData>
14
15
  extends TanStackTableMeta<T> {
15
16
  categoryGroups?: CategoryGroup[];
17
+ filterSort?: FILTER_SORT;
16
18
  }