@databiosphere/findable-ui 32.0.0 → 32.1.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.
- package/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +16 -0
- package/lib/components/Index/index.js +1 -1
- package/lib/components/Index/table/hook.d.ts +2 -2
- package/lib/components/Index/table/hook.js +5 -2
- package/lib/components/Index/table/types.d.ts +3 -0
- package/lib/hooks/useEntityList.js +1 -7
- package/lib/hooks/useURLFilterParams.d.ts +1 -0
- package/lib/hooks/useURLFilterParams.js +1 -0
- package/lib/hooks/useUpdateURLSearchParams.d.ts +1 -0
- package/lib/hooks/useUpdateURLSearchParams.js +1 -0
- package/lib/providers/exploreState/actions/clearMeta/action.d.ts +9 -0
- package/lib/providers/exploreState/actions/clearMeta/action.js +12 -0
- package/lib/providers/exploreState/actions/clearMeta/dispatch.d.ts +6 -0
- package/lib/providers/exploreState/actions/clearMeta/dispatch.js +11 -0
- package/lib/providers/exploreState/actions/clearMeta/types.d.ts +6 -0
- package/lib/providers/exploreState/actions/clearMeta/types.js +1 -0
- package/lib/providers/exploreState/actions/syncStateFromUrl/action.d.ts +10 -0
- package/lib/providers/exploreState/actions/syncStateFromUrl/action.js +25 -0
- package/lib/providers/exploreState/actions/syncStateFromUrl/dispatch.d.ts +7 -0
- package/lib/providers/exploreState/actions/syncStateFromUrl/dispatch.js +12 -0
- package/lib/providers/exploreState/actions/syncStateFromUrl/types.d.ts +8 -0
- package/lib/providers/exploreState/actions/syncStateFromUrl/types.js +1 -0
- package/lib/providers/exploreState/actions/syncStateFromUrl/utils.d.ts +9 -0
- package/lib/providers/exploreState/actions/syncStateFromUrl/utils.js +23 -0
- package/lib/providers/exploreState/constants.d.ts +5 -0
- package/lib/providers/exploreState/constants.js +5 -0
- package/lib/providers/exploreState/entities.d.ts +4 -0
- package/lib/providers/exploreState/hooks/UseBeforePopState/useBeforePopState.d.ts +18 -0
- package/lib/providers/exploreState/hooks/UseBeforePopState/useBeforePopState.js +41 -0
- package/lib/providers/exploreState/hooks/UseBeforePopState/utils.d.ts +23 -0
- package/lib/providers/exploreState/hooks/UseBeforePopState/utils.js +68 -0
- package/lib/providers/exploreState/hooks/UseMetaCommands/actions.d.ts +13 -0
- package/lib/providers/exploreState/hooks/UseMetaCommands/actions.js +24 -0
- package/lib/providers/exploreState/hooks/UseMetaCommands/types.d.ts +4 -0
- package/lib/providers/exploreState/hooks/UseMetaCommands/types.js +5 -0
- package/lib/providers/exploreState/hooks/UseMetaCommands/useMetaCommands.d.ts +2 -0
- package/lib/providers/exploreState/hooks/UseMetaCommands/useMetaCommands.js +21 -0
- package/lib/providers/exploreState/hooks/UseMetaCommands/utils.d.ts +9 -0
- package/lib/providers/exploreState/hooks/UseMetaCommands/utils.js +25 -0
- package/lib/providers/exploreState/initializer/constants.js +1 -0
- package/lib/providers/exploreState.d.ts +7 -2
- package/lib/providers/exploreState.js +36 -1
- package/package.json +1 -1
- package/src/components/Index/index.tsx +1 -1
- package/src/components/Index/table/hook.ts +8 -3
- package/src/components/Index/table/types.ts +4 -0
- package/src/hooks/useEntityList.ts +1 -14
- package/src/hooks/useURLFilterParams.ts +1 -0
- package/src/hooks/useUpdateURLSearchParams.ts +1 -0
- package/src/providers/exploreState/actions/clearMeta/action.ts +18 -0
- package/src/providers/exploreState/actions/clearMeta/dispatch.ts +13 -0
- package/src/providers/exploreState/actions/clearMeta/types.ts +8 -0
- package/src/providers/exploreState/actions/syncStateFromUrl/action.ts +44 -0
- package/src/providers/exploreState/actions/syncStateFromUrl/dispatch.ts +16 -0
- package/src/providers/exploreState/actions/syncStateFromUrl/types.ts +13 -0
- package/src/providers/exploreState/actions/syncStateFromUrl/utils.ts +31 -0
- package/src/providers/exploreState/constants.ts +6 -0
- package/src/providers/exploreState/entities.ts +5 -0
- package/src/providers/exploreState/hooks/UseBeforePopState/useBeforePopState.ts +46 -0
- package/src/providers/exploreState/hooks/UseBeforePopState/utils.ts +93 -0
- package/src/providers/exploreState/hooks/UseMetaCommands/actions.ts +29 -0
- package/src/providers/exploreState/hooks/UseMetaCommands/types.ts +4 -0
- package/src/providers/exploreState/hooks/UseMetaCommands/useMetaCommands.ts +27 -0
- package/src/providers/exploreState/hooks/UseMetaCommands/utils.ts +33 -0
- package/src/providers/exploreState/initializer/constants.ts +1 -0
- package/src/providers/exploreState.tsx +44 -1
|
@@ -4,9 +4,11 @@ import { CategoryView } from "../common/categories/views/types";
|
|
|
4
4
|
import { SelectedFilter } from "../common/entities";
|
|
5
5
|
import { RowPreviewState } from "../components/Table/features/RowPreview/entities";
|
|
6
6
|
import { CategoryGroup, SiteConfig } from "../config/entities";
|
|
7
|
+
import { ClearMetaAction } from "./exploreState/actions/clearMeta/types";
|
|
8
|
+
import { SyncStateFromUrlAction } from "./exploreState/actions/syncStateFromUrl/types";
|
|
7
9
|
import { UpdateGroupingAction } from "./exploreState/actions/updateGrouping/types";
|
|
8
10
|
import { UpdateColumnVisibilityAction } from "./exploreState/actions/updateVisibility/types";
|
|
9
|
-
import { EntityPageStateMapper, EntityStateByCategoryGroupConfigKey, ListItem } from "./exploreState/entities";
|
|
11
|
+
import { EntityPageStateMapper, EntityStateByCategoryGroupConfigKey, ListItem, Meta } from "./exploreState/entities";
|
|
10
12
|
import { ApplySavedFilterPayload, PaginateTablePayload, PatchExploreResponsePayload, ProcessExploreResponsePayload, ResetExploreResponsePayload, UpdateEntityFiltersPayload, UpdateEntityViewAccessPayload, UpdateFilterPayload, UpdateRowPreviewPayload, UpdateRowSelectionPayload, UpdateSortingPayload } from "./exploreState/payloads/entities";
|
|
11
13
|
export type CatalogState = string | undefined;
|
|
12
14
|
/**
|
|
@@ -30,6 +32,7 @@ export type ExploreState = {
|
|
|
30
32
|
filterState: SelectedFilter[];
|
|
31
33
|
listItems: ListItems;
|
|
32
34
|
loading: boolean;
|
|
35
|
+
meta: Meta | null;
|
|
33
36
|
paginationState: PaginationState;
|
|
34
37
|
rowPreview: RowPreviewState;
|
|
35
38
|
tabValue: string;
|
|
@@ -96,12 +99,14 @@ export declare function ExploreStateProvider({ children, entityListType, }: {
|
|
|
96
99
|
export declare enum ExploreActionKind {
|
|
97
100
|
ApplySavedFilter = "APPLY_SAVED_FILTER",
|
|
98
101
|
ClearFilters = "CLEAR_FILTERS",
|
|
102
|
+
ClearMeta = "CLEAR_META",
|
|
99
103
|
PaginateTable = "PAGINATE_TABLE",
|
|
100
104
|
PatchExploreResponse = "PATCH_EXPLORE_RESPONSE",
|
|
101
105
|
ProcessExploreResponse = "PROCESS_EXPLORE_RESPONSE",
|
|
102
106
|
ResetExploreResponse = "RESET_EXPLORE_RESPONSE",
|
|
103
107
|
ResetState = "RESET_STATE",
|
|
104
108
|
SelectEntityType = "SELECT_ENTITY_TYPE",
|
|
109
|
+
SyncStateFromUrl = "SYNC_STATE_FROM_URL",
|
|
105
110
|
UpdateColumnVisibility = "UPDATE_COLUMN_VISIBILITY",
|
|
106
111
|
UpdateEntityFilters = "UPDATE_ENTITY_FILTERS",
|
|
107
112
|
UpdateEntityViewAccess = "UPDATE_ENTITY_VIEW_ACCESS",
|
|
@@ -114,7 +119,7 @@ export declare enum ExploreActionKind {
|
|
|
114
119
|
/**
|
|
115
120
|
* Explore action.
|
|
116
121
|
*/
|
|
117
|
-
export type ExploreAction = ApplySavedFilterAction | ClearFiltersAction | PaginateTableAction | PatchExploreResponseAction | ProcessExploreResponseAction | ResetExploreResponseAction | ResetStateAction | SelectEntityTypeAction | UpdateColumnVisibilityAction | UpdateEntityFiltersAction | UpdateEntityViewAccessAction | UpdateFilterAction | UpdateGroupingAction | UpdateRowPreviewAction | UpdateRowSelectionAction | UpdateSortingAction;
|
|
122
|
+
export type ExploreAction = ApplySavedFilterAction | ClearFiltersAction | ClearMetaAction | PaginateTableAction | PatchExploreResponseAction | ProcessExploreResponseAction | ResetExploreResponseAction | ResetStateAction | SelectEntityTypeAction | SyncStateFromUrlAction | UpdateColumnVisibilityAction | UpdateEntityFiltersAction | UpdateEntityViewAccessAction | UpdateFilterAction | UpdateGroupingAction | UpdateRowPreviewAction | UpdateRowSelectionAction | UpdateSortingAction;
|
|
118
123
|
/**
|
|
119
124
|
* Apply saved filter action.
|
|
120
125
|
*/
|
|
@@ -3,8 +3,13 @@ import { useToken } from "../hooks/authentication/token/useToken";
|
|
|
3
3
|
import { buildCategoryViews, buildNextFilterState, } from "../hooks/useCategoryFilter";
|
|
4
4
|
import { useConfig } from "../hooks/useConfig";
|
|
5
5
|
import { useURLFilterParams } from "../hooks/useURLFilterParams";
|
|
6
|
+
import { clearMetaAction } from "./exploreState/actions/clearMeta/action";
|
|
7
|
+
import { syncStateFromUrlAction } from "./exploreState/actions/syncStateFromUrl/action";
|
|
6
8
|
import { updateGroupingAction } from "./exploreState/actions/updateGrouping/action";
|
|
7
9
|
import { updateColumnVisibilityAction } from "./exploreState/actions/updateVisibility/action";
|
|
10
|
+
import { useBeforePopState } from "./exploreState/hooks/UseBeforePopState/useBeforePopState";
|
|
11
|
+
import { META_COMMAND } from "./exploreState/hooks/UseMetaCommands/types";
|
|
12
|
+
import { useMetaCommands } from "./exploreState/hooks/UseMetaCommands/useMetaCommands";
|
|
8
13
|
import { DEFAULT_PAGINATION_STATE, INITIAL_STATE, } from "./exploreState/initializer/constants";
|
|
9
14
|
import { initReducerArguments } from "./exploreState/initializer/utils";
|
|
10
15
|
import { buildEntityStateSavedFilterState, buildNextSavedFilterState, closeRowPreview, getEntityCategoryGroupConfigKey, getEntityState, getEntityStateSavedProperty, getFilterCount, patchEntityListItems, resetPage, updateEntityPageState, updateEntityPageStateWithCommonCategoryGroupConfigKey, updateEntityStateByCategoryGroupConfigKey, updateSelectColumnVisibility, } from "./exploreState/utils";
|
|
@@ -51,6 +56,10 @@ export function ExploreStateProvider({ children, entityListType, }) {
|
|
|
51
56
|
type: ExploreActionKind.ResetExploreResponse,
|
|
52
57
|
});
|
|
53
58
|
}, [exploreDispatch, token]);
|
|
59
|
+
// Meta-command related side effects.
|
|
60
|
+
useMetaCommands({ exploreDispatch, exploreState });
|
|
61
|
+
// Before pop state related side effects (forward / backward navigation by browser buttons).
|
|
62
|
+
useBeforePopState({ exploreDispatch, exploreState });
|
|
54
63
|
return (React.createElement(ExploreStateContext.Provider, { value: exploreContextValue }, children));
|
|
55
64
|
}
|
|
56
65
|
/**
|
|
@@ -60,12 +69,14 @@ export var ExploreActionKind;
|
|
|
60
69
|
(function (ExploreActionKind) {
|
|
61
70
|
ExploreActionKind["ApplySavedFilter"] = "APPLY_SAVED_FILTER";
|
|
62
71
|
ExploreActionKind["ClearFilters"] = "CLEAR_FILTERS";
|
|
72
|
+
ExploreActionKind["ClearMeta"] = "CLEAR_META";
|
|
63
73
|
ExploreActionKind["PaginateTable"] = "PAGINATE_TABLE";
|
|
64
74
|
ExploreActionKind["PatchExploreResponse"] = "PATCH_EXPLORE_RESPONSE";
|
|
65
75
|
ExploreActionKind["ProcessExploreResponse"] = "PROCESS_EXPLORE_RESPONSE";
|
|
66
76
|
ExploreActionKind["ResetExploreResponse"] = "RESET_EXPLORE_RESPONSE";
|
|
67
77
|
ExploreActionKind["ResetState"] = "RESET_STATE";
|
|
68
78
|
ExploreActionKind["SelectEntityType"] = "SELECT_ENTITY_TYPE";
|
|
79
|
+
ExploreActionKind["SyncStateFromUrl"] = "SYNC_STATE_FROM_URL";
|
|
69
80
|
ExploreActionKind["UpdateColumnVisibility"] = "UPDATE_COLUMN_VISIBILITY";
|
|
70
81
|
ExploreActionKind["UpdateEntityFilters"] = "UPDATE_ENTITY_FILTERS";
|
|
71
82
|
ExploreActionKind["UpdateEntityViewAccess"] = "UPDATE_ENTITY_VIEW_ACCESS";
|
|
@@ -107,6 +118,7 @@ function exploreReducer(state, action, exploreContext) {
|
|
|
107
118
|
entityPageState: updateEntityPageStateWithCommonCategoryGroupConfigKey(state, { grouping, rowPreview, rowSelection, sorting }),
|
|
108
119
|
filterCount: getFilterCount(filterState),
|
|
109
120
|
filterState,
|
|
121
|
+
meta: { command: META_COMMAND.NAVIGATE_TO_FILTERS },
|
|
110
122
|
paginationState: resetPage(state.paginationState),
|
|
111
123
|
rowPreview,
|
|
112
124
|
};
|
|
@@ -129,10 +141,17 @@ function exploreReducer(state, action, exploreContext) {
|
|
|
129
141
|
entityPageState: updateEntityPageStateWithCommonCategoryGroupConfigKey(state, { rowPreview, rowSelection }),
|
|
130
142
|
filterCount,
|
|
131
143
|
filterState,
|
|
144
|
+
meta: { command: META_COMMAND.NAVIGATE_TO_FILTERS },
|
|
132
145
|
paginationState: resetPage(state.paginationState),
|
|
133
146
|
rowPreview,
|
|
134
147
|
};
|
|
135
148
|
}
|
|
149
|
+
/**
|
|
150
|
+
* Clear meta
|
|
151
|
+
*/
|
|
152
|
+
case ExploreActionKind.ClearMeta: {
|
|
153
|
+
return clearMetaAction(state, payload);
|
|
154
|
+
}
|
|
136
155
|
/**
|
|
137
156
|
* Paginate table
|
|
138
157
|
**/
|
|
@@ -214,7 +233,8 @@ function exploreReducer(state, action, exploreContext) {
|
|
|
214
233
|
**/
|
|
215
234
|
case ExploreActionKind.SelectEntityType: {
|
|
216
235
|
if (payload === state.tabValue) {
|
|
217
|
-
return
|
|
236
|
+
// Update meta to match command "REPLACE_TO_FILTERS" - facilitates navigation to filters on return back to entity from elsewhere.
|
|
237
|
+
return { ...state, meta: { command: META_COMMAND.REPLACE_TO_FILTERS } };
|
|
218
238
|
}
|
|
219
239
|
const entityState = getEntityState(state, getEntityCategoryGroupConfigKey(payload, state.entityPageState));
|
|
220
240
|
const rowPreview = closeRowPreview(state.rowPreview); // Close row preview, without updating the entity page state row preview.
|
|
@@ -226,11 +246,18 @@ function exploreReducer(state, action, exploreContext) {
|
|
|
226
246
|
filterState: entityState.filterState,
|
|
227
247
|
listItems: [],
|
|
228
248
|
loading: true,
|
|
249
|
+
meta: { command: META_COMMAND.REPLACE_TO_FILTERS },
|
|
229
250
|
paginationState: { ...resetPage(state.paginationState), rows: 0 },
|
|
230
251
|
rowPreview,
|
|
231
252
|
tabValue: payload,
|
|
232
253
|
};
|
|
233
254
|
}
|
|
255
|
+
/**
|
|
256
|
+
* Sync state from URL.
|
|
257
|
+
*/
|
|
258
|
+
case ExploreActionKind.SyncStateFromUrl: {
|
|
259
|
+
return syncStateFromUrlAction(state, payload);
|
|
260
|
+
}
|
|
234
261
|
/**
|
|
235
262
|
* Update column visibility
|
|
236
263
|
**/
|
|
@@ -262,6 +289,13 @@ function exploreReducer(state, action, exploreContext) {
|
|
|
262
289
|
...state,
|
|
263
290
|
entityPageState: updateEntityPageStateWithCommonCategoryGroupConfigKey(state, { grouping, rowPreview, rowSelection, sorting }, categoryGroupConfigKey),
|
|
264
291
|
rowPreview: closeRowPreview(state.rowPreview),
|
|
292
|
+
...(payload.entityListType === state.tabValue
|
|
293
|
+
? {
|
|
294
|
+
filterCount: getFilterCount(filterState),
|
|
295
|
+
filterState,
|
|
296
|
+
paginationState: { ...resetPage(state.paginationState), rows: 0 },
|
|
297
|
+
}
|
|
298
|
+
: {}),
|
|
265
299
|
};
|
|
266
300
|
}
|
|
267
301
|
/**
|
|
@@ -290,6 +324,7 @@ function exploreReducer(state, action, exploreContext) {
|
|
|
290
324
|
entityPageState: updateEntityPageStateWithCommonCategoryGroupConfigKey(state, { rowPreview, rowSelection }),
|
|
291
325
|
filterCount: getFilterCount(filterState),
|
|
292
326
|
filterState,
|
|
327
|
+
meta: { command: META_COMMAND.NAVIGATE_TO_FILTERS },
|
|
293
328
|
paginationState: resetPage(state.paginationState),
|
|
294
329
|
rowPreview,
|
|
295
330
|
};
|
package/package.json
CHANGED
|
@@ -25,7 +25,7 @@ export const Index = ({
|
|
|
25
25
|
}: IndexProps): JSX.Element => {
|
|
26
26
|
const { onChange, viewMode, viewStatus } = useEntitiesView();
|
|
27
27
|
const { dimensions } = useLayoutDimensions();
|
|
28
|
-
const { table } = useTable();
|
|
28
|
+
const { table } = useTable({ entityListType });
|
|
29
29
|
return (
|
|
30
30
|
<IndexLayout className={className} marginTop={dimensions.header.height}>
|
|
31
31
|
<Hero SideBarButton={SideBarButton} Summaries={Summaries} title={title} />
|
|
@@ -40,10 +40,12 @@ import { RowPreviewState } from "../../Table/features/RowPreview/entities";
|
|
|
40
40
|
import { buildBaseColumnDef } from "../../TableCreator/common/utils";
|
|
41
41
|
import { useTableOptions } from "../../TableCreator/options/hook";
|
|
42
42
|
import { createCell } from "./coreOptions/columns/cellFactory";
|
|
43
|
-
import { UseTable } from "./types";
|
|
43
|
+
import { UseTable, UseTableProps } from "./types";
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
export const useTable = <T extends RowData>({
|
|
46
|
+
entityListType,
|
|
47
|
+
}: // eslint-disable-next-line sonarjs/cognitive-complexity -- TODO fix complexity
|
|
48
|
+
UseTableProps): UseTable<T> => {
|
|
47
49
|
const { entityConfig } = useConfig();
|
|
48
50
|
const exploreMode = useExploreMode();
|
|
49
51
|
const { exploreDispatch, exploreState } = useExploreState();
|
|
@@ -209,6 +211,7 @@ export const useTable = <T extends RowData>(): UseTable<T> => {
|
|
|
209
211
|
|
|
210
212
|
// Process explore response - client-side filtering only.
|
|
211
213
|
useEffect(() => {
|
|
214
|
+
if (entityListType !== tabValue) return;
|
|
212
215
|
if (!listItems || listItems.length === 0) return;
|
|
213
216
|
if (clientFiltering) {
|
|
214
217
|
exploreDispatch({
|
|
@@ -229,9 +232,11 @@ export const useTable = <T extends RowData>(): UseTable<T> => {
|
|
|
229
232
|
allColumns,
|
|
230
233
|
clientFiltering,
|
|
231
234
|
columnFilters,
|
|
235
|
+
entityListType,
|
|
232
236
|
exploreDispatch,
|
|
233
237
|
listItems,
|
|
234
238
|
rows,
|
|
239
|
+
tabValue,
|
|
235
240
|
]);
|
|
236
241
|
|
|
237
242
|
return { table };
|
|
@@ -20,7 +20,6 @@ import { useEntityService } from "./useEntityService";
|
|
|
20
20
|
import { EXPLORE_MODE, ExploreMode } from "./useExploreMode/types";
|
|
21
21
|
import { useExploreMode } from "./useExploreMode/useExploreMode";
|
|
22
22
|
import { useExploreState } from "./useExploreState";
|
|
23
|
-
import { useURLFilterParams } from "./useURLFilterParams";
|
|
24
23
|
|
|
25
24
|
/**
|
|
26
25
|
* Hook handling the load and transformation of the values used by index pages. If the current entity loaded statically,
|
|
@@ -45,15 +44,8 @@ export const useEntityList = (
|
|
|
45
44
|
} = useEntityService();
|
|
46
45
|
const { exploreDispatch, exploreState } = useExploreState();
|
|
47
46
|
const { data, isIdle, isLoading, run } = useAsync<AzulEntitiesResponse>();
|
|
48
|
-
const {
|
|
49
|
-
catalogState,
|
|
50
|
-
entityPageState,
|
|
51
|
-
featureFlagState,
|
|
52
|
-
filterState,
|
|
53
|
-
tabValue,
|
|
54
|
-
} = exploreState;
|
|
47
|
+
const { entityPageState, filterState, tabValue } = exploreState;
|
|
55
48
|
const { pagination, termFacets } = data || {};
|
|
56
|
-
const { updateFilterQueryString } = useURLFilterParams();
|
|
57
49
|
const { sorting } = entityPageState[tabValue];
|
|
58
50
|
const entities = getEntities(staticData, data);
|
|
59
51
|
const shouldDispatchResponse = isDispatchable(
|
|
@@ -63,11 +55,6 @@ export const useEntityList = (
|
|
|
63
55
|
);
|
|
64
56
|
const isFetching = isIdle || isLoading;
|
|
65
57
|
|
|
66
|
-
// Update the filter query string when the filter state changes.
|
|
67
|
-
useEffect(() => {
|
|
68
|
-
updateFilterQueryString(catalogState, featureFlagState, filterState);
|
|
69
|
-
}, [catalogState, featureFlagState, filterState, updateFilterQueryString]);
|
|
70
|
-
|
|
71
58
|
// Fetch entities - on change of filter state - server-side fetching and server-side filtering.
|
|
72
59
|
useEffect(() => {
|
|
73
60
|
if (exploreMode === EXPLORE_MODE.SS_FETCH_SS_FILTERING) {
|
|
@@ -18,6 +18,7 @@ interface UseURLFilterParamsResult {
|
|
|
18
18
|
/**
|
|
19
19
|
* useURLFilterParams hook is used to keep track of the url search params, and update them,
|
|
20
20
|
* if needed
|
|
21
|
+
* @deprecated - Avoid using this hook if possible; we intend on using ExploreState actions to manage URL state.
|
|
21
22
|
* @returns an object containing a update function and the current filter
|
|
22
23
|
*/
|
|
23
24
|
export const useURLFilterParams = (): UseURLFilterParamsResult => {
|
|
@@ -4,6 +4,7 @@ import { useURLFilterParams } from "./useURLFilterParams";
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Updates URL search params when the filter state changes.
|
|
7
|
+
* @deprecated - Avoid using this hook if possible; we intend on using ExploreState actions to manage URL state.
|
|
7
8
|
*/
|
|
8
9
|
export const useUpdateURLSearchParams = (): void => {
|
|
9
10
|
const { exploreState } = useExploreState();
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ExploreState } from "../../../exploreState";
|
|
2
|
+
import { ClearMetaPayload } from "./types";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Reducer function to handle the "clear meta" action.
|
|
6
|
+
* @param state - Explore State.
|
|
7
|
+
* @param payload - Payload.
|
|
8
|
+
* @returns explore state.
|
|
9
|
+
*/
|
|
10
|
+
export function clearMetaAction(
|
|
11
|
+
state: ExploreState,
|
|
12
|
+
payload: ClearMetaPayload
|
|
13
|
+
): ExploreState {
|
|
14
|
+
return {
|
|
15
|
+
...state,
|
|
16
|
+
meta: payload,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ExploreActionKind } from "../../../exploreState";
|
|
2
|
+
import { ClearMetaAction } from "./types";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Action creator for clearing meta in the state.
|
|
6
|
+
* @returns Action with payload and action type.
|
|
7
|
+
*/
|
|
8
|
+
export function clearMeta(): ClearMetaAction {
|
|
9
|
+
return {
|
|
10
|
+
payload: null,
|
|
11
|
+
type: ExploreActionKind.ClearMeta,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { ExploreState } from "../../../exploreState";
|
|
2
|
+
import {
|
|
3
|
+
getEntityCategoryGroupConfigKey,
|
|
4
|
+
updateEntityStateByCategoryGroupConfigKey,
|
|
5
|
+
} from "../../utils";
|
|
6
|
+
import { SyncStateFromUrlPayload } from "./types";
|
|
7
|
+
import { buildNextState } from "./utils";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Reducer function to handle the "sync state from URL" action.
|
|
11
|
+
* Updates the catalog state, feature flag state, and the payload entity's filter state.
|
|
12
|
+
* @param state - Explore State.
|
|
13
|
+
* @param payload - Payload.
|
|
14
|
+
* @returns explore state.
|
|
15
|
+
*/
|
|
16
|
+
export function syncStateFromUrlAction(
|
|
17
|
+
state: ExploreState,
|
|
18
|
+
payload: SyncStateFromUrlPayload
|
|
19
|
+
): ExploreState {
|
|
20
|
+
// Build the next state.
|
|
21
|
+
const nextState = buildNextState(state, payload);
|
|
22
|
+
|
|
23
|
+
// Grab the category group config key for the payload entityListType.
|
|
24
|
+
const categoryGroupConfigKey = getEntityCategoryGroupConfigKey(
|
|
25
|
+
payload.entityListType,
|
|
26
|
+
state.entityPageState
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
// Update the entity state by category group config key.
|
|
30
|
+
updateEntityStateByCategoryGroupConfigKey(
|
|
31
|
+
state,
|
|
32
|
+
{
|
|
33
|
+
filterState: payload.filterState,
|
|
34
|
+
savedFilterState: [],
|
|
35
|
+
},
|
|
36
|
+
categoryGroupConfigKey
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
// Return the updated state.
|
|
40
|
+
return {
|
|
41
|
+
...state,
|
|
42
|
+
...nextState,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ExploreActionKind } from "../../../exploreState";
|
|
2
|
+
import { SyncStateFromUrlAction, SyncStateFromUrlPayload } from "./types";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Action creator for syncing state from URL.
|
|
6
|
+
* @param payload - Payload.
|
|
7
|
+
* @returns Action with payload and action type.
|
|
8
|
+
*/
|
|
9
|
+
export function syncStateFromUrl(
|
|
10
|
+
payload: SyncStateFromUrlPayload
|
|
11
|
+
): SyncStateFromUrlAction {
|
|
12
|
+
return {
|
|
13
|
+
payload,
|
|
14
|
+
type: ExploreActionKind.SyncStateFromUrl,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ExploreActionKind, ExploreState } from "../../../exploreState";
|
|
2
|
+
|
|
3
|
+
export type SyncStateFromUrlAction = {
|
|
4
|
+
payload: SyncStateFromUrlPayload;
|
|
5
|
+
type: ExploreActionKind.SyncStateFromUrl;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export type SyncStateFromUrlPayload = Pick<
|
|
9
|
+
ExploreState,
|
|
10
|
+
"catalogState" | "featureFlagState" | "filterState"
|
|
11
|
+
> & {
|
|
12
|
+
entityListType: string;
|
|
13
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ExploreState } from "../../../exploreState";
|
|
2
|
+
import { getFilterCount } from "../../../exploreState/utils";
|
|
3
|
+
import { SyncStateFromUrlPayload } from "./types";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Builds the next state, syncing the catalog state, feature flag state, and filter state.
|
|
7
|
+
* @param state - Explore state.
|
|
8
|
+
* @param payload - Payload.
|
|
9
|
+
* @returns state.
|
|
10
|
+
*/
|
|
11
|
+
export function buildNextState(
|
|
12
|
+
state: ExploreState,
|
|
13
|
+
payload: SyncStateFromUrlPayload
|
|
14
|
+
): Partial<ExploreState> {
|
|
15
|
+
// Initialize filter count and filter state from current state.
|
|
16
|
+
let filterCount = state.filterCount;
|
|
17
|
+
let filterState = state.filterState;
|
|
18
|
+
|
|
19
|
+
// Only update filter count and filter state if the payload entityListType matches the current tab value.
|
|
20
|
+
if (payload.entityListType === state.tabValue) {
|
|
21
|
+
filterCount = getFilterCount(payload.filterState);
|
|
22
|
+
filterState = payload.filterState;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
catalogState: payload.catalogState,
|
|
27
|
+
featureFlagState: payload.featureFlagState,
|
|
28
|
+
filterCount,
|
|
29
|
+
filterState,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
EntityPath,
|
|
19
19
|
SavedFilter,
|
|
20
20
|
} from "../../config/entities";
|
|
21
|
+
import { META_COMMAND } from "./hooks/UseMetaCommands/types";
|
|
21
22
|
|
|
22
23
|
export interface EntityPageState {
|
|
23
24
|
categoryGroupConfigKey: CategoryGroupConfigKey;
|
|
@@ -55,6 +56,10 @@ export type EntityStateSavedFilter = SavedFilter;
|
|
|
55
56
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO revisit when adding react query or similar
|
|
56
57
|
export type ListItem = any;
|
|
57
58
|
|
|
59
|
+
export interface Meta {
|
|
60
|
+
command: META_COMMAND;
|
|
61
|
+
}
|
|
62
|
+
|
|
58
63
|
export type SavedFilterByCategoryValueKey = Map<
|
|
59
64
|
CategoryValueKey,
|
|
60
65
|
EntityStateSavedFilter
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { useRouter } from "next/router";
|
|
2
|
+
import { useEffect } from "react";
|
|
3
|
+
import { ExploreStateContextProps } from "../../../exploreState";
|
|
4
|
+
import { syncStateFromUrl } from "../../../exploreState/actions/syncStateFromUrl/dispatch";
|
|
5
|
+
import { getSyncStateFromUrl } from "./utils";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* useBeforePopState
|
|
9
|
+
*
|
|
10
|
+
* Keeps ExploreState, URL, and page props aligned when the user clicks
|
|
11
|
+
* the browser Back / Forward buttons.
|
|
12
|
+
*
|
|
13
|
+
* This hook runs inside `ExploreStateProvider` (mounted once in `_app`).
|
|
14
|
+
* It adds a single `router.beforePopState` handler that:
|
|
15
|
+
* - Parses the destination URL.
|
|
16
|
+
* - Dispatches `syncStateFromUrl` so filters, catalogue, and feature-flags in context match the URL.
|
|
17
|
+
* - Calls `router.replace(...{ shallow: false })` to trigger a full page transition, ensuring Next.js
|
|
18
|
+
* re-executes data-fetching methods that shallow routing would otherwise skip.
|
|
19
|
+
*
|
|
20
|
+
* The provider never unmounts, and a second call to `router.beforePopState` simply overwrites the handler.
|
|
21
|
+
* Cleaning up here would wipe out any handler registered later by other code.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
export const useBeforePopState = ({
|
|
25
|
+
exploreDispatch,
|
|
26
|
+
}: ExploreStateContextProps): void => {
|
|
27
|
+
const router = useRouter();
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
router.beforePopState(({ as, options, url }) => {
|
|
30
|
+
// Grab the expected state from URL.
|
|
31
|
+
const payload = getSyncStateFromUrl(url, as);
|
|
32
|
+
// Only dispatch if the url contains the dynamic segment `[entityListType]`.
|
|
33
|
+
if (payload.entityListType) {
|
|
34
|
+
// Sync state from URL.
|
|
35
|
+
exploreDispatch(syncStateFromUrl(payload));
|
|
36
|
+
// Force a full route transition to ensure page props are refreshed.
|
|
37
|
+
router.replace(url, as, { ...options, shallow: false });
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
// Return true to allow the popstate event to continue.
|
|
41
|
+
return true;
|
|
42
|
+
});
|
|
43
|
+
// No cleanup handler: see the JSDoc above for why we do not reset beforePopState here.
|
|
44
|
+
// Resetting would remove any handler registered by other parts of the app after this hook mounted.
|
|
45
|
+
}, [exploreDispatch, router]);
|
|
46
|
+
};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { SelectedFilter } from "common/entities";
|
|
2
|
+
import { SyncStateFromUrlPayload } from "providers/exploreState/actions/syncStateFromUrl/types";
|
|
3
|
+
import { EXPLORE_URL_PARAMS } from "../../../exploreState/constants";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Returns the filters from the resolved URL.
|
|
7
|
+
* @param paramValue - The filter parameter value.
|
|
8
|
+
* @returns The filters or an empty array.
|
|
9
|
+
*/
|
|
10
|
+
export function decodeFiltersParamValue(
|
|
11
|
+
paramValue: string | undefined
|
|
12
|
+
): SelectedFilter[] {
|
|
13
|
+
return JSON.parse(decodeURIComponent(paramValue || "[]"));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Returns the dynamic segment from the resolved URL.
|
|
18
|
+
* @param pattern - The pattern URL.
|
|
19
|
+
* @param resolved - The resolved URL.
|
|
20
|
+
* @param dynamicSegment - The dynamic segment to extract.
|
|
21
|
+
* @returns The dynamic segment or an empty string.
|
|
22
|
+
*/
|
|
23
|
+
function getDynamicSegment(
|
|
24
|
+
pattern: string,
|
|
25
|
+
resolved: string,
|
|
26
|
+
dynamicSegment = "[entityListType]"
|
|
27
|
+
): string {
|
|
28
|
+
// Remove query strings.
|
|
29
|
+
const patternPath = pattern.split("?")[0];
|
|
30
|
+
const resolvedPath = resolved.split("?")[0];
|
|
31
|
+
|
|
32
|
+
// Check if the pattern ends with the dynamic segment e.g. /data/[entityListType].
|
|
33
|
+
// We are not interested in a pattern the suggests we are on an entity page e.g. /data/[entityListType]/[entityId].
|
|
34
|
+
if (!patternPath.endsWith(`/${dynamicSegment}`)) return "";
|
|
35
|
+
|
|
36
|
+
// Split into segments.
|
|
37
|
+
const patternSegments = patternPath.split("/");
|
|
38
|
+
const resolvedSegments = resolvedPath.split("/");
|
|
39
|
+
|
|
40
|
+
// Find the dynamic segment.
|
|
41
|
+
const idx = patternSegments.findIndex((seg) => seg === dynamicSegment);
|
|
42
|
+
|
|
43
|
+
if (idx === -1) return "";
|
|
44
|
+
|
|
45
|
+
return resolvedSegments[idx] || "";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Returns a parameter from the resolved URL.
|
|
50
|
+
* @param resolved - The resolved URL.
|
|
51
|
+
* @param param - The parameter to extract.
|
|
52
|
+
* @returns The parameter value or undefined.
|
|
53
|
+
*/
|
|
54
|
+
export function getParamValue(
|
|
55
|
+
resolved: string,
|
|
56
|
+
param: string
|
|
57
|
+
): string | undefined {
|
|
58
|
+
// Grab the params from the resolved URL.
|
|
59
|
+
const params = new URLSearchParams(resolved.split("?")[1]);
|
|
60
|
+
const paramValue = params.get(param);
|
|
61
|
+
|
|
62
|
+
// Return the parameter value or undefined.
|
|
63
|
+
if (!paramValue) return;
|
|
64
|
+
|
|
65
|
+
return paramValue;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Returns the entity list type, filters, and feature flag state from the URL.
|
|
70
|
+
* A dynamic segment that is not exactly `[entityListType]` is not supported will return entityListType as an empty string.
|
|
71
|
+
* @param url - The URL to extract from.
|
|
72
|
+
* @param as - The resolved URL.
|
|
73
|
+
* @returns An object containing the entity list type, filters, and feature flag state.
|
|
74
|
+
*/
|
|
75
|
+
export function getSyncStateFromUrl(
|
|
76
|
+
url: string,
|
|
77
|
+
as: string
|
|
78
|
+
): SyncStateFromUrlPayload {
|
|
79
|
+
// Get the entity list type from the url.
|
|
80
|
+
const entityListType = getDynamicSegment(url, as);
|
|
81
|
+
|
|
82
|
+
// Get the param values for each param.
|
|
83
|
+
const [catalogState, featureFlagState, filter] = [
|
|
84
|
+
EXPLORE_URL_PARAMS.CATALOG,
|
|
85
|
+
EXPLORE_URL_PARAMS.FEATURE_FLAG,
|
|
86
|
+
EXPLORE_URL_PARAMS.FILTER,
|
|
87
|
+
].map((param) => getParamValue(as, param));
|
|
88
|
+
|
|
89
|
+
// Decode the filter param value to selected filters.
|
|
90
|
+
const filterState = decodeFiltersParamValue(filter);
|
|
91
|
+
|
|
92
|
+
return { catalogState, entityListType, featureFlagState, filterState };
|
|
93
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import Router from "next/router";
|
|
2
|
+
import { ExploreState } from "../../../exploreState";
|
|
3
|
+
import { buildQuery } from "./utils";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Updates the URL query parameters for the entity list page based on the current explore state.
|
|
7
|
+
* Pushes the query to the router.
|
|
8
|
+
* @param exploreState - The current explore state.
|
|
9
|
+
*/
|
|
10
|
+
export function navigateToFilters(exploreState: ExploreState): void {
|
|
11
|
+
// Build the query object.
|
|
12
|
+
const query = buildQuery(exploreState);
|
|
13
|
+
|
|
14
|
+
// Push the query to the router.
|
|
15
|
+
Router.push({ query }, undefined, { shallow: true });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Updates the URL query parameters for the entity list page based on the current explore state.
|
|
20
|
+
* Replaces the query to the router.
|
|
21
|
+
* @param exploreState - The current explore state.
|
|
22
|
+
*/
|
|
23
|
+
export function replaceToFilters(exploreState: ExploreState): void {
|
|
24
|
+
// Build the query object.
|
|
25
|
+
const query = buildQuery(exploreState);
|
|
26
|
+
|
|
27
|
+
// Replace the query to the router.
|
|
28
|
+
Router.replace({ query }, undefined, { shallow: true });
|
|
29
|
+
}
|