@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
|
@@ -1,127 +1,35 @@
|
|
|
1
1
|
import { TableContainer } from "@mui/material";
|
|
2
|
-
import
|
|
3
|
-
import React, { Fragment, useCallback, useEffect, useMemo } from "react";
|
|
2
|
+
import React, { Fragment } from "react";
|
|
4
3
|
import { track } from "../../common/analytics/analytics";
|
|
5
|
-
import { EVENT_NAME, EVENT_PARAM, PAGINATION_DIRECTION,
|
|
4
|
+
import { EVENT_NAME, EVENT_PARAM, PAGINATION_DIRECTION, } from "../../common/analytics/entities";
|
|
6
5
|
import { BREAKPOINT_FN_NAME, useBreakpointHelper, } from "../../hooks/useBreakpointHelper";
|
|
7
6
|
import { useExploreMode } from "../../hooks/useExploreMode/useExploreMode";
|
|
8
7
|
import { useExploreState } from "../../hooks/useExploreState";
|
|
9
8
|
import { useScroll } from "../../hooks/useScroll";
|
|
10
9
|
import { ExploreActionKind } from "../../providers/exploreState";
|
|
11
|
-
import { DEFAULT_PAGINATION_STATE } from "../../providers/exploreState/initializer/constants";
|
|
12
10
|
import { TABLET } from "../../theme/common/breakpoints";
|
|
13
11
|
import { Loading, LOADING_PANEL_STYLE } from "../Loading/loading";
|
|
14
12
|
import { NoResults } from "../NoResults/noResults";
|
|
15
13
|
import { getColumnTrackSizing } from "../TableCreator/options/columnTrackSizing/utils";
|
|
16
|
-
import { arrIncludesSome } from "./columnDef/columnFilters/filterFn";
|
|
17
14
|
import { ROW_DIRECTION } from "./common/entities";
|
|
18
|
-
import {
|
|
15
|
+
import { isClientFilteringEnabled } from "./common/utils";
|
|
19
16
|
import { Pagination as DXPagination } from "./components/Pagination/pagination";
|
|
20
17
|
import { TableBody } from "./components/TableBody/tableBody";
|
|
21
18
|
import { TableHead } from "./components/TableHead/tableHead";
|
|
22
19
|
import { TableToolbar } from "./components/TableToolbar/tableToolbar";
|
|
23
|
-
import { ROW_POSITION } from "./features/RowPosition/constants";
|
|
24
|
-
import { ROW_PREVIEW } from "./features/RowPreview/constants";
|
|
25
20
|
import { GridTable } from "./table.styles";
|
|
26
|
-
|
|
27
|
-
* This table can be Controlled or Uncontrolled based on the set of props passed to it.
|
|
28
|
-
* Controlled table will receive the navigation functions, and it will be used for dynamic loads.
|
|
29
|
-
* Uncontrolled table will take advantage of React Table's state and will be used for static loads.
|
|
30
|
-
* @param tableProps - Set of props required for displaying the table.
|
|
31
|
-
* @param tableProps.columns - Set of columns to display.
|
|
32
|
-
* @param tableProps.getRowId - Function to customize the row ID.
|
|
33
|
-
* @param tableProps.items - Row data to display.
|
|
34
|
-
* @param tableProps.listView - List view configuration.
|
|
35
|
-
* @param tableProps.tableOptions - TanStack table options.
|
|
36
|
-
* @returns Configured table element for display.
|
|
37
|
-
*/
|
|
38
|
-
export const TableComponent = ({ columns, getRowId, items, listView, tableOptions, }) => {
|
|
21
|
+
export const TableComponent = ({ listView, loading, table, }) => {
|
|
39
22
|
const tabletDown = useBreakpointHelper(BREAKPOINT_FN_NAME.DOWN, TABLET);
|
|
40
23
|
const exploreMode = useExploreMode();
|
|
41
24
|
const { exploreDispatch, exploreState } = useExploreState();
|
|
42
|
-
const {
|
|
43
|
-
const {
|
|
44
|
-
const { currentPage, pages, pageSize, rows: pageCount } = paginationState;
|
|
25
|
+
const { paginationState } = exploreState;
|
|
26
|
+
const { currentPage, pages } = paginationState;
|
|
45
27
|
const { disablePagination = false } = listView || {};
|
|
46
28
|
const clientFiltering = isClientFilteringEnabled(exploreMode);
|
|
47
29
|
const rowDirection = tabletDown
|
|
48
30
|
? ROW_DIRECTION.VERTICAL
|
|
49
31
|
: ROW_DIRECTION.DEFAULT;
|
|
50
|
-
const
|
|
51
|
-
const onSortingChange = (updater) => {
|
|
52
|
-
// TODO(cc) memoize `onSortingChange` with `useCallback`.
|
|
53
|
-
// TODO(cc) copy `onSortingChange` to ../options/sorting/hook.ts see src/components/Table/options/grouping/hook.ts for example.
|
|
54
|
-
exploreDispatch({
|
|
55
|
-
payload: typeof updater === "function" ? updater(sorting) : updater,
|
|
56
|
-
type: ExploreActionKind.UpdateSorting,
|
|
57
|
-
});
|
|
58
|
-
// Execute GTM tracking.
|
|
59
|
-
// TODO(cc) update tracking to handle sorting of multiple columns.
|
|
60
|
-
// 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.
|
|
61
|
-
track(EVENT_NAME.ENTITY_TABLE_SORTED, {
|
|
62
|
-
[EVENT_PARAM.ENTITY_NAME]: exploreState.tabValue,
|
|
63
|
-
[EVENT_PARAM.COLUMN_NAME]: sorting?.[0]?.id, // TODO(cc) sorting should always be at least `[]` and never `undefined`.
|
|
64
|
-
[EVENT_PARAM.SORT_DIRECTION]: sorting?.[0]?.desc // TODO(cc) sorting should always be at least `[]` and never `undefined`.
|
|
65
|
-
? SORT_DIRECTION.DESC
|
|
66
|
-
: SORT_DIRECTION.ASC,
|
|
67
|
-
});
|
|
68
|
-
};
|
|
69
|
-
const onRowPreviewChange = useCallback((updater) => {
|
|
70
|
-
exploreDispatch({
|
|
71
|
-
payload: typeof updater === "function" ? updater(rowPreview) : updater,
|
|
72
|
-
type: ExploreActionKind.UpdateRowPreview,
|
|
73
|
-
});
|
|
74
|
-
}, [exploreDispatch, rowPreview]);
|
|
75
|
-
const onRowSelectionChange = useCallback((updater) => {
|
|
76
|
-
// TODO(cc) refactor `onRowSelectionChange` to /options/rowSelection/hook.ts see onGroupingChange.
|
|
77
|
-
exploreDispatch({
|
|
78
|
-
payload: typeof updater === "function" ? updater(rowSelection) : updater,
|
|
79
|
-
type: ExploreActionKind.UpdateRowSelection,
|
|
80
|
-
});
|
|
81
|
-
}, [exploreDispatch, rowSelection]);
|
|
82
|
-
const state = {
|
|
83
|
-
columnVisibility,
|
|
84
|
-
grouping,
|
|
85
|
-
pagination,
|
|
86
|
-
rowPreview,
|
|
87
|
-
rowSelection,
|
|
88
|
-
sorting,
|
|
89
|
-
};
|
|
90
|
-
/**
|
|
91
|
-
* TODO: Update `ColumnConfig` to follow the `ColumnDef` API of TanStack Table.
|
|
92
|
-
* - Standardize column definitions to leverage the full power of TanStack Table's feature set and improve compatibility.
|
|
93
|
-
* TODO: Define `sorting` directly within `ListConfig` via the `tableOptions.initialState` property.
|
|
94
|
-
* - This will simplify the configuration structure and centralize table state definitions, reducing redundancy and improving clarity.
|
|
95
|
-
*/
|
|
96
|
-
const tableInstance = useReactTable({
|
|
97
|
-
_features: [ROW_POSITION, ROW_PREVIEW],
|
|
98
|
-
columns,
|
|
99
|
-
data: items,
|
|
100
|
-
enableColumnFilters: true, // client-side filtering.
|
|
101
|
-
enableFilters: true, // client-side filtering.
|
|
102
|
-
enableMultiSort: clientFiltering, // TODO(cc) move to sorting options; default to false and let the table options in config flag this value.
|
|
103
|
-
filterFns: { arrIncludesSome },
|
|
104
|
-
getCoreRowModel: getCoreRowModel(),
|
|
105
|
-
getFacetedRowModel: clientFiltering ? getFacetedRowModel() : undefined,
|
|
106
|
-
getFacetedUniqueValues: clientFiltering
|
|
107
|
-
? getFacetedUniqueValuesWithArrayValues()
|
|
108
|
-
: undefined,
|
|
109
|
-
getFilteredRowModel: clientFiltering ? getFilteredRowModel() : undefined,
|
|
110
|
-
getPaginationRowModel: getPaginationRowModel(),
|
|
111
|
-
getRowId,
|
|
112
|
-
getSortedRowModel: clientFiltering ? getSortedRowModel() : undefined,
|
|
113
|
-
manualPagination: true,
|
|
114
|
-
manualSorting: !clientFiltering,
|
|
115
|
-
onRowPreviewChange,
|
|
116
|
-
onRowSelectionChange,
|
|
117
|
-
onSortingChange,
|
|
118
|
-
pageCount,
|
|
119
|
-
state,
|
|
120
|
-
...tableOptions,
|
|
121
|
-
});
|
|
122
|
-
const { getAllColumns, getRowModel, getState, getVisibleFlatColumns, nextPage: tableNextPage, previousPage: tablePreviousPage, } = tableInstance;
|
|
123
|
-
const allColumns = getAllColumns();
|
|
124
|
-
const { columnFilters } = getState();
|
|
32
|
+
const { getRowModel, getVisibleFlatColumns, nextPage: tableNextPage, previousPage: tablePreviousPage, } = table;
|
|
125
33
|
const { rows } = getRowModel();
|
|
126
34
|
const noResults = !loading && (!rows || rows.length === 0);
|
|
127
35
|
const scrollTop = useScroll();
|
|
@@ -160,42 +68,6 @@ export const TableComponent = ({ columns, getRowId, items, listView, tableOption
|
|
|
160
68
|
previousPage();
|
|
161
69
|
scrollTop();
|
|
162
70
|
};
|
|
163
|
-
// Sets react table column filters `columnFilters` state - for client-side filtering only - with update of filterState.
|
|
164
|
-
useEffect(() => {
|
|
165
|
-
if (clientFiltering) {
|
|
166
|
-
tableInstance.setColumnFilters(filterState.map(({ categoryKey, value }) => ({
|
|
167
|
-
id: categoryKey,
|
|
168
|
-
value,
|
|
169
|
-
})));
|
|
170
|
-
}
|
|
171
|
-
}, [clientFiltering, filterState, tableInstance]);
|
|
172
|
-
// Process explore response - client-side filtering only.
|
|
173
|
-
useEffect(() => {
|
|
174
|
-
if (!listItems || listItems.length === 0)
|
|
175
|
-
return;
|
|
176
|
-
if (clientFiltering) {
|
|
177
|
-
exploreDispatch({
|
|
178
|
-
payload: {
|
|
179
|
-
listItems,
|
|
180
|
-
loading: false,
|
|
181
|
-
paginationResponse: {
|
|
182
|
-
...DEFAULT_PAGINATION_STATE,
|
|
183
|
-
pageSize: rows.filter(({ getIsGrouped }) => !getIsGrouped()).length,
|
|
184
|
-
rows: rows.filter(({ getIsGrouped }) => !getIsGrouped()).length,
|
|
185
|
-
},
|
|
186
|
-
selectCategories: buildCategoryViews(allColumns, columnFilters),
|
|
187
|
-
},
|
|
188
|
-
type: ExploreActionKind.ProcessExploreResponse,
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
}, [
|
|
192
|
-
allColumns,
|
|
193
|
-
clientFiltering,
|
|
194
|
-
columnFilters,
|
|
195
|
-
exploreDispatch,
|
|
196
|
-
listItems,
|
|
197
|
-
rows,
|
|
198
|
-
]);
|
|
199
71
|
function canNextPage() {
|
|
200
72
|
return currentPage < pages;
|
|
201
73
|
}
|
|
@@ -203,12 +75,12 @@ export const TableComponent = ({ columns, getRowId, items, listView, tableOption
|
|
|
203
75
|
return currentPage > 1;
|
|
204
76
|
}
|
|
205
77
|
return noResults ? (React.createElement(NoResults, { Paper: null, title: "No Results found" })) : (React.createElement(Fragment, null,
|
|
206
|
-
React.createElement(TableToolbar, { listView: listView, rowDirection: rowDirection, tableInstance:
|
|
78
|
+
React.createElement(TableToolbar, { listView: listView, rowDirection: rowDirection, tableInstance: table }),
|
|
207
79
|
React.createElement(Loading, { appear: false, autoPosition: false, loading: loading, panelStyle: LOADING_PANEL_STYLE.INHERIT }),
|
|
208
80
|
React.createElement(TableContainer, null,
|
|
209
81
|
React.createElement(GridTable, { collapsable: true, gridTemplateColumns: getColumnTrackSizing(getVisibleFlatColumns()) },
|
|
210
|
-
React.createElement(TableHead, { rowDirection: rowDirection, tableInstance:
|
|
211
|
-
React.createElement(TableBody, { rows: rows, rowDirection: rowDirection, tableInstance:
|
|
82
|
+
React.createElement(TableHead, { rowDirection: rowDirection, tableInstance: table }),
|
|
83
|
+
React.createElement(TableBody, { rows: rows, rowDirection: rowDirection, tableInstance: table }))),
|
|
212
84
|
!disablePagination && (React.createElement(DXPagination, { canNextPage: canNextPage(), canPreviousPage: canPreviousPage(), currentPage: currentPage, onNextPage: handleTableNextPage, onPreviousPage: handleTablePreviousPage, totalPage: pages ?? 0 }))));
|
|
213
85
|
};
|
|
214
86
|
// TODO(Dave) review whether memo is necessary - flash between tabs / loading state.
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { RowData, Table as TanStackTable } from "@tanstack/react-table";
|
|
2
|
+
import { ListViewConfig } from "../../config/entities";
|
|
3
3
|
export interface TableCreatorProps<T> {
|
|
4
|
-
columns: ColumnConfig<T>[];
|
|
5
|
-
getRowId?: CoreOptions<T>["getRowId"];
|
|
6
|
-
items: T[];
|
|
7
4
|
listView?: ListViewConfig;
|
|
8
|
-
loading
|
|
5
|
+
loading: boolean;
|
|
6
|
+
table: TanStackTable<T>;
|
|
9
7
|
}
|
|
10
|
-
export declare const TableCreator: <T extends RowData>({
|
|
8
|
+
export declare const TableCreator: <T extends RowData>({ listView, loading, table, }: TableCreatorProps<T>) => JSX.Element;
|
|
@@ -1,37 +1,5 @@
|
|
|
1
|
-
import React
|
|
2
|
-
import { ComponentCreator } from "../ComponentCreator/ComponentCreator";
|
|
3
|
-
import { COLUMN_DEF } from "../Table/common/columnDef";
|
|
4
|
-
import { sortingFn } from "../Table/common/utils";
|
|
1
|
+
import React from "react";
|
|
5
2
|
import { Table } from "../Table/table";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const createCell = (config) => function CellCreator(cellContext) {
|
|
9
|
-
return (React.createElement(ComponentCreator, { components: [config.componentConfig], response: cellContext.row.original, viewContext: { cellContext } }));
|
|
10
|
-
};
|
|
11
|
-
export const TableCreator = ({ columns, getRowId, items, listView, loading, }) => {
|
|
12
|
-
const tableOptions = useTableOptions();
|
|
13
|
-
const columnDefs = useMemo(() => columns.reduce((acc, {
|
|
14
|
-
/**
|
|
15
|
-
* Applies the custom `arrIncludesSome` filter function as the default for multi-value filtering.
|
|
16
|
-
* Although `ColumnFilter["value"]` is typed as `unknown`, in practice it's consistently an array (`unknown[]`) in entity lists.
|
|
17
|
-
* This custom filter function supports multi-select filtering, even when individual cell values are single strings.
|
|
18
|
-
* This override of TanStack's default `arrIncludesSome` resolves a limitation where the base implementation
|
|
19
|
-
* does not support matching an array of filter values against a single string cell value.
|
|
20
|
-
* For range filtering, specify TanStack's `inNumberRange` filter function on the column definition.
|
|
21
|
-
*/
|
|
22
|
-
filterFn = "arrIncludesSome", ...columnConfig }) => {
|
|
23
|
-
acc.push({
|
|
24
|
-
...buildBaseColumnDef(columnConfig),
|
|
25
|
-
cell: createCell(columnConfig),
|
|
26
|
-
filterFn,
|
|
27
|
-
sortingFn: sortingFn,
|
|
28
|
-
});
|
|
29
|
-
return acc;
|
|
30
|
-
}, [
|
|
31
|
-
/* Initialize column definitions with the "row position" column */
|
|
32
|
-
COLUMN_DEF.ROW_POSITION,
|
|
33
|
-
/* Initialize column definitions with the "row selection" column */
|
|
34
|
-
COLUMN_DEF.ROW_SELECTION,
|
|
35
|
-
]), [columns]);
|
|
36
|
-
return (React.createElement(Table, { columns: columnDefs, getRowId: getRowId, items: items, listView: listView, loading: loading, tableOptions: tableOptions }));
|
|
3
|
+
export const TableCreator = ({ listView, loading, table, }) => {
|
|
4
|
+
return React.createElement(Table, { listView: listView, loading: loading, table: table });
|
|
37
5
|
};
|
|
@@ -5,8 +5,6 @@ import { ComponentCreator } from "../../components/ComponentCreator/ComponentCre
|
|
|
5
5
|
import { ClearAllFilters } from "../../components/Filter/components/ClearAllFilters/clearAllFilters";
|
|
6
6
|
import { Filters, } from "../../components/Filter/components/Filters/filters";
|
|
7
7
|
import { SearchAllFilters } from "../../components/Filter/components/SearchAllFilters/searchAllFilters";
|
|
8
|
-
import { ChartView } from "../../components/Index/components/EntitiesView/components/ChartView/chartView";
|
|
9
|
-
import { EntityList } from "../../components/Index/components/EntitiesView/components/EntityList/entityList";
|
|
10
8
|
import { Tabs } from "../../components/Index/components/Tabs/tabs";
|
|
11
9
|
import { Index as IndexView } from "../../components/Index/index";
|
|
12
10
|
import { SidebarButton } from "../../components/Layout/components/Sidebar/components/SidebarButton/sidebarButton";
|
|
@@ -105,7 +103,7 @@ export const ExploreView = (props) => {
|
|
|
105
103
|
React.createElement(ClearAllFilters, null),
|
|
106
104
|
React.createElement(SearchAllFilters, { categoryViews: categoryViews, drawerOpen: isDrawerOpen, onFilter: onFilterChange.bind(null, true) })),
|
|
107
105
|
React.createElement(Filters, { categoryFilters: categoryFilters, closeAncestor: onCloseDrawer, onFilter: onFilterChange.bind(null, false), trackFilterOpened: trackingConfig?.trackFilterOpened }))),
|
|
108
|
-
React.createElement(IndexView, { className: props.className,
|
|
106
|
+
React.createElement(IndexView, { className: props.className, categoryFilters: categoryFilters, entityListType: entityListType, entityName: label, loading: loading, ListHero: renderComponent(listHero), SideBarButton: tabletDown ? (React.createElement(SidebarButton, { count: filterCount, label: "Filter", onClick: onOpenDrawer })) : undefined, SubTitleHero: renderComponent(subTitleHero), Summaries: renderSummary(summaryConfig, summaryResponse), Tabs: React.createElement(Tabs, null), title: entityConfig.explorerTitle || explorerTitle })));
|
|
109
107
|
};
|
|
110
108
|
/**
|
|
111
109
|
* Builds the category views into category views grouped by the given category group configuration.
|
package/package.json
CHANGED
|
@@ -28,7 +28,7 @@ export const ChartView = ({
|
|
|
28
28
|
{selectCategoryViews.map(({ key, label, values }) => (
|
|
29
29
|
<StyledGridPaperSection key={key}>
|
|
30
30
|
<Typography variant={TYPOGRAPHY_PROPS.VARIANT.TEXT_HEADING_SMALL}>
|
|
31
|
-
{entityName}
|
|
31
|
+
{entityName} by {label}
|
|
32
32
|
</Typography>
|
|
33
33
|
<Chart selectCategoryValueViews={values} width={width} />
|
|
34
34
|
</StyledGridPaperSection>
|
|
@@ -5,9 +5,11 @@ import { PALETTE } from "../../../../../../../../../styles/common/constants/pale
|
|
|
5
5
|
import { formatCountSize } from "../../../../../../../../../utils/formatCountSize";
|
|
6
6
|
import { DATA_FIELD, MARGIN_LEFT, TEXT_PADDING } from "./constants";
|
|
7
7
|
import {
|
|
8
|
+
getCategoryTotalCount,
|
|
8
9
|
getCategoryValueText,
|
|
9
10
|
getCategoryValueTextFill,
|
|
10
11
|
getColorRangeValue,
|
|
12
|
+
getCountText,
|
|
11
13
|
getCountTextFill,
|
|
12
14
|
getPlotHeight,
|
|
13
15
|
getTicks,
|
|
@@ -23,6 +25,7 @@ export function getPlotOptions(
|
|
|
23
25
|
width: number
|
|
24
26
|
): PlotOptions {
|
|
25
27
|
const isCategorySelected = isAnyValueSelected(selectCategoryValueViews);
|
|
28
|
+
const totalCount = getCategoryTotalCount(selectCategoryValueViews);
|
|
26
29
|
return {
|
|
27
30
|
color: {
|
|
28
31
|
domain: [false, true], // false = unselected, true = selected.
|
|
@@ -77,7 +80,7 @@ export function getPlotOptions(
|
|
|
77
80
|
fontWeight: 500,
|
|
78
81
|
lineHeight: 0.8125,
|
|
79
82
|
render: renderText,
|
|
80
|
-
text:
|
|
83
|
+
text: (d) => getCountText(d, totalCount),
|
|
81
84
|
textAnchor: "end",
|
|
82
85
|
x: DATA_FIELD.COUNT,
|
|
83
86
|
y: DATA_FIELD.LABEL,
|
|
@@ -15,6 +15,17 @@ import {
|
|
|
15
15
|
TICKS,
|
|
16
16
|
} from "./constants";
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Calculates the total count of all category values from the provided array of category value views.
|
|
20
|
+
* @param selectCategoryValueViews - An array of objects representing category values, where each object contains a count property.
|
|
21
|
+
* @returns The total sum of the count property from all category value objects in the array.
|
|
22
|
+
*/
|
|
23
|
+
export function getCategoryTotalCount(
|
|
24
|
+
selectCategoryValueViews: SelectCategoryValueView[]
|
|
25
|
+
): number {
|
|
26
|
+
return selectCategoryValueViews.reduce((acc, { count }) => acc + count, 0);
|
|
27
|
+
}
|
|
28
|
+
|
|
18
29
|
/**
|
|
19
30
|
* Returns the text for the category value point.
|
|
20
31
|
* @param d - Data point.
|
|
@@ -49,6 +60,38 @@ export function getColorRangeValue(isCategorySelected: boolean): string {
|
|
|
49
60
|
return isCategorySelected ? PALETTE.SMOKE_LIGHT : "#C5E3FC";
|
|
50
61
|
}
|
|
51
62
|
|
|
63
|
+
/**
|
|
64
|
+
* Calculates the percentage representation of a count value relative to a total.
|
|
65
|
+
* @param d - Data point containing the count value to calculate the percentage for.
|
|
66
|
+
* @param total - The total value used as the denominator to compute the percentage.
|
|
67
|
+
* @returns The calculated percentage value, rounded to one decimal place.
|
|
68
|
+
*/
|
|
69
|
+
export function getCountPercentage(
|
|
70
|
+
d: SelectCategoryValueView,
|
|
71
|
+
total: number
|
|
72
|
+
): string {
|
|
73
|
+
if (total === 0) return "0";
|
|
74
|
+
const percentage = (d.count / total) * 100;
|
|
75
|
+
const roundedPercentage = Math.round(percentage * 10) / 10;
|
|
76
|
+
if (roundedPercentage < 0.1) return "< 0.1";
|
|
77
|
+
// Round to one decimal place.
|
|
78
|
+
return roundedPercentage.toFixed(1);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Returns the text for the count point.
|
|
83
|
+
* Includes the count and its percentage of the total.
|
|
84
|
+
* @param d - Data point.
|
|
85
|
+
* @param total - Total count.
|
|
86
|
+
* @returns Text string.
|
|
87
|
+
*/
|
|
88
|
+
export function getCountText(
|
|
89
|
+
d: SelectCategoryValueView,
|
|
90
|
+
total: number
|
|
91
|
+
): string {
|
|
92
|
+
return `${d.count.toLocaleString()} (${getCountPercentage(d, total)}%)`;
|
|
93
|
+
}
|
|
94
|
+
|
|
52
95
|
/**
|
|
53
96
|
* Returns the fill color for the count point.
|
|
54
97
|
* @param d - Data point.
|
|
@@ -1,29 +1,22 @@
|
|
|
1
|
+
import { RowData } from "@tanstack/react-table";
|
|
1
2
|
import React from "react";
|
|
2
3
|
import { useConfig } from "../../../../../../hooks/useConfig";
|
|
3
4
|
import { useExploreState } from "../../../../../../hooks/useExploreState";
|
|
4
5
|
import { TableCreator } from "../../../../../TableCreator/tableCreator";
|
|
5
6
|
import { EntityListProps } from "./types";
|
|
6
7
|
|
|
7
|
-
export const EntityList = ({
|
|
8
|
+
export const EntityList = <T extends RowData>({
|
|
8
9
|
entityListType,
|
|
9
|
-
|
|
10
|
+
loading,
|
|
11
|
+
table,
|
|
12
|
+
}: EntityListProps<T>): JSX.Element | null => {
|
|
10
13
|
const { entityConfig } = useConfig();
|
|
11
14
|
const { exploreState } = useExploreState();
|
|
12
|
-
const {
|
|
13
|
-
const { getId: getRowId, list, listView } = entityConfig;
|
|
14
|
-
const { columns: columnsConfig } = list;
|
|
15
|
+
const { listView } = entityConfig;
|
|
15
16
|
|
|
16
17
|
// required currently for client-side fetching as the pre-rendered page
|
|
17
18
|
// loads with the previous tabs data on the first render after switching tabs. (or similar)
|
|
18
19
|
if (entityListType !== exploreState.tabValue) return null;
|
|
19
20
|
|
|
20
|
-
return
|
|
21
|
-
<TableCreator
|
|
22
|
-
columns={columnsConfig}
|
|
23
|
-
getRowId={getRowId}
|
|
24
|
-
items={listItems ?? []}
|
|
25
|
-
listView={listView}
|
|
26
|
-
loading={loading}
|
|
27
|
-
/>
|
|
28
|
-
);
|
|
21
|
+
return <TableCreator listView={listView} loading={loading} table={table} />;
|
|
29
22
|
};
|
|
@@ -1,19 +1,15 @@
|
|
|
1
1
|
import { Meta, StoryObj } from "@storybook/react";
|
|
2
|
-
import { Summaries } from "
|
|
2
|
+
import { Summaries } from "../summaries";
|
|
3
3
|
|
|
4
|
-
const meta = {
|
|
5
|
-
argTypes: {
|
|
6
|
-
summaries: { control: "object" },
|
|
7
|
-
},
|
|
4
|
+
const meta: Meta<typeof Summaries> = {
|
|
8
5
|
component: Summaries,
|
|
9
|
-
|
|
10
|
-
} satisfies Meta<typeof Summaries>;
|
|
6
|
+
};
|
|
11
7
|
|
|
12
8
|
export default meta;
|
|
13
9
|
|
|
14
10
|
type Story = StoryObj<typeof meta>;
|
|
15
11
|
|
|
16
|
-
export const
|
|
12
|
+
export const Default: Story = {
|
|
17
13
|
args: {
|
|
18
14
|
summaries: [
|
|
19
15
|
{ count: "1", label: "Species" },
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { composeStories, Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import * as summaryStories from "../components/Summaries/stories/summaries.stories";
|
|
4
|
+
import { Hero } from "../hero";
|
|
5
|
+
|
|
6
|
+
const { Default: Summaries } = composeStories(summaryStories);
|
|
7
|
+
|
|
8
|
+
const meta: Meta<typeof Hero> = {
|
|
9
|
+
component: Hero,
|
|
10
|
+
parameters: { layout: "fullscreen" },
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export default meta;
|
|
14
|
+
|
|
15
|
+
type Story = StoryObj<typeof meta>;
|
|
16
|
+
|
|
17
|
+
export const Default: Story = {
|
|
18
|
+
args: {
|
|
19
|
+
Summaries: <Summaries />,
|
|
20
|
+
title: "Data Explorer",
|
|
21
|
+
},
|
|
22
|
+
};
|
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { useLayoutDimensions } from "../../providers/layoutDimensions/hook";
|
|
3
|
+
import { ChartView } from "./components/EntitiesView/components/ChartView/chartView";
|
|
4
|
+
import { EntityList } from "./components/EntitiesView/components/EntityList/entityList";
|
|
3
5
|
import { EntitiesView } from "./components/EntitiesView/entitiesView";
|
|
4
6
|
import { useEntitiesView } from "./components/EntitiesView/hooks/UseEntitiesView/hook";
|
|
5
7
|
import { VIEW_MODE } from "./components/EntitiesView/hooks/UseEntitiesView/types";
|
|
6
8
|
import { Hero } from "./components/Hero/hero";
|
|
7
9
|
import { Index as IndexLayout } from "./index.styles";
|
|
10
|
+
import { useTable } from "./table/hook";
|
|
8
11
|
import { IndexProps } from "./types";
|
|
9
12
|
|
|
10
13
|
export const Index = ({
|
|
11
|
-
|
|
14
|
+
categoryFilters,
|
|
12
15
|
className,
|
|
13
|
-
|
|
16
|
+
entityListType,
|
|
17
|
+
entityName,
|
|
14
18
|
ListHero,
|
|
19
|
+
loading,
|
|
15
20
|
SideBarButton,
|
|
16
21
|
SubTitleHero,
|
|
17
22
|
Summaries,
|
|
@@ -20,6 +25,7 @@ export const Index = ({
|
|
|
20
25
|
}: IndexProps): JSX.Element => {
|
|
21
26
|
const { onChange, viewMode, viewStatus } = useEntitiesView();
|
|
22
27
|
const { dimensions } = useLayoutDimensions();
|
|
28
|
+
const { table } = useTable();
|
|
23
29
|
return (
|
|
24
30
|
<IndexLayout className={className} marginTop={dimensions.header.height}>
|
|
25
31
|
<Hero SideBarButton={SideBarButton} Summaries={Summaries} title={title} />
|
|
@@ -31,7 +37,19 @@ export const Index = ({
|
|
|
31
37
|
viewMode={viewMode}
|
|
32
38
|
viewStatus={viewStatus}
|
|
33
39
|
>
|
|
34
|
-
{viewMode === VIEW_MODE.TABLE ?
|
|
40
|
+
{viewMode === VIEW_MODE.TABLE ? (
|
|
41
|
+
<EntityList
|
|
42
|
+
entityListType={entityListType}
|
|
43
|
+
loading={loading}
|
|
44
|
+
table={table}
|
|
45
|
+
/>
|
|
46
|
+
) : (
|
|
47
|
+
<ChartView
|
|
48
|
+
categoryFilters={categoryFilters}
|
|
49
|
+
entityName={entityName}
|
|
50
|
+
loading={loading}
|
|
51
|
+
/>
|
|
52
|
+
)}
|
|
35
53
|
</EntitiesView>
|
|
36
54
|
</IndexLayout>
|
|
37
55
|
);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { composeStories, Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import * as summaryStories from "../components/Hero/components/Summaries/stories/summaries.stories";
|
|
4
|
+
import { Index } from "../index";
|
|
5
|
+
|
|
6
|
+
const { Default: Summaries } = composeStories(summaryStories);
|
|
7
|
+
|
|
8
|
+
const meta: Meta<typeof Index> = {
|
|
9
|
+
component: Index,
|
|
10
|
+
parameters: { layout: "fullscreen" },
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export default meta;
|
|
14
|
+
|
|
15
|
+
type Story = StoryObj<typeof meta>;
|
|
16
|
+
|
|
17
|
+
export const Default: Story = {
|
|
18
|
+
args: {
|
|
19
|
+
Summaries: <Summaries />,
|
|
20
|
+
Tabs: undefined,
|
|
21
|
+
title: "Explore Data",
|
|
22
|
+
},
|
|
23
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { CellContext, RowData } from "@tanstack/react-table";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { ComponentCreator } from "../../../../../components/ComponentCreator/ComponentCreator";
|
|
4
|
+
import { ColumnConfig } from "../../../../../config/entities";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Creates a cell renderer function for a given column configuration.
|
|
8
|
+
* This factory pattern allows us to create cell renderers without defining JSX in hook files.
|
|
9
|
+
* @param config - The column configuration.
|
|
10
|
+
* @returns A function that renders the cell content.
|
|
11
|
+
*/
|
|
12
|
+
export function createCell<T extends RowData>(config: ColumnConfig<T>) {
|
|
13
|
+
return function CellCreator(context: CellContext<T, unknown>): JSX.Element {
|
|
14
|
+
return (
|
|
15
|
+
<ComponentCreator
|
|
16
|
+
components={[config.componentConfig]}
|
|
17
|
+
response={context.row.original}
|
|
18
|
+
viewContext={{ cellContext: context }}
|
|
19
|
+
/>
|
|
20
|
+
);
|
|
21
|
+
};
|
|
22
|
+
}
|