@databiosphere/findable-ui 27.0.0 → 28.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.
- package/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +17 -0
- package/lib/components/Index/components/EntitiesView/components/ChartView/chartView.js +1 -1
- package/lib/components/Index/components/EntitiesView/components/ChartView/components/Chart/barX/plot.js +3 -2
- package/lib/components/Index/components/EntitiesView/components/ChartView/components/Chart/barX/utils.d.ts +21 -0
- package/lib/components/Index/components/EntitiesView/components/ChartView/components/Chart/barX/utils.js +34 -0
- package/lib/components/Index/components/EntitiesView/components/EntityList/entityList.d.ts +2 -1
- package/lib/components/Index/components/EntitiesView/components/EntityList/entityList.js +3 -5
- package/lib/components/Index/components/EntitiesView/components/EntityList/types.d.ts +4 -1
- package/lib/components/Index/components/Hero/components/Summaries/stories/summaries.stories.d.ts +6 -0
- package/lib/components/Index/components/Hero/components/Summaries/{summaries.stories.js → stories/summaries.stories.js} +2 -6
- package/lib/components/Index/components/Hero/stories/hero.stories.d.ts +6 -0
- package/lib/components/Index/components/Hero/stories/hero.stories.js +16 -0
- package/lib/components/Index/index.d.ts +1 -1
- package/lib/components/Index/index.js +6 -2
- package/lib/components/Index/stories/index.stories.d.ts +6 -0
- package/lib/components/Index/stories/index.stories.js +17 -0
- package/lib/components/Index/table/coreOptions/columns/cellFactory.d.ts +9 -0
- package/lib/components/Index/table/coreOptions/columns/cellFactory.js +13 -0
- package/lib/components/Index/table/hook.d.ts +3 -0
- package/lib/components/Index/table/hook.js +166 -0
- package/lib/components/Index/table/types.d.ts +4 -0
- package/lib/components/Index/table/types.js +1 -0
- package/lib/components/Index/types.d.ts +6 -2
- package/lib/components/Table/table.d.ts +5 -20
- package/lib/components/Table/table.js +10 -138
- package/lib/components/TableCreator/tableCreator.d.ts +5 -7
- package/lib/components/TableCreator/tableCreator.js +3 -35
- package/lib/views/ExploreView/exploreView.js +1 -3
- package/package.json +1 -1
- package/src/components/Index/components/EntitiesView/components/ChartView/chartView.tsx +1 -1
- package/src/components/Index/components/EntitiesView/components/ChartView/components/Chart/barX/plot.ts +4 -1
- package/src/components/Index/components/EntitiesView/components/ChartView/components/Chart/barX/utils.ts +43 -0
- package/src/components/Index/components/EntitiesView/components/EntityList/entityList.tsx +7 -14
- package/src/components/Index/components/EntitiesView/components/EntityList/types.ts +5 -1
- package/src/components/Index/components/Hero/components/Summaries/{summaries.stories.tsx → stories/summaries.stories.tsx} +4 -8
- package/src/components/Index/components/Hero/stories/hero.stories.tsx +22 -0
- package/src/components/Index/index.tsx +21 -3
- package/src/components/Index/stories/index.stories.tsx +23 -0
- package/src/components/Index/table/coreOptions/columns/cellFactory.tsx +22 -0
- package/src/components/Index/table/hook.ts +234 -0
- package/src/components/Index/table/types.ts +5 -0
- package/src/components/Index/types.ts +6 -2
- package/src/components/Table/table.tsx +16 -199
- package/src/components/TableCreator/tableCreator.tsx +7 -79
- package/src/views/ExploreView/exploreView.tsx +4 -10
- package/tests/chart.test.tsx +19 -8
- package/tests/chartView.test.tsx +1 -1
- package/lib/components/Index/components/Hero/components/Summaries/summaries.stories.d.ts +0 -13
- package/lib/components/Index/components/Hero/hero.stories.d.ts +0 -23
- package/lib/components/Index/components/Hero/hero.stories.js +0 -22
- package/lib/components/Index/index.stories.d.ts +0 -6
- package/lib/components/Index/index.stories.js +0 -26
- package/src/components/Index/components/Hero/hero.stories.tsx +0 -28
- package/src/components/Index/index.stories.tsx +0 -31
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ColumnDef,
|
|
3
|
+
ColumnSort,
|
|
4
|
+
getCoreRowModel,
|
|
5
|
+
getFacetedRowModel,
|
|
6
|
+
getFilteredRowModel,
|
|
7
|
+
getPaginationRowModel,
|
|
8
|
+
getSortedRowModel,
|
|
9
|
+
RowData,
|
|
10
|
+
RowSelectionState,
|
|
11
|
+
TableState,
|
|
12
|
+
Updater,
|
|
13
|
+
useReactTable,
|
|
14
|
+
} from "@tanstack/react-table";
|
|
15
|
+
import { useCallback, useEffect, useMemo } from "react";
|
|
16
|
+
import { track } from "../../../common/analytics/analytics";
|
|
17
|
+
import {
|
|
18
|
+
EVENT_NAME,
|
|
19
|
+
EVENT_PARAM,
|
|
20
|
+
SORT_DIRECTION,
|
|
21
|
+
} from "../../../common/analytics/entities";
|
|
22
|
+
import { useConfig } from "../../../hooks/useConfig";
|
|
23
|
+
import { useExploreMode } from "../../../hooks/useExploreMode/useExploreMode";
|
|
24
|
+
import { useExploreState } from "../../../hooks/useExploreState";
|
|
25
|
+
import { ExploreActionKind } from "../../../providers/exploreState";
|
|
26
|
+
import { DEFAULT_PAGINATION_STATE } from "../../../providers/exploreState/initializer/constants";
|
|
27
|
+
import { arrIncludesSome } from "../../Table/columnDef/columnFilters/filterFn";
|
|
28
|
+
import { COLUMN_DEF } from "../../Table/common/columnDef";
|
|
29
|
+
import {
|
|
30
|
+
buildCategoryViews,
|
|
31
|
+
getFacetedUniqueValuesWithArrayValues,
|
|
32
|
+
getTableStatePagination,
|
|
33
|
+
isClientFilteringEnabled,
|
|
34
|
+
sortingFn,
|
|
35
|
+
} from "../../Table/common/utils";
|
|
36
|
+
import { ROW_POSITION } from "../../Table/features/RowPosition/constants";
|
|
37
|
+
import { ROW_PREVIEW } from "../../Table/features/RowPreview/constants";
|
|
38
|
+
import { RowPreviewState } from "../../Table/features/RowPreview/entities";
|
|
39
|
+
import { buildBaseColumnDef } from "../../TableCreator/common/utils";
|
|
40
|
+
import { useTableOptions } from "../../TableCreator/options/hook";
|
|
41
|
+
import { createCell } from "./coreOptions/columns/cellFactory";
|
|
42
|
+
import { UseTable } from "./types";
|
|
43
|
+
|
|
44
|
+
// eslint-disable-next-line sonarjs/cognitive-complexity -- TODO fix component length / complexity
|
|
45
|
+
export const useTable = <T extends RowData>(): UseTable<T> => {
|
|
46
|
+
const { entityConfig } = useConfig();
|
|
47
|
+
const exploreMode = useExploreMode();
|
|
48
|
+
const { exploreDispatch, exploreState } = useExploreState();
|
|
49
|
+
const tableOptions = useTableOptions<T>();
|
|
50
|
+
const {
|
|
51
|
+
entityPageState,
|
|
52
|
+
filterState,
|
|
53
|
+
listItems,
|
|
54
|
+
paginationState,
|
|
55
|
+
rowPreview,
|
|
56
|
+
tabValue,
|
|
57
|
+
} = exploreState;
|
|
58
|
+
const { getId: getRowId, list } = entityConfig;
|
|
59
|
+
const { columns: columnsConfig } = list;
|
|
60
|
+
const { columnVisibility, grouping, rowSelection, sorting } =
|
|
61
|
+
entityPageState[tabValue];
|
|
62
|
+
const { currentPage, pageSize, rows: pageCount } = paginationState;
|
|
63
|
+
const clientFiltering = isClientFilteringEnabled(exploreMode);
|
|
64
|
+
const pagination = useMemo(
|
|
65
|
+
() => getTableStatePagination(currentPage - 1, pageSize),
|
|
66
|
+
[currentPage, pageSize]
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
const columnDefs: ColumnDef<T>[] = useMemo(
|
|
70
|
+
() =>
|
|
71
|
+
columnsConfig.reduce(
|
|
72
|
+
(
|
|
73
|
+
acc,
|
|
74
|
+
{
|
|
75
|
+
/**
|
|
76
|
+
* Applies the custom `arrIncludesSome` filter function as the default for multi-value filtering.
|
|
77
|
+
* Although `ColumnFilter["value"]` is typed as `unknown`, in practice it's consistently an array (`unknown[]`) in entity lists.
|
|
78
|
+
* This custom filter function supports multi-select filtering, even when individual cell values are single strings.
|
|
79
|
+
* This override of TanStack's default `arrIncludesSome` resolves a limitation where the base implementation
|
|
80
|
+
* does not support matching an array of filter values against a single string cell value.
|
|
81
|
+
* For range filtering, specify TanStack's `inNumberRange` filter function on the column definition.
|
|
82
|
+
*/
|
|
83
|
+
filterFn = "arrIncludesSome",
|
|
84
|
+
...columnConfig
|
|
85
|
+
}
|
|
86
|
+
) => {
|
|
87
|
+
acc.push({
|
|
88
|
+
...buildBaseColumnDef<T>(columnConfig),
|
|
89
|
+
cell: createCell(columnConfig),
|
|
90
|
+
filterFn,
|
|
91
|
+
sortingFn,
|
|
92
|
+
});
|
|
93
|
+
return acc;
|
|
94
|
+
},
|
|
95
|
+
[
|
|
96
|
+
/* Initialize column definitions with the "row position" column */
|
|
97
|
+
COLUMN_DEF.ROW_POSITION,
|
|
98
|
+
/* Initialize column definitions with the "row selection" column */
|
|
99
|
+
COLUMN_DEF.ROW_SELECTION,
|
|
100
|
+
] as ColumnDef<T>[]
|
|
101
|
+
),
|
|
102
|
+
[columnsConfig]
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
const onSortingChange = (updater: Updater<ColumnSort[]>): void => {
|
|
106
|
+
// TODO(cc) memoize `onSortingChange` with `useCallback`.
|
|
107
|
+
// TODO(cc) copy `onSortingChange` to ../options/sorting/hook.ts see src/components/Table/options/grouping/hook.ts for example.
|
|
108
|
+
exploreDispatch({
|
|
109
|
+
payload: typeof updater === "function" ? updater(sorting) : updater,
|
|
110
|
+
type: ExploreActionKind.UpdateSorting,
|
|
111
|
+
});
|
|
112
|
+
// Execute GTM tracking.
|
|
113
|
+
// TODO(cc) update tracking to handle sorting of multiple columns.
|
|
114
|
+
// TODO(cc) GTM tracking when `onSortingChange` is triggered only tracks the first column sorted, and takes the value from explore state which is not updated yet.
|
|
115
|
+
track(EVENT_NAME.ENTITY_TABLE_SORTED, {
|
|
116
|
+
[EVENT_PARAM.ENTITY_NAME]: exploreState.tabValue,
|
|
117
|
+
[EVENT_PARAM.COLUMN_NAME]: sorting?.[0]?.id, // TODO(cc) sorting should always be at least `[]` and never `undefined`.
|
|
118
|
+
[EVENT_PARAM.SORT_DIRECTION]: sorting?.[0]?.desc // TODO(cc) sorting should always be at least `[]` and never `undefined`.
|
|
119
|
+
? SORT_DIRECTION.DESC
|
|
120
|
+
: SORT_DIRECTION.ASC,
|
|
121
|
+
});
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const onRowPreviewChange = useCallback(
|
|
125
|
+
(updater: Updater<RowPreviewState>): void => {
|
|
126
|
+
exploreDispatch({
|
|
127
|
+
payload: typeof updater === "function" ? updater(rowPreview) : updater,
|
|
128
|
+
type: ExploreActionKind.UpdateRowPreview,
|
|
129
|
+
});
|
|
130
|
+
},
|
|
131
|
+
[exploreDispatch, rowPreview]
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
const onRowSelectionChange = useCallback(
|
|
135
|
+
(updater: Updater<RowSelectionState>): void => {
|
|
136
|
+
// TODO(cc) refactor `onRowSelectionChange` to /options/rowSelection/hook.ts see onGroupingChange.
|
|
137
|
+
exploreDispatch({
|
|
138
|
+
payload:
|
|
139
|
+
typeof updater === "function" ? updater(rowSelection) : updater,
|
|
140
|
+
type: ExploreActionKind.UpdateRowSelection,
|
|
141
|
+
});
|
|
142
|
+
},
|
|
143
|
+
[exploreDispatch, rowSelection]
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
const state: Partial<TableState> = {
|
|
147
|
+
columnVisibility,
|
|
148
|
+
grouping,
|
|
149
|
+
pagination,
|
|
150
|
+
rowPreview,
|
|
151
|
+
rowSelection,
|
|
152
|
+
sorting,
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* TODO: Update `ColumnConfig` to follow the `ColumnDef` API of TanStack Table.
|
|
157
|
+
* - Standardize column definitions to leverage the full power of TanStack Table's feature set and improve compatibility.
|
|
158
|
+
* TODO: Define `sorting` directly within `ListConfig` via the `tableOptions.initialState` property.
|
|
159
|
+
* - This will simplify the configuration structure and centralize table state definitions, reducing redundancy and improving clarity.
|
|
160
|
+
*/
|
|
161
|
+
const table = useReactTable<T>({
|
|
162
|
+
_features: [ROW_POSITION, ROW_PREVIEW],
|
|
163
|
+
columns: columnDefs,
|
|
164
|
+
data: listItems || [],
|
|
165
|
+
enableColumnFilters: true, // client-side filtering.
|
|
166
|
+
enableFilters: true, // client-side filtering.
|
|
167
|
+
enableMultiSort: clientFiltering, // TODO(cc) move to sorting options; default to false and let the table options in config flag this value.
|
|
168
|
+
filterFns: { arrIncludesSome },
|
|
169
|
+
getCoreRowModel: getCoreRowModel(),
|
|
170
|
+
getFacetedRowModel: clientFiltering ? getFacetedRowModel() : undefined,
|
|
171
|
+
getFacetedUniqueValues: clientFiltering
|
|
172
|
+
? getFacetedUniqueValuesWithArrayValues()
|
|
173
|
+
: undefined,
|
|
174
|
+
getFilteredRowModel: clientFiltering ? getFilteredRowModel() : undefined,
|
|
175
|
+
getPaginationRowModel: getPaginationRowModel(),
|
|
176
|
+
getRowId,
|
|
177
|
+
getSortedRowModel: clientFiltering ? getSortedRowModel() : undefined,
|
|
178
|
+
manualPagination: true,
|
|
179
|
+
manualSorting: !clientFiltering,
|
|
180
|
+
onRowPreviewChange,
|
|
181
|
+
onRowSelectionChange,
|
|
182
|
+
onSortingChange,
|
|
183
|
+
pageCount,
|
|
184
|
+
state,
|
|
185
|
+
...tableOptions,
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
const { getAllColumns, getRowModel, getState } = table;
|
|
189
|
+
|
|
190
|
+
const allColumns = getAllColumns();
|
|
191
|
+
const { columnFilters } = getState();
|
|
192
|
+
const { rows } = getRowModel();
|
|
193
|
+
|
|
194
|
+
// Sets react table column filters `columnFilters` state - for client-side filtering only - with update of filterState.
|
|
195
|
+
useEffect(() => {
|
|
196
|
+
if (clientFiltering) {
|
|
197
|
+
table.setColumnFilters(
|
|
198
|
+
filterState.map(({ categoryKey, value }) => ({
|
|
199
|
+
id: categoryKey,
|
|
200
|
+
value,
|
|
201
|
+
}))
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
}, [clientFiltering, filterState, table]);
|
|
205
|
+
|
|
206
|
+
// Process explore response - client-side filtering only.
|
|
207
|
+
useEffect(() => {
|
|
208
|
+
if (!listItems || listItems.length === 0) return;
|
|
209
|
+
if (clientFiltering) {
|
|
210
|
+
exploreDispatch({
|
|
211
|
+
payload: {
|
|
212
|
+
listItems,
|
|
213
|
+
loading: false,
|
|
214
|
+
paginationResponse: {
|
|
215
|
+
...DEFAULT_PAGINATION_STATE,
|
|
216
|
+
pageSize: rows.filter(({ getIsGrouped }) => !getIsGrouped()).length,
|
|
217
|
+
rows: rows.filter(({ getIsGrouped }) => !getIsGrouped()).length,
|
|
218
|
+
},
|
|
219
|
+
selectCategories: buildCategoryViews(allColumns, columnFilters),
|
|
220
|
+
},
|
|
221
|
+
type: ExploreActionKind.ProcessExploreResponse,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
}, [
|
|
225
|
+
allColumns,
|
|
226
|
+
clientFiltering,
|
|
227
|
+
columnFilters,
|
|
228
|
+
exploreDispatch,
|
|
229
|
+
listItems,
|
|
230
|
+
rows,
|
|
231
|
+
]);
|
|
232
|
+
|
|
233
|
+
return { table };
|
|
234
|
+
};
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { BaseComponentProps } from "components/types";
|
|
2
|
+
import { EntityConfig } from "config/entities";
|
|
2
3
|
import { ReactNode } from "react";
|
|
4
|
+
import { CategoryFilter } from "../../components/Filter/components/Filters/filters";
|
|
3
5
|
|
|
4
6
|
export interface IndexProps extends BaseComponentProps {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
+
categoryFilters: CategoryFilter[];
|
|
8
|
+
entityListType: string;
|
|
9
|
+
entityName: EntityConfig["label"];
|
|
7
10
|
ListHero?: ReactNode | ReactNode[];
|
|
11
|
+
loading: boolean;
|
|
8
12
|
SideBarButton?: ReactNode;
|
|
9
13
|
SubTitleHero?: ReactNode | ReactNode[];
|
|
10
14
|
Summaries?: ReactNode;
|
|
@@ -1,28 +1,13 @@
|
|
|
1
1
|
import { TableContainer } from "@mui/material";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
ColumnSort,
|
|
5
|
-
CoreOptions,
|
|
6
|
-
getCoreRowModel,
|
|
7
|
-
getFacetedRowModel,
|
|
8
|
-
getFilteredRowModel,
|
|
9
|
-
getPaginationRowModel,
|
|
10
|
-
getSortedRowModel,
|
|
11
|
-
RowData,
|
|
12
|
-
RowSelectionState,
|
|
13
|
-
TableState,
|
|
14
|
-
Updater,
|
|
15
|
-
useReactTable,
|
|
16
|
-
} from "@tanstack/react-table";
|
|
17
|
-
import React, { Fragment, useCallback, useEffect, useMemo } from "react";
|
|
2
|
+
import { RowData, Table as TanStackTable } from "@tanstack/react-table";
|
|
3
|
+
import React, { Fragment } from "react";
|
|
18
4
|
import { track } from "../../common/analytics/analytics";
|
|
19
5
|
import {
|
|
20
6
|
EVENT_NAME,
|
|
21
7
|
EVENT_PARAM,
|
|
22
8
|
PAGINATION_DIRECTION,
|
|
23
|
-
SORT_DIRECTION,
|
|
24
9
|
} from "../../common/analytics/entities";
|
|
25
|
-
import {
|
|
10
|
+
import { ListViewConfig } from "../../config/entities";
|
|
26
11
|
import {
|
|
27
12
|
BREAKPOINT_FN_NAME,
|
|
28
13
|
useBreakpointHelper,
|
|
@@ -31,173 +16,47 @@ import { useExploreMode } from "../../hooks/useExploreMode/useExploreMode";
|
|
|
31
16
|
import { useExploreState } from "../../hooks/useExploreState";
|
|
32
17
|
import { useScroll } from "../../hooks/useScroll";
|
|
33
18
|
import { ExploreActionKind } from "../../providers/exploreState";
|
|
34
|
-
import { DEFAULT_PAGINATION_STATE } from "../../providers/exploreState/initializer/constants";
|
|
35
19
|
import { TABLET } from "../../theme/common/breakpoints";
|
|
36
20
|
import { Loading, LOADING_PANEL_STYLE } from "../Loading/loading";
|
|
37
21
|
import { NoResults } from "../NoResults/noResults";
|
|
38
22
|
import { getColumnTrackSizing } from "../TableCreator/options/columnTrackSizing/utils";
|
|
39
|
-
import { arrIncludesSome } from "./columnDef/columnFilters/filterFn";
|
|
40
23
|
import { ROW_DIRECTION } from "./common/entities";
|
|
41
|
-
import {
|
|
42
|
-
buildCategoryViews,
|
|
43
|
-
getFacetedUniqueValuesWithArrayValues,
|
|
44
|
-
getTableStatePagination,
|
|
45
|
-
isClientFilteringEnabled,
|
|
46
|
-
} from "./common/utils";
|
|
24
|
+
import { isClientFilteringEnabled } from "./common/utils";
|
|
47
25
|
import { Pagination as DXPagination } from "./components/Pagination/pagination";
|
|
48
26
|
import { TableBody } from "./components/TableBody/tableBody";
|
|
49
27
|
import { TableHead } from "./components/TableHead/tableHead";
|
|
50
28
|
import { TableToolbar } from "./components/TableToolbar/tableToolbar";
|
|
51
|
-
import { ROW_POSITION } from "./features/RowPosition/constants";
|
|
52
|
-
import { ROW_PREVIEW } from "./features/RowPreview/constants";
|
|
53
|
-
import { RowPreviewState } from "./features/RowPreview/entities";
|
|
54
29
|
import { GridTable } from "./table.styles";
|
|
55
30
|
|
|
56
31
|
export interface TableProps<T extends RowData> {
|
|
57
|
-
columns: ColumnDef<T>[];
|
|
58
|
-
getRowId?: CoreOptions<T>["getRowId"];
|
|
59
|
-
items: T[];
|
|
60
32
|
listView?: ListViewConfig;
|
|
61
|
-
loading
|
|
62
|
-
|
|
33
|
+
loading: boolean;
|
|
34
|
+
table: TanStackTable<T>;
|
|
63
35
|
}
|
|
64
36
|
|
|
65
|
-
/**
|
|
66
|
-
* This table can be Controlled or Uncontrolled based on the set of props passed to it.
|
|
67
|
-
* Controlled table will receive the navigation functions, and it will be used for dynamic loads.
|
|
68
|
-
* Uncontrolled table will take advantage of React Table's state and will be used for static loads.
|
|
69
|
-
* @param tableProps - Set of props required for displaying the table.
|
|
70
|
-
* @param tableProps.columns - Set of columns to display.
|
|
71
|
-
* @param tableProps.getRowId - Function to customize the row ID.
|
|
72
|
-
* @param tableProps.items - Row data to display.
|
|
73
|
-
* @param tableProps.listView - List view configuration.
|
|
74
|
-
* @param tableProps.tableOptions - TanStack table options.
|
|
75
|
-
* @returns Configured table element for display.
|
|
76
|
-
*/
|
|
77
37
|
export const TableComponent = <T extends RowData>({
|
|
78
|
-
columns,
|
|
79
|
-
getRowId,
|
|
80
|
-
items,
|
|
81
38
|
listView,
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
TableProps<T>): JSX.Element => {
|
|
39
|
+
loading,
|
|
40
|
+
table,
|
|
41
|
+
}: TableProps<T>): JSX.Element => {
|
|
85
42
|
const tabletDown = useBreakpointHelper(BREAKPOINT_FN_NAME.DOWN, TABLET);
|
|
86
43
|
const exploreMode = useExploreMode();
|
|
87
44
|
const { exploreDispatch, exploreState } = useExploreState();
|
|
88
|
-
const {
|
|
89
|
-
|
|
90
|
-
filterState,
|
|
91
|
-
listItems,
|
|
92
|
-
loading = false,
|
|
93
|
-
paginationState,
|
|
94
|
-
rowPreview,
|
|
95
|
-
tabValue,
|
|
96
|
-
} = exploreState;
|
|
97
|
-
const { columnVisibility, grouping, rowSelection, sorting } =
|
|
98
|
-
entityPageState[tabValue];
|
|
99
|
-
const { currentPage, pages, pageSize, rows: pageCount } = paginationState;
|
|
45
|
+
const { paginationState } = exploreState;
|
|
46
|
+
const { currentPage, pages } = paginationState;
|
|
100
47
|
const { disablePagination = false } = listView || {};
|
|
101
48
|
const clientFiltering = isClientFilteringEnabled(exploreMode);
|
|
102
49
|
const rowDirection = tabletDown
|
|
103
50
|
? ROW_DIRECTION.VERTICAL
|
|
104
51
|
: ROW_DIRECTION.DEFAULT;
|
|
105
|
-
const pagination = useMemo(
|
|
106
|
-
() => getTableStatePagination(currentPage - 1, pageSize),
|
|
107
|
-
[currentPage, pageSize]
|
|
108
|
-
);
|
|
109
|
-
|
|
110
|
-
const onSortingChange = (updater: Updater<ColumnSort[]>): void => {
|
|
111
|
-
// TODO(cc) memoize `onSortingChange` with `useCallback`.
|
|
112
|
-
// TODO(cc) copy `onSortingChange` to ../options/sorting/hook.ts see src/components/Table/options/grouping/hook.ts for example.
|
|
113
|
-
exploreDispatch({
|
|
114
|
-
payload: typeof updater === "function" ? updater(sorting) : updater,
|
|
115
|
-
type: ExploreActionKind.UpdateSorting,
|
|
116
|
-
});
|
|
117
|
-
// Execute GTM tracking.
|
|
118
|
-
// TODO(cc) update tracking to handle sorting of multiple columns.
|
|
119
|
-
// TODO(cc) GTM tracking when `onSortingChange` is triggered only tracks the first column sorted, and takes the value from explore state which is not updated yet.
|
|
120
|
-
track(EVENT_NAME.ENTITY_TABLE_SORTED, {
|
|
121
|
-
[EVENT_PARAM.ENTITY_NAME]: exploreState.tabValue,
|
|
122
|
-
[EVENT_PARAM.COLUMN_NAME]: sorting?.[0]?.id, // TODO(cc) sorting should always be at least `[]` and never `undefined`.
|
|
123
|
-
[EVENT_PARAM.SORT_DIRECTION]: sorting?.[0]?.desc // TODO(cc) sorting should always be at least `[]` and never `undefined`.
|
|
124
|
-
? SORT_DIRECTION.DESC
|
|
125
|
-
: SORT_DIRECTION.ASC,
|
|
126
|
-
});
|
|
127
|
-
};
|
|
128
52
|
|
|
129
|
-
const onRowPreviewChange = useCallback(
|
|
130
|
-
(updater: Updater<RowPreviewState>): void => {
|
|
131
|
-
exploreDispatch({
|
|
132
|
-
payload: typeof updater === "function" ? updater(rowPreview) : updater,
|
|
133
|
-
type: ExploreActionKind.UpdateRowPreview,
|
|
134
|
-
});
|
|
135
|
-
},
|
|
136
|
-
[exploreDispatch, rowPreview]
|
|
137
|
-
);
|
|
138
|
-
|
|
139
|
-
const onRowSelectionChange = useCallback(
|
|
140
|
-
(updater: Updater<RowSelectionState>): void => {
|
|
141
|
-
// TODO(cc) refactor `onRowSelectionChange` to /options/rowSelection/hook.ts see onGroupingChange.
|
|
142
|
-
exploreDispatch({
|
|
143
|
-
payload:
|
|
144
|
-
typeof updater === "function" ? updater(rowSelection) : updater,
|
|
145
|
-
type: ExploreActionKind.UpdateRowSelection,
|
|
146
|
-
});
|
|
147
|
-
},
|
|
148
|
-
[exploreDispatch, rowSelection]
|
|
149
|
-
);
|
|
150
|
-
|
|
151
|
-
const state: Partial<TableState> = {
|
|
152
|
-
columnVisibility,
|
|
153
|
-
grouping,
|
|
154
|
-
pagination,
|
|
155
|
-
rowPreview,
|
|
156
|
-
rowSelection,
|
|
157
|
-
sorting,
|
|
158
|
-
};
|
|
159
|
-
/**
|
|
160
|
-
* TODO: Update `ColumnConfig` to follow the `ColumnDef` API of TanStack Table.
|
|
161
|
-
* - Standardize column definitions to leverage the full power of TanStack Table's feature set and improve compatibility.
|
|
162
|
-
* TODO: Define `sorting` directly within `ListConfig` via the `tableOptions.initialState` property.
|
|
163
|
-
* - This will simplify the configuration structure and centralize table state definitions, reducing redundancy and improving clarity.
|
|
164
|
-
*/
|
|
165
|
-
const tableInstance = useReactTable({
|
|
166
|
-
_features: [ROW_POSITION, ROW_PREVIEW],
|
|
167
|
-
columns,
|
|
168
|
-
data: items,
|
|
169
|
-
enableColumnFilters: true, // client-side filtering.
|
|
170
|
-
enableFilters: true, // client-side filtering.
|
|
171
|
-
enableMultiSort: clientFiltering, // TODO(cc) move to sorting options; default to false and let the table options in config flag this value.
|
|
172
|
-
filterFns: { arrIncludesSome },
|
|
173
|
-
getCoreRowModel: getCoreRowModel(),
|
|
174
|
-
getFacetedRowModel: clientFiltering ? getFacetedRowModel() : undefined,
|
|
175
|
-
getFacetedUniqueValues: clientFiltering
|
|
176
|
-
? getFacetedUniqueValuesWithArrayValues()
|
|
177
|
-
: undefined,
|
|
178
|
-
getFilteredRowModel: clientFiltering ? getFilteredRowModel() : undefined,
|
|
179
|
-
getPaginationRowModel: getPaginationRowModel(),
|
|
180
|
-
getRowId,
|
|
181
|
-
getSortedRowModel: clientFiltering ? getSortedRowModel() : undefined,
|
|
182
|
-
manualPagination: true,
|
|
183
|
-
manualSorting: !clientFiltering,
|
|
184
|
-
onRowPreviewChange,
|
|
185
|
-
onRowSelectionChange,
|
|
186
|
-
onSortingChange,
|
|
187
|
-
pageCount,
|
|
188
|
-
state,
|
|
189
|
-
...tableOptions,
|
|
190
|
-
});
|
|
191
53
|
const {
|
|
192
|
-
getAllColumns,
|
|
193
54
|
getRowModel,
|
|
194
|
-
getState,
|
|
195
55
|
getVisibleFlatColumns,
|
|
196
56
|
nextPage: tableNextPage,
|
|
197
57
|
previousPage: tablePreviousPage,
|
|
198
|
-
} =
|
|
199
|
-
|
|
200
|
-
const { columnFilters } = getState();
|
|
58
|
+
} = table;
|
|
59
|
+
|
|
201
60
|
const { rows } = getRowModel();
|
|
202
61
|
const noResults = !loading && (!rows || rows.length === 0);
|
|
203
62
|
const scrollTop = useScroll();
|
|
@@ -239,45 +98,6 @@ TableProps<T>): JSX.Element => {
|
|
|
239
98
|
scrollTop();
|
|
240
99
|
};
|
|
241
100
|
|
|
242
|
-
// Sets react table column filters `columnFilters` state - for client-side filtering only - with update of filterState.
|
|
243
|
-
useEffect(() => {
|
|
244
|
-
if (clientFiltering) {
|
|
245
|
-
tableInstance.setColumnFilters(
|
|
246
|
-
filterState.map(({ categoryKey, value }) => ({
|
|
247
|
-
id: categoryKey,
|
|
248
|
-
value,
|
|
249
|
-
}))
|
|
250
|
-
);
|
|
251
|
-
}
|
|
252
|
-
}, [clientFiltering, filterState, tableInstance]);
|
|
253
|
-
|
|
254
|
-
// Process explore response - client-side filtering only.
|
|
255
|
-
useEffect(() => {
|
|
256
|
-
if (!listItems || listItems.length === 0) return;
|
|
257
|
-
if (clientFiltering) {
|
|
258
|
-
exploreDispatch({
|
|
259
|
-
payload: {
|
|
260
|
-
listItems,
|
|
261
|
-
loading: false,
|
|
262
|
-
paginationResponse: {
|
|
263
|
-
...DEFAULT_PAGINATION_STATE,
|
|
264
|
-
pageSize: rows.filter(({ getIsGrouped }) => !getIsGrouped()).length,
|
|
265
|
-
rows: rows.filter(({ getIsGrouped }) => !getIsGrouped()).length,
|
|
266
|
-
},
|
|
267
|
-
selectCategories: buildCategoryViews(allColumns, columnFilters),
|
|
268
|
-
},
|
|
269
|
-
type: ExploreActionKind.ProcessExploreResponse,
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
|
-
}, [
|
|
273
|
-
allColumns,
|
|
274
|
-
clientFiltering,
|
|
275
|
-
columnFilters,
|
|
276
|
-
exploreDispatch,
|
|
277
|
-
listItems,
|
|
278
|
-
rows,
|
|
279
|
-
]);
|
|
280
|
-
|
|
281
101
|
function canNextPage(): boolean {
|
|
282
102
|
return currentPage < pages;
|
|
283
103
|
}
|
|
@@ -293,7 +113,7 @@ TableProps<T>): JSX.Element => {
|
|
|
293
113
|
<TableToolbar
|
|
294
114
|
listView={listView}
|
|
295
115
|
rowDirection={rowDirection}
|
|
296
|
-
tableInstance={
|
|
116
|
+
tableInstance={table}
|
|
297
117
|
/>
|
|
298
118
|
<Loading
|
|
299
119
|
appear={false}
|
|
@@ -306,14 +126,11 @@ TableProps<T>): JSX.Element => {
|
|
|
306
126
|
collapsable={true}
|
|
307
127
|
gridTemplateColumns={getColumnTrackSizing(getVisibleFlatColumns())}
|
|
308
128
|
>
|
|
309
|
-
<TableHead
|
|
310
|
-
rowDirection={rowDirection}
|
|
311
|
-
tableInstance={tableInstance}
|
|
312
|
-
/>
|
|
129
|
+
<TableHead rowDirection={rowDirection} tableInstance={table} />
|
|
313
130
|
<TableBody
|
|
314
131
|
rows={rows}
|
|
315
132
|
rowDirection={rowDirection}
|
|
316
|
-
tableInstance={
|
|
133
|
+
tableInstance={table}
|
|
317
134
|
/>
|
|
318
135
|
</GridTable>
|
|
319
136
|
</TableContainer>
|
|
@@ -1,90 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
CoreOptions,
|
|
5
|
-
RowData,
|
|
6
|
-
} from "@tanstack/react-table";
|
|
7
|
-
import React, { useMemo } from "react";
|
|
8
|
-
import { ColumnConfig, ListViewConfig } from "../../config/entities";
|
|
9
|
-
import { ComponentCreator } from "../ComponentCreator/ComponentCreator";
|
|
10
|
-
import { COLUMN_DEF } from "../Table/common/columnDef";
|
|
11
|
-
import { sortingFn } from "../Table/common/utils";
|
|
1
|
+
import { RowData, Table as TanStackTable } from "@tanstack/react-table";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { ListViewConfig } from "../../config/entities";
|
|
12
4
|
import { Table } from "../Table/table";
|
|
13
|
-
import { buildBaseColumnDef } from "./common/utils";
|
|
14
|
-
import { useTableOptions } from "./options/hook";
|
|
15
5
|
|
|
16
6
|
export interface TableCreatorProps<T> {
|
|
17
|
-
columns: ColumnConfig<T>[];
|
|
18
|
-
getRowId?: CoreOptions<T>["getRowId"];
|
|
19
|
-
items: T[];
|
|
20
7
|
listView?: ListViewConfig;
|
|
21
|
-
loading
|
|
8
|
+
loading: boolean;
|
|
9
|
+
table: TanStackTable<T>;
|
|
22
10
|
}
|
|
23
11
|
|
|
24
|
-
const createCell = <T extends RowData = RowData, TData = unknown>(
|
|
25
|
-
config: ColumnConfig<T>
|
|
26
|
-
) =>
|
|
27
|
-
function CellCreator(cellContext: CellContext<T, TData>): JSX.Element {
|
|
28
|
-
return (
|
|
29
|
-
<ComponentCreator
|
|
30
|
-
components={[config.componentConfig]}
|
|
31
|
-
response={cellContext.row.original}
|
|
32
|
-
viewContext={{ cellContext }}
|
|
33
|
-
/>
|
|
34
|
-
);
|
|
35
|
-
};
|
|
36
|
-
|
|
37
12
|
export const TableCreator = <T extends RowData>({
|
|
38
|
-
columns,
|
|
39
|
-
getRowId,
|
|
40
|
-
items,
|
|
41
13
|
listView,
|
|
42
14
|
loading,
|
|
15
|
+
table,
|
|
43
16
|
}: TableCreatorProps<T>): JSX.Element => {
|
|
44
|
-
|
|
45
|
-
const columnDefs: ColumnDef<T>[] = useMemo(
|
|
46
|
-
() =>
|
|
47
|
-
columns.reduce(
|
|
48
|
-
(
|
|
49
|
-
acc,
|
|
50
|
-
{
|
|
51
|
-
/**
|
|
52
|
-
* Applies the custom `arrIncludesSome` filter function as the default for multi-value filtering.
|
|
53
|
-
* Although `ColumnFilter["value"]` is typed as `unknown`, in practice it's consistently an array (`unknown[]`) in entity lists.
|
|
54
|
-
* This custom filter function supports multi-select filtering, even when individual cell values are single strings.
|
|
55
|
-
* This override of TanStack's default `arrIncludesSome` resolves a limitation where the base implementation
|
|
56
|
-
* does not support matching an array of filter values against a single string cell value.
|
|
57
|
-
* For range filtering, specify TanStack's `inNumberRange` filter function on the column definition.
|
|
58
|
-
*/
|
|
59
|
-
filterFn = "arrIncludesSome",
|
|
60
|
-
...columnConfig
|
|
61
|
-
}
|
|
62
|
-
) => {
|
|
63
|
-
acc.push({
|
|
64
|
-
...buildBaseColumnDef(columnConfig),
|
|
65
|
-
cell: createCell(columnConfig),
|
|
66
|
-
filterFn,
|
|
67
|
-
sortingFn: sortingFn,
|
|
68
|
-
});
|
|
69
|
-
return acc;
|
|
70
|
-
},
|
|
71
|
-
[
|
|
72
|
-
/* Initialize column definitions with the "row position" column */
|
|
73
|
-
COLUMN_DEF.ROW_POSITION,
|
|
74
|
-
/* Initialize column definitions with the "row selection" column */
|
|
75
|
-
COLUMN_DEF.ROW_SELECTION,
|
|
76
|
-
] as ColumnDef<T>[]
|
|
77
|
-
),
|
|
78
|
-
[columns]
|
|
79
|
-
);
|
|
80
|
-
return (
|
|
81
|
-
<Table<T>
|
|
82
|
-
columns={columnDefs}
|
|
83
|
-
getRowId={getRowId}
|
|
84
|
-
items={items}
|
|
85
|
-
listView={listView}
|
|
86
|
-
loading={loading}
|
|
87
|
-
tableOptions={tableOptions}
|
|
88
|
-
/>
|
|
89
|
-
);
|
|
17
|
+
return <Table listView={listView} loading={loading} table={table} />;
|
|
90
18
|
};
|
|
@@ -17,8 +17,6 @@ import {
|
|
|
17
17
|
Filters,
|
|
18
18
|
} from "../../components/Filter/components/Filters/filters";
|
|
19
19
|
import { SearchAllFilters } from "../../components/Filter/components/SearchAllFilters/searchAllFilters";
|
|
20
|
-
import { ChartView } from "../../components/Index/components/EntitiesView/components/ChartView/chartView";
|
|
21
|
-
import { EntityList } from "../../components/Index/components/EntitiesView/components/EntityList/entityList";
|
|
22
20
|
import { Tabs } from "../../components/Index/components/Tabs/tabs";
|
|
23
21
|
import { Index as IndexView } from "../../components/Index/index";
|
|
24
22
|
import { SidebarButton } from "../../components/Layout/components/Sidebar/components/SidebarButton/sidebarButton";
|
|
@@ -165,14 +163,10 @@ export const ExploreView = (props: ExploreViewProps): JSX.Element => {
|
|
|
165
163
|
)}
|
|
166
164
|
<IndexView
|
|
167
165
|
className={props.className}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
loading={loading}
|
|
173
|
-
/>
|
|
174
|
-
}
|
|
175
|
-
list={<EntityList entityListType={entityListType} />}
|
|
166
|
+
categoryFilters={categoryFilters}
|
|
167
|
+
entityListType={entityListType}
|
|
168
|
+
entityName={label}
|
|
169
|
+
loading={loading}
|
|
176
170
|
ListHero={renderComponent(listHero)}
|
|
177
171
|
SideBarButton={
|
|
178
172
|
tabletDown ? (
|