@databiosphere/findable-ui 18.0.0 → 19.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.
Files changed (180) hide show
  1. package/.release-please-manifest.json +1 -1
  2. package/CHANGELOG.md +11 -0
  3. package/lib/components/Detail/components/Table/components/TableRows/components/CollapsableRows/collapsableRows.js +2 -0
  4. package/lib/components/Detail/components/Table/components/TableRows/tableRows.js +1 -15
  5. package/lib/components/Export/components/ExportForm/components/ExportFileSummaryForm/exportFileSummaryForm.js +9 -10
  6. package/lib/components/Links/components/Link/components/ExploreViewLink/exploreViewLink.js +23 -12
  7. package/lib/components/Table/common/columnDef.js +1 -0
  8. package/lib/components/Table/common/gridTable.styles.js +5 -11
  9. package/lib/components/Table/common/utils.d.ts +3 -29
  10. package/lib/components/Table/common/utils.js +25 -60
  11. package/lib/components/Table/components/TableBody/hooks/virtualizer/constants.d.ts +3 -0
  12. package/lib/components/Table/components/TableBody/hooks/virtualizer/constants.js +3 -0
  13. package/lib/components/Table/components/TableBody/hooks/virtualizer/hook.d.ts +8 -0
  14. package/lib/components/Table/components/TableBody/hooks/virtualizer/hook.js +18 -0
  15. package/lib/components/Table/components/TableBody/hooks/virtualizer/types.d.ts +4 -0
  16. package/lib/components/Table/components/TableBody/tableBody.d.ts +3 -2
  17. package/lib/components/Table/components/TableBody/tableBody.js +7 -28
  18. package/lib/components/Table/components/TableBody/utils.d.ts +12 -0
  19. package/lib/components/Table/components/TableBody/utils.js +17 -0
  20. package/lib/components/Table/components/TableCell/components/CollapsableCell/collapsableCell.d.ts +3 -1
  21. package/lib/components/Table/components/TableCell/components/CollapsableCell/collapsableCell.js +6 -14
  22. package/lib/components/Table/components/TableFeatures/ColumnGrouping/utils.d.ts +28 -0
  23. package/lib/components/Table/components/TableFeatures/ColumnGrouping/utils.js +52 -0
  24. package/lib/components/Table/components/TableFeatures/ColumnVisibility/utils.d.ts +21 -0
  25. package/lib/components/Table/components/TableFeatures/ColumnVisibility/utils.js +37 -0
  26. package/lib/components/Table/components/TableFeatures/RowSorting/utils.d.ts +29 -0
  27. package/lib/components/Table/components/TableFeatures/RowSorting/utils.js +68 -0
  28. package/lib/components/Table/components/TableHead/tableHead.d.ts +2 -6
  29. package/lib/components/Table/components/TableHead/tableHead.js +6 -4
  30. package/lib/components/Table/components/TableHead/types.d.ts +6 -0
  31. package/lib/components/Table/components/TableHead/types.js +1 -0
  32. package/lib/components/Table/components/TableHead/utils.d.ts +19 -0
  33. package/lib/components/Table/components/TableHead/utils.js +34 -0
  34. package/lib/components/Table/components/TableRow/tableRow.styles.d.ts +1 -0
  35. package/lib/components/Table/components/TableRow/tableRow.styles.js +14 -2
  36. package/lib/components/Table/components/TableRows/components/CollapsableRows/collapsableRows.d.ts +3 -2
  37. package/lib/components/Table/components/TableRows/components/CollapsableRows/collapsableRows.js +8 -4
  38. package/lib/components/Table/components/TableRows/components/CollapsableRows/hook.d.ts +9 -0
  39. package/lib/components/Table/components/TableRows/components/CollapsableRows/hook.js +17 -0
  40. package/lib/components/Table/components/TableRows/tableRows.d.ts +3 -3
  41. package/lib/components/Table/components/TableRows/tableRows.js +9 -5
  42. package/lib/components/Table/components/TableToolbar/components/ColumnGrouping/columnGrouping.d.ts +3 -0
  43. package/lib/components/Table/components/TableToolbar/components/ColumnGrouping/columnGrouping.js +26 -0
  44. package/lib/components/Table/components/TableToolbar/components/ColumnGrouping/columnGrouping.styles.d.ts +2 -0
  45. package/lib/components/Table/components/TableToolbar/components/ColumnGrouping/columnGrouping.styles.js +16 -0
  46. package/lib/components/Table/components/TableToolbar/components/ColumnGrouping/components/MenuItems/menuItems.d.ts +3 -0
  47. package/lib/components/Table/components/TableToolbar/components/ColumnGrouping/components/MenuItems/menuItems.js +12 -0
  48. package/lib/components/Table/components/TableToolbar/components/ColumnGrouping/components/MenuItems/types.d.ts +7 -0
  49. package/lib/components/Table/components/TableToolbar/components/ColumnGrouping/components/MenuItems/types.js +1 -0
  50. package/lib/components/Table/components/TableToolbar/components/ColumnGrouping/constants.d.ts +4 -0
  51. package/lib/components/Table/components/TableToolbar/components/ColumnGrouping/constants.js +13 -0
  52. package/lib/components/Table/components/TableToolbar/components/ColumnGrouping/types.d.ts +4 -0
  53. package/lib/components/Table/components/TableToolbar/components/ColumnGrouping/types.js +1 -0
  54. package/lib/components/Table/components/TableToolbar/components/ColumnGrouping/utils.d.ts +17 -0
  55. package/lib/components/Table/components/TableToolbar/components/ColumnGrouping/utils.js +36 -0
  56. package/lib/components/Table/components/TableToolbar/components/RowSelection/components/DropdownMenu/dropdownMenu.d.ts +2 -3
  57. package/lib/components/Table/components/TableToolbar/components/RowSelection/components/DropdownMenu/dropdownMenu.js +3 -11
  58. package/lib/components/Table/components/TableToolbar/tableToolbar.js +4 -2
  59. package/lib/components/Table/features/RowPosition/constants.js +2 -2
  60. package/lib/components/Table/features/RowPosition/utils.d.ts +2 -2
  61. package/lib/components/Table/features/RowPosition/utils.js +12 -5
  62. package/lib/components/Table/options/updater.d.ts +10 -0
  63. package/lib/components/Table/options/updater.js +24 -0
  64. package/lib/components/Table/table.js +22 -17
  65. package/lib/components/TableCreator/common/constants.d.ts +2 -2
  66. package/lib/components/TableCreator/common/constants.js +2 -1
  67. package/lib/components/TableCreator/common/utils.d.ts +5 -2
  68. package/lib/components/TableCreator/common/utils.js +11 -4
  69. package/lib/components/TableCreator/options/expanded/constants.d.ts +2 -0
  70. package/lib/components/TableCreator/options/expanded/constants.js +6 -0
  71. package/lib/components/TableCreator/options/expanded/hook.d.ts +2 -0
  72. package/lib/components/TableCreator/options/expanded/hook.js +4 -0
  73. package/lib/components/TableCreator/options/grouping/constants.d.ts +2 -0
  74. package/lib/components/TableCreator/options/grouping/constants.js +5 -0
  75. package/lib/components/TableCreator/options/grouping/hook.d.ts +2 -0
  76. package/lib/components/TableCreator/options/grouping/hook.js +9 -0
  77. package/lib/components/TableCreator/options/hook.d.ts +2 -0
  78. package/lib/components/TableCreator/options/hook.js +25 -0
  79. package/lib/components/TableCreator/options/rowSelection/constants.d.ts +2 -0
  80. package/lib/components/TableCreator/options/rowSelection/constants.js +5 -0
  81. package/lib/components/TableCreator/options/rowSelection/hook.d.ts +2 -0
  82. package/lib/components/TableCreator/options/rowSelection/hook.js +4 -0
  83. package/lib/components/TableCreator/options/sorting/constants.d.ts +2 -0
  84. package/lib/components/TableCreator/options/sorting/constants.js +5 -0
  85. package/lib/components/TableCreator/options/sorting/hook.d.ts +2 -0
  86. package/lib/components/TableCreator/options/sorting/hook.js +4 -0
  87. package/lib/components/TableCreator/tableCreator.d.ts +3 -5
  88. package/lib/components/TableCreator/tableCreator.js +4 -2
  89. package/lib/components/common/DropdownMenu/dropdownMenu.d.ts +2 -3
  90. package/lib/components/common/DropdownMenu/dropdownMenu.js +7 -7
  91. package/lib/components/common/DropdownMenu/dropdownMenu.styles.d.ts +1 -1
  92. package/lib/components/common/DropdownMenu/dropdownMenu.styles.js +1 -1
  93. package/lib/components/common/Menu/hooks/useMenu.d.ts +5 -6
  94. package/lib/config/entities.d.ts +6 -6
  95. package/lib/config/utils.d.ts +0 -7
  96. package/lib/config/utils.js +0 -12
  97. package/lib/providers/exploreState/actions/updateGrouping/action.d.ts +10 -0
  98. package/lib/providers/exploreState/actions/updateGrouping/action.js +16 -0
  99. package/lib/providers/exploreState/actions/updateGrouping/dispatch.d.ts +7 -0
  100. package/lib/providers/exploreState/actions/updateGrouping/dispatch.js +12 -0
  101. package/lib/providers/exploreState/actions/updateGrouping/types.d.ts +9 -0
  102. package/lib/providers/exploreState/actions/updateGrouping/types.js +1 -0
  103. package/lib/providers/exploreState/actions/updateGrouping/utils.d.ts +10 -0
  104. package/lib/providers/exploreState/actions/updateGrouping/utils.js +26 -0
  105. package/lib/providers/exploreState/entities.d.ts +3 -4
  106. package/lib/providers/exploreState/initializer/utils.js +41 -8
  107. package/lib/providers/exploreState/payloads/entities.d.ts +2 -1
  108. package/lib/providers/exploreState/utils.d.ts +6 -4
  109. package/lib/providers/exploreState/utils.js +7 -4
  110. package/lib/providers/exploreState.d.ts +3 -1
  111. package/lib/providers/exploreState.js +15 -5
  112. package/lib/views/ExploreView/exploreView.js +2 -2
  113. package/package.json +1 -1
  114. package/src/components/Detail/components/Table/components/TableRows/components/CollapsableRows/collapsableRows.tsx +2 -0
  115. package/src/components/Detail/components/Table/components/TableRows/tableRows.tsx +3 -18
  116. package/src/components/Export/components/ExportForm/components/ExportFileSummaryForm/exportFileSummaryForm.tsx +28 -32
  117. package/src/components/Links/components/Link/components/ExploreViewLink/exploreViewLink.tsx +27 -13
  118. package/src/components/Table/common/columnDef.ts +1 -0
  119. package/src/components/Table/common/gridTable.styles.ts +5 -11
  120. package/src/components/Table/common/utils.ts +44 -107
  121. package/src/components/Table/components/TableBody/hooks/virtualizer/constants.ts +3 -0
  122. package/src/components/Table/components/TableBody/hooks/virtualizer/hook.ts +23 -0
  123. package/src/components/Table/components/TableBody/hooks/virtualizer/types.ts +5 -0
  124. package/src/components/Table/components/TableBody/tableBody.tsx +14 -30
  125. package/src/components/Table/components/TableBody/utils.ts +21 -0
  126. package/src/components/Table/components/TableCell/components/CollapsableCell/collapsableCell.tsx +14 -15
  127. package/src/components/Table/components/TableFeatures/ColumnGrouping/utils.ts +70 -0
  128. package/src/components/Table/components/TableFeatures/ColumnVisibility/utils.ts +52 -0
  129. package/src/components/Table/components/TableFeatures/RowSorting/utils.ts +87 -0
  130. package/src/components/Table/components/TableHead/tableHead.tsx +21 -29
  131. package/src/components/Table/components/TableHead/types.ts +7 -0
  132. package/src/components/Table/components/TableHead/utils.ts +42 -0
  133. package/src/components/Table/components/TableRow/tableRow.styles.ts +19 -2
  134. package/src/components/Table/components/TableRows/components/CollapsableRows/collapsableRows.tsx +8 -2
  135. package/src/components/Table/components/TableRows/components/CollapsableRows/hook.ts +20 -0
  136. package/src/components/Table/components/TableRows/tableRows.tsx +18 -15
  137. package/src/components/Table/components/TableToolbar/components/ColumnGrouping/columnGrouping.styles.ts +17 -0
  138. package/src/components/Table/components/TableToolbar/components/ColumnGrouping/columnGrouping.tsx +54 -0
  139. package/src/components/Table/components/TableToolbar/components/ColumnGrouping/components/MenuItems/menuItems.tsx +28 -0
  140. package/src/components/Table/components/TableToolbar/components/ColumnGrouping/components/MenuItems/types.ts +8 -0
  141. package/src/components/Table/components/TableToolbar/components/ColumnGrouping/constants.ts +17 -0
  142. package/src/components/Table/components/TableToolbar/components/ColumnGrouping/types.ts +5 -0
  143. package/src/components/Table/components/TableToolbar/components/ColumnGrouping/utils.ts +45 -0
  144. package/src/components/Table/components/TableToolbar/components/RowSelection/components/DropdownMenu/dropdownMenu.tsx +7 -20
  145. package/src/components/Table/components/TableToolbar/tableToolbar.tsx +8 -2
  146. package/src/components/Table/features/RowPosition/constants.ts +2 -2
  147. package/src/components/Table/features/RowPosition/utils.ts +10 -5
  148. package/src/components/Table/options/updater.ts +29 -0
  149. package/src/components/Table/table.tsx +43 -25
  150. package/src/components/TableCreator/common/constants.ts +4 -6
  151. package/src/components/TableCreator/common/utils.ts +14 -7
  152. package/src/components/TableCreator/options/expanded/constants.ts +14 -0
  153. package/src/components/TableCreator/options/expanded/hook.ts +6 -0
  154. package/src/components/TableCreator/options/grouping/constants.ts +6 -0
  155. package/src/components/TableCreator/options/grouping/hook.ts +17 -0
  156. package/src/components/TableCreator/options/hook.ts +31 -0
  157. package/src/components/TableCreator/options/rowSelection/constants.ts +10 -0
  158. package/src/components/TableCreator/options/rowSelection/hook.ts +8 -0
  159. package/src/components/TableCreator/options/sorting/constants.ts +10 -0
  160. package/src/components/TableCreator/options/sorting/hook.ts +6 -0
  161. package/src/components/TableCreator/tableCreator.tsx +4 -11
  162. package/src/components/common/DropdownMenu/dropdownMenu.styles.ts +1 -1
  163. package/src/components/common/DropdownMenu/dropdownMenu.tsx +19 -17
  164. package/src/components/common/Menu/hooks/useMenu.ts +8 -9
  165. package/src/config/entities.ts +10 -9
  166. package/src/config/utils.ts +0 -14
  167. package/src/providers/exploreState/actions/updateGrouping/action.ts +26 -0
  168. package/src/providers/exploreState/actions/updateGrouping/dispatch.ts +16 -0
  169. package/src/providers/exploreState/actions/updateGrouping/types.ts +11 -0
  170. package/src/providers/exploreState/actions/updateGrouping/utils.ts +33 -0
  171. package/src/providers/exploreState/entities.ts +3 -3
  172. package/src/providers/exploreState/initializer/utils.ts +58 -10
  173. package/src/providers/exploreState/payloads/entities.ts +2 -0
  174. package/src/providers/exploreState/utils.ts +10 -7
  175. package/src/providers/exploreState.tsx +29 -6
  176. package/src/views/ExploreView/exploreView.tsx +1 -3
  177. package/types/data-explorer-ui.d.ts +8 -1
  178. package/lib/components/TableCreator/common/entities.d.ts +0 -5
  179. package/src/components/TableCreator/common/entities.ts +0 -6
  180. /package/lib/components/{TableCreator/common/entities.js → Table/components/TableBody/hooks/virtualizer/types.js} +0 -0
@@ -0,0 +1,87 @@
1
+ import { Column, ColumnSort, RowData, Table } from "@tanstack/react-table";
2
+ import { MouseEvent } from "react";
3
+
4
+ /**
5
+ * Constructs a `ColumnSort` object from a column.
6
+ * @param column - Column.
7
+ * @returns `ColumnSort` object with sorting direction and column ID.
8
+ */
9
+ export function buildColumnSort<T extends RowData>(
10
+ column: Column<T>
11
+ ): ColumnSort {
12
+ return { desc: column.getFirstSortDir() === "desc", id: column.id };
13
+ }
14
+
15
+ /**
16
+ * Toggles the sorting state for the specified column.
17
+ * The sorting state of a table is modified based on user interaction, table grouping state, and
18
+ * its sorting configuration (single-sort or multi-sort modes).
19
+ * ### Sorting State:
20
+ * - **Ungrouped Table**:
21
+ * - Sorting is toggled normally, respecting single-sort or multi-sort modes.
22
+ * - **Grouped Table**:
23
+ * - **Multi-Sort Request**:
24
+ * - Sorting is toggled normally.
25
+ * - **Single-Sort Request**:
26
+ * - **Multi-Sort Mode**:
27
+ * - Grouped column is preserved as the first sort key, and the requested column is sorted as the second sort key.
28
+ * - **Single-Sort Mode**:
29
+ * - If the grouped column is already sorted, no action taken (the grouped column remains the primary sort key).
30
+ * - Otherwise, sorting is toggled normally.
31
+ * @param mouseEvent - Mouse event.
32
+ * @param table - Table.
33
+ * @param column - Column.
34
+ */
35
+ export function handleToggleSorting<T extends RowData>(
36
+ mouseEvent: MouseEvent,
37
+ table: Table<T>,
38
+ column: Column<T>
39
+ ): void {
40
+ const {
41
+ getColumn,
42
+ getState,
43
+ options: { enableMultiSort, isMultiSortEvent },
44
+ setSorting,
45
+ } = table;
46
+ const { getCanSort, getSortIndex, toggleSorting } = column;
47
+ const { grouping, sorting } = getState();
48
+
49
+ // Table and column sorting is not enabled.
50
+ if (!getCanSort()) return;
51
+
52
+ // Sorting is enabled.
53
+ // Table is not grouped; toggle sorting as usual.
54
+ if (grouping.length === 0) {
55
+ toggleSorting(undefined, isMultiSortEvent?.(mouseEvent));
56
+ return;
57
+ }
58
+
59
+ // Table is grouped.
60
+ // Multi-sort mode and multi-sort requested; toggle sorting as usual.
61
+ if (enableMultiSort && isMultiSortEvent?.(mouseEvent)) {
62
+ toggleSorting(undefined, true);
63
+ return;
64
+ }
65
+
66
+ // Retrieve the grouped column.
67
+ const groupedColumn = getColumn(grouping[0]); // Grouping state currently only supports single-column grouping.
68
+
69
+ // Single-sort requested (either single-sort or multi-sort mode).
70
+ // Grouped column is sorted.
71
+ if (groupedColumn?.getIsSorted()) {
72
+ if (enableMultiSort) {
73
+ // Multi-sort mode.
74
+ // Column to be sorted is not the last most recent sorted column.
75
+ if (getSortIndex() !== sorting.length - 1) {
76
+ // Reset sorting state, preserving grouped column as the first sort, and requested column to be sorted as the second sort.
77
+ setSorting([buildColumnSort(groupedColumn), buildColumnSort(column)]);
78
+ return;
79
+ }
80
+ } else {
81
+ // Special case; in single-sort mode we do not override a grouped sort.
82
+ return;
83
+ }
84
+ }
85
+
86
+ toggleSorting();
87
+ }
@@ -1,22 +1,20 @@
1
+ import SouthRoundedIcon from "@mui/icons-material/SouthRounded";
1
2
  import {
2
3
  TableHead as MTableHead,
3
4
  TableRow as MTableRow,
4
5
  TableCell,
5
6
  TableSortLabel,
6
7
  } from "@mui/material";
7
- import { flexRender, RowData, Table } from "@tanstack/react-table";
8
+ import { flexRender, RowData } from "@tanstack/react-table";
8
9
  import React, { Fragment } from "react";
9
10
  import { ROW_DIRECTION } from "../../common/entities";
10
- import { getTableSortLabelProps } from "../../common/utils";
11
11
  import {
12
12
  getTableCellAlign,
13
13
  getTableCellPadding,
14
14
  } from "../TableCell/common/utils";
15
-
16
- export interface TableHeadProps<T extends RowData> {
17
- rowDirection: ROW_DIRECTION;
18
- tableInstance: Table<T>;
19
- }
15
+ import { handleToggleSorting } from "../TableFeatures/RowSorting/utils";
16
+ import { TableHeadProps } from "./types";
17
+ import { isSortDisabled, shouldSortColumn } from "./utils";
20
18
 
21
19
  export const TableHead = <T extends RowData>({
22
20
  rowDirection,
@@ -28,34 +26,28 @@ export const TableHead = <T extends RowData>({
28
26
  tableInstance.getHeaderGroups().map((headerGroup) => (
29
27
  <MTableHead key={headerGroup.id}>
30
28
  <MTableRow>
31
- {headerGroup.headers.map((header) => {
32
- const {
33
- column: {
34
- columnDef: {
35
- meta: { enableSortingInteraction = true } = {},
36
- },
37
- },
38
- } = header;
39
- return header.column.getIsGrouped() ? null : (
29
+ {headerGroup.headers.map(({ column, getContext, id }) => {
30
+ const { columnDef, getIsGrouped, getIsSorted } = column;
31
+ return getIsGrouped() ? null : (
40
32
  <TableCell
41
- key={header.id}
42
- align={getTableCellAlign(header.column)}
43
- padding={getTableCellPadding(header.id)}
33
+ key={id}
34
+ align={getTableCellAlign(column)}
35
+ padding={getTableCellPadding(id)}
44
36
  >
45
- {header.column.getCanSort() && enableSortingInteraction ? (
37
+ {shouldSortColumn(tableInstance, column) ? (
46
38
  <TableSortLabel
47
- {...getTableSortLabelProps(header.column)}
39
+ IconComponent={SouthRoundedIcon}
40
+ active={Boolean(getIsSorted())}
41
+ direction={getIsSorted() || undefined}
42
+ disabled={isSortDisabled(tableInstance)}
43
+ onClick={(mouseEvent) =>
44
+ handleToggleSorting(mouseEvent, tableInstance, column)
45
+ }
48
46
  >
49
- {flexRender(
50
- header.column.columnDef.header,
51
- header.getContext()
52
- )}
47
+ {flexRender(columnDef.header, getContext())}
53
48
  </TableSortLabel>
54
49
  ) : (
55
- flexRender(
56
- header.column.columnDef.header,
57
- header.getContext()
58
- )
50
+ flexRender(columnDef.header, getContext())
59
51
  )}
60
52
  </TableCell>
61
53
  );
@@ -0,0 +1,7 @@
1
+ import { RowData, Table } from "@tanstack/react-table";
2
+ import { ROW_DIRECTION } from "../../common/entities";
3
+
4
+ export interface TableHeadProps<T extends RowData> {
5
+ rowDirection: ROW_DIRECTION;
6
+ tableInstance: Table<T>;
7
+ }
@@ -0,0 +1,42 @@
1
+ import { Column, RowData, Table } from "@tanstack/react-table";
2
+
3
+ /**
4
+ * Determines if the column sorting interaction should be disabled.
5
+ * The sort label is disabled under the following conditions:
6
+ * - The table is in single-sort mode, and is grouped, and the grouped column is already sorted.
7
+ * When multi-sort mode is enabled or the table is not grouped, the sort label remains enabled.
8
+ * @param table - Table.
9
+ * @returns `true` if the sort label should be disabled; otherwise, `false`.
10
+ */
11
+ export function isSortDisabled<T extends RowData>(table: Table<T>): boolean {
12
+ const {
13
+ getColumn,
14
+ getState,
15
+ options: { enableMultiSort },
16
+ } = table;
17
+ const { grouping } = getState();
18
+ // Multi-sort is enabled - sorting is always enabled.
19
+ if (enableMultiSort) return false;
20
+ // Table is not grouped - sorting is always enabled.
21
+ if (grouping.length === 0) return false;
22
+ // In single-sort mode, check if the grouped column is already sorted.
23
+ const groupedColumn = getColumn(grouping[0]); // Grouping state currently only supports single-column grouping.
24
+ return Boolean(groupedColumn?.getIsSorted());
25
+ }
26
+
27
+ /**
28
+ * Checks whether a column in a table can be sorted.
29
+ * A column is sortable if both table sorting interactions are enabled
30
+ * and the column itself is marked as sortable.
31
+ * @param table - Table.
32
+ * @param column - Column.
33
+ * @returns {boolean} `true` if the column can be sorted; otherwise, `false`.
34
+ */
35
+ export function shouldSortColumn<T extends RowData>(
36
+ table: Table<T>,
37
+ column: Column<T>
38
+ ): boolean {
39
+ const { enableSortingInteraction } = table.options;
40
+ const canSort = column.getCanSort();
41
+ return Boolean(enableSortingInteraction && canSort);
42
+ }
@@ -1,18 +1,35 @@
1
1
  import { css } from "@emotion/react";
2
2
  import styled from "@emotion/styled";
3
3
  import { TableRow as MTableRow } from "@mui/material";
4
- import { primaryLightest } from "../../../../styles/common/mixins/colors";
4
+ import {
5
+ primaryLightest,
6
+ smokeLightest,
7
+ } from "../../../../styles/common/mixins/colors";
8
+ import { textBodySmall500 } from "../../../../styles/common/mixins/fonts";
5
9
 
6
10
  interface Props {
11
+ isGrouped?: boolean;
7
12
  isPreview?: boolean;
8
13
  }
9
14
 
10
15
  export const TableRow = styled(MTableRow, {
11
- shouldForwardProp: (prop) => prop !== "isPreview",
16
+ shouldForwardProp: (prop) => prop !== "isPreview" && prop !== "isGrouped",
12
17
  })<Props>`
13
18
  && {
14
19
  transition: background-color 300ms ease-in;
15
20
 
21
+ ${(props) =>
22
+ props.isGrouped &&
23
+ css`
24
+ background-color: ${smokeLightest(props)};
25
+
26
+ td {
27
+ ${textBodySmall500(props)};
28
+ background-color: inherit;
29
+ grid-column: 1 / -1;
30
+ }
31
+ `}
32
+
16
33
  ${(props) =>
17
34
  props.isPreview &&
18
35
  css`
@@ -4,24 +4,29 @@ import React, { Fragment } from "react";
4
4
  import { isCollapsableRowDisabled } from "../../../../common/utils";
5
5
  import { CollapsableCell } from "../../../TableCell/components/CollapsableCell/collapsableCell";
6
6
  import { TableRow } from "../../../TableRow/tableRow.styles";
7
+ import { useCollapsableRows } from "./hook";
7
8
 
8
9
  export interface CollapsableRowsProps<T extends RowData> {
10
+ rows: Row<T>[];
9
11
  tableInstance: Table<T>;
10
12
  virtualizer: Virtualizer<Window, Element>;
11
13
  }
12
14
 
13
15
  export const CollapsableRows = <T extends RowData>({
16
+ rows,
14
17
  tableInstance,
15
18
  virtualizer,
16
19
  }: CollapsableRowsProps<T>): JSX.Element => {
17
- const { getRowModel } = tableInstance;
18
- const { rows } = getRowModel();
20
+ const { getState } = tableInstance;
21
+ const { grouping } = getState();
19
22
  const virtualItems = virtualizer.getVirtualItems();
23
+ useCollapsableRows(tableInstance);
20
24
  return (
21
25
  <Fragment>
22
26
  {virtualItems.map((virtualRow) => {
23
27
  const row = rows[virtualRow.index] as Row<T>;
24
28
  const { getIsPreview } = row;
29
+ if (grouping.length > 0 && row.depth > 0) return null; // TODO(cc) hide sub rows -- sub-rows are within collapsed content -- UI TBD.
25
30
  return (
26
31
  <TableRow
27
32
  key={row.id}
@@ -32,6 +37,7 @@ export const CollapsableRows = <T extends RowData>({
32
37
  <CollapsableCell
33
38
  isDisabled={isCollapsableRowDisabled(tableInstance)}
34
39
  row={row}
40
+ virtualizer={virtualizer}
35
41
  />
36
42
  </TableRow>
37
43
  );
@@ -0,0 +1,20 @@
1
+ import { RowData, Table } from "@tanstack/react-table";
2
+ import { useEffect } from "react";
3
+
4
+ /**
5
+ * Hook to manage the expanded row state during viewport transitions (e.g., desktop to mobile and vice versa).
6
+ * Designed for use with the `CollapsableRows` component, where row expansion is a work in progress for desktop viewports.
7
+ * This hook ensures that all rows are collapsed when transitioning from desktop to mobile (when `CollapsableRows` component is mounted).
8
+ * It also resets the expanded row state when transitioning back to desktop (when the `CollapsableRows` component is unmounted).
9
+ * @param table - Table.
10
+ */
11
+ export function useCollapsableRows<T extends RowData>(table: Table<T>): void {
12
+ const { resetExpanded, toggleAllRowsExpanded } = table;
13
+
14
+ useEffect(() => {
15
+ toggleAllRowsExpanded(false);
16
+ return (): void => {
17
+ resetExpanded();
18
+ };
19
+ }, [resetExpanded, toggleAllRowsExpanded]);
20
+ }
@@ -1,5 +1,5 @@
1
1
  import { TableCell } from "@mui/material";
2
- import { flexRender, Row, RowData, Table } from "@tanstack/react-table";
2
+ import { flexRender, Row, RowData } from "@tanstack/react-table";
3
3
  import { Virtualizer } from "@tanstack/react-virtual";
4
4
  import React, { Fragment } from "react";
5
5
  import {
@@ -9,38 +9,41 @@ import {
9
9
  import { TableRow } from "../TableRow/tableRow.styles";
10
10
 
11
11
  export interface TableRowsProps<T extends RowData> {
12
- tableInstance: Table<T>;
12
+ rows: Row<T>[];
13
13
  virtualizer: Virtualizer<Window, Element>;
14
14
  }
15
15
 
16
16
  export const TableRows = <T extends RowData>({
17
- tableInstance,
17
+ rows,
18
18
  virtualizer,
19
19
  }: TableRowsProps<T>): JSX.Element => {
20
- const { getRowModel } = tableInstance;
21
- const { rows } = getRowModel();
22
20
  const virtualItems = virtualizer.getVirtualItems();
23
21
  return (
24
22
  <Fragment>
25
23
  {virtualItems.map((virtualRow) => {
26
24
  const row = rows[virtualRow.index] as Row<T>;
27
- const { getIsPreview } = row;
25
+ const { getIsGrouped, getIsPreview } = row;
28
26
  return (
29
27
  <TableRow
30
28
  key={row.id}
31
29
  data-index={virtualRow.index}
30
+ isGrouped={getIsGrouped()}
32
31
  isPreview={getIsPreview()}
33
32
  ref={virtualizer.measureElement}
34
33
  >
35
- {row.getVisibleCells().map((cell) => (
36
- <TableCell
37
- key={cell.id}
38
- align={getTableCellAlign(cell.column)}
39
- padding={getTableCellPadding(cell.column.id)}
40
- >
41
- {flexRender(cell.column.columnDef.cell, cell.getContext())}
42
- </TableCell>
43
- ))}
34
+ {row.getVisibleCells().map((cell) => {
35
+ if (cell.getIsAggregated()) return null; // Display of aggregated cells is currently not supported.
36
+ if (cell.getIsPlaceholder()) return null; // Display of placeholder cells is currently not supported.
37
+ return (
38
+ <TableCell
39
+ key={cell.id}
40
+ align={getTableCellAlign(cell.column)}
41
+ padding={getTableCellPadding(cell.column.id)}
42
+ >
43
+ {flexRender(cell.column.columnDef.cell, cell.getContext())}
44
+ </TableCell>
45
+ );
46
+ })}
44
47
  </TableRow>
45
48
  );
46
49
  })}
@@ -0,0 +1,17 @@
1
+ import styled from "@emotion/styled";
2
+ import { MenuItem } from "@mui/material";
3
+
4
+ export const StyledMenuItem = styled(MenuItem)`
5
+ &:hover {
6
+ background-color: transparent;
7
+
8
+ .MuiListItemText-root {
9
+ .MuiListItemText-primary {
10
+ text-decoration: underline;
11
+ text-decoration-skip-ink: none;
12
+ text-underline-color: currentColor;
13
+ text-underline-position: from-font;
14
+ }
15
+ }
16
+ }
17
+ ` as typeof MenuItem;
@@ -0,0 +1,54 @@
1
+ import { ListItemText } from "@mui/material";
2
+ import { RowData } from "@tanstack/react-table";
3
+ import React from "react";
4
+ import { DropdownButton } from "../../../../../common/Button/components/DropdownButton/dropdownButton";
5
+ import { DropdownMenu } from "../../../../../common/DropdownMenu/dropdownMenu";
6
+ import { handleClearGroupingState } from "../../../TableFeatures/ColumnGrouping/utils";
7
+ import { StyledMenuItem } from "./columnGrouping.styles";
8
+ import { MenuItems } from "./components/MenuItems/menuItems";
9
+ import { LIST_ITEM_TEXT_PROPS, MENU_ITEM_PROPS, MENU_PROPS } from "./constants";
10
+ import { ColumnGroupingProps } from "./types";
11
+ import { getButtonLabel, getColumnGrouping } from "./utils";
12
+
13
+ export const ColumnGrouping = <T extends RowData>({
14
+ tableInstance,
15
+ }: ColumnGroupingProps<T>): JSX.Element | null => {
16
+ const {
17
+ getState,
18
+ options: { enableGrouping },
19
+ } = tableInstance;
20
+ const { grouping } = getState();
21
+ const groupingByColumnId = getColumnGrouping(tableInstance);
22
+ if (!enableGrouping) return null;
23
+ if (groupingByColumnId.size === 0) return null;
24
+ return (
25
+ <DropdownMenu
26
+ {...MENU_PROPS}
27
+ button={(props) => (
28
+ <DropdownButton {...props}>
29
+ {getButtonLabel(groupingByColumnId, grouping)}
30
+ </DropdownButton>
31
+ )}
32
+ >
33
+ {({ closeMenu }): JSX.Element[] => [
34
+ <MenuItems
35
+ key="column-grouping"
36
+ closeMenu={closeMenu}
37
+ groupingByColumnId={groupingByColumnId}
38
+ tableInstance={tableInstance}
39
+ />,
40
+ <StyledMenuItem
41
+ {...MENU_ITEM_PROPS}
42
+ key="reset-grouping-state"
43
+ disabled={grouping.length === 0}
44
+ onClick={() => {
45
+ handleClearGroupingState(tableInstance);
46
+ closeMenu();
47
+ }}
48
+ >
49
+ <ListItemText {...LIST_ITEM_TEXT_PROPS}>Clear Grouping</ListItemText>
50
+ </StyledMenuItem>,
51
+ ]}
52
+ </DropdownMenu>
53
+ );
54
+ };
@@ -0,0 +1,28 @@
1
+ import { ListItemText, MenuItem } from "@mui/material";
2
+ import { RowData } from "@tanstack/react-table";
3
+ import React from "react";
4
+ import { handleToggleGrouping } from "../../../../../TableFeatures/ColumnGrouping/utils";
5
+ import { MENU_ITEM_PROPS } from "../../constants";
6
+ import { MenuItemsProps } from "./types";
7
+
8
+ export const MenuItems = <T extends RowData>({
9
+ closeMenu,
10
+ groupingByColumnId,
11
+ tableInstance,
12
+ }: MenuItemsProps<T>): JSX.Element[] => {
13
+ const columnByMenuItem = [...groupingByColumnId.values()];
14
+ return columnByMenuItem.map(([menuItem, column]) => (
15
+ <MenuItem
16
+ {...MENU_ITEM_PROPS}
17
+ key={column.id}
18
+ disabled={!column.getCanGroup()}
19
+ onClick={() => {
20
+ handleToggleGrouping(tableInstance, column);
21
+ closeMenu();
22
+ }}
23
+ selected={column.getIsGrouped()}
24
+ >
25
+ <ListItemText disableTypography>{menuItem}</ListItemText>
26
+ </MenuItem>
27
+ ));
28
+ };
@@ -0,0 +1,8 @@
1
+ import { Column, RowData, Table } from "@tanstack/react-table";
2
+ import { UseMenu } from "../../../../../../../common/Menu/hooks/useMenu";
3
+
4
+ export interface MenuItemsProps<T extends RowData> {
5
+ closeMenu: UseMenu<HTMLButtonElement>["onClose"];
6
+ groupingByColumnId: Map<string, [string, Column<T>]>;
7
+ tableInstance: Table<T>;
8
+ }
@@ -0,0 +1,17 @@
1
+ import { ListItemTextProps, MenuItemProps, MenuProps } from "@mui/material";
2
+
3
+ export const LIST_ITEM_TEXT_PROPS: Partial<ListItemTextProps> = {
4
+ primaryTypographyProps: {
5
+ color: "primary",
6
+ component: "span",
7
+ variant: "inherit",
8
+ },
9
+ };
10
+
11
+ export const MENU_PROPS: Partial<MenuProps> = {
12
+ variant: "menu",
13
+ };
14
+
15
+ export const MENU_ITEM_PROPS: Partial<MenuItemProps> = {
16
+ component: "li",
17
+ };
@@ -0,0 +1,5 @@
1
+ import { RowData, Table } from "@tanstack/react-table";
2
+
3
+ export interface ColumnGroupingProps<T extends RowData> {
4
+ tableInstance: Table<T>;
5
+ }
@@ -0,0 +1,45 @@
1
+ import { Column, GroupingState, RowData, Table } from "@tanstack/react-table";
2
+
3
+ /**
4
+ * Retrieves the button label for the column grouping dropdown.
5
+ * Currently, the grouping state supports grouping by a single column only.
6
+ * @param groupingByColumnId - Map of column groupings by column ID.
7
+ * @param groupingState - Grouping state.
8
+ * @returns button label.
9
+ */
10
+ export function getButtonLabel<T extends RowData>(
11
+ groupingByColumnId: Map<string, [string, Column<T>]>,
12
+ groupingState: GroupingState
13
+ ): string {
14
+ const grouping = groupingByColumnId.get(groupingState[0]);
15
+ if (!grouping) return "Group by";
16
+ return `Group by: ${grouping[0]}`;
17
+ }
18
+
19
+ /**
20
+ * Retrieves a map of column groupings by column ID from the given table instance.
21
+ * Columns that are visible, group-able with a `string` header are included and are keyed by
22
+ * their column ID. The value is a tuple containing the column header and the column instance.
23
+ * @param table - Table.
24
+ * @returns map of column grouping by column id.
25
+ */
26
+ export function getColumnGrouping<T extends RowData>(
27
+ table: Table<T>
28
+ ): Map<string, [string, Column<T>]> {
29
+ const groupingByColumnId = new Map<string, [string, Column<T>]>();
30
+ for (const column of table.getAllColumns()) {
31
+ const {
32
+ columnDef: { header },
33
+ getCanGroup,
34
+ getIsVisible,
35
+ id,
36
+ } = column;
37
+ if (!getCanGroup()) continue;
38
+ if (!getIsVisible()) continue;
39
+ // Currently, headers are configured as strings.
40
+ // Only include columns that have a string header (for now).
41
+ if (typeof header !== "string") continue;
42
+ groupingByColumnId.set(id, [header, column]);
43
+ }
44
+ return groupingByColumnId;
45
+ }
@@ -1,10 +1,7 @@
1
1
  import { MenuProps as MMenuProps } from "@mui/material";
2
2
  import React from "react";
3
- import { DropdownButton as DXDropdownButton } from "../../../../../../../common/Button/components/DropdownButton/dropdownButton";
4
- import {
5
- DropdownMenuButtonProps,
6
- DropdownMenuItemProps,
7
- } from "../../../../../../../common/DropdownMenu/common/entities";
3
+ import { DropdownButton } from "../../../../../../../common/Button/components/DropdownButton/dropdownButton";
4
+ import { DropdownMenuItemProps } from "../../../../../../../common/DropdownMenu/common/entities";
8
5
  import { DropdownMenuProps as DXDropdownMenuProps } from "../../../../../../../common/DropdownMenu/dropdownMenu";
9
6
  import { DropdownMenu as RowSelectionDropdownMenu } from "./dropdownMenu.styles";
10
7
 
@@ -14,16 +11,15 @@ const DEFAULT_MENU_PROPS: Partial<MMenuProps> = {
14
11
  };
15
12
 
16
13
  export interface DropdownMenuProps {
17
- Button?: DXDropdownMenuProps["Button"];
18
- buttonLabel?: string;
14
+ button?: DXDropdownMenuProps["button"];
19
15
  children: ({ closeMenu }: DropdownMenuItemProps) => JSX.Element[];
20
16
  className?: string;
21
17
  }
22
18
 
23
19
  export const DropdownMenu = ({
24
- Button = (props: DropdownMenuButtonProps): JSX.Element =>
25
- renderButton({ children: buttonLabel, ...props }),
26
- buttonLabel = "Edit",
20
+ button = (props): JSX.Element => (
21
+ <DropdownButton {...props}>Edit</DropdownButton>
22
+ ),
27
23
  children,
28
24
  className,
29
25
  ...props /* Spread props to allow for Mui Menu specific prop overrides e.g. "anchorOrigin". */
@@ -32,19 +28,10 @@ export const DropdownMenu = ({
32
28
  <RowSelectionDropdownMenu
33
29
  {...DEFAULT_MENU_PROPS}
34
30
  className={className}
35
- Button={Button}
31
+ button={button}
36
32
  {...props}
37
33
  >
38
34
  {({ closeMenu }): JSX.Element[] => children({ closeMenu })}
39
35
  </RowSelectionDropdownMenu>
40
36
  );
41
37
  };
42
-
43
- /**
44
- * Return the dropdown button.
45
- * @param props - Button props e.g. "onClick".
46
- * @returns button element.
47
- */
48
- function renderButton(props: DropdownMenuButtonProps): JSX.Element {
49
- return <DXDropdownButton {...props} />;
50
- }
@@ -7,6 +7,7 @@ import { getEditColumnOptions, isAnyRowSelected } from "../../common/utils";
7
7
  import { CheckboxMenu } from "../CheckboxMenu/checkboxMenu";
8
8
  import { DownloadEntityResults } from "../DownloadEntityResults/downloadEntityResults";
9
9
  import { PaginationSummary } from "../PaginationSummary/paginationSummary";
10
+ import { ColumnGrouping } from "./components/ColumnGrouping/columnGrouping";
10
11
  import { RowPreview } from "./components/RowPreview/rowPreview";
11
12
  import { RowSelection } from "./components/RowSelection/rowSelection";
12
13
  import { Toolbar, ToolbarActions } from "./tableToolbar.styles";
@@ -25,13 +26,17 @@ export const TableToolbar = <T extends RowData>({
25
26
  const { exploreState } = useExploreState();
26
27
  const { paginationState } = exploreState;
27
28
  const { currentPage, pages, pageSize, rows } = paginationState;
28
- const { getSelectedRowModel, resetColumnVisibility } = tableInstance;
29
+ const {
30
+ getSelectedRowModel,
31
+ options: { enableGrouping },
32
+ resetColumnVisibility,
33
+ } = tableInstance;
29
34
  const { enableDownload, rowPreviewView } = listView || {};
30
35
  const isLastPage = currentPage === pages;
31
36
  const editColumnOptions = getEditColumnOptions(tableInstance);
32
37
  const showToolbar =
33
38
  rowDirection === ROW_DIRECTION.DEFAULT &&
34
- (editColumnOptions || enableDownload);
39
+ (editColumnOptions || enableDownload || enableGrouping);
35
40
 
36
41
  /**
37
42
  * Resets column visibility to default state.
@@ -63,6 +68,7 @@ export const TableToolbar = <T extends RowData>({
63
68
  rows={tableInstance.getFilteredRowModel().rows}
64
69
  />
65
70
  )}
71
+ <ColumnGrouping tableInstance={tableInstance} />
66
72
  <CheckboxMenu
67
73
  label="Edit Columns"
68
74
  onReset={onResetColumnVisibility}