@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
@@ -26,12 +26,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
26
26
  exports.ExploreActionKind = exports.ExploreStateProvider = exports.ExploreStateContext = exports.ENTITY_VIEW = void 0;
27
27
  const react_1 = __importStar(require("react"));
28
28
  const useAuthentication_1 = require("../hooks/useAuthentication/useAuthentication");
29
- const useCategoryConfigs_1 = require("../hooks/useCategoryConfigs");
30
29
  const useCategoryFilter_1 = require("../hooks/useCategoryFilter");
31
30
  const useConfig_1 = require("../hooks/useConfig");
32
31
  const useURLFilterParams_1 = require("../hooks/useURLFilterParams");
33
- const constants_1 = require("./exploreState/constants");
34
- const utils_1 = require("./exploreState/utils");
32
+ const constants_1 = require("./exploreState/initializer/constants");
33
+ const utils_1 = require("./exploreState/initializer/utils");
34
+ const utils_2 = require("./exploreState/utils");
35
35
  /**
36
36
  * Entity view.
37
37
  */
@@ -64,16 +64,14 @@ exports.ExploreStateContext = (0, react_1.createContext)({
64
64
  */
65
65
  function ExploreStateProvider({ children, entityListType, }) {
66
66
  const { config, defaultEntityListType } = (0, useConfig_1.useConfig)();
67
- const categoryConfigs = (0, useCategoryConfigs_1.useCategoryConfigs)();
68
67
  const { decodedCatalogParam, decodedFeatureFlagParam, decodedFilterParam } = (0, useURLFilterParams_1.useURLFilterParams)();
69
68
  const { isEnabled: isAuthEnabled, token } = (0, useAuthentication_1.useAuthentication)();
70
69
  const entityList = entityListType || defaultEntityListType;
71
- const [initReducerState] = (0, react_1.useState)(() => (0, utils_1.initExploreState)(config, entityList, decodedFilterParam, decodedCatalogParam, decodedFeatureFlagParam));
70
+ const [initializerArg] = (0, react_1.useState)(() => (0, utils_1.initReducerArguments)(config, entityList, decodedFilterParam, decodedCatalogParam, decodedFeatureFlagParam));
72
71
  const [exploreState, exploreDispatch] = (0, react_1.useReducer)((s, a) => exploreReducer(s, a, {
73
- categoryConfigs,
74
72
  config,
75
73
  entityList,
76
- }), initReducerState);
74
+ }), initializerArg);
77
75
  // does this help? https://hswolff.com/blog/how-to-usecontext-with-usereducer/
78
76
  const exploreContextValue = (0, react_1.useMemo)(() => {
79
77
  return { exploreDispatch, exploreState };
@@ -116,13 +114,17 @@ var ExploreActionKind;
116
114
  */
117
115
  function exploreReducer(state, action, exploreContext) {
118
116
  const { payload, type } = action;
119
- const { categoryConfigs, config, entityList } = exploreContext;
117
+ const { config, entityList } = exploreContext;
120
118
  switch (type) {
121
119
  /**
122
120
  * Clear all filters
123
121
  **/
124
122
  case ExploreActionKind.ClearFilters: {
125
- return Object.assign(Object.assign({}, state), { filterCount: 0, filterState: [], paginationState: (0, utils_1.resetPage)(state.paginationState) });
123
+ const filterCount = 0;
124
+ const filterState = [];
125
+ (0, utils_2.updateEntityStateByCategoryGroupConfigKey)(state, { filterState });
126
+ return Object.assign(Object.assign({}, state), { filterCount,
127
+ filterState, paginationState: (0, utils_2.resetPage)(state.paginationState) });
126
128
  }
127
129
  /**
128
130
  * Paginate table
@@ -143,9 +145,13 @@ function exploreReducer(state, action, exploreContext) {
143
145
  * Process explore response
144
146
  **/
145
147
  case ExploreActionKind.ProcessExploreResponse: {
146
- return Object.assign(Object.assign({}, state), { categoryViews: payload.selectCategories
147
- ? (0, useCategoryFilter_1.buildCategoryViews)(payload.selectCategories, categoryConfigs, state.filterState)
148
- : state.categoryViews, listItems: payload.loading ? [] : payload.listItems, loading: payload.loading, paginationState: Object.assign(Object.assign({}, state.paginationState), payload.paginationResponse) });
148
+ const nextCategoryViews = payload.selectCategories
149
+ ? (0, useCategoryFilter_1.buildCategoryViews)(payload.selectCategories, (0, utils_2.getEntityCategoryConfigs)(state), state.filterState)
150
+ : state.categoryViews;
151
+ (0, utils_2.updateEntityStateByCategoryGroupConfigKey)(state, {
152
+ categoryViews: nextCategoryViews,
153
+ });
154
+ return Object.assign(Object.assign({}, state), { categoryViews: nextCategoryViews, listItems: payload.loading ? [] : payload.listItems, loading: payload.loading, paginationState: Object.assign(Object.assign({}, state.paginationState), payload.paginationResponse) });
149
155
  }
150
156
  /**
151
157
  * Process related response
@@ -163,7 +169,7 @@ function exploreReducer(state, action, exploreContext) {
163
169
  * Reset the current state to the initial
164
170
  */
165
171
  case ExploreActionKind.ResetState: {
166
- return (0, utils_1.initExploreState)(config, entityList, "");
172
+ return (0, utils_1.initReducerArguments)(config, entityList, "");
167
173
  }
168
174
  /**
169
175
  * Select entity type
@@ -172,7 +178,8 @@ function exploreReducer(state, action, exploreContext) {
172
178
  if (payload === state.tabValue) {
173
179
  return state;
174
180
  }
175
- return Object.assign(Object.assign({}, state), { listItems: [], loading: true, paginationState: (0, utils_1.resetPage)(state.paginationState), tabValue: payload });
181
+ const entityState = (0, utils_2.getEntityState)((0, utils_2.getEntityCategoryGroupConfigKey)(payload, state.entityPageState), state);
182
+ return Object.assign(Object.assign({}, state), { categoryGroups: entityState.categoryGroups, categoryViews: entityState.categoryViews, filterCount: (0, utils_2.getFilterCount)(entityState.filterState), filterState: entityState.filterState, listItems: [], loading: true, paginationState: (0, utils_2.resetPage)(state.paginationState), tabValue: payload });
176
183
  }
177
184
  /**
178
185
  * Toggle entity view
@@ -185,23 +192,20 @@ function exploreReducer(state, action, exploreContext) {
185
192
  **/
186
193
  case ExploreActionKind.UpdateFilter: {
187
194
  const filterState = (0, useCategoryFilter_1.buildNextFilterState)(state.filterState, payload.categoryKey, payload.selectedValue, payload.selected);
188
- return Object.assign(Object.assign({}, state), { filterCount: (0, utils_1.getFilterCount)(filterState), filterState, paginationState: (0, utils_1.resetPage)(state.paginationState) });
195
+ (0, utils_2.updateEntityStateByCategoryGroupConfigKey)(state, { filterState });
196
+ return Object.assign(Object.assign({}, state), { filterCount: (0, utils_2.getFilterCount)(filterState), filterState, paginationState: (0, utils_2.resetPage)(state.paginationState) });
189
197
  }
190
198
  /**
191
199
  * Update sorting
192
200
  **/
193
201
  case ExploreActionKind.UpdateSorting: {
194
- const currentEntity = state.tabValue;
195
- const currentPageState = state.entityPageState[currentEntity];
196
- return Object.assign(Object.assign({}, state), { entityPageState: Object.assign(Object.assign({}, state.entityPageState), { [currentEntity]: Object.assign(Object.assign({}, currentPageState), { sorting: payload }) }), paginationState: (0, utils_1.resetPage)(state.paginationState) });
202
+ return Object.assign(Object.assign({}, state), { entityPageState: (0, utils_2.updateEntityPageState)(state.tabValue, state.entityPageState, { sorting: payload }), paginationState: (0, utils_2.resetPage)(state.paginationState) });
197
203
  }
198
204
  /**
199
205
  * Update column visibility
200
206
  **/
201
207
  case ExploreActionKind.UpdateColumnVisibility: {
202
- const currentEntity = state.tabValue;
203
- const currentPageState = state.entityPageState[currentEntity];
204
- return Object.assign(Object.assign({}, state), { entityPageState: Object.assign(Object.assign({}, state.entityPageState), { [currentEntity]: Object.assign(Object.assign({}, currentPageState), { columnsVisibility: payload }) }) });
208
+ return Object.assign(Object.assign({}, state), { entityPageState: (0, utils_2.updateEntityPageState)(state.tabValue, state.entityPageState, { columnsVisibility: payload }) });
205
209
  }
206
210
  default:
207
211
  return state;
@@ -65,17 +65,17 @@ const ExploreView = (props) => {
65
65
  const tabletDown = (0, useBreakpointHelper_1.useBreakpointHelper)(useBreakpointHelper_1.BREAKPOINT_FN_NAME.DOWN, breakpoints_1.DESKTOP_SM);
66
66
  const { config, entityConfig } = (0, useConfig_1.useConfig)(); // Get app level config.
67
67
  const { exploreDispatch, exploreState } = (0, useExploreState_1.useExploreState)(); // Get the useReducer state and dispatch for "Explore".
68
- const { categoryGroupConfigs, entities, explorerTitle, summaryConfig, trackingConfig, } = config;
68
+ const { entities, explorerTitle, summaryConfig, trackingConfig } = config;
69
69
  const { listView } = entityConfig;
70
70
  const { listHero, subTitleHero } = listView || {};
71
- const { categoryViews, filterCount, isRelatedView, tabValue } = exploreState;
71
+ const { categoryGroups, categoryViews, filterCount, isRelatedView, tabValue, } = exploreState;
72
72
  const { push } = (0, router_1.useRouter)();
73
73
  const tabs = getTabs(entities);
74
74
  const { response: summaryResponse } = (0, useSummary_1.useSummary)(); // Fetch summary.
75
75
  (0, useEntityList_1.useEntityList)(props); // Fetch entities.
76
76
  (0, useEntityListRelatedView_1.useEntityListRelatedView)(); // Fetch related entities.
77
77
  const { entityListType } = props;
78
- const categoryFilters = (0, react_1.useMemo)(() => buildCategoryFilters(categoryViews, categoryGroupConfigs), [categoryViews, categoryGroupConfigs]);
78
+ const categoryFilters = (0, react_1.useMemo)(() => buildCategoryFilters(categoryViews, categoryGroups), [categoryGroups, categoryViews]);
79
79
  /**
80
80
  * Closes filter drawer.
81
81
  */
@@ -159,14 +159,14 @@ exports.ExploreView = ExploreView;
159
159
  /**
160
160
  * Builds the category views into category views grouped by the given category group configuration.
161
161
  * @param selectCategoryViews - View models of categories to display.
162
- * @param categoryGroupConfigs - Category group configuration.
162
+ * @param categoryGroups - Category groups.
163
163
  * @returns category filters.
164
164
  */
165
- function buildCategoryFilters(selectCategoryViews, categoryGroupConfigs) {
166
- if (!categoryGroupConfigs) {
165
+ function buildCategoryFilters(selectCategoryViews, categoryGroups) {
166
+ if (!categoryGroups) {
167
167
  return [{ categoryViews: selectCategoryViews }];
168
168
  }
169
- return categoryGroupConfigs.map(({ categoryConfigs, label }) => {
169
+ return categoryGroups.map(({ categoryConfigs, label }) => {
170
170
  // Grab the category views for the configured grouped categories.
171
171
  const categoryViews = categoryConfigs.reduce((acc, { key: categoryKey }) => {
172
172
  const categoryView = selectCategoryViews.find(({ key }) => key === categoryKey);
@@ -208,7 +208,7 @@ function renderList(exploreState, entityConfig, entityListType) {
208
208
  if (entityListType !== tabValue) {
209
209
  // required currently for client-side fetching as the pre-rendered page
210
210
  // loads with the previous tabs data on the first render after switching tabs. (or similar)
211
- return react_1.default.createElement(react_1.default.Fragment, null); // TODO(Fran) review loading and return.
211
+ return react_1.default.createElement(react_1.default.Fragment, null);
212
212
  }
213
213
  return (react_1.default.createElement(tableCreator_1.TableCreator, { columns: columnsConfig, defaultSort: defaultSort, items: isRelatedView && relatedListItems ? relatedListItems : listItems !== null && listItems !== void 0 ? listItems : [], listView: listView, loading: loading, pages: paginationState.pages, pageSize: paginationState.pageSize, pagination: undefined, total: paginationState.rows }));
214
214
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@databiosphere/findable-ui",
3
- "version": "1.0.1",
3
+ "version": "2.0.1",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "test": "jest",
@@ -21,6 +21,7 @@ export enum LOADING_PANEL_STYLE {
21
21
  }
22
22
 
23
23
  export interface LoadingProps {
24
+ appear?: boolean; // Note, if false, the component will not transition on the initial render and onEnter callbacks will not be called. In this instance, ensure the parent container is styled correctly with position relative.
24
25
  iconSize?: SvgIconProps["fontSize"];
25
26
  loading: boolean;
26
27
  panelStyle?: LoadingPanelStyle; // Enables loading to mirror parent container styles.
@@ -28,6 +29,7 @@ export interface LoadingProps {
28
29
  }
29
30
 
30
31
  export const Loading = ({
32
+ appear = true,
31
33
  iconSize = "large",
32
34
  loading,
33
35
  panelStyle = PAPER_PANEL_STYLE.ROUNDED,
@@ -35,6 +37,7 @@ export const Loading = ({
35
37
  }: LoadingProps): JSX.Element | null => {
36
38
  return (
37
39
  <Fade
40
+ appear={appear}
38
41
  in={loading}
39
42
  mountOnEnter
40
43
  onEnter={(node: HTMLElement): void => onFadeEnter(node)}
@@ -59,7 +62,7 @@ export const Loading = ({
59
62
  */
60
63
  function onFadeEnter(node: HTMLElement): void {
61
64
  const parentEl = node.parentElement;
62
- if (parentEl) parentEl.style.position = "relative";
65
+ if (parentEl) parentEl.style.setProperty("position", "relative");
63
66
  }
64
67
 
65
68
  /**
@@ -69,5 +72,5 @@ function onFadeEnter(node: HTMLElement): void {
69
72
  */
70
73
  function onFadeExited(node: HTMLElement): void {
71
74
  const parentEl = node.parentElement;
72
- if (parentEl) parentEl.style.position = "";
75
+ if (parentEl) parentEl.style.removeProperty("position");
73
76
  }
@@ -31,7 +31,7 @@ import { useExploreMode } from "../../hooks/useExploreMode";
31
31
  import { useExploreState } from "../../hooks/useExploreState";
32
32
  import { useScroll } from "../../hooks/useScroll";
33
33
  import { ENTITY_VIEW, ExploreActionKind } from "../../providers/exploreState";
34
- import { DEFAULT_PAGINATION_STATE } from "../../providers/exploreState/constants";
34
+ import { DEFAULT_PAGINATION_STATE } from "../../providers/exploreState/initializer/constants";
35
35
  import { TABLET } from "../../theme/common/breakpoints";
36
36
  import { FluidPaper, GridPaper } from "../common/Paper/paper.styles";
37
37
  import { NoResults } from "../NoResults/noResults";
@@ -0,0 +1,5 @@
1
+ import styled from "@emotion/styled";
2
+
3
+ export const TableCreator = styled.div`
4
+ position: relative; // Required; when the table mounts, the loading element should not transition in and therefore to position the loading element correctly, the parent container must be styled with position relative.
5
+ `;
@@ -11,6 +11,7 @@ import {
11
11
  sortingFn,
12
12
  } from "../Table/common/utils";
13
13
  import { Table } from "../Table/table";
14
+ import { TableCreator as TableCreatorContainer } from "./tableCreator.styles";
14
15
 
15
16
  export interface TableCreatorProps<T> {
16
17
  columns: ColumnConfig<T>[];
@@ -69,8 +70,9 @@ export const TableCreator = <T extends object>({
69
70
  );
70
71
  const initialState = getInitialState(columns, defaultSort);
71
72
  return (
72
- <div>
73
+ <TableCreatorContainer>
73
74
  <Loading
75
+ appear={false}
74
76
  loading={loading || false}
75
77
  panelStyle={PAPER_PANEL_STYLE.FLUID}
76
78
  />
@@ -86,6 +88,6 @@ export const TableCreator = <T extends object>({
86
88
  pagination={pagination}
87
89
  total={total}
88
90
  />
89
- </div>
91
+ </TableCreatorContainer>
90
92
  );
91
93
  };
@@ -6,6 +6,7 @@ export const AnchorLink = styled(Link)`
6
6
  color: ${inkLight};
7
7
  margin-left: 4px;
8
8
  opacity: 0;
9
+ position: absolute;
9
10
  transition: opacity 0.2s ease-in-out;
10
11
 
11
12
  svg {
@@ -51,9 +51,17 @@ export interface BackPageTabConfig extends TabConfig {
51
51
  }
52
52
 
53
53
  /**
54
- * Model of grouped configured categories in site config.
54
+ * Model of category group config in site config.
55
55
  */
56
56
  export interface CategoryGroupConfig {
57
+ categoryGroups: CategoryGroup[];
58
+ key: string;
59
+ }
60
+
61
+ /**
62
+ * Model of grouped configured categories in site config.
63
+ */
64
+ export interface CategoryGroup {
57
65
  categoryConfigs: CategoryConfig[];
58
66
  label?: string;
59
67
  }
@@ -143,6 +151,7 @@ export type EntityPath = string;
143
151
  // eslint-disable-next-line @typescript-eslint/no-explicit-any -- This config model is part of a generic array
144
152
  export interface EntityConfig<T = any, I = any> extends TabConfig {
145
153
  apiPath?: EntityPath;
154
+ categoryGroupConfig?: CategoryGroupConfig;
146
155
  detail: BackPageConfig;
147
156
  entityMapper?: EntityMapper<T, I>;
148
157
  exploreMode: ExploreMode;
@@ -357,7 +366,7 @@ export interface SiteConfig {
357
366
  appTitle: string;
358
367
  authentication?: AuthenticationConfig;
359
368
  browserURL: string;
360
- categoryGroupConfigs?: CategoryGroupConfig[];
369
+ categoryGroupConfig?: CategoryGroupConfig;
361
370
  contentDir?: string;
362
371
  contentThemeOptionsFn?: ThemeOptionsFn;
363
372
  dataSource: DataSourceConfig;
@@ -12,7 +12,7 @@ import {
12
12
  import { EntityMapper } from "../config/entities";
13
13
  import { getEntityConfig } from "../config/utils";
14
14
  import { ExploreActionKind } from "../providers/exploreState";
15
- import { DEFAULT_PAGINATION_STATE } from "../providers/exploreState/constants";
15
+ import { DEFAULT_PAGINATION_STATE } from "../providers/exploreState/initializer/constants";
16
16
  import { useAsync } from "./useAsync";
17
17
  import { useAuthentication } from "./useAuthentication/useAuthentication";
18
18
  import { useConfig } from "./useConfig";
@@ -1,4 +1,6 @@
1
- import { useEffect, useState } from "react";
1
+ import Router from "next/router";
2
+ import { useCallback, useEffect, useState } from "react";
3
+ import { useConfig } from "./useConfig";
2
4
  import { useLocation } from "./useLocation";
3
5
 
4
6
  export const INACTIVITY_PARAM = "inactivityTimeout";
@@ -13,15 +15,19 @@ interface UseSessionTimeout {
13
15
  * @returns flag indicating if the session has timed out.
14
16
  */
15
17
  export const useSessionTimeout = (): UseSessionTimeout => {
18
+ const {
19
+ config: { redirectRootToPath },
20
+ } = useConfig();
16
21
  const [isSessionTimeout, setIsSessionTimeout] = useState<boolean>(false);
17
22
  // Get the session timeout from URL parameters.
18
23
  const { search } = useLocation() || {};
19
24
  const sessionTimeout = search?.get(INACTIVITY_PARAM);
20
25
 
21
26
  // Clears session timeout state.
22
- const clearSessionTimeout = (): void => {
27
+ const clearSessionTimeout = useCallback((): void => {
23
28
  setIsSessionTimeout(false);
24
- };
29
+ Router.replace(redirectRootToPath);
30
+ }, [redirectRootToPath]);
25
31
 
26
32
  useEffect(() => {
27
33
  setIsSessionTimeout(sessionTimeout === "true");
@@ -0,0 +1,32 @@
1
+ import { ColumnSort } from "@tanstack/react-table";
2
+ import { SelectCategory, SelectedFilter } from "../../common/entities";
3
+ import {
4
+ CategoryConfig,
5
+ CategoryGroup,
6
+ CategoryGroupConfig,
7
+ EntityPath,
8
+ } from "../../config/entities";
9
+
10
+ export interface EntityPageState {
11
+ categoryGroupConfigKey: CategoryGroupConfigKey;
12
+ columnsVisibility: Record<string, boolean>;
13
+ sorting: ColumnSort[];
14
+ }
15
+
16
+ export interface EntityPageStateMapper {
17
+ [key: EntityPath]: EntityPageState;
18
+ }
19
+
20
+ export interface EntityState {
21
+ categoryConfigs?: CategoryConfig[];
22
+ categoryGroups?: CategoryGroup[];
23
+ categoryViews: SelectCategory[];
24
+ filterState: SelectedFilter[];
25
+ }
26
+
27
+ export type EntityStateByCategoryGroupConfigKey = Map<
28
+ CategoryGroupConfigKey,
29
+ EntityState
30
+ >;
31
+
32
+ export type CategoryGroupConfigKey = CategoryGroupConfig["key"];
@@ -1,6 +1,11 @@
1
- import { ExploreState, PaginationState } from "../exploreState";
1
+ import { ExploreState, PaginationState } from "../../exploreState";
2
+ import { EntityState } from "../entities";
3
+
4
+ export const DEFAULT_ENTITY_STATE: EntityState = {
5
+ categoryViews: [],
6
+ filterState: [],
7
+ };
2
8
 
3
- // Template constants
4
9
  export const DEFAULT_PAGINATION_STATE: PaginationState = {
5
10
  currentPage: 1,
6
11
  index: null,
@@ -11,11 +16,11 @@ export const DEFAULT_PAGINATION_STATE: PaginationState = {
11
16
  rows: 0,
12
17
  };
13
18
 
14
- // Initial state
15
19
  export const INITIAL_STATE: ExploreState = {
16
20
  catalogState: undefined,
17
21
  categoryViews: [],
18
22
  entityPageState: {},
23
+ entityStateByCategoryGroupConfigKey: new Map(),
19
24
  featureFlagState: undefined,
20
25
  filterCount: 0,
21
26
  filterState: [],
@@ -0,0 +1,183 @@
1
+ import { SelectedFilter } from "../../../common/entities";
2
+ import { getInitialTableColumnVisibility } from "../../../components/Table/common/utils";
3
+ import {
4
+ CategoryConfig,
5
+ CategoryGroup,
6
+ CategoryGroupConfig,
7
+ EntityConfig,
8
+ SiteConfig,
9
+ } from "../../../config/entities";
10
+ import { getDefaultSorting } from "../../../config/utils";
11
+ import { ExploreState } from "../../exploreState";
12
+ import {
13
+ CategoryGroupConfigKey,
14
+ EntityPageStateMapper,
15
+ EntityStateByCategoryGroupConfigKey,
16
+ } from "../entities";
17
+ import { getEntityCategoryGroupConfigKey, getFilterCount } from "../utils";
18
+ import { DEFAULT_ENTITY_STATE, INITIAL_STATE } from "./constants";
19
+
20
+ /**
21
+ * Returns entity related configured category group config where entity config takes precedence over site config.
22
+ * @param siteConfig - Site config.
23
+ * @param entityConfig - Entity config.
24
+ * @returns entity related category group config.
25
+ */
26
+ function getEntityCategoryGroupConfig(
27
+ siteConfig: SiteConfig,
28
+ entityConfig: EntityConfig
29
+ ): CategoryGroupConfig | undefined {
30
+ const siteCategoryGroupConfig = siteConfig.categoryGroupConfig;
31
+ const entityCategoryGroupConfig = entityConfig.categoryGroupConfig;
32
+ return entityCategoryGroupConfig ?? siteCategoryGroupConfig;
33
+ }
34
+
35
+ /**
36
+ * Returns configured category groups as a list of configured categories.
37
+ * @param categoryGroups - Configured category groups.
38
+ * @returns a list of configured categories.
39
+ */
40
+ function flattenCategoryGroups(
41
+ categoryGroups?: CategoryGroup[]
42
+ ): CategoryConfig[] | undefined {
43
+ return categoryGroups?.flatMap(({ categoryConfigs }) => categoryConfigs);
44
+ }
45
+
46
+ /**
47
+ * Initializes category group config key for the entity.
48
+ * @param siteConfig - Site config.
49
+ * @param entityConfig - Entity config.
50
+ * @returns category group config key.
51
+ */
52
+ function initCategoryGroupConfigKey(
53
+ siteConfig: SiteConfig,
54
+ entityConfig: EntityConfig
55
+ ): CategoryGroupConfigKey {
56
+ const categoryGroupConfig = getEntityCategoryGroupConfig(
57
+ siteConfig,
58
+ entityConfig
59
+ );
60
+ return categoryGroupConfig?.key || entityConfig.route;
61
+ }
62
+
63
+ /**
64
+ * Initializes category groups for the current entity.
65
+ * @param entityStateByCategoryGroupConfigKey - Entity state by category group config key.
66
+ * @param categoryGroupConfigKey - Category group config key.
67
+ * @returns category groups.
68
+ */
69
+ function initCategoryGroups(
70
+ entityStateByCategoryGroupConfigKey: EntityStateByCategoryGroupConfigKey,
71
+ categoryGroupConfigKey: CategoryGroupConfigKey
72
+ ): CategoryGroup[] | undefined {
73
+ return entityStateByCategoryGroupConfigKey.get(categoryGroupConfigKey)
74
+ ?.categoryGroups;
75
+ }
76
+
77
+ /**
78
+ * Initializes entity page state.
79
+ * @param config - Site config.
80
+ * @returns entity page state.
81
+ */
82
+ function initEntityPageState(config: SiteConfig): EntityPageStateMapper {
83
+ return config.entities.reduce((acc, entity): EntityPageStateMapper => {
84
+ return {
85
+ ...acc,
86
+ [entity.route]: {
87
+ categoryGroupConfigKey: initCategoryGroupConfigKey(config, entity),
88
+ columnsVisibility: getInitialTableColumnVisibility(entity.list.columns),
89
+ sorting: getDefaultSorting(entity),
90
+ },
91
+ };
92
+ }, {} as EntityPageStateMapper);
93
+ }
94
+
95
+ /**
96
+ * Initializes entity state by category group config key.
97
+ * @param config - Site config.
98
+ * @param categoryGroupConfigKey - Category group config key.
99
+ * @param filterState - Filter state.
100
+ * @returns entity state by category group config key.
101
+ */
102
+ function initEntityStateByCategoryGroupConfigKey(
103
+ config: SiteConfig,
104
+ categoryGroupConfigKey: CategoryGroupConfigKey,
105
+ filterState: SelectedFilter[]
106
+ ): EntityStateByCategoryGroupConfigKey {
107
+ const entityStateByCategoryGroupConfigKey: EntityStateByCategoryGroupConfigKey =
108
+ new Map();
109
+ for (const entity of config.entities) {
110
+ const categoryGroupConfig = getEntityCategoryGroupConfig(config, entity);
111
+ if (!categoryGroupConfig) continue;
112
+ const { categoryGroups, key } = categoryGroupConfig;
113
+ if (entityStateByCategoryGroupConfigKey.has(key)) continue;
114
+ entityStateByCategoryGroupConfigKey.set(key, {
115
+ ...DEFAULT_ENTITY_STATE,
116
+ categoryConfigs: flattenCategoryGroups(categoryGroups),
117
+ categoryGroups,
118
+ filterState: key === categoryGroupConfigKey ? filterState : [],
119
+ });
120
+ }
121
+ return entityStateByCategoryGroupConfigKey;
122
+ }
123
+
124
+ /**
125
+ * Initializes filter state from URL "filter" parameter.
126
+ * @param decodedFilterParam - Decoded filter parameter.
127
+ * @returns filter state.
128
+ */
129
+ function initFilterState(decodedFilterParam: string): SelectedFilter[] {
130
+ // Define filter state, from URL "filter" parameter, if present and valid.
131
+ let filterState: SelectedFilter[] = [];
132
+ try {
133
+ filterState = JSON.parse(decodedFilterParam);
134
+ } catch {
135
+ // do nothing
136
+ }
137
+ return filterState;
138
+ }
139
+
140
+ /**
141
+ * Returns the explore state reducer initial arguments.
142
+ * @param config - Site config.
143
+ * @param entityListType - Entity list type.
144
+ * @param decodedFilterParam - Decoded filter parameter.
145
+ * @param decodedCatalogParam - Decoded catalog parameter.
146
+ * @param decodedFeatureFlagParam - Decoded feature flag parameter.
147
+ * @returns explore state reducer initial arguments.
148
+ */
149
+ export function initReducerArguments(
150
+ config: SiteConfig,
151
+ entityListType: string,
152
+ decodedFilterParam: string,
153
+ decodedCatalogParam?: string,
154
+ decodedFeatureFlagParam?: string
155
+ ): ExploreState {
156
+ const filterState = initFilterState(decodedFilterParam);
157
+ const entityPageState = initEntityPageState(config);
158
+ const categoryGroupConfigKey = getEntityCategoryGroupConfigKey(
159
+ entityListType,
160
+ entityPageState
161
+ );
162
+ const entityStateByCategoryGroupConfigKey =
163
+ initEntityStateByCategoryGroupConfigKey(
164
+ config,
165
+ categoryGroupConfigKey,
166
+ filterState
167
+ );
168
+ const categoryGroups = initCategoryGroups(
169
+ entityStateByCategoryGroupConfigKey,
170
+ categoryGroupConfigKey
171
+ );
172
+ return {
173
+ ...INITIAL_STATE,
174
+ catalogState: decodedCatalogParam,
175
+ categoryGroups,
176
+ entityPageState,
177
+ entityStateByCategoryGroupConfigKey,
178
+ featureFlagState: decodedFeatureFlagParam,
179
+ filterCount: getFilterCount(filterState),
180
+ filterState,
181
+ tabValue: entityListType,
182
+ };
183
+ }