@orchestrator-ui/orchestrator-ui-components 1.38.2 → 2.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 (93) hide show
  1. package/.turbo/turbo-build.log +7 -7
  2. package/.turbo/turbo-lint.log +5 -2
  3. package/.turbo/turbo-test.log +12 -12
  4. package/CHANGELOG.md +18 -0
  5. package/dist/index.d.ts +246 -170
  6. package/dist/index.js +4350 -3504
  7. package/package.json +1 -1
  8. package/src/components/WfoForms/formFields/SubscriptionSummaryField.tsx +6 -5
  9. package/src/components/WfoPageTemplate/WfoPageHeader/WfoPageHeader.tsx +3 -1
  10. package/src/components/WfoPageTemplate/WfoSidebar/WfoCopyright.tsx +1 -1
  11. package/src/components/WfoProcessList/WfoProcessesList.tsx +52 -51
  12. package/src/components/WfoRadioDropdown/WfoRadioDropdown.tsx +88 -0
  13. package/src/components/WfoRadioDropdown/index.ts +1 -0
  14. package/src/components/WfoSubscription/WfoInSyncField.tsx +13 -9
  15. package/src/components/WfoSubscription/WfoInUseByRelations.tsx +4 -2
  16. package/src/components/WfoSubscription/WfoProcessesTimeline.tsx +76 -29
  17. package/src/components/WfoSubscription/WfoRelatedSubscriptions.tsx +50 -56
  18. package/src/components/WfoSubscription/WfoSubscriptionDetailTree.tsx +17 -9
  19. package/src/components/WfoSubscription/WfoSubscriptionGeneral.tsx +18 -156
  20. package/src/components/WfoSubscription/WfoSubscriptionGeneralSections/WfoSubscriptionDetailSection.tsx +113 -0
  21. package/src/components/WfoSubscription/WfoSubscriptionGeneralSections/WfoSubscriptionFixedInputSection.tsx +28 -0
  22. package/src/components/WfoSubscription/WfoSubscriptionGeneralSections/WfoSubscriptionMetadataSection.tsx +29 -0
  23. package/src/components/WfoSubscription/WfoSubscriptionGeneralSections/WfoSubscriptionProductInfoSection.tsx +55 -0
  24. package/src/components/WfoSubscription/WfoSubscriptionGeneralSections/index.ts +4 -0
  25. package/src/components/WfoSubscription/index.ts +3 -0
  26. package/src/components/WfoSubscription/overrides/index.ts +1 -0
  27. package/src/components/WfoSubscription/styles.ts +4 -1
  28. package/src/components/WfoSubscription/utils/utils.spec.ts +0 -13
  29. package/src/components/WfoSubscription/utils/utils.ts +18 -5
  30. package/src/components/WfoSubscriptionsList/WfoSubscriptionsList.tsx +105 -100
  31. package/src/components/WfoTable/{WfoTableWithFilter/WfoTableWithFilter.tsx → WfoAdvancedTable/WfoAdvancedTable.tsx} +65 -123
  32. package/src/components/WfoTable/WfoAdvancedTable/getRowDetailData.tsx +55 -0
  33. package/src/components/WfoTable/WfoAdvancedTable/index.ts +4 -0
  34. package/src/components/WfoTable/WfoAdvancedTable/toSortedTableColumnConfig.ts +12 -0
  35. package/src/components/WfoTable/WfoAdvancedTable/types.ts +23 -0
  36. package/src/components/WfoTable/WfoStatusColorField/WfoStatusColorField.tsx +16 -0
  37. package/src/components/WfoTable/WfoStatusColorField/index.ts +1 -0
  38. package/src/components/WfoTable/WfoStatusColorField/styles.ts +20 -0
  39. package/src/components/WfoTable/WfoTable/WfoExpandedRow.tsx +33 -0
  40. package/src/components/WfoTable/WfoTable/WfoGroupedTable/WfoExpandableRow.tsx +48 -0
  41. package/src/components/WfoTable/WfoTable/WfoGroupedTable/WfoExpandedGroupRow.tsx +71 -0
  42. package/src/components/WfoTable/WfoTable/WfoGroupedTable/WfoGroupedTable.tsx +100 -0
  43. package/src/components/WfoTable/WfoTable/WfoGroupedTable/WfoGroupedTableGroups.tsx +74 -0
  44. package/src/components/WfoTable/WfoTable/WfoGroupedTable/styles.ts +39 -0
  45. package/src/components/WfoTable/WfoTable/WfoGroupedTable/useGroupedTableConfig.tsx +184 -0
  46. package/src/components/WfoTable/WfoTable/WfoGroupedTable/utils.spec.ts +133 -0
  47. package/src/components/WfoTable/WfoTable/WfoGroupedTable/utils.ts +41 -0
  48. package/src/components/WfoTable/WfoTable/WfoMultilineCell.tsx +13 -0
  49. package/src/components/WfoTable/WfoTable/WfoTable.tsx +201 -0
  50. package/src/components/WfoTable/WfoTable/WfoTableDataRows.tsx +112 -0
  51. package/src/components/WfoTable/{WfoBasicTable → WfoTable/WfoTableHeaderCell}/WfoSortDirectionIcon.tsx +3 -3
  52. package/src/components/WfoTable/{WfoBasicTable → WfoTable/WfoTableHeaderCell}/WfoTableHeaderCell.tsx +4 -3
  53. package/src/components/WfoTable/WfoTable/WfoTableHeaderCell/index.ts +2 -0
  54. package/src/components/WfoTable/WfoTable/WfoTableHeaderRow.tsx +114 -0
  55. package/src/components/WfoTable/WfoTable/WfoTruncateCell.tsx +23 -0
  56. package/src/components/WfoTable/WfoTable/constants.ts +1 -0
  57. package/src/components/WfoTable/WfoTable/index.ts +14 -0
  58. package/src/components/WfoTable/WfoTable/styles.ts +117 -0
  59. package/src/components/WfoTable/WfoTable/utils.spec.ts +79 -0
  60. package/src/components/WfoTable/WfoTable/utils.ts +78 -0
  61. package/src/components/WfoTable/WfoTableSettingsModal/WfoTableSettingsModal.tsx +13 -12
  62. package/src/components/WfoTable/WfoTableWithFilter/index.ts +1 -1
  63. package/src/components/WfoTable/index.ts +4 -5
  64. package/src/components/WfoTable/utils/columns.ts +1 -48
  65. package/src/components/WfoTable/utils/tableUtils.ts +13 -10
  66. package/src/components/WfoTree/WfoTreeBranch.tsx +10 -1
  67. package/src/components/WfoTree/WfoTreeNode.tsx +8 -57
  68. package/src/components/WfoTree/WfoTreeNodeListItem.tsx +65 -0
  69. package/src/components/WfoTree/styles.ts +28 -4
  70. package/src/components/index.ts +1 -0
  71. package/src/configuration/policy-resources.ts +1 -0
  72. package/src/configuration/version.ts +1 -1
  73. package/src/icons/WfoXMarkSmall.tsx +29 -0
  74. package/src/messages/en-GB.json +4 -0
  75. package/src/messages/nl-NL.json +4 -0
  76. package/src/pages/metadata/WfoProductBlocksPage.tsx +42 -59
  77. package/src/pages/metadata/WfoProductsPage.tsx +41 -47
  78. package/src/pages/metadata/WfoResourceTypesPage.tsx +26 -35
  79. package/src/pages/metadata/WfoTasksPage.tsx +35 -33
  80. package/src/pages/metadata/WfoWorkflowsPage.tsx +33 -29
  81. package/src/pages/tasks/WfoTasksListPage.tsx +25 -19
  82. package/src/utils/getObjectKeys.ts +3 -0
  83. package/src/utils/index.ts +5 -3
  84. package/src/components/WfoTable/WfoBasicTable/WfoBasicTable.tsx +0 -194
  85. package/src/components/WfoTable/WfoBasicTable/WfoStatusColorField.tsx +0 -21
  86. package/src/components/WfoTable/WfoBasicTable/index.ts +0 -5
  87. package/src/components/WfoTable/WfoDataGridTable/WfoDataGridTable.stories.tsx +0 -136
  88. package/src/components/WfoTable/WfoDataGridTable/WfoDataGridTable.tsx +0 -146
  89. package/src/components/WfoTable/WfoDataGridTable/WfodataGridColumns.spec.ts +0 -113
  90. package/src/components/WfoTable/WfoDataGridTable/WfodataGridColumns.ts +0 -81
  91. package/src/components/WfoTable/utils/mapSortableAndFilterableValuesToTableColumnConfig.spec.ts +0 -52
  92. package/src/components/WfoTable/utils/mapSortableAndFilterableValuesToTableColumnConfig.ts +0 -23
  93. /package/src/components/WfoTable/{WfoBasicTable → WfoTable/WfoTableHeaderCell}/styles.ts +0 -0
@@ -0,0 +1,74 @@
1
+ import React, { Ref, useImperativeHandle } from 'react';
2
+
3
+ import { useWithOrchestratorTheme } from '@/hooks';
4
+
5
+ import { WfoTableDataRows } from '../WfoTableDataRows';
6
+ import { getWfoTableStyles } from '../styles';
7
+ import { WfoGroupedTableProps } from './WfoGroupedTable';
8
+ import { getWfoGroupedTableStyles } from './styles';
9
+ import { useGroupedTableConfig } from './useGroupedTableConfig';
10
+
11
+ export type WfoGroupedTableGroupsRef = {
12
+ expandAllRows: () => void;
13
+ };
14
+
15
+ export type WfoGroupedTableGroupsProps<T extends object> = Pick<
16
+ WfoGroupedTableProps<T>,
17
+ 'columnConfig' | 'data' | 'groupNameLabel'
18
+ > & {
19
+ nestingLevel?: number;
20
+ onExpandRowChange: (isAllExpanded: boolean) => void;
21
+ };
22
+
23
+ export const WfoGroupedTableGroups = React.forwardRef(
24
+ <T extends object>(
25
+ {
26
+ data,
27
+ groupNameLabel,
28
+ columnConfig,
29
+ nestingLevel = 1,
30
+ onExpandRowChange,
31
+ }: WfoGroupedTableGroupsProps<T>,
32
+ reference: Ref<WfoGroupedTableGroupsRef>,
33
+ ) => {
34
+ const { expandedRowStyle } =
35
+ useWithOrchestratorTheme(getWfoTableStyles);
36
+ const { getNestingStyle } = useWithOrchestratorTheme(
37
+ getWfoGroupedTableStyles,
38
+ );
39
+
40
+ const {
41
+ groups,
42
+ groupColumnConfig,
43
+ uniqueRowIdToExpandedRowMap,
44
+ toggleExpandedRow,
45
+ expandAllRows,
46
+ } = useGroupedTableConfig({
47
+ data,
48
+ groupNameLabel,
49
+ columnConfig,
50
+ nestingLevel,
51
+ notifyParent: onExpandRowChange,
52
+ });
53
+
54
+ useImperativeHandle(reference, () => ({
55
+ expandAllRows: () => {
56
+ expandAllRows();
57
+ },
58
+ }));
59
+
60
+ return (
61
+ <WfoTableDataRows
62
+ css={[expandedRowStyle, getNestingStyle(nestingLevel)]}
63
+ data={groups}
64
+ columnConfig={groupColumnConfig}
65
+ onRowClick={(row) => toggleExpandedRow(row.groupName)}
66
+ rowExpandingConfiguration={{
67
+ uniqueRowId: 'groupName',
68
+ uniqueRowIdToExpandedRowMap,
69
+ }}
70
+ />
71
+ );
72
+ },
73
+ );
74
+ WfoGroupedTableGroups.displayName = 'WfoGroupedTableGroups';
@@ -0,0 +1,39 @@
1
+ import { css } from '@emotion/react';
2
+
3
+ import { WfoTheme } from '@/hooks';
4
+
5
+ export const getWfoGroupedTableStyles = ({ theme }: WfoTheme) => {
6
+ // Matches the default width of a EuiButtonIcon component
7
+ const expandRowButtonWidth = '24px';
8
+ const marginBetweenButtonAndGroupLabel = theme.size.m;
9
+
10
+ const innerTableHeaderStyle = css({
11
+ fontSize: theme.size.m,
12
+ textAlign: 'left',
13
+ backgroundColor: theme.colors.lightestShade,
14
+ });
15
+
16
+ const expandableRowContainerStyle = css({
17
+ display: 'flex',
18
+ alignItems: 'center',
19
+ });
20
+
21
+ const expandableRowTextStyle = css({
22
+ marginLeft: marginBetweenButtonAndGroupLabel,
23
+ fontWeight: theme.font.weight.medium,
24
+ });
25
+
26
+ const getNestingStyle = (nestingLevel: number) =>
27
+ css({
28
+ 'th:first-child > *:first-child, td:first-child > *:first-child': {
29
+ marginLeft: `calc(${nestingLevel} * (${expandRowButtonWidth} + ${marginBetweenButtonAndGroupLabel}))`,
30
+ },
31
+ });
32
+
33
+ return {
34
+ innerTableHeaderStyle,
35
+ expandableRowContainerStyle,
36
+ expandableRowTextStyle,
37
+ getNestingStyle,
38
+ };
39
+ };
@@ -0,0 +1,184 @@
1
+ import React, { ReactNode, useEffect, useRef, useState } from 'react';
2
+
3
+ import { getObjectKeys } from '@/utils';
4
+
5
+ import { ColumnType, WfoTableColumnConfig } from '../WfoTable';
6
+ import { WfoExpandableRow } from './WfoExpandableRow';
7
+ import { WfoExpandedGroupRow } from './WfoExpandedGroupRow';
8
+ import { GroupType, WfoGroupedTableProps } from './WfoGroupedTable';
9
+ import { WfoGroupedTableGroupsRef } from './WfoGroupedTableGroups';
10
+ import { getTotalNumberOfRows } from './utils';
11
+
12
+ export type UseGroupedTableConfigProps<T extends object> = Pick<
13
+ WfoGroupedTableProps<T>,
14
+ 'columnConfig' | 'data' | 'groupNameLabel'
15
+ > & {
16
+ nestingLevel?: number;
17
+ notifyParent?: (meAndAllMySubgroupsAreExpanded: boolean) => void;
18
+ };
19
+
20
+ export const useGroupedTableConfig = <T extends object>({
21
+ data,
22
+ columnConfig,
23
+ groupNameLabel,
24
+ nestingLevel = 0,
25
+ notifyParent,
26
+ }: UseGroupedTableConfigProps<T>) => {
27
+ const groupReferences = useRef(new Map<string, WfoGroupedTableGroupsRef>());
28
+
29
+ const [expandedRowIds, setExpandedRowIds] = useState<string[]>([]);
30
+ const [isAllGroupsExpanded, setIsAllGroupsExpanded] = useState(false);
31
+ const [isAllSubgroupsExpanded, setIsAllSubgroupsExpanded] = useState<
32
+ string[]
33
+ >([]);
34
+
35
+ // Expanding all children needs another render cycle, because they do not exist in the DOM yet
36
+ const [isExpanding, setIsExpanding] = useState(false);
37
+
38
+ useEffect(() => {
39
+ if (isExpanding) {
40
+ groupReferences.current.forEach((ref) => {
41
+ ref.expandAllRows();
42
+ });
43
+ setIsExpanding(false);
44
+ }
45
+ }, [isExpanding]);
46
+
47
+ useEffect(() => {
48
+ if (notifyParent) {
49
+ notifyParent(
50
+ isAllGroupsExpanded &&
51
+ groups.every(({ groupName }) =>
52
+ isAllSubgroupsExpanded.includes(groupName),
53
+ ),
54
+ );
55
+ }
56
+ }, [isAllSubgroupsExpanded, isAllGroupsExpanded]);
57
+
58
+ const groups: GroupType[] = getObjectKeys(data).map((key) => ({
59
+ groupName: key.toString(),
60
+ }));
61
+ const numberOfColumnsInnerTable = Object.keys(columnConfig).length;
62
+
63
+ const groupColumnConfig: WfoTableColumnConfig<GroupType> = {
64
+ groupName: {
65
+ columnType: ColumnType.CONTROL,
66
+ label: groupNameLabel,
67
+ numberOfColumnsToSpan: numberOfColumnsInnerTable,
68
+ renderControl: ({ groupName }) => {
69
+ const isExpanded = expandedRowIds.includes(groupName);
70
+ const groupData = data[groupName];
71
+
72
+ const numberOfRowsInGroup = Array.isArray(groupData)
73
+ ? groupData.length
74
+ : getTotalNumberOfRows(groupData);
75
+
76
+ return (
77
+ <WfoExpandableRow
78
+ groupName={groupName}
79
+ isExpanded={isExpanded}
80
+ updateExpandedRows={toggleExpandedRow}
81
+ numberOfRowsInGroup={numberOfRowsInGroup}
82
+ />
83
+ );
84
+ },
85
+ },
86
+ };
87
+
88
+ const getReferenceCallbackForRow = (groupName: string) => {
89
+ return (ref: WfoGroupedTableGroupsRef | null) => {
90
+ if (ref) {
91
+ groupReferences.current.set(groupName, ref);
92
+ } else if (groupName && groupReferences.current.has(groupName)) {
93
+ groupReferences.current.delete(groupName);
94
+ }
95
+ };
96
+ };
97
+
98
+ const uniqueRowIdToExpandedRowMap = expandedRowIds.reduce<
99
+ Record<string, ReactNode>
100
+ >((accumulator, groupName) => {
101
+ const groupData = data[groupName];
102
+
103
+ accumulator[groupName] = (
104
+ <WfoExpandedGroupRow
105
+ key={groupName}
106
+ ref={getReferenceCallbackForRow(groupName)}
107
+ data={groupData}
108
+ columnConfig={columnConfig}
109
+ groupNameLabel={groupNameLabel}
110
+ nestingLevel={nestingLevel + 1}
111
+ onExpandRowChange={(isAllSubgroupsExpanded) =>
112
+ setIsAllSubgroupsExpanded((prevState) => {
113
+ if (isAllSubgroupsExpanded) {
114
+ return [...prevState, groupName];
115
+ }
116
+
117
+ return prevState.filter((id) => id !== groupName);
118
+ })
119
+ }
120
+ />
121
+ );
122
+
123
+ return accumulator;
124
+ }, {});
125
+
126
+ const toggleExpandedRow = (groupName: string) => {
127
+ const groupData = data[groupName];
128
+ const groupHasData = Array.isArray(groupData)
129
+ ? groupData.length > 0
130
+ : Object.keys(groupData).length > 0;
131
+
132
+ if (groupHasData) {
133
+ setExpandedRowIds((prevState) => {
134
+ // Collapse group
135
+ if (prevState.includes(groupName)) {
136
+ setIsAllGroupsExpanded(false);
137
+ return prevState.filter((value) => value !== groupName);
138
+ }
139
+
140
+ // Expand group
141
+ if (prevState.length + 1 >= groups.length) {
142
+ setIsAllGroupsExpanded(true);
143
+ }
144
+ if (Array.isArray(groupData)) {
145
+ setIsAllSubgroupsExpanded((prevState) => {
146
+ return [...prevState, groupName];
147
+ });
148
+ }
149
+ return [...prevState, groupName];
150
+ });
151
+ }
152
+ };
153
+
154
+ const expandAllRows = () => {
155
+ setExpandedRowIds(() => groups.map(({ groupName }) => groupName));
156
+ setIsExpanding(true);
157
+ setIsAllGroupsExpanded(true);
158
+ setIsAllSubgroupsExpanded(() =>
159
+ groups.map(({ groupName }) => groupName),
160
+ );
161
+ };
162
+
163
+ const collapseAllRows = () => {
164
+ setExpandedRowIds([]);
165
+ setIsAllGroupsExpanded(false);
166
+ };
167
+
168
+ const isAllGroupsAndSubgroupsExpanded =
169
+ isAllGroupsExpanded &&
170
+ groups.every(({ groupName }) =>
171
+ isAllSubgroupsExpanded.includes(groupName),
172
+ );
173
+
174
+ return {
175
+ groups,
176
+ numberOfColumnsInnerTable,
177
+ groupColumnConfig,
178
+ uniqueRowIdToExpandedRowMap,
179
+ isAllGroupsAndSubgroupsExpanded,
180
+ toggleExpandedRow,
181
+ expandAllRows,
182
+ collapseAllRows,
183
+ };
184
+ };
@@ -0,0 +1,133 @@
1
+ import { GroupedData } from './WfoGroupedTable';
2
+ import { getTotalNumberOfRows, groupData } from './utils';
3
+
4
+ type TestObject = {
5
+ name: string;
6
+ age: number;
7
+ group: 'group1' | 'group2';
8
+ subGroup: 'subGroup1' | 'subGroup2';
9
+ };
10
+
11
+ describe('WfoGroupedTable - utils', () => {
12
+ describe('groupData()', () => {
13
+ it('successfully groups the data', () => {
14
+ // Given
15
+ const testData: TestObject[] = [
16
+ {
17
+ name: 'John',
18
+ age: 25,
19
+ group: 'group2',
20
+ subGroup: 'subGroup1',
21
+ },
22
+ {
23
+ name: 'Bob',
24
+ age: 40,
25
+ group: 'group2',
26
+ subGroup: 'subGroup2',
27
+ },
28
+ {
29
+ name: 'Jane',
30
+ age: 30,
31
+ group: 'group2',
32
+ subGroup: 'subGroup2',
33
+ },
34
+ {
35
+ name: 'Tom',
36
+ age: 35,
37
+ group: 'group1',
38
+ subGroup: 'subGroup1',
39
+ },
40
+ ];
41
+
42
+ // When
43
+ const result = groupData(testData, [
44
+ (data) => data.group,
45
+ (data) => data.subGroup,
46
+ ]);
47
+
48
+ // Then
49
+ const expected: GroupedData<TestObject> = {
50
+ group1: {
51
+ subGroup1: [
52
+ {
53
+ name: 'Tom',
54
+ age: 35,
55
+ group: 'group1',
56
+ subGroup: 'subGroup1',
57
+ },
58
+ ],
59
+ },
60
+ group2: {
61
+ subGroup1: [
62
+ {
63
+ name: 'John',
64
+ age: 25,
65
+ group: 'group2',
66
+ subGroup: 'subGroup1',
67
+ },
68
+ ],
69
+ subGroup2: [
70
+ {
71
+ name: 'Bob',
72
+ age: 40,
73
+ group: 'group2',
74
+ subGroup: 'subGroup2',
75
+ },
76
+ {
77
+ name: 'Jane',
78
+ age: 30,
79
+ group: 'group2',
80
+ subGroup: 'subGroup2',
81
+ },
82
+ ],
83
+ },
84
+ };
85
+ expect(result).toEqual(expected);
86
+ });
87
+ });
88
+
89
+ describe('getTotalNumberOfRows()', () => {
90
+ it('counts the total number of data rows', () => {
91
+ const testData: GroupedData<TestObject> = {
92
+ group1: {
93
+ subGroup1: [
94
+ {
95
+ name: 'John',
96
+ age: 25,
97
+ group: 'group1',
98
+ subGroup: 'subGroup1',
99
+ },
100
+ {
101
+ name: 'Bob',
102
+ age: 40,
103
+ group: 'group1',
104
+ subGroup: 'subGroup1',
105
+ },
106
+ ],
107
+ subGroup2: [
108
+ {
109
+ name: 'Jane',
110
+ age: 30,
111
+ group: 'group1',
112
+ subGroup: 'subGroup2',
113
+ },
114
+ ],
115
+ },
116
+ group2: {
117
+ subGroup1: [
118
+ {
119
+ name: 'Tom',
120
+ age: 35,
121
+ group: 'group1',
122
+ subGroup: 'subGroup1',
123
+ },
124
+ ],
125
+ },
126
+ };
127
+
128
+ const result = getTotalNumberOfRows(testData);
129
+
130
+ expect(result).toEqual(4);
131
+ });
132
+ });
133
+ });
@@ -0,0 +1,41 @@
1
+ import { GroupedData } from './WfoGroupedTable';
2
+
3
+ export const groupData = <T>(
4
+ data: T[],
5
+ groupByFunctions: Array<(data: T) => string>,
6
+ ): GroupedData<T> => {
7
+ const groupedData = data.reduce<Record<string, T[]>>(
8
+ (groupedData, relatedSubscription) => {
9
+ // In case the array of groupedByFunctions is empty
10
+ const groupName =
11
+ groupByFunctions[0]?.(relatedSubscription) ?? 'Ungrouped';
12
+
13
+ return {
14
+ ...groupedData,
15
+ [groupName]: [
16
+ ...(groupedData[groupName] || []),
17
+ relatedSubscription,
18
+ ],
19
+ };
20
+ },
21
+ {},
22
+ );
23
+
24
+ if (groupByFunctions.length <= 1) {
25
+ return groupedData;
26
+ }
27
+
28
+ const entries = Object.entries(groupedData).map(([key, value]) => [
29
+ key,
30
+ groupData(value, groupByFunctions.slice(1)),
31
+ ]);
32
+ return Object.fromEntries(entries);
33
+ };
34
+
35
+ export const getTotalNumberOfRows = <T>(groupedData: GroupedData<T>): number =>
36
+ Object.entries(groupedData).reduce((totalEntries, entry) => {
37
+ const value = entry[1];
38
+ return Array.isArray(value)
39
+ ? totalEntries + value.length
40
+ : totalEntries + getTotalNumberOfRows(value);
41
+ }, 0);
@@ -0,0 +1,13 @@
1
+ import React, { FC, ReactElement } from 'react';
2
+
3
+ export type WfoMultilineCellProps = {
4
+ children?: ReactElement | string | null;
5
+ };
6
+
7
+ export const WfoMultilineCell: FC<WfoMultilineCellProps> = ({ children }) => {
8
+ if (!children) {
9
+ return null;
10
+ }
11
+
12
+ return <p css={{ textWrap: 'wrap' }}>{children}</p>;
13
+ };
@@ -0,0 +1,201 @@
1
+ import React, { CSSProperties, ReactNode } from 'react';
2
+
3
+ import { useTranslations } from 'next-intl';
4
+
5
+ import { EuiSpacer, EuiTablePagination, useEuiScrollBar } from '@elastic/eui';
6
+
7
+ import { useWithOrchestratorTheme } from '@/hooks';
8
+
9
+ import {
10
+ TableColumnKeys,
11
+ WfoDataSearch,
12
+ WfoDataSorting,
13
+ } from '../utils/columns';
14
+ import { DEFAULT_PAGE_SIZES } from '../utils/constants';
15
+ import { WfoTableDataRows } from './WfoTableDataRows';
16
+ import { WfoTableHeaderRow } from './WfoTableHeaderRow';
17
+ import { getWfoTableStyles } from './styles';
18
+ import { getSortedVisibleColumns } from './utils';
19
+
20
+ export type Pagination = {
21
+ pageSize: number;
22
+ pageIndex: number;
23
+ totalItemCount: number;
24
+ pageSizeOptions?: number[];
25
+ onChangeItemsPerPage?: (pageSize: number) => void;
26
+ onChangePage?: (pageIndex: number) => void;
27
+ };
28
+
29
+ export enum ColumnType {
30
+ DATA = 'data',
31
+ CONTROL = 'control',
32
+ }
33
+
34
+ type CommonTableColumnConfigItemProps = {
35
+ numberOfColumnsToSpan?: number;
36
+ disableDefaultCellStyle?: boolean;
37
+ width?: CSSProperties['width'];
38
+ };
39
+
40
+ export type WfoTableDataColumnConfigItem<
41
+ T extends object,
42
+ Property extends keyof T,
43
+ > = CommonTableColumnConfigItemProps & {
44
+ columnType: ColumnType.DATA;
45
+ label: string;
46
+ isSortable?: boolean;
47
+ isFilterable?: boolean;
48
+ renderData?: (cellValue: T[Property], row: T) => ReactNode;
49
+ };
50
+
51
+ export type WfoTableControlColumnConfigItem<T extends object> =
52
+ CommonTableColumnConfigItemProps & {
53
+ columnType: ColumnType.CONTROL;
54
+ label?: string;
55
+ renderControl: (row: T) => ReactNode;
56
+ };
57
+
58
+ export type WfoTableDataColumnConfig<T extends object> = {
59
+ [Property in keyof T]:
60
+ | WfoTableDataColumnConfigItem<T, Property>
61
+ | WfoTableControlColumnConfigItem<T>;
62
+ };
63
+
64
+ export type WfoTableControlColumnConfig<T extends object> = {
65
+ [key: string]: WfoTableControlColumnConfigItem<T>; // from WfoTableColumnConfig -- consider extracting type
66
+ };
67
+
68
+ // Applying "Partial" since data should not always be shown in the table, but can still be needed for rendering
69
+ // Not providing a config for a property of T means that the column will not be shown in the table
70
+ export type WfoTableColumnConfig<T extends object> = Partial<
71
+ WfoTableDataColumnConfig<T> | WfoTableControlColumnConfig<T>
72
+ >;
73
+
74
+ export type WfoTableProps<T extends object> = {
75
+ data: T[];
76
+ columnConfig: WfoTableColumnConfig<T>;
77
+ hiddenColumns?: TableColumnKeys<T>;
78
+ columnOrder?: TableColumnKeys<T>;
79
+ isLoading?: boolean;
80
+ dataSorting?: WfoDataSorting<T>[];
81
+ rowExpandingConfiguration?: {
82
+ uniqueRowId: keyof WfoTableColumnConfig<T>;
83
+ uniqueRowIdToExpandedRowMap: Record<string, ReactNode>;
84
+ };
85
+ pagination?: Pagination;
86
+ overrideHeader?: (
87
+ tableHeaderEntries: Array<
88
+ [
89
+ string,
90
+ (
91
+ | WfoTableControlColumnConfigItem<T>
92
+ | WfoTableDataColumnConfigItem<T, keyof T>
93
+ ),
94
+ ]
95
+ >,
96
+ ) => ReactNode;
97
+ onRowClick?: (row: T) => void;
98
+ onUpdateDataSorting?: (updatedDataSorting: WfoDataSorting<T>) => void;
99
+ onUpdateDataSearch?: (updatedDataSearch: WfoDataSearch<T>) => void;
100
+ className?: string;
101
+ };
102
+
103
+ export const WfoTable = <T extends object>({
104
+ data,
105
+ columnConfig,
106
+ hiddenColumns = [],
107
+ columnOrder = [],
108
+ isLoading = false,
109
+ dataSorting = [],
110
+ rowExpandingConfiguration,
111
+ pagination,
112
+ overrideHeader,
113
+ onUpdateDataSorting,
114
+ onUpdateDataSearch,
115
+ onRowClick,
116
+ className,
117
+ }: WfoTableProps<T>) => {
118
+ const {
119
+ tableContainerStyle,
120
+ tableStyle,
121
+ headerStyle,
122
+ bodyLoadingStyle,
123
+ cellStyle,
124
+ rowStyle,
125
+ emptyTableMessageStyle,
126
+ } = useWithOrchestratorTheme(getWfoTableStyles);
127
+ const t = useTranslations('common');
128
+
129
+ const sortedVisibleColumns = getSortedVisibleColumns(
130
+ columnConfig,
131
+ columnOrder,
132
+ hiddenColumns,
133
+ );
134
+
135
+ return (
136
+ <>
137
+ <div css={[tableContainerStyle, useEuiScrollBar()]}>
138
+ <table className={className} css={tableStyle}>
139
+ {overrideHeader ? (
140
+ overrideHeader(sortedVisibleColumns)
141
+ ) : (
142
+ <thead css={headerStyle}>
143
+ <WfoTableHeaderRow
144
+ columnConfig={columnConfig}
145
+ hiddenColumns={hiddenColumns}
146
+ columnOrder={columnOrder}
147
+ dataSorting={dataSorting}
148
+ onUpdateDataSorting={onUpdateDataSorting}
149
+ onUpdateDataSearch={onUpdateDataSearch}
150
+ />
151
+ </thead>
152
+ )}
153
+ {data.length === 0 ? (
154
+ <tbody css={isLoading && bodyLoadingStyle}>
155
+ <tr css={rowStyle}>
156
+ <td
157
+ colSpan={sortedVisibleColumns.length}
158
+ css={[cellStyle, emptyTableMessageStyle]}
159
+ >
160
+ {isLoading
161
+ ? t('loading')
162
+ : t('noItemsFound')}
163
+ </td>
164
+ </tr>
165
+ </tbody>
166
+ ) : (
167
+ <tbody css={isLoading && bodyLoadingStyle}>
168
+ <WfoTableDataRows
169
+ data={data}
170
+ columnConfig={columnConfig}
171
+ hiddenColumns={hiddenColumns}
172
+ columnOrder={columnOrder}
173
+ rowExpandingConfiguration={
174
+ rowExpandingConfiguration
175
+ }
176
+ onRowClick={onRowClick}
177
+ />
178
+ </tbody>
179
+ )}
180
+ </table>
181
+ </div>
182
+ {pagination && (
183
+ <>
184
+ <EuiSpacer size="xs" />
185
+ <EuiTablePagination
186
+ pageCount={Math.ceil(
187
+ pagination.totalItemCount / pagination.pageSize,
188
+ )}
189
+ activePage={pagination.pageIndex}
190
+ itemsPerPage={pagination.pageSize}
191
+ itemsPerPageOptions={
192
+ pagination.pageSizeOptions ?? DEFAULT_PAGE_SIZES
193
+ }
194
+ onChangePage={pagination.onChangePage}
195
+ onChangeItemsPerPage={pagination.onChangeItemsPerPage}
196
+ />
197
+ </>
198
+ )}
199
+ </>
200
+ );
201
+ };