@databiosphere/findable-ui 1.0.1 → 2.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.
@@ -13,9 +13,10 @@ export declare enum LOADING_PANEL_STYLE {
13
13
  INHERIT = "INHERIT"
14
14
  }
15
15
  export interface LoadingProps {
16
+ appear?: boolean;
16
17
  iconSize?: SvgIconProps["fontSize"];
17
18
  loading: boolean;
18
19
  panelStyle?: LoadingPanelStyle;
19
20
  text?: string;
20
21
  }
21
- export declare const Loading: ({ iconSize, loading, panelStyle, text, }: LoadingProps) => JSX.Element | null;
22
+ export declare const Loading: ({ appear, iconSize, loading, panelStyle, text, }: LoadingProps) => JSX.Element | null;
@@ -16,8 +16,8 @@ var LOADING_PANEL_STYLE;
16
16
  (function (LOADING_PANEL_STYLE) {
17
17
  LOADING_PANEL_STYLE["INHERIT"] = "INHERIT";
18
18
  })(LOADING_PANEL_STYLE = exports.LOADING_PANEL_STYLE || (exports.LOADING_PANEL_STYLE = {}));
19
- const Loading = ({ iconSize = "large", loading, panelStyle = paper_1.PAPER_PANEL_STYLE.ROUNDED, text, }) => {
20
- return (react_1.default.createElement(material_1.Fade, { in: loading, mountOnEnter: true, onEnter: (node) => onFadeEnter(node), onExited: (node) => onFadeExited(node), timeout: 300, unmountOnExit: true },
19
+ const Loading = ({ appear = true, iconSize = "large", loading, panelStyle = paper_1.PAPER_PANEL_STYLE.ROUNDED, text, }) => {
20
+ return (react_1.default.createElement(material_1.Fade, { appear: appear, in: loading, mountOnEnter: true, onEnter: (node) => onFadeEnter(node), onExited: (node) => onFadeExited(node), timeout: 300, unmountOnExit: true },
21
21
  react_1.default.createElement(loading_styles_1.LoadingPositioner, { panelStyle: panelStyle },
22
22
  react_1.default.createElement(loading_styles_1.LoadingPaper, { panelStyle: panelStyle },
23
23
  react_1.default.createElement(loadingIcon_1.LoadingIcon, { color: "primary", fontSize: iconSize }),
@@ -32,7 +32,7 @@ exports.Loading = Loading;
32
32
  function onFadeEnter(node) {
33
33
  const parentEl = node.parentElement;
34
34
  if (parentEl)
35
- parentEl.style.position = "relative";
35
+ parentEl.style.setProperty("position", "relative");
36
36
  }
37
37
  /**
38
38
  * Callback fired after the "exited" status is applied.
@@ -42,5 +42,5 @@ function onFadeEnter(node) {
42
42
  function onFadeExited(node) {
43
43
  const parentEl = node.parentElement;
44
44
  if (parentEl)
45
- parentEl.style.position = "";
45
+ parentEl.style.removeProperty("position");
46
46
  }
@@ -41,6 +41,7 @@ const ComponentCreator_1 = require("../ComponentCreator/ComponentCreator");
41
41
  const loading_1 = require("../Loading/loading");
42
42
  const utils_1 = require("../Table/common/utils");
43
43
  const table_1 = require("../Table/table");
44
+ const tableCreator_styles_1 = require("./tableCreator.styles");
44
45
  const createCell = (config) =>
45
46
  // eslint-disable-next-line @typescript-eslint/no-explicit-any -- We can't determine the cell type
46
47
  function CellCreator({ row }) {
@@ -66,8 +67,8 @@ const TableCreator = ({ columns, defaultSort, items, listView, loading, pageCoun
66
67
  });
67
68
  }), [columns]);
68
69
  const initialState = (0, utils_1.getInitialState)(columns, defaultSort);
69
- return (react_1.default.createElement("div", null,
70
- react_1.default.createElement(loading_1.Loading, { loading: loading || false, panelStyle: paper_1.PAPER_PANEL_STYLE.FLUID }),
70
+ return (react_1.default.createElement(tableCreator_styles_1.TableCreator, null,
71
+ react_1.default.createElement(loading_1.Loading, { appear: false, loading: loading || false, panelStyle: paper_1.PAPER_PANEL_STYLE.FLUID }),
71
72
  react_1.default.createElement(table_1.Table, { columns: columnDefs, count: pageCount, initialState: initialState, items: items, listView: listView, loading: loading, pages: pages, pageSize: pageSize, pagination: pagination, total: total })));
72
73
  };
73
74
  exports.TableCreator = TableCreator;
@@ -0,0 +1,5 @@
1
+ /// <reference types="react" />
2
+ export declare const TableCreator: import("@emotion/styled").StyledComponent<{
3
+ theme?: import("@emotion/react").Theme | undefined;
4
+ as?: import("react").ElementType<any> | undefined;
5
+ }, import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {}>;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.TableCreator = void 0;
7
+ const styled_1 = __importDefault(require("@emotion/styled"));
8
+ exports.TableCreator = styled_1.default.div `
9
+ 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.
10
+ `;
@@ -115,6 +115,7 @@ export declare type EntityPath = string;
115
115
  */
116
116
  export interface EntityConfig<T = any, I = any> extends TabConfig {
117
117
  apiPath?: EntityPath;
118
+ categoryGroupConfigs?: CategoryGroupConfig[];
118
119
  detail: BackPageConfig;
119
120
  entityMapper?: EntityMapper<T, I>;
120
121
  exploreMode: ExploreMode;
@@ -24,12 +24,21 @@ exports.getFilterCount = getFilterCount;
24
24
  * @returns explore state.
25
25
  */
26
26
  function initExploreState(config, entityListType, decodedFilterParam, decodedCatalogParam, decodedFeatureFlagParam) {
27
+ const entityPageState = initEntityPageState(config);
27
28
  const filterState = initFilterState(decodedFilterParam);
28
29
  const filterCount = getFilterCount(filterState);
29
- return Object.assign(Object.assign({}, constants_1.INITIAL_STATE), { catalogState: decodedCatalogParam, entityPageState: initEntityPageState(config), featureFlagState: decodedFeatureFlagParam, filterCount,
30
+ return Object.assign(Object.assign({}, constants_1.INITIAL_STATE), { catalogState: decodedCatalogParam, categoryGroupConfigs: entityPageState[entityListType].categoryGroupConfigs, entityPageState: initEntityPageState(config), featureFlagState: decodedFeatureFlagParam, filterCount,
30
31
  filterState, listView: exploreState_1.ENTITY_VIEW.EXACT, tabValue: entityListType });
31
32
  }
32
33
  exports.initExploreState = initExploreState;
34
+ /**
35
+ * Returns configured grouped configured categories as a list of configured categories.
36
+ * @param categoryGroupConfigs - Configured category groups.
37
+ * @returns a list of configured categories.
38
+ */
39
+ function flattenCategoryGroupConfigs(categoryGroupConfigs) {
40
+ return categoryGroupConfigs === null || categoryGroupConfigs === void 0 ? void 0 : categoryGroupConfigs.flatMap(({ categoryConfigs }) => categoryConfigs);
41
+ }
33
42
  /**
34
43
  * Initializes filter state from URL "filter" parameter.
35
44
  * @param decodedFilterParam - Decoded filter parameter.
@@ -53,10 +62,19 @@ exports.initFilterState = initFilterState;
53
62
  * @returns entity page state.
54
63
  */
55
64
  function initEntityPageState(config) {
56
- return config.entities.reduce((acc, entity) => (Object.assign(Object.assign({}, acc), { [entity.route]: {
57
- columnsVisibility: (0, utils_1.getInitialTableColumnVisibility)(entity.list.columns),
58
- sorting: (0, utils_2.getDefaultSorting)(entity),
59
- } })), {});
65
+ const { categoryGroupConfigs } = config;
66
+ return config.entities.reduce((acc, entity) => {
67
+ var _a, _b;
68
+ return (Object.assign(Object.assign({}, acc), { [entity.route]: {
69
+ categoryConfigs: flattenCategoryGroupConfigs((_a = entity.categoryGroupConfigs) !== null && _a !== void 0 ? _a : categoryGroupConfigs),
70
+ categoryGroupConfigs: (_b = entity.categoryGroupConfigs) !== null && _b !== void 0 ? _b : categoryGroupConfigs,
71
+ categoryViews: [],
72
+ columnsVisibility: (0, utils_1.getInitialTableColumnVisibility)(entity.list.columns),
73
+ filterCount: 0,
74
+ filterState: [],
75
+ sorting: (0, utils_2.getDefaultSorting)(entity),
76
+ } }));
77
+ }, {});
60
78
  }
61
79
  exports.initEntityPageState = initEntityPageState;
62
80
  /**
@@ -2,7 +2,7 @@ import { ColumnSort } from "@tanstack/react-table";
2
2
  import React, { Dispatch, ReactNode } from "react";
3
3
  import { AzulSearchIndex } from "../apis/azul/common/entities";
4
4
  import { SelectCategory, SelectedFilter } from "../common/entities";
5
- import { CategoryConfig, EntityPath, SiteConfig } from "../config/entities";
5
+ import { CategoryConfig, CategoryGroupConfig, EntityPath, SiteConfig } from "../config/entities";
6
6
  import { PaginateTablePayload, ProcessExploreResponsePayload, ProcessRelatedResponsePayload, ResetExploreResponsePayload, ToggleEntityViewPayload, UpdateColumnVisibilityPayload, UpdateFilterPayload, UpdateSortingPayload } from "./exploreState/payloads/entities";
7
7
  export declare type CatalogState = string | undefined;
8
8
  /**
@@ -16,7 +16,6 @@ export declare enum ENTITY_VIEW {
16
16
  * Explore context.
17
17
  */
18
18
  export interface ExploreContext {
19
- categoryConfigs?: CategoryConfig[];
20
19
  config: SiteConfig;
21
20
  entityList: string;
22
21
  }
@@ -24,7 +23,12 @@ export interface ExploreContext {
24
23
  * State for each entity.
25
24
  */
26
25
  export interface EntityPageState {
26
+ categoryConfigs?: CategoryConfig[];
27
+ categoryGroupConfigs?: CategoryGroupConfig[];
28
+ categoryViews: SelectCategory[];
27
29
  columnsVisibility: Record<string, boolean>;
30
+ filterCount: number;
31
+ filterState: SelectedFilter[];
28
32
  sorting: ColumnSort[];
29
33
  }
30
34
  /**
@@ -38,6 +42,7 @@ export interface EntityPageStateMapper {
38
42
  */
39
43
  export declare type ExploreState = {
40
44
  catalogState: CatalogState;
45
+ categoryGroupConfigs?: CategoryGroupConfig[];
41
46
  categoryViews: SelectCategory[];
42
47
  entityPageState: EntityPageStateMapper;
43
48
  featureFlagState: FeatureFlagState;
@@ -26,7 +26,6 @@ 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");
@@ -64,13 +63,11 @@ exports.ExploreStateContext = (0, react_1.createContext)({
64
63
  */
65
64
  function ExploreStateProvider({ children, entityListType, }) {
66
65
  const { config, defaultEntityListType } = (0, useConfig_1.useConfig)();
67
- const categoryConfigs = (0, useCategoryConfigs_1.useCategoryConfigs)();
68
66
  const { decodedCatalogParam, decodedFeatureFlagParam, decodedFilterParam } = (0, useURLFilterParams_1.useURLFilterParams)();
69
67
  const { isEnabled: isAuthEnabled, token } = (0, useAuthentication_1.useAuthentication)();
70
68
  const entityList = entityListType || defaultEntityListType;
71
69
  const [initReducerState] = (0, react_1.useState)(() => (0, utils_1.initExploreState)(config, entityList, decodedFilterParam, decodedCatalogParam, decodedFeatureFlagParam));
72
70
  const [exploreState, exploreDispatch] = (0, react_1.useReducer)((s, a) => exploreReducer(s, a, {
73
- categoryConfigs,
74
71
  config,
75
72
  entityList,
76
73
  }), initReducerState);
@@ -116,7 +113,7 @@ var ExploreActionKind;
116
113
  */
117
114
  function exploreReducer(state, action, exploreContext) {
118
115
  const { payload, type } = action;
119
- const { categoryConfigs, config, entityList } = exploreContext;
116
+ const { config, entityList } = exploreContext;
120
117
  switch (type) {
121
118
  /**
122
119
  * Clear all filters
@@ -143,9 +140,12 @@ function exploreReducer(state, action, exploreContext) {
143
140
  * Process explore response
144
141
  **/
145
142
  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) });
143
+ const { entityPageState, tabValue } = state;
144
+ const { categoryConfigs, categoryViews, filterState } = entityPageState[tabValue];
145
+ const nextCategoryViews = payload.selectCategories
146
+ ? (0, useCategoryFilter_1.buildCategoryViews)(payload.selectCategories, categoryConfigs, filterState)
147
+ : undefined;
148
+ return Object.assign(Object.assign({}, state), { categoryGroupConfigs: entityPageState[tabValue].categoryGroupConfigs, categoryViews: nextCategoryViews !== null && nextCategoryViews !== void 0 ? nextCategoryViews : categoryViews, entityPageState: Object.assign(Object.assign({}, entityPageState), { [tabValue]: Object.assign(Object.assign({}, entityPageState[tabValue]), { categoryViews: nextCategoryViews !== null && nextCategoryViews !== void 0 ? nextCategoryViews : categoryViews }) }), listItems: payload.loading ? [] : payload.listItems, loading: payload.loading, paginationState: Object.assign(Object.assign({}, state.paginationState), payload.paginationResponse) });
149
149
  }
150
150
  /**
151
151
  * Process related response
@@ -172,7 +172,7 @@ function exploreReducer(state, action, exploreContext) {
172
172
  if (payload === state.tabValue) {
173
173
  return state;
174
174
  }
175
- return Object.assign(Object.assign({}, state), { listItems: [], loading: true, paginationState: (0, utils_1.resetPage)(state.paginationState), tabValue: payload });
175
+ return Object.assign(Object.assign({}, state), { filterCount: state.entityPageState[payload].filterCount, filterState: state.entityPageState[payload].filterState, listItems: [], loading: true, paginationState: (0, utils_1.resetPage)(state.paginationState), tabValue: payload });
176
176
  }
177
177
  /**
178
178
  * Toggle entity view
@@ -185,7 +185,10 @@ function exploreReducer(state, action, exploreContext) {
185
185
  **/
186
186
  case ExploreActionKind.UpdateFilter: {
187
187
  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) });
188
+ const filterCount = (0, utils_1.getFilterCount)(filterState);
189
+ return Object.assign(Object.assign({}, state), { entityPageState: Object.assign(Object.assign({}, state.entityPageState), { [state.tabValue]: Object.assign(Object.assign({}, state.entityPageState[state.tabValue]), { filterCount,
190
+ filterState }) }), filterCount,
191
+ filterState, paginationState: (0, utils_1.resetPage)(state.paginationState) });
189
192
  }
190
193
  /**
191
194
  * Update sorting
@@ -65,10 +65,10 @@ 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 { categoryGroupConfigs, 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.
@@ -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.0",
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
  }
@@ -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
  };
@@ -143,6 +143,7 @@ export type EntityPath = string;
143
143
  // eslint-disable-next-line @typescript-eslint/no-explicit-any -- This config model is part of a generic array
144
144
  export interface EntityConfig<T = any, I = any> extends TabConfig {
145
145
  apiPath?: EntityPath;
146
+ categoryGroupConfigs?: CategoryGroupConfig[];
146
147
  detail: BackPageConfig;
147
148
  entityMapper?: EntityMapper<T, I>;
148
149
  exploreMode: ExploreMode;
@@ -1,6 +1,10 @@
1
1
  import { SelectedFilter } from "../../common/entities";
2
2
  import { getInitialTableColumnVisibility } from "../../components/Table/common/utils";
3
- import { SiteConfig } from "../../config/entities";
3
+ import {
4
+ CategoryConfig,
5
+ CategoryGroupConfig,
6
+ SiteConfig,
7
+ } from "../../config/entities";
4
8
  import { getDefaultSorting } from "../../config/utils";
5
9
  import {
6
10
  EntityPageStateMapper,
@@ -35,11 +39,13 @@ export function initExploreState(
35
39
  decodedCatalogParam?: string,
36
40
  decodedFeatureFlagParam?: string
37
41
  ): ExploreState {
42
+ const entityPageState = initEntityPageState(config);
38
43
  const filterState = initFilterState(decodedFilterParam);
39
44
  const filterCount = getFilterCount(filterState);
40
45
  return {
41
46
  ...INITIAL_STATE,
42
47
  catalogState: decodedCatalogParam,
48
+ categoryGroupConfigs: entityPageState[entityListType].categoryGroupConfigs,
43
49
  entityPageState: initEntityPageState(config),
44
50
  featureFlagState: decodedFeatureFlagParam,
45
51
  filterCount,
@@ -49,6 +55,19 @@ export function initExploreState(
49
55
  };
50
56
  }
51
57
 
58
+ /**
59
+ * Returns configured grouped configured categories as a list of configured categories.
60
+ * @param categoryGroupConfigs - Configured category groups.
61
+ * @returns a list of configured categories.
62
+ */
63
+ function flattenCategoryGroupConfigs(
64
+ categoryGroupConfigs?: CategoryGroupConfig[]
65
+ ): CategoryConfig[] | undefined {
66
+ return categoryGroupConfigs?.flatMap(
67
+ ({ categoryConfigs }) => categoryConfigs
68
+ );
69
+ }
70
+
52
71
  /**
53
72
  * Initializes filter state from URL "filter" parameter.
54
73
  * @param decodedFilterParam - Decoded filter parameter.
@@ -71,11 +90,20 @@ export function initFilterState(decodedFilterParam: string): SelectedFilter[] {
71
90
  * @returns entity page state.
72
91
  */
73
92
  export function initEntityPageState(config: SiteConfig): EntityPageStateMapper {
93
+ const { categoryGroupConfigs } = config;
74
94
  return config.entities.reduce(
75
95
  (acc, entity) => ({
76
96
  ...acc,
77
97
  [entity.route]: {
98
+ categoryConfigs: flattenCategoryGroupConfigs(
99
+ entity.categoryGroupConfigs ?? categoryGroupConfigs
100
+ ),
101
+ categoryGroupConfigs:
102
+ entity.categoryGroupConfigs ?? categoryGroupConfigs,
103
+ categoryViews: [],
78
104
  columnsVisibility: getInitialTableColumnVisibility(entity.list.columns),
105
+ filterCount: 0,
106
+ filterState: [],
79
107
  sorting: getDefaultSorting(entity),
80
108
  },
81
109
  }),
@@ -10,9 +10,13 @@ import React, {
10
10
  } from "react";
11
11
  import { AzulSearchIndex } from "../apis/azul/common/entities";
12
12
  import { SelectCategory, SelectedFilter } from "../common/entities";
13
- import { CategoryConfig, EntityPath, SiteConfig } from "../config/entities";
13
+ import {
14
+ CategoryConfig,
15
+ CategoryGroupConfig,
16
+ EntityPath,
17
+ SiteConfig,
18
+ } from "../config/entities";
14
19
  import { useAuthentication } from "../hooks/useAuthentication/useAuthentication";
15
- import { useCategoryConfigs } from "../hooks/useCategoryConfigs";
16
20
  import {
17
21
  buildCategoryViews,
18
22
  buildNextFilterState,
@@ -53,7 +57,6 @@ export enum ENTITY_VIEW {
53
57
  * Explore context.
54
58
  */
55
59
  export interface ExploreContext {
56
- categoryConfigs?: CategoryConfig[];
57
60
  config: SiteConfig;
58
61
  entityList: string;
59
62
  }
@@ -62,7 +65,12 @@ export interface ExploreContext {
62
65
  * State for each entity.
63
66
  */
64
67
  export interface EntityPageState {
68
+ categoryConfigs?: CategoryConfig[];
69
+ categoryGroupConfigs?: CategoryGroupConfig[];
70
+ categoryViews: SelectCategory[];
65
71
  columnsVisibility: Record<string, boolean>;
72
+ filterCount: number;
73
+ filterState: SelectedFilter[];
66
74
  sorting: ColumnSort[];
67
75
  }
68
76
 
@@ -78,6 +86,7 @@ export interface EntityPageStateMapper {
78
86
  */
79
87
  export type ExploreState = {
80
88
  catalogState: CatalogState;
89
+ categoryGroupConfigs?: CategoryGroupConfig[];
81
90
  categoryViews: SelectCategory[];
82
91
  entityPageState: EntityPageStateMapper;
83
92
  featureFlagState: FeatureFlagState;
@@ -177,7 +186,6 @@ export function ExploreStateProvider({
177
186
  entityListType: string;
178
187
  }): JSX.Element {
179
188
  const { config, defaultEntityListType } = useConfig();
180
- const categoryConfigs = useCategoryConfigs();
181
189
  const { decodedCatalogParam, decodedFeatureFlagParam, decodedFilterParam } =
182
190
  useURLFilterParams();
183
191
  const { isEnabled: isAuthEnabled, token } = useAuthentication();
@@ -195,7 +203,6 @@ export function ExploreStateProvider({
195
203
  const [exploreState, exploreDispatch] = useReducer(
196
204
  (s: ExploreState, a: ExploreAction) =>
197
205
  exploreReducer(s, a, {
198
- categoryConfigs,
199
206
  config,
200
207
  entityList,
201
208
  }),
@@ -357,7 +364,7 @@ function exploreReducer(
357
364
  exploreContext: ExploreContext
358
365
  ): ExploreState {
359
366
  const { payload, type } = action;
360
- const { categoryConfigs, config, entityList } = exploreContext;
367
+ const { config, entityList } = exploreContext;
361
368
 
362
369
  switch (type) {
363
370
  /**
@@ -392,15 +399,27 @@ function exploreReducer(
392
399
  * Process explore response
393
400
  **/
394
401
  case ExploreActionKind.ProcessExploreResponse: {
402
+ const { entityPageState, tabValue } = state;
403
+ const { categoryConfigs, categoryViews, filterState } =
404
+ entityPageState[tabValue];
405
+ const nextCategoryViews = payload.selectCategories
406
+ ? buildCategoryViews(
407
+ payload.selectCategories,
408
+ categoryConfigs,
409
+ filterState
410
+ )
411
+ : undefined;
395
412
  return {
396
413
  ...state,
397
- categoryViews: payload.selectCategories
398
- ? buildCategoryViews(
399
- payload.selectCategories,
400
- categoryConfigs,
401
- state.filterState
402
- )
403
- : state.categoryViews,
414
+ categoryGroupConfigs: entityPageState[tabValue].categoryGroupConfigs,
415
+ categoryViews: nextCategoryViews ?? categoryViews,
416
+ entityPageState: {
417
+ ...entityPageState,
418
+ [tabValue]: {
419
+ ...entityPageState[tabValue],
420
+ categoryViews: nextCategoryViews ?? categoryViews,
421
+ },
422
+ },
404
423
  listItems: payload.loading ? [] : payload.listItems,
405
424
  loading: payload.loading,
406
425
  paginationState: {
@@ -444,6 +463,8 @@ function exploreReducer(
444
463
  }
445
464
  return {
446
465
  ...state,
466
+ filterCount: state.entityPageState[payload].filterCount,
467
+ filterState: state.entityPageState[payload].filterState,
447
468
  listItems: [],
448
469
  loading: true,
449
470
  paginationState: resetPage(state.paginationState),
@@ -470,9 +491,18 @@ function exploreReducer(
470
491
  payload.selectedValue,
471
492
  payload.selected
472
493
  );
494
+ const filterCount = getFilterCount(filterState);
473
495
  return {
474
496
  ...state,
475
- filterCount: getFilterCount(filterState),
497
+ entityPageState: {
498
+ ...state.entityPageState,
499
+ [state.tabValue]: {
500
+ ...state.entityPageState[state.tabValue],
501
+ filterCount,
502
+ filterState,
503
+ },
504
+ },
505
+ filterCount,
476
506
  filterState,
477
507
  paginationState: resetPage(state.paginationState),
478
508
  };
@@ -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
+ categoryGroupConfigs,
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.
@@ -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
- };