@databiosphere/findable-ui 44.0.0 → 45.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.
Files changed (95) hide show
  1. package/.release-please-manifest.json +1 -1
  2. package/CHANGELOG.md +29 -0
  3. package/lib/components/DataDictionary/components/Table/options/core/constants.js +2 -1
  4. package/lib/components/DataDictionary/components/Table/options/hook.js +2 -0
  5. package/lib/components/Detail/components/Table/common/utils.js +1 -1
  6. package/lib/components/Detail/components/Table/components/TableRows/components/CollapsableRows/collapsableRows.js +1 -1
  7. package/lib/components/Detail/components/Table/components/TableRows/tableRows.js +1 -1
  8. package/lib/components/Detail/components/Table/table.js +10 -1
  9. package/lib/components/Index/table/hook.js +10 -1
  10. package/lib/components/Table/common/columnDef.js +2 -0
  11. package/lib/components/Table/common/utils.d.ts +0 -6
  12. package/lib/components/Table/common/utils.js +0 -63
  13. package/lib/components/Table/components/TableCell/components/RowSelectionCell/constants.d.ts +2 -0
  14. package/lib/components/Table/components/TableCell/components/RowSelectionCell/constants.js +13 -0
  15. package/lib/components/Table/components/TableCell/components/RowSelectionCell/rowSelectionCell.js +12 -8
  16. package/lib/components/Table/components/TableFeatures/TableDownload/tableDownload.d.ts +3 -0
  17. package/lib/components/Table/components/TableFeatures/TableDownload/tableDownload.js +9 -0
  18. package/lib/components/Table/components/{DownloadEntityResults/downloadEntityResults.styles.js → TableFeatures/TableDownload/tableDownload.styles.js} +1 -1
  19. package/lib/components/Table/components/TableFeatures/TableDownload/types.d.ts +5 -0
  20. package/lib/components/Table/components/TableFeatures/TableDownload/types.js +1 -0
  21. package/lib/components/Table/components/TableRow/tableRow.styles.d.ts +2 -0
  22. package/lib/components/Table/components/TableRow/tableRow.styles.js +15 -1
  23. package/lib/components/Table/components/TableRows/components/CollapsableRows/collapsableRows.js +1 -1
  24. package/lib/components/Table/components/TableRows/tableRows.js +1 -1
  25. package/lib/components/Table/components/TableToolbar/tableToolbar.js +3 -3
  26. package/lib/components/Table/components/TableToolbar2/tableToolbar2.d.ts +3 -0
  27. package/lib/components/Table/components/TableToolbar2/tableToolbar2.js +8 -0
  28. package/lib/components/Table/components/TableToolbar2/tableToolbar2.styles.d.ts +10 -0
  29. package/lib/components/Table/components/TableToolbar2/tableToolbar2.styles.js +16 -0
  30. package/lib/components/Table/components/TableToolbar2/types.d.ts +4 -0
  31. package/lib/components/Table/components/TableToolbar2/types.js +1 -0
  32. package/lib/components/Table/featureOptions/tableDownload/constants.d.ts +3 -0
  33. package/lib/components/Table/featureOptions/tableDownload/constants.js +3 -0
  34. package/lib/components/Table/features/RowSelectionValidation/constants.d.ts +2 -0
  35. package/lib/components/Table/features/RowSelectionValidation/constants.js +14 -0
  36. package/lib/components/Table/features/RowSelectionValidation/types.d.ts +8 -0
  37. package/lib/components/Table/features/RowSelectionValidation/types.js +1 -0
  38. package/lib/components/Table/features/RowSelectionValidation/utils.d.ts +8 -0
  39. package/lib/components/Table/features/RowSelectionValidation/utils.js +11 -0
  40. package/lib/components/Table/features/TableDownload/constants.d.ts +2 -0
  41. package/lib/components/Table/features/TableDownload/constants.js +10 -0
  42. package/lib/components/Table/features/TableDownload/onDownload/utils.d.ts +14 -0
  43. package/lib/components/Table/features/TableDownload/onDownload/utils.js +63 -0
  44. package/lib/components/Table/features/TableDownload/types.d.ts +15 -0
  45. package/lib/components/Table/features/TableDownload/types.js +1 -0
  46. package/lib/components/Table/features/TableDownload/utils.d.ts +19 -0
  47. package/lib/components/Table/features/TableDownload/utils.js +46 -0
  48. package/lib/components/Table/features/entities.d.ts +9 -3
  49. package/lib/components/TableCreator/options/hook.js +0 -3
  50. package/lib/components/common/CustomIcon/components/UncheckedDisabledIcon/uncheckedDisabledIcon.d.ts +2 -0
  51. package/lib/components/common/CustomIcon/components/UncheckedDisabledIcon/uncheckedDisabledIcon.js +8 -0
  52. package/lib/config/entities.d.ts +0 -1
  53. package/package.json +1 -1
  54. package/src/components/DataDictionary/components/Table/options/core/constants.ts +2 -1
  55. package/src/components/DataDictionary/components/Table/options/hook.ts +2 -0
  56. package/src/components/Detail/components/Table/common/utils.ts +1 -1
  57. package/src/components/Detail/components/Table/components/TableRows/components/CollapsableRows/collapsableRows.tsx +1 -0
  58. package/src/components/Detail/components/Table/components/TableRows/tableRows.tsx +2 -0
  59. package/src/components/Detail/components/Table/table.tsx +10 -1
  60. package/src/components/Index/table/hook.ts +10 -1
  61. package/src/components/Table/common/columnDef.ts +2 -0
  62. package/src/components/Table/common/utils.ts +0 -75
  63. package/src/components/Table/components/TableCell/components/RowSelectionCell/constants.ts +15 -0
  64. package/src/components/Table/components/TableCell/components/RowSelectionCell/rowSelectionCell.tsx +25 -13
  65. package/src/components/Table/components/{DownloadEntityResults/downloadEntityResults.styles.ts → TableFeatures/TableDownload/tableDownload.styles.ts} +1 -1
  66. package/src/components/Table/components/TableFeatures/TableDownload/tableDownload.tsx +23 -0
  67. package/src/components/Table/components/TableFeatures/TableDownload/types.ts +7 -0
  68. package/src/components/Table/components/TableRow/tableRow.styles.ts +19 -1
  69. package/src/components/Table/components/TableRows/components/CollapsableRows/collapsableRows.tsx +1 -0
  70. package/src/components/Table/components/TableRows/tableRows.tsx +2 -0
  71. package/src/components/Table/components/TableToolbar/tableToolbar.tsx +3 -8
  72. package/src/components/Table/components/TableToolbar2/tableToolbar2.styles.ts +18 -0
  73. package/src/components/Table/components/TableToolbar2/tableToolbar2.tsx +17 -0
  74. package/src/components/Table/components/TableToolbar2/types.ts +5 -0
  75. package/src/components/Table/featureOptions/tableDownload/constants.ts +9 -0
  76. package/src/components/Table/features/RowSelectionValidation/constants.ts +24 -0
  77. package/src/components/Table/features/RowSelectionValidation/types.ts +10 -0
  78. package/src/components/Table/features/RowSelectionValidation/utils.ts +15 -0
  79. package/src/components/Table/features/TableDownload/constants.ts +24 -0
  80. package/src/components/Table/features/TableDownload/onDownload/utils.ts +76 -0
  81. package/src/components/Table/features/TableDownload/types.ts +19 -0
  82. package/src/components/Table/features/TableDownload/utils.ts +60 -0
  83. package/src/components/Table/features/entities.ts +27 -3
  84. package/src/components/TableCreator/options/hook.ts +0 -3
  85. package/src/components/common/CustomIcon/components/UncheckedDisabledIcon/uncheckedDisabledIcon.tsx +27 -0
  86. package/src/config/entities.ts +0 -1
  87. package/tests/rowSelectionValidation.test.ts +282 -0
  88. package/types/data-explorer-ui.d.ts +16 -2
  89. package/lib/components/Table/components/DownloadEntityResults/downloadEntityResults.d.ts +0 -6
  90. package/lib/components/Table/components/DownloadEntityResults/downloadEntityResults.js +0 -18
  91. package/lib/components/common/Button/components/FileDownloadButton/fileDownloadButton.d.ts +0 -5
  92. package/lib/components/common/Button/components/FileDownloadButton/fileDownloadButton.js +0 -14
  93. package/src/components/Table/components/DownloadEntityResults/downloadEntityResults.tsx +0 -39
  94. package/src/components/common/Button/components/FileDownloadButton/fileDownloadButton.tsx +0 -27
  95. /package/lib/components/Table/components/{DownloadEntityResults/downloadEntityResults.styles.d.ts → TableFeatures/TableDownload/tableDownload.styles.d.ts} +0 -0
@@ -0,0 +1,63 @@
1
+ import { getColumnHeader } from "../../../common/utils";
2
+ /**
3
+ * Format data to TSV string.
4
+ * @param data - Table data.
5
+ * @returns table data formatted into a TSV string.
6
+ */
7
+ export function formatDataToTSV(data) {
8
+ return data
9
+ .map((row) => {
10
+ return row
11
+ .map((value) => {
12
+ // Use empty string in place of undefined and null.
13
+ if (value === undefined || value === null)
14
+ return "";
15
+ // Convert unknown value to string.
16
+ let valueStr;
17
+ // Test if object / array.
18
+ if (typeof value === "object") {
19
+ if (Array.isArray(value)) {
20
+ // Handle array.
21
+ valueStr = value.join(", ") || "";
22
+ }
23
+ else {
24
+ // Handle object.
25
+ valueStr = JSON.stringify(value);
26
+ }
27
+ }
28
+ else {
29
+ // Handle primitive.
30
+ valueStr = String(value);
31
+ }
32
+ // Quote if necessary.
33
+ return /[\t\r\n"]/.test(valueStr)
34
+ ? `"${valueStr.replaceAll('"', '""')}"`
35
+ : valueStr;
36
+ })
37
+ .join("\t");
38
+ })
39
+ .join("\n");
40
+ }
41
+ export function getBlob(table) {
42
+ const rows = table.getRowModel().rows;
43
+ if (rows.length === 0)
44
+ return;
45
+ // Get downloadable columns based on getCanDownload property.
46
+ const downloadableColumns = getDownloadableColumns(table);
47
+ // Extract column IDs for filtering.
48
+ const downloadableColumnIds = downloadableColumns.map((column) => column.id);
49
+ // Get table headers from downloadable columns.
50
+ const tableHeaders = downloadableColumns.map(getColumnHeader);
51
+ // Get row data but only for downloadable columns.
52
+ const tableData = rows.map((row) => downloadableColumnIds.map((columnId) => row.getValue(columnId)));
53
+ const tsv = formatDataToTSV([tableHeaders, ...tableData]);
54
+ return new Blob([tsv], { type: "text/tab-separated-values" });
55
+ }
56
+ /**
57
+ * Filters table columns from column.getCanDownload API.
58
+ * @param table - Table.
59
+ * @returns Columns configured to be downloadable.
60
+ */
61
+ export function getDownloadableColumns(table) {
62
+ return table.getAllColumns().filter((column) => column.getCanTableDownload());
63
+ }
@@ -0,0 +1,15 @@
1
+ import { RowData, Table } from "@tanstack/react-table";
2
+ export interface TableDownloadColumn {
3
+ getCanTableDownload: () => boolean;
4
+ }
5
+ export interface TableDownloadColumnDef {
6
+ enableTableDownload?: boolean;
7
+ }
8
+ export interface TableDownloadInstance {
9
+ downloadData: () => void;
10
+ }
11
+ export interface TableDownloadOptions<T extends RowData> {
12
+ downloadFilename?: string;
13
+ enableTableDownload?: boolean;
14
+ onTableDownload?: (table: Table<T>) => void;
15
+ }
@@ -0,0 +1,19 @@
1
+ import { Column, RowData, Table } from "@tanstack/react-table";
2
+ /**
3
+ * Function that downloads the table data as a TSV file.
4
+ * Uses the onDownload callback, or a custom download function if provided.
5
+ * @param table - Table.
6
+ */
7
+ export declare function downloadData<T extends RowData>(table: Table<T>): void;
8
+ /**
9
+ * Returns true if column can be downloaded.
10
+ * @param column - Column.
11
+ * @param table - Table.
12
+ * @returns True if column can be downloaded.
13
+ */
14
+ export declare function getCanTableDownload<T extends RowData, TValue>(column: Column<T, TValue>, table: Table<T>): boolean;
15
+ /**
16
+ * Default download function that downloads the table data as a TSV file.
17
+ * @param table - Table.
18
+ */
19
+ export declare function onTableDownload<T extends RowData>(table: Table<T>): void;
@@ -0,0 +1,46 @@
1
+ import { getBlob } from "./onDownload/utils";
2
+ /**
3
+ * Function that downloads the table data as a TSV file.
4
+ * Uses the onDownload callback, or a custom download function if provided.
5
+ * @param table - Table.
6
+ */
7
+ export function downloadData(table) {
8
+ table.options.onTableDownload?.(table);
9
+ }
10
+ /**
11
+ * Returns true if column can be downloaded.
12
+ * @param column - Column.
13
+ * @param table - Table.
14
+ * @returns True if column can be downloaded.
15
+ */
16
+ export function getCanTableDownload(column, table) {
17
+ return ((column.columnDef.enableTableDownload ?? true) &&
18
+ (table.options.enableTableDownload ?? false));
19
+ }
20
+ /**
21
+ * Default download function that downloads the table data as a TSV file.
22
+ * @param table - Table.
23
+ */
24
+ export function onTableDownload(table) {
25
+ // Check if download is enabled.
26
+ if (!(table.options.enableTableDownload ?? false))
27
+ return;
28
+ // Generate the blob.
29
+ const blob = getBlob(table);
30
+ if (!blob)
31
+ return;
32
+ // Create an object URL for the blob.
33
+ const url = URL.createObjectURL(blob);
34
+ // Create a download link.
35
+ const anchorEl = document.createElement("a");
36
+ anchorEl.href = url;
37
+ anchorEl.download = table.options.downloadFilename || "table-data.tsv";
38
+ // Append to the document body for better cross-browser support.
39
+ document.body.appendChild(anchorEl);
40
+ // Trigger the download.
41
+ anchorEl.click();
42
+ // Remove the element after triggering download.
43
+ document.body.removeChild(anchorEl);
44
+ // Clean up.
45
+ URL.revokeObjectURL(url);
46
+ }
@@ -1,8 +1,14 @@
1
1
  import { RowData } from "@tanstack/react-table";
2
2
  import { RowPositionOptions, RowPositionRow } from "./RowPosition/types";
3
3
  import { RowPreviewInstance, RowPreviewOptions, RowPreviewRow, RowPreviewTableState } from "./RowPreview/entities";
4
+ import { RowSelectionValidationOptions, RowSelectionValidationRow } from "./RowSelectionValidation/types";
5
+ import { TableDownloadColumn, TableDownloadInstance, TableDownloadOptions } from "./TableDownload/types";
6
+ export type CustomFeatureColumn = TableDownloadColumn;
7
+ export interface CustomFeatureInstance<T extends RowData> extends TableDownloadInstance, RowPreviewInstance<T> {
8
+ }
4
9
  export type CustomFeatureInitialTableState = Partial<RowPreviewTableState>;
5
- export type CustomFeatureInstance<T extends RowData> = RowPreviewInstance<T>;
6
- export type CustomFeatureOptions = RowPreviewOptions & RowPositionOptions;
7
- export type CustomFeatureRow = RowPositionRow & RowPreviewRow;
10
+ export interface CustomFeatureOptions<T extends RowData> extends TableDownloadOptions<T>, RowPositionOptions, RowPreviewOptions, RowSelectionValidationOptions<T> {
11
+ }
12
+ export interface CustomFeatureRow extends RowPositionRow, RowPreviewRow, RowSelectionValidationRow {
13
+ }
8
14
  export type CustomFeatureTableState = RowPreviewTableState;
@@ -2,7 +2,6 @@ import { useConfig } from "../../../hooks/useConfig";
2
2
  import { useExpandedOptions } from "./expanded/hook";
3
3
  import { useGroupingOptions } from "./grouping/hook";
4
4
  import { useInitialState } from "./initialState/hook";
5
- import { useRowSelectionOptions } from "./rowSelection/hook";
6
5
  import { useSortingOptions } from "./sorting/hook";
7
6
  import { useVisibilityOptions } from "./visibility/hook";
8
7
  export function useTableOptions() {
@@ -10,14 +9,12 @@ export function useTableOptions() {
10
9
  const tableOptions = entityConfig.list.tableOptions;
11
10
  const expandedOptions = useExpandedOptions();
12
11
  const groupingOptions = useGroupingOptions();
13
- const rowSelectionOptions = useRowSelectionOptions();
14
12
  const sortingOptions = useSortingOptions();
15
13
  const visibilityOptions = useVisibilityOptions();
16
14
  const initialState = useInitialState(tableOptions);
17
15
  return {
18
16
  ...expandedOptions,
19
17
  ...groupingOptions,
20
- ...rowSelectionOptions,
21
18
  ...sortingOptions, // TODO(cc) merge of all sorting options.
22
19
  ...visibilityOptions,
23
20
  ...tableOptions,
@@ -0,0 +1,2 @@
1
+ import { SvgIconProps } from "@mui/material";
2
+ export declare const UncheckedDisabledIcon: ({ fontSize, viewBox, ...props }: SvgIconProps) => JSX.Element;
@@ -0,0 +1,8 @@
1
+ import { SvgIcon } from "@mui/material";
2
+ import React from "react";
3
+ import { PALETTE } from "../../../../../styles/common/constants/palette";
4
+ export const UncheckedDisabledIcon = ({ fontSize = "xsmall", viewBox = "0 0 18 18", ...props }) => {
5
+ return (React.createElement(SvgIcon, { fontSize: fontSize, viewBox: viewBox, ...props },
6
+ React.createElement("path", { d: "M4 0.5H14C15.933 0.5 17.5 2.067 17.5 4V14C17.5 15.933 15.933 17.5 14 17.5H4C2.067 17.5 0.5 15.933 0.5 14V4C0.5 2.067 2.067 0.5 4 0.5Z", stroke: PALETTE.SMOKE_MAIN }),
7
+ React.createElement("rect", { x: "0.5", y: "0.5", width: "17", height: "17", rx: "3.5", fill: PALETTE.SMOKE_LIGHT, stroke: PALETTE.SMOKE_MAIN })));
8
+ };
@@ -215,7 +215,6 @@ export interface ListConfig<T extends RowData> {
215
215
  */
216
216
  export interface ListViewConfig {
217
217
  disablePagination?: boolean;
218
- enableDownload?: boolean;
219
218
  rowPreviewView?: ComponentsConfig;
220
219
  rowSelectionView?: ComponentsConfig;
221
220
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@databiosphere/findable-ui",
3
- "version": "44.0.0",
3
+ "version": "45.1.0",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
@@ -1,11 +1,12 @@
1
1
  import { CoreOptions, getCoreRowModel, RowData } from "@tanstack/react-table";
2
2
  import { ROW_POSITION } from "../../../../../Table/features/RowPosition/constants";
3
3
  import { ROW_PREVIEW } from "../../../../../Table/features/RowPreview/constants";
4
+ import { TABLE_DOWNLOAD } from "../../../../../Table/features/TableDownload/constants";
4
5
 
5
6
  export const CORE_OPTIONS: Pick<
6
7
  CoreOptions<RowData>,
7
8
  "_features" | "getCoreRowModel"
8
9
  > = {
9
- _features: [ROW_POSITION, ROW_PREVIEW],
10
+ _features: [ROW_POSITION, ROW_PREVIEW, TABLE_DOWNLOAD],
10
11
  getCoreRowModel: getCoreRowModel(),
11
12
  };
@@ -1,5 +1,6 @@
1
1
  import { RowData, TableOptions } from "@tanstack/react-table";
2
2
  import { Attribute } from "../../../../../common/entities";
3
+ import { TABLE_DOWNLOAD_OPTIONS } from "../../../../Table/featureOptions/tableDownload/constants";
3
4
  import { useColumnFiltersOptions } from "./columnFilters/hook";
4
5
  import { CORE_OPTIONS } from "./core/constants";
5
6
  import { EXPANDED_OPTIONS } from "./expanded/constants";
@@ -30,6 +31,7 @@ export const useTableOptions = <T extends RowData = Attribute>(): Omit<
30
31
  ...GROUPING_OPTIONS,
31
32
  ...PAGINATION_OPTIONS,
32
33
  ...SORTING_OPTIONS,
34
+ ...TABLE_DOWNLOAD_OPTIONS,
33
35
  ...VISIBILITY_OPTIONS,
34
36
  } as TableOptions<T>;
35
37
  };
@@ -13,7 +13,7 @@ export function generateColumnDefinitions<T extends RowData>(
13
13
  ...column,
14
14
  meta: {
15
15
  ...column.meta,
16
- header: column.meta?.header || (column.header as string),
16
+ header: column.meta?.header ?? (column.header as string),
17
17
  },
18
18
  };
19
19
  });
@@ -31,6 +31,7 @@ export const CollapsableRows = <T extends RowData>({
31
31
  key={row.id}
32
32
  id={row.id}
33
33
  isPreview={row.getIsPreview()}
34
+ isSelected={row.getIsSelected()}
34
35
  >
35
36
  <CollapsableCell
36
37
  isDisabled={isCollapsableRowDisabled(tableInstance)}
@@ -37,9 +37,11 @@ export const TableRows = <T extends RowData>({
37
37
  key={row.id}
38
38
  id={row.id}
39
39
  canExpand={row.getCanExpand()}
40
+ canSelect={row.getCanSelect()}
40
41
  isExpanded={row.getIsExpanded()}
41
42
  isGrouped={row.getIsGrouped()}
42
43
  isPreview={row.getIsPreview()}
44
+ isSelected={row.getIsSelected()}
43
45
  onClick={() => handleToggleExpanded(row)}
44
46
  >
45
47
  {row.getVisibleCells().map((cell) => {
@@ -20,8 +20,11 @@ import { arrIncludesSome } from "../../../Table/columnDef/columnFilters/filterFn
20
20
  import { COLUMN_DEF } from "../../../Table/common/columnDef";
21
21
  import { ROW_DIRECTION } from "../../../Table/common/entities";
22
22
  import { TableHead } from "../../../Table/components/TableHead/tableHead";
23
+ import { TABLE_DOWNLOAD_OPTIONS } from "../../../Table/featureOptions/tableDownload/constants";
23
24
  import { ROW_POSITION } from "../../../Table/features/RowPosition/constants";
24
25
  import { ROW_PREVIEW } from "../../../Table/features/RowPreview/constants";
26
+ import { ROW_SELECTION_VALIDATION } from "../../../Table/features/RowSelectionValidation/constants";
27
+ import { TABLE_DOWNLOAD } from "../../../Table/features/TableDownload/constants";
25
28
  import { GridTable } from "../../../Table/table.styles";
26
29
  import { generateColumnDefinitions } from "./common/utils";
27
30
  import { TableBody } from "./components/TableBody/tableBody";
@@ -58,7 +61,12 @@ export const Table = <T extends RowData>({
58
61
  const { stickyHeader = false } = table || {};
59
62
  const { sx: tableContainerSx } = tableContainer || {};
60
63
  const tableInstance = useReactTable({
61
- _features: [ROW_POSITION, ROW_PREVIEW],
64
+ _features: [
65
+ ROW_POSITION,
66
+ ROW_PREVIEW,
67
+ ROW_SELECTION_VALIDATION,
68
+ TABLE_DOWNLOAD,
69
+ ],
62
70
  columns: generateColumnDefinitions([
63
71
  COLUMN_DEF.ROW_POSITION as ColumnDef<T>,
64
72
  ...columns,
@@ -67,6 +75,7 @@ export const Table = <T extends RowData>({
67
75
  enableSorting: false,
68
76
  filterFns: { arrIncludesSome },
69
77
  getCoreRowModel: getCoreRowModel(),
78
+ ...TABLE_DOWNLOAD_OPTIONS,
70
79
  ...tableOptions,
71
80
  });
72
81
  return (
@@ -34,9 +34,12 @@ import {
34
34
  sortingFn,
35
35
  } from "../../Table/common/utils";
36
36
  import { getFacetedMinMaxValues } from "../../Table/featureOptions/facetedColumn/getFacetedMinMaxValues";
37
+ import { TABLE_DOWNLOAD_OPTIONS } from "../../Table/featureOptions/tableDownload/constants";
37
38
  import { ROW_POSITION } from "../../Table/features/RowPosition/constants";
38
39
  import { ROW_PREVIEW } from "../../Table/features/RowPreview/constants";
39
40
  import { RowPreviewState } from "../../Table/features/RowPreview/entities";
41
+ import { ROW_SELECTION_VALIDATION } from "../../Table/features/RowSelectionValidation/constants";
42
+ import { TABLE_DOWNLOAD } from "../../Table/features/TableDownload/constants";
40
43
  import { buildBaseColumnDef } from "../../TableCreator/common/utils";
41
44
  import { useTableOptions } from "../../TableCreator/options/hook";
42
45
  import { createCell } from "./coreOptions/columns/cellFactory";
@@ -162,7 +165,12 @@ UseTableProps): UseTable<T> => {
162
165
  * - This will simplify the configuration structure and centralize table state definitions, reducing redundancy and improving clarity.
163
166
  */
164
167
  const table = useReactTable<T>({
165
- _features: [ROW_POSITION, ROW_PREVIEW],
168
+ _features: [
169
+ ROW_POSITION,
170
+ ROW_PREVIEW,
171
+ ROW_SELECTION_VALIDATION,
172
+ TABLE_DOWNLOAD,
173
+ ],
166
174
  columns: columnDefs,
167
175
  data: listItems || [],
168
176
  enableColumnFilters: true, // client-side filtering.
@@ -187,6 +195,7 @@ UseTableProps): UseTable<T> => {
187
195
  onRowSelectionChange,
188
196
  onSortingChange,
189
197
  pageCount,
198
+ ...TABLE_DOWNLOAD_OPTIONS,
190
199
  state,
191
200
  ...tableOptions,
192
201
  });
@@ -12,6 +12,7 @@ export const COLUMN_DEF: Record<string, ColumnDef<RowData>> = {
12
12
  enableGrouping: false,
13
13
  enableHiding: false,
14
14
  enableSorting: false,
15
+ enableTableDownload: false,
15
16
  header: "",
16
17
  id: COLUMN_IDENTIFIER.ROW_POSITION,
17
18
  meta: {
@@ -27,6 +28,7 @@ export const COLUMN_DEF: Record<string, ColumnDef<RowData>> = {
27
28
  enableGrouping: false,
28
29
  enableHiding: false,
29
30
  enableSorting: false,
31
+ enableTableDownload: false,
30
32
  header: HeadSelectionCell,
31
33
  id: COLUMN_IDENTIFIER.ROW_SELECTION,
32
34
  meta: {
@@ -11,7 +11,6 @@ import {
11
11
  } from "@tanstack/react-table";
12
12
  import { Category } from "../../../common/categories/models/types";
13
13
  import { EXPLORE_MODE, ExploreMode } from "../../../hooks/useExploreMode/types";
14
- import { COLUMN_IDENTIFIER } from "./columnIdentifier";
15
14
 
16
15
  /**
17
16
  * Internal model of a category term count keyed by category term.
@@ -24,11 +23,6 @@ type CountByTerms = Map<any, number>;
24
23
  */
25
24
  type PinnedCell<T extends RowData> = [Cell<T, unknown>, number];
26
25
 
27
- /**
28
- * Model of possible table data values.
29
- */
30
- type TableData = number | string | string[];
31
-
32
26
  /**
33
27
  * Build view-specific models from react table faceted values function.
34
28
  * @param columns - Table columns.
@@ -100,49 +94,6 @@ export function getColumnHeader<T extends RowData>(column: Column<T>): string {
100
94
  return meta?.header || id;
101
95
  }
102
96
 
103
- /**
104
- * Format data to TSV string.
105
- * @param data - Table data.
106
- * @returns table data formatted into a TSV string.
107
- */
108
- function formatDataToTSV(data: TableData[][]): string {
109
- return data
110
- .map((row) => {
111
- return row
112
- .map((data) => {
113
- // Use empty string in place of undefined and null.
114
- if (data === undefined || data === null) return "";
115
- // Convert to string.
116
- const dataString = Array.isArray(data)
117
- ? data.join(", ")
118
- : String(data);
119
- // Quote if necessary.
120
- return /[\t\r\n"]/.test(dataString)
121
- ? `"${dataString.replaceAll('"', '""')}"`
122
- : dataString;
123
- })
124
- .join("\t");
125
- })
126
- .join("\n");
127
- }
128
-
129
- /**
130
- * Returns filtered entity results as a blob.
131
- * @param rows - Table rows.
132
- * @returns filtered entity results as a blob.
133
- */
134
- export function generateDownloadBlob<T extends RowData>(
135
- rows: Row<T>[]
136
- ): Blob | undefined {
137
- if (rows.length === 0) {
138
- return;
139
- }
140
- const tableHeaders = getHeadersTableData(rows);
141
- const tableData = getRowsTableData(rows);
142
- const tsv = formatDataToTSV([tableHeaders, ...tableData]);
143
- return new Blob([tsv], { type: "text/tab-separated-values" });
144
- }
145
-
146
97
  /**
147
98
  * Returns unique category term counts keyed by category terms.
148
99
  * Custom function based off react table function getFacetedUniqueValues, see
@@ -225,32 +176,6 @@ export function getTableStatePagination(
225
176
  };
226
177
  }
227
178
 
228
- /**
229
- * Returns the list of table headers, excluding "rowSelection" column.
230
- * @param rows - Table rows.
231
- * @returns list of headers.
232
- */
233
- function getHeadersTableData<T extends RowData>(rows: Row<T>[]): TableData[] {
234
- return rows[0]
235
- .getAllCells()
236
- .filter((cell) => cell.column.id !== COLUMN_IDENTIFIER.ROW_SELECTION)
237
- .map((cell) => cell.column.columnDef.header as TableData);
238
- }
239
-
240
- /**
241
- * Returns the list of table data, excluding "rowSelection" column.
242
- * @param rows - Table rows.
243
- * @returns list of data.
244
- */
245
- function getRowsTableData<T extends RowData>(rows: Row<T>[]): TableData[][] {
246
- return rows.map((row) =>
247
- row
248
- .getAllCells()
249
- .filter((cell) => cell.column.id !== COLUMN_IDENTIFIER.ROW_SELECTION)
250
- .map((cell) => cell.getValue() as TableData)
251
- );
252
- }
253
-
254
179
  /**
255
180
  * Returns true if any or all table rows are selected.
256
181
  * @param table - Table.
@@ -0,0 +1,15 @@
1
+ import { TooltipProps } from "@mui/material";
2
+
3
+ export const TOOLTIP_PROPS: Partial<TooltipProps> = {
4
+ arrow: true,
5
+ disableInteractive: true,
6
+ slotProps: {
7
+ popper: {
8
+ modifiers: [
9
+ { name: "offset", options: { offset: [0, -4] } },
10
+ { name: "preventOverflow", options: { padding: 8 } },
11
+ ],
12
+ },
13
+ tooltip: { sx: { maxWidth: "220px" } },
14
+ },
15
+ };
@@ -1,24 +1,36 @@
1
- import { Checkbox as MCheckbox } from "@mui/material";
1
+ import { Checkbox as MCheckbox, Tooltip } from "@mui/material";
2
2
  import { CellContext, RowData } from "@tanstack/react-table";
3
3
  import React from "react";
4
4
  import { CheckedIcon } from "../../../../../common/CustomIcon/components/CheckedIcon/checkedIcon";
5
+ import { UncheckedDisabledIcon } from "../../../../../common/CustomIcon/components/UncheckedDisabledIcon/uncheckedDisabledIcon";
5
6
  import { UncheckedIcon } from "../../../../../common/CustomIcon/components/UncheckedIcon/uncheckedIcon";
7
+ import { TOOLTIP_PROPS } from "./constants";
6
8
 
7
9
  export const RowSelectionCell = <T extends RowData, TValue = unknown>({
8
10
  row,
9
11
  }: CellContext<T, TValue>): JSX.Element => {
10
- const { getIsSelected, getToggleSelectedHandler } = row;
12
+ const {
13
+ getCanSelect,
14
+ getIsSelected,
15
+ getSelectionValidation,
16
+ getToggleSelectedHandler,
17
+ } = row;
11
18
  return (
12
- <MCheckbox
13
- checked={getIsSelected()}
14
- checkedIcon={<CheckedIcon />}
15
- icon={<UncheckedIcon />}
16
- /*
17
- * Prevents click events from bubbling up to parent components
18
- * (such as CardActionArea or Accordion) when the checkbox is activated.
19
- */
20
- onClick={(e) => e.stopPropagation()}
21
- onChange={getToggleSelectedHandler()}
22
- />
19
+ <Tooltip {...TOOLTIP_PROPS} title={getSelectionValidation?.()}>
20
+ <span>
21
+ <MCheckbox
22
+ checked={getIsSelected()}
23
+ checkedIcon={<CheckedIcon />}
24
+ disabled={!getCanSelect()}
25
+ icon={getCanSelect() ? <UncheckedIcon /> : <UncheckedDisabledIcon />}
26
+ /*
27
+ * Prevents click events from bubbling up to parent components
28
+ * (such as CardActionArea or Accordion) when the checkbox is activated.
29
+ */
30
+ onClick={(e) => e.stopPropagation()}
31
+ onChange={getToggleSelectedHandler()}
32
+ />
33
+ </span>
34
+ </Tooltip>
23
35
  );
24
36
  };
@@ -1,6 +1,6 @@
1
1
  import styled from "@emotion/styled";
2
2
  import { Button } from "@mui/material";
3
- import { bpDownSm } from "../../../../styles/common/mixins/breakpoints";
3
+ import { bpDownSm } from "../../../../../styles/common/mixins/breakpoints";
4
4
 
5
5
  export const StyledButton = styled(Button)`
6
6
  ${bpDownSm} {
@@ -0,0 +1,23 @@
1
+ import { RowData } from "@tanstack/react-table";
2
+ import React from "react";
3
+ import { BUTTON_PROPS } from "../../../../common/Button/constants";
4
+ import { DownloadIcon } from "../../../../common/CustomIcon/components/DownloadIcon/downloadIcon";
5
+ import { StyledButton } from "./tableDownload.styles";
6
+ import { TableDownloadProps } from "./types";
7
+
8
+ export const TableDownload = <T extends RowData>({
9
+ className,
10
+ table,
11
+ }: TableDownloadProps<T>): JSX.Element | null => {
12
+ if (!table.options.enableTableDownload) return null;
13
+ return (
14
+ <StyledButton
15
+ {...BUTTON_PROPS.SECONDARY_CONTAINED}
16
+ className={className}
17
+ onClick={() => table.downloadData()}
18
+ startIcon={<DownloadIcon />}
19
+ >
20
+ Download TSV
21
+ </StyledButton>
22
+ );
23
+ };
@@ -0,0 +1,7 @@
1
+ import { RowData, Table } from "@tanstack/react-table";
2
+ import { BaseComponentProps } from "../../../../types";
3
+
4
+ export interface TableDownloadProps<T extends RowData>
5
+ extends BaseComponentProps {
6
+ table: Table<T>;
7
+ }
@@ -6,17 +6,21 @@ import { PALETTE } from "../../../../styles/common/constants/palette";
6
6
 
7
7
  export interface StyledTableRowProps {
8
8
  canExpand?: boolean;
9
+ canSelect?: boolean;
9
10
  isExpanded?: boolean;
10
11
  isGrouped?: boolean;
11
12
  isPreview?: boolean;
13
+ isSelected?: boolean;
12
14
  }
13
15
 
14
16
  export const StyledTableRow = styled(MTableRow, {
15
17
  shouldForwardProp: (prop) =>
16
18
  prop !== "canExpand" &&
19
+ prop !== "canSelect" &&
17
20
  prop !== "isExpanded" &&
21
+ prop !== "isGrouped" &&
18
22
  prop !== "isPreview" &&
19
- prop !== "isGrouped",
23
+ prop !== "isSelected",
20
24
  })<StyledTableRowProps>`
21
25
  && {
22
26
  transition: background-color 300ms ease-in;
@@ -54,5 +58,19 @@ export const StyledTableRow = styled(MTableRow, {
54
58
  css`
55
59
  background-color: ${PALETTE.PRIMARY_LIGHTEST};
56
60
  `}
61
+
62
+ ${({ isSelected }) =>
63
+ isSelected &&
64
+ css`
65
+ background-color: ${PALETTE.PRIMARY_LIGHTEST};
66
+ `}
67
+
68
+ ${({ canSelect }) =>
69
+ !canSelect &&
70
+ css`
71
+ .MuiTableCell-root {
72
+ color: ${PALETTE.INK_LIGHT};
73
+ }
74
+ `}
57
75
  }
58
76
  `;
@@ -33,6 +33,7 @@ export const CollapsableRows = <T extends RowData>({
33
33
  data-index={rowIndex}
34
34
  ref={virtualizer.measureElement}
35
35
  isPreview={row.getIsPreview()}
36
+ isSelected={row.getIsSelected()}
36
37
  >
37
38
  <CollapsableCell
38
39
  isDisabled={isCollapsableRowDisabled(tableInstance)}
@@ -30,10 +30,12 @@ export const TableRows = <T extends RowData>({
30
30
  <StyledTableRow
31
31
  key={row.id}
32
32
  canExpand={getCanExpand()}
33
+ canSelect={row.getCanSelect()}
33
34
  data-index={rowIndex}
34
35
  isExpanded={getIsExpanded()}
35
36
  isGrouped={getIsGrouped()}
36
37
  isPreview={getIsPreview()}
38
+ isSelected={row.getIsSelected()}
37
39
  onClick={() => handleToggleExpanded(row)}
38
40
  ref={virtualizer.measureElement}
39
41
  >