@databiosphere/findable-ui 1.0.1 → 2.0.1

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 (37) hide show
  1. package/lib/components/Loading/loading.d.ts +2 -1
  2. package/lib/components/Loading/loading.js +4 -4
  3. package/lib/components/Table/table.js +1 -1
  4. package/lib/components/TableCreator/tableCreator.js +3 -2
  5. package/lib/components/TableCreator/tableCreator.styles.d.ts +5 -0
  6. package/lib/components/TableCreator/tableCreator.styles.js +10 -0
  7. package/lib/components/common/AnchorLink/anchorLink.styles.js +1 -0
  8. package/lib/config/entities.d.ts +10 -2
  9. package/lib/hooks/useEntityList.js +1 -1
  10. package/lib/hooks/useSessionTimeout.js +9 -2
  11. package/lib/providers/exploreState/entities.d.ts +19 -0
  12. package/lib/providers/exploreState/entities.js +2 -0
  13. package/lib/providers/exploreState/initializer/constants.d.ts +5 -0
  14. package/lib/providers/exploreState/initializer/constants.js +32 -0
  15. package/lib/providers/exploreState/initializer/utils.d.ts +12 -0
  16. package/lib/providers/exploreState/initializer/utils.js +116 -0
  17. package/lib/providers/exploreState/utils.d.ts +37 -22
  18. package/lib/providers/exploreState/utils.js +64 -47
  19. package/lib/providers/exploreState.d.ts +4 -16
  20. package/lib/providers/exploreState.js +25 -21
  21. package/lib/views/ExploreView/exploreView.js +8 -8
  22. package/package.json +1 -1
  23. package/src/components/Loading/loading.tsx +5 -2
  24. package/src/components/Table/table.tsx +1 -1
  25. package/src/components/TableCreator/tableCreator.styles.ts +5 -0
  26. package/src/components/TableCreator/tableCreator.tsx +4 -2
  27. package/src/components/common/AnchorLink/anchorLink.styles.ts +1 -0
  28. package/src/config/entities.ts +11 -2
  29. package/src/hooks/useEntityList.ts +1 -1
  30. package/src/hooks/useSessionTimeout.ts +9 -3
  31. package/src/providers/exploreState/entities.ts +32 -0
  32. package/src/providers/exploreState/{constants.ts → initializer/constants.ts} +8 -3
  33. package/src/providers/exploreState/initializer/utils.ts +183 -0
  34. package/src/providers/exploreState/utils.ts +104 -65
  35. package/src/providers/exploreState.tsx +54 -55
  36. package/src/views/ExploreView/exploreView.tsx +16 -16
  37. package/src/hooks/useCategoryConfigs.ts +0 -17
@@ -1,86 +1,64 @@
1
1
  import { SelectedFilter } from "../../common/entities";
2
- import { getInitialTableColumnVisibility } from "../../components/Table/common/utils";
3
- import { SiteConfig } from "../../config/entities";
4
- import { getDefaultSorting } from "../../config/utils";
2
+ import { CategoryConfig } from "../../config/entities";
3
+ import { ExploreState, PaginationState } from "../exploreState";
5
4
  import {
5
+ CategoryGroupConfigKey,
6
+ EntityPageState,
6
7
  EntityPageStateMapper,
7
- ENTITY_VIEW,
8
- ExploreState,
9
- PaginationState,
10
- } from "../exploreState";
11
- import { INITIAL_STATE } from "./constants";
8
+ EntityState,
9
+ } from "./entities";
10
+ import { DEFAULT_ENTITY_STATE } from "./initializer/constants";
12
11
 
13
12
  /**
14
- * Returns the filter count.
15
- * @param filterState - Filter state.
16
- * @returns filter count.
13
+ * Returns the category group config key for the current entity.
14
+ * @param entityPath - Entity path.
15
+ * @param entityPageState - Entity page state mapper.
16
+ * @returns category group config key.
17
17
  */
18
- export function getFilterCount(filterState: SelectedFilter[]): number {
19
- return filterState.reduce((acc, filter) => acc + filter.value.length, 0);
18
+ export function getEntityCategoryGroupConfigKey(
19
+ entityPath: string,
20
+ entityPageState: EntityPageStateMapper
21
+ ): CategoryGroupConfigKey {
22
+ return entityPageState[entityPath].categoryGroupConfigKey;
20
23
  }
21
24
 
22
25
  /**
23
- * Returns the initial explore state.
24
- * @param config - Site config.
25
- * @param entityListType - Entity list type.
26
- * @param decodedFilterParam - Decoded filter parameter.
27
- * @param decodedCatalogParam - Decoded catalog parameter.
28
- * @param decodedFeatureFlagParam - Decoded feature flag parameter.
29
- * @returns explore state.
26
+ * Returns the category configs for the current entity.
27
+ * @param state - Explore state.
28
+ * @returns category configs.
30
29
  */
31
- export function initExploreState(
32
- config: SiteConfig,
33
- entityListType: string,
34
- decodedFilterParam: string,
35
- decodedCatalogParam?: string,
36
- decodedFeatureFlagParam?: string
37
- ): ExploreState {
38
- const filterState = initFilterState(decodedFilterParam);
39
- const filterCount = getFilterCount(filterState);
40
- return {
41
- ...INITIAL_STATE,
42
- catalogState: decodedCatalogParam,
43
- entityPageState: initEntityPageState(config),
44
- featureFlagState: decodedFeatureFlagParam,
45
- filterCount,
46
- filterState,
47
- listView: ENTITY_VIEW.EXACT,
48
- tabValue: entityListType,
49
- };
30
+ export function getEntityCategoryConfigs(
31
+ state: ExploreState
32
+ ): CategoryConfig[] | undefined {
33
+ return getEntityState(
34
+ getEntityCategoryGroupConfigKey(state.tabValue, state.entityPageState),
35
+ state
36
+ ).categoryConfigs;
50
37
  }
51
38
 
52
39
  /**
53
- * Initializes filter state from URL "filter" parameter.
54
- * @param decodedFilterParam - Decoded filter parameter.
55
- * @returns filter state.
40
+ * Returns the entity state for the given category group config key.
41
+ * @param categoryGroupConfigKey - Category group config key.
42
+ * @param state - Explore state.
43
+ * @returns entity state.
56
44
  */
57
- export function initFilterState(decodedFilterParam: string): SelectedFilter[] {
58
- // Define filter state, from URL "filter" parameter, if present and valid.
59
- let filterState: SelectedFilter[] = [];
60
- try {
61
- filterState = JSON.parse(decodedFilterParam);
62
- } catch {
63
- // do nothing
64
- }
65
- return filterState;
45
+ export function getEntityState(
46
+ categoryGroupConfigKey: CategoryGroupConfigKey,
47
+ state: ExploreState
48
+ ): EntityState {
49
+ return (
50
+ state.entityStateByCategoryGroupConfigKey.get(categoryGroupConfigKey) ||
51
+ DEFAULT_ENTITY_STATE
52
+ );
66
53
  }
67
54
 
68
55
  /**
69
- * Initializes entity page state.
70
- * @param config - Site config.
71
- * @returns entity page state.
56
+ * Returns the filter count.
57
+ * @param filterState - Filter state.
58
+ * @returns filter count.
72
59
  */
73
- export function initEntityPageState(config: SiteConfig): EntityPageStateMapper {
74
- return config.entities.reduce(
75
- (acc, entity) => ({
76
- ...acc,
77
- [entity.route]: {
78
- columnsVisibility: getInitialTableColumnVisibility(entity.list.columns),
79
- sorting: getDefaultSorting(entity),
80
- },
81
- }),
82
- {}
83
- );
60
+ export function getFilterCount(filterState: SelectedFilter[]): number {
61
+ return filterState.reduce((acc, filter) => acc + filter.value.length, 0);
84
62
  }
85
63
 
86
64
  /**
@@ -94,3 +72,64 @@ export function resetPage(paginationState: PaginationState): PaginationState {
94
72
  nextPaginationState.currentPage = 1;
95
73
  return nextPaginationState;
96
74
  }
75
+
76
+ /**
77
+ * Sets entity state for the given category group config key.
78
+ * @param categoryGroupConfigKey - Category group config key.
79
+ * @param state - Explore state.
80
+ * @param nextEntityState - Next entity state.
81
+ */
82
+ function setEntityStateByCategoryGroupConfigKey(
83
+ categoryGroupConfigKey: CategoryGroupConfigKey,
84
+ state: ExploreState,
85
+ nextEntityState: EntityState
86
+ ): void {
87
+ state.entityStateByCategoryGroupConfigKey.set(
88
+ categoryGroupConfigKey,
89
+ nextEntityState
90
+ );
91
+ }
92
+
93
+ /**
94
+ * Updates entity page state for the given entity path.
95
+ * @param entityPath - Entity path.
96
+ * @param entityPageState - Entity page state.
97
+ * @param nextEntityPageState - Partial next entity page state.
98
+ * @returns updated entity page state.
99
+ */
100
+ export function updateEntityPageState(
101
+ entityPath: string, // entityListType.
102
+ entityPageState: EntityPageStateMapper,
103
+ nextEntityPageState: Partial<EntityPageState>
104
+ ): EntityPageStateMapper {
105
+ return {
106
+ ...entityPageState,
107
+ [entityPath]: {
108
+ ...entityPageState[entityPath],
109
+ ...nextEntityPageState,
110
+ },
111
+ };
112
+ }
113
+
114
+ /**
115
+ * Updates entity state by category group config key.
116
+ * @param state - Explore state.
117
+ * @param nextEntityState - Partial next entity state.
118
+ * @returns updated entity state by category group config key.
119
+ */
120
+ export function updateEntityStateByCategoryGroupConfigKey(
121
+ state: ExploreState,
122
+ nextEntityState: Partial<EntityState>
123
+ ): void {
124
+ const categoryGroupConfigKey = getEntityCategoryGroupConfigKey(
125
+ state.tabValue,
126
+ state.entityPageState
127
+ );
128
+ const entityState = getEntityState(categoryGroupConfigKey, state);
129
+ if (entityState) {
130
+ setEntityStateByCategoryGroupConfigKey(categoryGroupConfigKey, state, {
131
+ ...entityState,
132
+ ...nextEntityState,
133
+ });
134
+ }
135
+ }
@@ -1,4 +1,3 @@
1
- import { ColumnSort } from "@tanstack/react-table";
2
1
  import React, {
3
2
  createContext,
4
3
  Dispatch,
@@ -10,19 +9,23 @@ import React, {
10
9
  } from "react";
11
10
  import { AzulSearchIndex } from "../apis/azul/common/entities";
12
11
  import { SelectCategory, SelectedFilter } from "../common/entities";
13
- import { CategoryConfig, EntityPath, SiteConfig } from "../config/entities";
12
+ import { CategoryGroup, SiteConfig } from "../config/entities";
14
13
  import { useAuthentication } from "../hooks/useAuthentication/useAuthentication";
15
- import { useCategoryConfigs } from "../hooks/useCategoryConfigs";
16
14
  import {
17
15
  buildCategoryViews,
18
16
  buildNextFilterState,
19
17
  } from "../hooks/useCategoryFilter";
20
18
  import { useConfig } from "../hooks/useConfig";
21
19
  import { useURLFilterParams } from "../hooks/useURLFilterParams";
20
+ import {
21
+ EntityPageStateMapper,
22
+ EntityStateByCategoryGroupConfigKey,
23
+ } from "./exploreState/entities";
22
24
  import {
23
25
  DEFAULT_PAGINATION_STATE,
24
26
  INITIAL_STATE,
25
- } from "./exploreState/constants";
27
+ } from "./exploreState/initializer/constants";
28
+ import { initReducerArguments } from "./exploreState/initializer/utils";
26
29
  import {
27
30
  PaginateTablePayload,
28
31
  ProcessExploreResponsePayload,
@@ -34,9 +37,13 @@ import {
34
37
  UpdateSortingPayload,
35
38
  } from "./exploreState/payloads/entities";
36
39
  import {
40
+ getEntityCategoryConfigs,
41
+ getEntityCategoryGroupConfigKey,
42
+ getEntityState,
37
43
  getFilterCount,
38
- initExploreState,
39
44
  resetPage,
45
+ updateEntityPageState,
46
+ updateEntityStateByCategoryGroupConfigKey,
40
47
  } from "./exploreState/utils";
41
48
 
42
49
  export type CatalogState = string | undefined;
@@ -53,33 +60,19 @@ export enum ENTITY_VIEW {
53
60
  * Explore context.
54
61
  */
55
62
  export interface ExploreContext {
56
- categoryConfigs?: CategoryConfig[];
57
63
  config: SiteConfig;
58
64
  entityList: string;
59
65
  }
60
66
 
61
- /**
62
- * State for each entity.
63
- */
64
- export interface EntityPageState {
65
- columnsVisibility: Record<string, boolean>;
66
- sorting: ColumnSort[];
67
- }
68
-
69
- /**
70
- * State for all entities.
71
- */
72
- export interface EntityPageStateMapper {
73
- [key: EntityPath]: EntityPageState;
74
- }
75
-
76
67
  /**
77
68
  * Explore state.
78
69
  */
79
70
  export type ExploreState = {
80
71
  catalogState: CatalogState;
72
+ categoryGroups?: CategoryGroup[];
81
73
  categoryViews: SelectCategory[];
82
74
  entityPageState: EntityPageStateMapper;
75
+ entityStateByCategoryGroupConfigKey: EntityStateByCategoryGroupConfigKey;
83
76
  featureFlagState: FeatureFlagState;
84
77
  filterCount: number;
85
78
  filterState: SelectedFilter[];
@@ -177,13 +170,12 @@ export function ExploreStateProvider({
177
170
  entityListType: string;
178
171
  }): JSX.Element {
179
172
  const { config, defaultEntityListType } = useConfig();
180
- const categoryConfigs = useCategoryConfigs();
181
173
  const { decodedCatalogParam, decodedFeatureFlagParam, decodedFilterParam } =
182
174
  useURLFilterParams();
183
175
  const { isEnabled: isAuthEnabled, token } = useAuthentication();
184
176
  const entityList = entityListType || defaultEntityListType;
185
- const [initReducerState] = useState(() =>
186
- initExploreState(
177
+ const [initializerArg] = useState(() =>
178
+ initReducerArguments(
187
179
  config,
188
180
  entityList,
189
181
  decodedFilterParam,
@@ -195,11 +187,10 @@ export function ExploreStateProvider({
195
187
  const [exploreState, exploreDispatch] = useReducer(
196
188
  (s: ExploreState, a: ExploreAction) =>
197
189
  exploreReducer(s, a, {
198
- categoryConfigs,
199
190
  config,
200
191
  entityList,
201
192
  }),
202
- initReducerState
193
+ initializerArg
203
194
  );
204
195
 
205
196
  // does this help? https://hswolff.com/blog/how-to-usecontext-with-usereducer/
@@ -357,17 +348,20 @@ function exploreReducer(
357
348
  exploreContext: ExploreContext
358
349
  ): ExploreState {
359
350
  const { payload, type } = action;
360
- const { categoryConfigs, config, entityList } = exploreContext;
351
+ const { config, entityList } = exploreContext;
361
352
 
362
353
  switch (type) {
363
354
  /**
364
355
  * Clear all filters
365
356
  **/
366
357
  case ExploreActionKind.ClearFilters: {
358
+ const filterCount = 0;
359
+ const filterState: SelectedFilter[] = [];
360
+ updateEntityStateByCategoryGroupConfigKey(state, { filterState });
367
361
  return {
368
362
  ...state,
369
- filterCount: 0,
370
- filterState: [],
363
+ filterCount,
364
+ filterState,
371
365
  paginationState: resetPage(state.paginationState),
372
366
  };
373
367
  }
@@ -392,15 +386,19 @@ function exploreReducer(
392
386
  * Process explore response
393
387
  **/
394
388
  case ExploreActionKind.ProcessExploreResponse: {
389
+ const nextCategoryViews = payload.selectCategories
390
+ ? buildCategoryViews(
391
+ payload.selectCategories,
392
+ getEntityCategoryConfigs(state),
393
+ state.filterState
394
+ )
395
+ : state.categoryViews;
396
+ updateEntityStateByCategoryGroupConfigKey(state, {
397
+ categoryViews: nextCategoryViews,
398
+ });
395
399
  return {
396
400
  ...state,
397
- categoryViews: payload.selectCategories
398
- ? buildCategoryViews(
399
- payload.selectCategories,
400
- categoryConfigs,
401
- state.filterState
402
- )
403
- : state.categoryViews,
401
+ categoryViews: nextCategoryViews,
404
402
  listItems: payload.loading ? [] : payload.listItems,
405
403
  loading: payload.loading,
406
404
  paginationState: {
@@ -433,7 +431,7 @@ function exploreReducer(
433
431
  * Reset the current state to the initial
434
432
  */
435
433
  case ExploreActionKind.ResetState: {
436
- return initExploreState(config, entityList, "");
434
+ return initReducerArguments(config, entityList, "");
437
435
  }
438
436
  /**
439
437
  * Select entity type
@@ -442,8 +440,16 @@ function exploreReducer(
442
440
  if (payload === state.tabValue) {
443
441
  return state;
444
442
  }
443
+ const entityState = getEntityState(
444
+ getEntityCategoryGroupConfigKey(payload, state.entityPageState),
445
+ state
446
+ );
445
447
  return {
446
448
  ...state,
449
+ categoryGroups: entityState.categoryGroups,
450
+ categoryViews: entityState.categoryViews,
451
+ filterCount: getFilterCount(entityState.filterState),
452
+ filterState: entityState.filterState,
447
453
  listItems: [],
448
454
  loading: true,
449
455
  paginationState: resetPage(state.paginationState),
@@ -470,6 +476,7 @@ function exploreReducer(
470
476
  payload.selectedValue,
471
477
  payload.selected
472
478
  );
479
+ updateEntityStateByCategoryGroupConfigKey(state, { filterState });
473
480
  return {
474
481
  ...state,
475
482
  filterCount: getFilterCount(filterState),
@@ -481,17 +488,13 @@ function exploreReducer(
481
488
  * Update sorting
482
489
  **/
483
490
  case ExploreActionKind.UpdateSorting: {
484
- const currentEntity = state.tabValue;
485
- const currentPageState = state.entityPageState[currentEntity];
486
491
  return {
487
492
  ...state,
488
- entityPageState: {
489
- ...state.entityPageState,
490
- [currentEntity]: {
491
- ...currentPageState,
492
- sorting: payload,
493
- },
494
- },
493
+ entityPageState: updateEntityPageState(
494
+ state.tabValue,
495
+ state.entityPageState,
496
+ { sorting: payload }
497
+ ),
495
498
  paginationState: resetPage(state.paginationState),
496
499
  };
497
500
  }
@@ -499,17 +502,13 @@ function exploreReducer(
499
502
  * Update column visibility
500
503
  **/
501
504
  case ExploreActionKind.UpdateColumnVisibility: {
502
- const currentEntity = state.tabValue;
503
- const currentPageState = state.entityPageState[currentEntity];
504
505
  return {
505
506
  ...state,
506
- entityPageState: {
507
- ...state.entityPageState,
508
- [currentEntity]: {
509
- ...currentPageState,
510
- columnsVisibility: payload,
511
- },
512
- },
507
+ entityPageState: updateEntityPageState(
508
+ state.tabValue,
509
+ state.entityPageState,
510
+ { columnsVisibility: payload }
511
+ ),
513
512
  };
514
513
  }
515
514
 
@@ -26,7 +26,7 @@ import { SidebarTools } from "../../components/Layout/components/Sidebar/compone
26
26
  import { Sidebar } from "../../components/Layout/components/Sidebar/sidebar";
27
27
  import { TableCreator } from "../../components/TableCreator/tableCreator";
28
28
  import {
29
- CategoryGroupConfig,
29
+ CategoryGroup,
30
30
  ComponentsConfig,
31
31
  EntityConfig,
32
32
  SummaryConfig,
@@ -66,16 +66,16 @@ export const ExploreView = (props: ExploreViewProps): JSX.Element => {
66
66
  const tabletDown = useBreakpointHelper(BREAKPOINT_FN_NAME.DOWN, DESKTOP_SM);
67
67
  const { config, entityConfig } = useConfig(); // Get app level config.
68
68
  const { exploreDispatch, exploreState } = useExploreState(); // Get the useReducer state and dispatch for "Explore".
69
- const {
70
- categoryGroupConfigs,
71
- entities,
72
- explorerTitle,
73
- summaryConfig,
74
- trackingConfig,
75
- } = config;
69
+ const { entities, explorerTitle, summaryConfig, trackingConfig } = config;
76
70
  const { listView } = entityConfig;
77
71
  const { listHero, subTitleHero } = listView || {};
78
- const { categoryViews, filterCount, isRelatedView, tabValue } = exploreState;
72
+ const {
73
+ categoryGroups,
74
+ categoryViews,
75
+ filterCount,
76
+ isRelatedView,
77
+ tabValue,
78
+ } = exploreState;
79
79
  const { push } = useRouter();
80
80
  const tabs = getTabs(entities);
81
81
  const { response: summaryResponse } = useSummary(); // Fetch summary.
@@ -83,8 +83,8 @@ export const ExploreView = (props: ExploreViewProps): JSX.Element => {
83
83
  useEntityListRelatedView(); // Fetch related entities.
84
84
  const { entityListType } = props;
85
85
  const categoryFilters = useMemo(
86
- () => buildCategoryFilters(categoryViews, categoryGroupConfigs),
87
- [categoryViews, categoryGroupConfigs]
86
+ () => buildCategoryFilters(categoryViews, categoryGroups),
87
+ [categoryGroups, categoryViews]
88
88
  );
89
89
 
90
90
  /**
@@ -216,17 +216,17 @@ export const ExploreView = (props: ExploreViewProps): JSX.Element => {
216
216
  /**
217
217
  * Builds the category views into category views grouped by the given category group configuration.
218
218
  * @param selectCategoryViews - View models of categories to display.
219
- * @param categoryGroupConfigs - Category group configuration.
219
+ * @param categoryGroups - Category groups.
220
220
  * @returns category filters.
221
221
  */
222
222
  function buildCategoryFilters(
223
223
  selectCategoryViews: SelectCategoryView[],
224
- categoryGroupConfigs?: CategoryGroupConfig[]
224
+ categoryGroups?: CategoryGroup[]
225
225
  ): CategoryFilter[] {
226
- if (!categoryGroupConfigs) {
226
+ if (!categoryGroups) {
227
227
  return [{ categoryViews: selectCategoryViews }];
228
228
  }
229
- return categoryGroupConfigs.map(({ categoryConfigs, label }) => {
229
+ return categoryGroups.map(({ categoryConfigs, label }) => {
230
230
  // Grab the category views for the configured grouped categories.
231
231
  const categoryViews = categoryConfigs.reduce(
232
232
  (acc, { key: categoryKey }) => {
@@ -291,7 +291,7 @@ function renderList(
291
291
  if (entityListType !== tabValue) {
292
292
  // required currently for client-side fetching as the pre-rendered page
293
293
  // loads with the previous tabs data on the first render after switching tabs. (or similar)
294
- return <></>; // TODO(Fran) review loading and return.
294
+ return <></>;
295
295
  }
296
296
 
297
297
  return (
@@ -1,17 +0,0 @@
1
- import { useMemo } from "react";
2
- import { CategoryConfig } from "../config/entities";
3
- import { useConfig } from "./useConfig";
4
-
5
- /**
6
- * Returns configured grouped configured categories as a list of configured categories.
7
- * @returns a list of configured categories.
8
- */
9
- export const useCategoryConfigs = (): CategoryConfig[] | undefined => {
10
- const { config } = useConfig();
11
- const { categoryGroupConfigs } = config;
12
- return useMemo(() => {
13
- return categoryGroupConfigs?.flatMap(
14
- ({ categoryConfigs }) => categoryConfigs
15
- );
16
- }, [categoryGroupConfigs]);
17
- };