@onewelcome/react-lib-components 8.1.2 → 8.2.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 (75) hide show
  1. package/dist/cjs/DataGrid/DataGrid.cjs.js +1 -1
  2. package/dist/cjs/DataGrid/DataGrid.cjs.js.map +1 -1
  3. package/dist/cjs/DataGrid/DataGridBody/DataGridBody.cjs.js +1 -1
  4. package/dist/cjs/DataGrid/DataGridBody/DataGridBody.cjs.js.map +1 -1
  5. package/dist/cjs/DataGrid/DataGridBody/DataGridCell/DataGridCell.cjs.js +1 -1
  6. package/dist/cjs/DataGrid/DataGridBody/DataGridCell/DataGridCell.cjs.js.map +1 -1
  7. package/dist/cjs/DataGrid/DataGridBody/DataGridCell/DataGridCell.module.scss.cjs.js +1 -1
  8. package/dist/cjs/DataGrid/DataGridBody/DataGridRow/DataGridRow.cjs.js +1 -1
  9. package/dist/cjs/DataGrid/DataGridBody/DataGridRow/DataGridRow.cjs.js.map +1 -1
  10. package/dist/cjs/DataGrid/DataGridBody/DataGridRow/DataGridRow.module.scss.cjs.js +1 -1
  11. package/dist/cjs/DataGrid/DataGridBody/DataGridRow/useNestedRow.cjs.js +2 -0
  12. package/dist/cjs/DataGrid/DataGridBody/DataGridRow/useNestedRow.cjs.js.map +1 -0
  13. package/dist/cjs/DataGrid/DataGridFilters/DataGridFilterPopover.cjs.js +1 -1
  14. package/dist/cjs/DataGrid/DataGridFilters/DataGridFilterPopover.cjs.js.map +1 -1
  15. package/dist/cjs/DataGrid/DataGridFilters/DataGridFilters.interfaces.cjs.js.map +1 -1
  16. package/dist/cjs/DataGrid/DataGridFilters/DataGridToolbar.module.scss.cjs.js +1 -1
  17. package/dist/cjs/DataGrid/DataGridHeader/DataGridHeader.cjs.js +1 -1
  18. package/dist/cjs/DataGrid/DataGridHeader/DataGridHeader.cjs.js.map +1 -1
  19. package/dist/cjs/Form/Wrapper/InputWrapper/InputWrapper.module.scss.cjs.js +1 -1
  20. package/dist/cjs/Notifications/Alert/AlertContainer/AlertContainer.module.scss.cjs.js +1 -1
  21. package/dist/cjs/Notifications/Alert/AlertItem/AlertItem.module.scss.cjs.js +1 -1
  22. package/dist/cjs/src/components/DataGrid/DataGrid.d.ts +3 -0
  23. package/dist/cjs/src/components/DataGrid/DataGridBody/DataGridBody.d.ts +3 -0
  24. package/dist/cjs/src/components/DataGrid/DataGridBody/DataGridCell/DataGridCell.d.ts +1 -0
  25. package/dist/cjs/src/components/DataGrid/DataGridBody/DataGridRow/DataGridRow.d.ts +19 -3
  26. package/dist/cjs/src/components/DataGrid/DataGridBody/DataGridRow/DataGridWithNestedRows.test.d.ts +1 -0
  27. package/dist/cjs/src/components/DataGrid/DataGridBody/DataGridRow/useNestedRow.d.ts +855 -0
  28. package/dist/cjs/src/components/DataGrid/DataGridFilters/DataGridFilters.interfaces.d.ts +1 -0
  29. package/dist/cjs/src/components/DataGrid/DataGridHeader/DataGridHeader.d.ts +1 -0
  30. package/dist/esm/DataGrid/DataGrid.esm.js +3 -3
  31. package/dist/esm/DataGrid/DataGrid.esm.js.map +1 -1
  32. package/dist/esm/DataGrid/DataGridBody/DataGridBody.esm.js +9 -2
  33. package/dist/esm/DataGrid/DataGridBody/DataGridBody.esm.js.map +1 -1
  34. package/dist/esm/DataGrid/DataGridBody/DataGridCell/DataGridCell.esm.js +7 -4
  35. package/dist/esm/DataGrid/DataGridBody/DataGridCell/DataGridCell.esm.js.map +1 -1
  36. package/dist/esm/DataGrid/DataGridBody/DataGridCell/DataGridCell.module.scss.esm.js +2 -2
  37. package/dist/esm/DataGrid/DataGridBody/DataGridRow/DataGridRow.esm.js +41 -11
  38. package/dist/esm/DataGrid/DataGridBody/DataGridRow/DataGridRow.esm.js.map +1 -1
  39. package/dist/esm/DataGrid/DataGridBody/DataGridRow/DataGridRow.module.scss.esm.js +2 -2
  40. package/dist/esm/DataGrid/DataGridBody/DataGridRow/useNestedRow.esm.js +92 -0
  41. package/dist/esm/DataGrid/DataGridBody/DataGridRow/useNestedRow.esm.js.map +1 -0
  42. package/dist/esm/DataGrid/DataGridFilters/DataGridFilterPopover.esm.js +13 -9
  43. package/dist/esm/DataGrid/DataGridFilters/DataGridFilterPopover.esm.js.map +1 -1
  44. package/dist/esm/DataGrid/DataGridFilters/DataGridFilters.interfaces.esm.js.map +1 -1
  45. package/dist/esm/DataGrid/DataGridFilters/DataGridToolbar.module.scss.esm.js +1 -1
  46. package/dist/esm/DataGrid/DataGridHeader/DataGridHeader.esm.js +1 -1
  47. package/dist/esm/DataGrid/DataGridHeader/DataGridHeader.esm.js.map +1 -1
  48. package/dist/esm/Form/Wrapper/InputWrapper/InputWrapper.module.scss.esm.js +2 -2
  49. package/dist/esm/Notifications/Alert/AlertContainer/AlertContainer.module.scss.esm.js +1 -1
  50. package/dist/esm/Notifications/Alert/AlertItem/AlertItem.module.scss.esm.js +1 -1
  51. package/dist/esm/src/components/DataGrid/DataGrid.d.ts +3 -0
  52. package/dist/esm/src/components/DataGrid/DataGridBody/DataGridBody.d.ts +3 -0
  53. package/dist/esm/src/components/DataGrid/DataGridBody/DataGridCell/DataGridCell.d.ts +1 -0
  54. package/dist/esm/src/components/DataGrid/DataGridBody/DataGridRow/DataGridRow.d.ts +19 -3
  55. package/dist/esm/src/components/DataGrid/DataGridBody/DataGridRow/DataGridWithNestedRows.test.d.ts +1 -0
  56. package/dist/esm/src/components/DataGrid/DataGridBody/DataGridRow/useNestedRow.d.ts +855 -0
  57. package/dist/esm/src/components/DataGrid/DataGridFilters/DataGridFilters.interfaces.d.ts +1 -0
  58. package/dist/esm/src/components/DataGrid/DataGridHeader/DataGridHeader.d.ts +1 -0
  59. package/package.json +6 -5
  60. package/src/components/DataGrid/DataGrid.tsx +6 -0
  61. package/src/components/DataGrid/DataGridBody/DataGridBody.tsx +12 -1
  62. package/src/components/DataGrid/DataGridBody/DataGridCell/DataGridCell.module.scss +6 -0
  63. package/src/components/DataGrid/DataGridBody/DataGridCell/DataGridCell.tsx +30 -5
  64. package/src/components/DataGrid/DataGridBody/DataGridRow/DataGridRow.module.scss +98 -5
  65. package/src/components/DataGrid/DataGridBody/DataGridRow/DataGridRow.tsx +83 -16
  66. package/src/components/DataGrid/DataGridBody/DataGridRow/useNestedRow.tsx +143 -0
  67. package/src/components/DataGrid/DataGridFilters/DataGridFilterPopover.tsx +15 -10
  68. package/src/components/DataGrid/DataGridFilters/DataGridFilters.interfaces.ts +1 -0
  69. package/src/components/DataGrid/DataGridFilters/DataGridToolbar.module.scss +2 -0
  70. package/src/components/DataGrid/DataGridHeader/DataGridHeader.tsx +2 -0
  71. package/src/components/Form/Wrapper/InputWrapper/InputWrapper.module.scss +2 -0
  72. package/src/components/Notifications/Alert/AlertContainer/AlertContainer.module.scss +2 -1
  73. package/src/components/Notifications/Alert/AlertItem/AlertItem.module.scss +3 -1
  74. package/src/components/Notifications/Alert/alertVariables.scss +17 -0
  75. package/src/mixins.module.scss +12 -0
@@ -34,6 +34,7 @@ export interface DataGridColumnMetadata {
34
34
  headline: string;
35
35
  operators?: string[];
36
36
  defaultValues?: string[];
37
+ disableAddNew?: boolean;
37
38
  }
38
39
  export interface PopoverTranslations {
39
40
  columnSelectLabel?: string;
@@ -5,6 +5,7 @@ export interface Props extends ComponentPropsWithRef<"thead"> {
5
5
  initialSort?: Sort;
6
6
  onSort?: OnSortFunction;
7
7
  enableExpandableRow?: boolean;
8
+ enableNestedRows?: boolean;
8
9
  disableContextMenuColumn?: boolean;
9
10
  enableMultiSorting?: boolean;
10
11
  spacing?: React.CSSProperties;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "homepage": "http://onewelcome.github.io/react-lib-components",
3
3
  "name": "@onewelcome/react-lib-components",
4
- "version": "8.1.2",
4
+ "version": "8.2.0",
5
5
  "license": "Apache-2.0",
6
6
  "author": "OneWelcome B.V.",
7
7
  "main": "dist/cjs/src/index.cjs.js",
@@ -31,12 +31,12 @@
31
31
  },
32
32
  "scripts": {
33
33
  "analyze": "size-limit --why",
34
- "build": "rm -rf dist && rollup -c --bundleConfigAsCjs",
34
+ "build": "rimraf dist && rollup -c --bundleConfigAsCjs",
35
35
  "build-storybook": "storybook build",
36
36
  "chromatic": "chromatic --exit-zero-on-changes",
37
37
  "dev": "npm-run-all -p start test:watch storybook",
38
- "lint": "eslint '**/*.{ts,tsx}'",
39
- "prepare": "husky install && rollup -c --bundleConfigAsCjs",
38
+ "lint": "eslint \"**/*.{ts,tsx}\"",
39
+ "prepare": "husky && rollup -c --bundleConfigAsCjs",
40
40
  "size": "size-limit",
41
41
  "start": "rollup -c -w --bundleConfigAsCjs",
42
42
  "test": "jest",
@@ -102,7 +102,7 @@
102
102
  "eslint-plugin-react": "^7.34.3",
103
103
  "eslint-plugin-react-hooks": "^4.6.2",
104
104
  "eslint-plugin-storybook": "^0.8.0",
105
- "husky": "^9.0.11",
105
+ "husky": "^9.1.6",
106
106
  "identity-obj-proxy": "^3.0.0",
107
107
  "jest": "^29.7.0",
108
108
  "jest-environment-jsdom": "^29.7.0",
@@ -120,6 +120,7 @@
120
120
  "react-is": "^18.3.1",
121
121
  "react-router-dom": "^6.24.1",
122
122
  "remark-gfm": "^4.0.0",
123
+ "rimraf": "^6.0.1",
123
124
  "rollup": "^4.18.0",
124
125
  "rollup-plugin-postcss": "^4.0.2",
125
126
  "sass": "^1.77.6",
@@ -52,6 +52,9 @@ export interface Props<T> extends Omit<ComponentPropsWithRef<"div">, "children">
52
52
  searchBtnProps?: ButtonProps;
53
53
  searchIconBtnProps?: ButtonProps;
54
54
  };
55
+ nestedRowConfig?: {
56
+ nestedItemsKey?: keyof T;
57
+ };
55
58
  emptyLabel?: string;
56
59
  paginationProps?: PaginationProps;
57
60
  disableContextMenuColumn?: boolean;
@@ -75,6 +78,7 @@ const DataGridInner = <T extends {}>(
75
78
  headers,
76
79
  actions = {},
77
80
  paginationProps,
81
+ nestedRowConfig,
78
82
  disableContextMenuColumn,
79
83
  enableExpandableRow,
80
84
  filters,
@@ -177,6 +181,7 @@ const DataGridInner = <T extends {}>(
177
181
  onSort={onSort}
178
182
  disableContextMenuColumn={disableContextMenuColumn}
179
183
  enableExpandableRow={enableExpandableRow}
184
+ enableNestedRows={!!nestedRowConfig}
180
185
  enableMultiSorting={enableMultiSorting}
181
186
  spacing={styleWithSpacing}
182
187
  />
@@ -185,6 +190,7 @@ const DataGridInner = <T extends {}>(
185
190
  headers={internalHeaders}
186
191
  isLoading={isLoading}
187
192
  disableContextMenuColumn={disableContextMenuColumn}
193
+ nestedRowConfig={nestedRowConfig}
188
194
  emptyLabel={emptyLabel}
189
195
  spacing={styleWithSpacing}
190
196
  searchValue={search?.initialSearchValue}
@@ -24,6 +24,9 @@ import { Typography } from "../../Typography/Typography";
24
24
  export interface Props<T> extends Omit<ComponentPropsWithRef<"tbody">, "children"> {
25
25
  children: ({ item, index }: { item: T; index: number }) => ReactElement;
26
26
  data?: T[];
27
+ nestedRowConfig?: {
28
+ nestedItemsKey?: keyof T;
29
+ };
27
30
  headers: HeaderCell[];
28
31
  isLoading?: boolean;
29
32
  disableContextMenuColumn?: boolean;
@@ -39,6 +42,7 @@ const DataGridBodyInner = <T extends {}>(
39
42
  children,
40
43
  data,
41
44
  headers,
45
+ nestedRowConfig,
42
46
  isLoading,
43
47
  disableContextMenuColumn,
44
48
  emptyLabel,
@@ -82,7 +86,14 @@ const DataGridBodyInner = <T extends {}>(
82
86
  searchValue: searchValue,
83
87
  headers,
84
88
  spacing,
85
- disableContextMenuColumn
89
+ item,
90
+ disableContextMenuColumn,
91
+ nestedRowProps: {
92
+ rowTemplate: children,
93
+ enableNestedRow: !!nestedRowConfig,
94
+ nestedItemsKey: nestedRowConfig?.nestedItemsKey,
95
+ isLastChild: index + 1 === data.length
96
+ }
86
97
  });
87
98
  });
88
99
  };
@@ -28,6 +28,12 @@
28
28
  padding-right: 1rem;
29
29
  }
30
30
 
31
+ .content-wrapper {
32
+ display: flex;
33
+ align-items: center;
34
+ gap: 6px;
35
+ }
36
+
31
37
  .text {
32
38
  font-size: var(--font-size-data-grid);
33
39
  line-height: var(--data-grid-line-height);
@@ -25,6 +25,7 @@ import classes from "./DataGridCell.module.scss";
25
25
 
26
26
  export interface Props extends ComponentPropsWithRef<"td"> {
27
27
  children?: ReactElement | string | number;
28
+ prefixElement?: ReactElement;
28
29
  isLoading?: boolean;
29
30
  spacing?: React.CSSProperties;
30
31
  searchValue?: string;
@@ -39,6 +40,7 @@ const DataGridCellComponent: ForwardRefRenderFunction<HTMLTableCellElement, Prop
39
40
  className,
40
41
  isLoading,
41
42
  spacing,
43
+ prefixElement,
42
44
  searchValue,
43
45
  cellIndex,
44
46
  columnLength,
@@ -91,11 +93,34 @@ const DataGridCellComponent: ForwardRefRenderFunction<HTMLTableCellElement, Prop
91
93
  style={{ ...rest.style, ...cellStyle }}
92
94
  className={`${classes["cell"]} ${className ?? ""}`}
93
95
  >
94
- {isLoading && <div className={classes["loading"]} aria-busy="true" aria-live="polite"></div>}
95
- {!isLoading && (
96
- <Typography className={classes["text"]} variant="body" tag="span">
97
- {renderContent()}
98
- </Typography>
96
+ {prefixElement ? (
97
+ <div className={classes["content-wrapper"]}>
98
+ {prefixElement}
99
+ {isLoading && (
100
+ <div className={classes["loading"]} aria-busy="true" aria-live="polite"></div>
101
+ )}
102
+ {!isLoading && (
103
+ <Typography
104
+ className={classes["text"]}
105
+ variant="body"
106
+ tag="span"
107
+ spacing={{ marginBottom: 0 }}
108
+ >
109
+ {renderContent()}
110
+ </Typography>
111
+ )}
112
+ </div>
113
+ ) : (
114
+ <>
115
+ {isLoading && (
116
+ <div className={classes["loading"]} aria-busy="true" aria-live="polite"></div>
117
+ )}
118
+ {!isLoading && (
119
+ <Typography className={classes["text"]} variant="body" tag="span">
120
+ {renderContent()}
121
+ </Typography>
122
+ )}
123
+ </>
99
124
  )}
100
125
  </td>
101
126
  );
@@ -13,6 +13,7 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
+ $nesting-limit: 6;
16
17
 
17
18
  .row {
18
19
  position: relative;
@@ -22,24 +23,45 @@
22
23
  background-color: var(--data-grid-row-hover-background-color);
23
24
  }
24
25
 
25
- @mixin borderAfter {
26
+ @mixin borderAfter($i, $no-offset: false) {
26
27
  content: "";
27
28
  position: absolute;
28
- width: 100%;
29
- left: 0;
29
+
30
+ @if $no-offset {
31
+ width: 100%;
32
+ left: 0;
33
+ } @else if $i > 0 {
34
+ width: calc(100% - calc(3.0625rem + $i * 4.125rem));
35
+ left: calc(3.0625rem + $i * 4.125rem);
36
+ } @else {
37
+ width: calc(100% - 3.0625rem);
38
+ left: 3.0625rem;
39
+ }
30
40
  bottom: 0;
31
41
  border-bottom: 1px solid var(--light-grey-border);
32
42
  }
33
43
 
44
+ @mixin border-x {
45
+ @for $i from 0 through $nesting-limit {
46
+ &.border-#{$i} {
47
+ &:not(:last-child)::after {
48
+ @include borderAfter($i);
49
+ }
50
+ }
51
+ }
52
+ }
53
+
54
+ @include border-x();
55
+
34
56
  &.border {
35
57
  &:not(:last-child)::after {
36
- @include borderAfter();
58
+ @include borderAfter(0, true);
37
59
  }
38
60
  }
39
61
 
40
62
  &.border-drawer {
41
63
  &:not(:last-child):not(:nth-last-child(2))::after {
42
- @include borderAfter();
64
+ @include borderAfter(0, true);
43
65
  }
44
66
  }
45
67
  }
@@ -47,3 +69,74 @@
47
69
  .expand-button-cell {
48
70
  padding: 0;
49
71
  }
72
+
73
+ @mixin offset-left {
74
+ @for $i from 0 through $nesting-limit {
75
+ &.offset-left-#{$i} {
76
+ left: $i * 4.125rem;
77
+ }
78
+ }
79
+ }
80
+
81
+ .connector {
82
+ position: absolute;
83
+ top: 0;
84
+ left: 0;
85
+ width: 2.875rem;
86
+ height: 3.25rem;
87
+
88
+ @include offset-left();
89
+
90
+ &.vertical {
91
+ &::before {
92
+ position: absolute;
93
+ content: "";
94
+ left: 50%;
95
+ height: 100%;
96
+ width: 0.0625rem;
97
+ background-color: var(--color-blue-grey200);
98
+ }
99
+ }
100
+
101
+ &.t-shape {
102
+ &::before {
103
+ position: absolute;
104
+ content: "";
105
+ left: 50%;
106
+ height: 3.25rem;
107
+ width: 0.0625rem;
108
+ background-color: var(--color-blue-grey200);
109
+ }
110
+
111
+ &::after {
112
+ position: absolute;
113
+ content: "";
114
+ top: 50%;
115
+ left: 50%;
116
+ height: 0.0625rem;
117
+ width: 100%;
118
+ background-color: var(--color-blue-grey200);
119
+ }
120
+ }
121
+
122
+ &.line {
123
+ &::before {
124
+ position: absolute;
125
+ content: "";
126
+ left: 50%;
127
+ height: 50%;
128
+ width: 1px;
129
+ background-color: var(--color-blue-grey200);
130
+ }
131
+
132
+ &::after {
133
+ position: absolute;
134
+ content: "";
135
+ top: 50%;
136
+ left: 50%;
137
+ height: 1px;
138
+ width: 100%;
139
+ background-color: var(--color-blue-grey200);
140
+ }
141
+ }
142
+ }
@@ -14,7 +14,7 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- import React, { ComponentPropsWithRef, useState, Fragment, ForwardRefRenderFunction } from "react";
17
+ import React, { ComponentPropsWithRef, useState, Fragment, Ref, ReactElement } from "react";
18
18
  import { HeaderCell } from "../../datagrid.interfaces";
19
19
  import classes from "./DataGridRow.module.scss";
20
20
  import { IconButton } from "../../../Button/IconButton";
@@ -22,13 +22,22 @@ import { Icon, Icons } from "../../../Icon/Icon";
22
22
  import { DataGridCell } from "../DataGridCell/DataGridCell";
23
23
  import { DataGridDrawer } from "../DataGridDrawer/DataGridDrawer";
24
24
  import { generateID } from "../../../../util/helper";
25
+ import { useNestedRow } from "./useNestedRow";
25
26
 
26
- export interface Props extends ComponentPropsWithRef<"tr"> {
27
+ export interface Props<T> extends ComponentPropsWithRef<"tr"> {
28
+ item?: T;
27
29
  headers?: HeaderCell[];
28
30
  isLoading?: boolean;
29
31
  spacing?: React.CSSProperties;
30
32
  searchValue?: string;
31
33
  disableContextMenuColumn?: boolean;
34
+ nestedRowProps?: {
35
+ rowTemplate?: ({ item, index }: { item: T; index: number }) => ReactElement;
36
+ indentationLevel?: number;
37
+ indentationLevels?: { level: number; isLastChild: boolean }[];
38
+ enableNestedRow?: boolean;
39
+ nestedItemsKey?: keyof T;
40
+ };
32
41
  expandableRowProps?: {
33
42
  enableExpandableRow: boolean;
34
43
  expandableRowContent: React.ReactNode;
@@ -38,8 +47,9 @@ export interface Props extends ComponentPropsWithRef<"tr"> {
38
47
  };
39
48
  }
40
49
 
41
- const DataGridRowComponent: ForwardRefRenderFunction<HTMLTableRowElement, Props> = (
50
+ const DataGridRowComponent = <T,>(
42
51
  {
52
+ item,
43
53
  children,
44
54
  className,
45
55
  headers,
@@ -48,9 +58,10 @@ const DataGridRowComponent: ForwardRefRenderFunction<HTMLTableRowElement, Props>
48
58
  spacing,
49
59
  expandableRowProps,
50
60
  disableContextMenuColumn,
61
+ nestedRowProps,
51
62
  ...rest
52
- },
53
- ref
63
+ }: Props<T>,
64
+ ref: Ref<HTMLTableRowElement>
54
65
  ) => {
55
66
  const {
56
67
  enableExpandableRow = false,
@@ -59,15 +70,73 @@ const DataGridRowComponent: ForwardRefRenderFunction<HTMLTableRowElement, Props>
59
70
  drawerId = `ID-${generateID()}`,
60
71
  expandableRowContent
61
72
  } = expandableRowProps || {};
73
+ const {
74
+ indentationLevels,
75
+ indentationLevel = 0,
76
+ nestedItemsKey,
77
+ enableNestedRow,
78
+ rowTemplate
79
+ } = nestedRowProps || {};
62
80
  const [isRowExpanded, setIsRowExpanded] = useState(false);
81
+ const { renderNestedRowConnectors, renderNestedRow, getNestedChildSpacing, hasNestedChildren } =
82
+ useNestedRow({
83
+ indentationLevels,
84
+ indentationLevel,
85
+ item,
86
+ nestedItemsKey,
87
+ rowTemplate,
88
+ isRowExpanded,
89
+ enableNestedRow,
90
+ rowProps: {
91
+ searchValue,
92
+ headers,
93
+ spacing,
94
+ disableContextMenuColumn
95
+ }
96
+ });
97
+
98
+ const classNames = [classes["row"]];
99
+ const rowBorderClass = enableNestedRow
100
+ ? classes[`border-${indentationLevel}`]
101
+ : classes[`border`];
102
+
103
+ className && classNames.push(className);
104
+ enableExpandableRow
105
+ ? !isRowExpanded && classNames.push(classes["border-drawer"])
106
+ : classNames.push(rowBorderClass);
107
+ isLoading && classNames.push(classes["loading"]);
108
+
109
+ const getPrefixButton = (hasNestedChildren: boolean) =>
110
+ hasNestedChildren ? (
111
+ <IconButton
112
+ id={expandButtonId}
113
+ title={expandButtonTitle}
114
+ aria-expanded={isRowExpanded}
115
+ onClick={() => setIsRowExpanded(!isRowExpanded)}
116
+ >
117
+ <Icon size="0.75rem" icon={isRowExpanded ? Icons.ChevronUp : Icons.ChevronDown} />
118
+ </IconButton>
119
+ ) : null;
120
+
63
121
  const visibleCells = React.Children.map(children as React.ReactElement[], (child, index) => {
122
+ const childSpacing = enableNestedRow ? getNestedChildSpacing(spacing, index) : spacing;
123
+
124
+ const prefixElement =
125
+ enableNestedRow && index === 0 ? (
126
+ <>
127
+ {getPrefixButton(!!hasNestedChildren)}
128
+ {renderNestedRowConnectors()}
129
+ </>
130
+ ) : null;
131
+
64
132
  if (child) {
65
133
  const cellWithSpacing = React.cloneElement(child, {
66
134
  searchValue,
67
- spacing: spacing,
135
+ spacing: childSpacing,
68
136
  cellIndex: index,
69
137
  columnLength: headers?.length,
70
- disableContextMenuColumn
138
+ disableContextMenuColumn,
139
+ prefixElement
71
140
  });
72
141
 
73
142
  const visible = headers && headers.length > index ? !headers[index].hidden : true;
@@ -76,13 +145,6 @@ const DataGridRowComponent: ForwardRefRenderFunction<HTMLTableRowElement, Props>
76
145
  return null;
77
146
  });
78
147
 
79
- const classNames = [classes["row"]];
80
- className && classNames.push(className);
81
- enableExpandableRow
82
- ? !isRowExpanded && classNames.push(classes["border-drawer"])
83
- : classNames.push(classes["border"]);
84
- isLoading && classNames.push(classes["loading"]);
85
-
86
148
  return (
87
149
  <Fragment>
88
150
  <tr {...rest} ref={ref} className={classNames.join(" ")}>
@@ -90,7 +152,9 @@ const DataGridRowComponent: ForwardRefRenderFunction<HTMLTableRowElement, Props>
90
152
  <DataGridCell
91
153
  className={classes["expand-button-cell"]}
92
154
  onClick={() => setIsRowExpanded(!isRowExpanded)}
93
- style={{ width: "1px" }}
155
+ style={{
156
+ width: "1px"
157
+ }}
94
158
  >
95
159
  <IconButton
96
160
  id={expandButtonId}
@@ -118,8 +182,11 @@ const DataGridRowComponent: ForwardRefRenderFunction<HTMLTableRowElement, Props>
118
182
  </td>
119
183
  </tr>
120
184
  )}
185
+ {enableNestedRow && renderNestedRow()}
121
186
  </Fragment>
122
187
  );
123
188
  };
124
189
 
125
- export const DataGridRow = React.forwardRef(DataGridRowComponent);
190
+ export const DataGridRow = React.forwardRef(DataGridRowComponent) as <T extends {}>(
191
+ p: Props<T> & { ref?: Ref<HTMLTableRowElement> }
192
+ ) => ReactElement;
@@ -0,0 +1,143 @@
1
+ /*
2
+ * Copyright 2022 OneWelcome B.V.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import React, { ReactElement } from "react";
18
+ import classes from "./DataGridRow.module.scss";
19
+ import { HeaderCell } from "../../datagrid.interfaces";
20
+
21
+ export interface UseNestedRowProps<T> {
22
+ indentationLevels?: { level: number; isLastChild: boolean }[];
23
+ item?: T;
24
+ nestedItemsKey?: keyof T;
25
+ rowTemplate?: ({ item, index }: { item: T; index: number }) => ReactElement;
26
+ isRowExpanded?: boolean;
27
+ indentationLevel: number;
28
+ enableNestedRow?: boolean;
29
+ rowProps: {
30
+ searchValue?: string;
31
+ headers?: HeaderCell[];
32
+ spacing?: React.CSSProperties;
33
+ disableContextMenuColumn?: boolean;
34
+ };
35
+ }
36
+
37
+ export const useNestedRow = <T,>({
38
+ indentationLevels,
39
+ item,
40
+ nestedItemsKey,
41
+ rowTemplate,
42
+ isRowExpanded,
43
+ indentationLevel,
44
+ enableNestedRow,
45
+ rowProps: { searchValue, headers, spacing, disableContextMenuColumn }
46
+ }: UseNestedRowProps<T>) => {
47
+ const hasNestedChildren = item && nestedItemsKey && item[nestedItemsKey];
48
+
49
+ const getNestedChildSpacing = (spacing: React.CSSProperties | undefined, index: number) => {
50
+ const expandButtonWidth = 46;
51
+ const nestedChildOffset = !hasNestedChildren ? expandButtonWidth : 0;
52
+
53
+ const connectorWidth = 68;
54
+ const nestedChildIndentation = `${nestedChildOffset + indentationLevel * connectorWidth}`;
55
+
56
+ const gap = 4;
57
+ const notIndentedWithNoChildrenOffset =
58
+ indentationLevel === 0 && !hasNestedChildren ? expandButtonWidth + gap : gap;
59
+ const childIndentation = `${indentationLevel ? nestedChildIndentation : notIndentedWithNoChildrenOffset}px`;
60
+
61
+ if (spacing) {
62
+ return {
63
+ ...spacing,
64
+ paddingLeft: index === 0 ? childIndentation : spacing.paddingLeft
65
+ };
66
+ }
67
+ return {
68
+ paddingLeft: index === 0 ? childIndentation : ""
69
+ };
70
+ };
71
+
72
+ const renderNestedRowConnectors = () => {
73
+ if (indentationLevels) {
74
+ return indentationLevels.map(({ level, isLastChild }) =>
75
+ renderNestedRowConnector(level, isLastChild, indentationLevels.length)
76
+ );
77
+ }
78
+
79
+ return null;
80
+ };
81
+
82
+ const renderNestedRowConnector = (level: number, isLastChild: boolean, levelsLength: number) => {
83
+ const offsetLeftClass = classes[`offset-left-${level - 1}`];
84
+
85
+ if (level === levelsLength) {
86
+ const variant = isLastChild ? "line" : "t-shape";
87
+
88
+ return (
89
+ <div
90
+ key={level}
91
+ data-testid="dataGridRowConnector"
92
+ className={`${classes["connector"]} ${classes[variant]} ${offsetLeftClass}`}
93
+ />
94
+ );
95
+ } else if (!isLastChild) {
96
+ return (
97
+ <div
98
+ key={level}
99
+ data-testid="dataGridRowConnector"
100
+ className={`${classes["connector"]} ${classes["vertical"]} ${offsetLeftClass}`}
101
+ />
102
+ );
103
+ }
104
+ return null;
105
+ };
106
+
107
+ const renderNestedRow = () => {
108
+ if (rowTemplate && item && nestedItemsKey && item[nestedItemsKey] && isRowExpanded) {
109
+ const nestedItemsArray: T[] = item[nestedItemsKey] as T[];
110
+ const getIndentationLevel = (index: number) => ({
111
+ level: indentationLevel + 1,
112
+ isLastChild: index + 1 === nestedItemsArray.length
113
+ });
114
+
115
+ return nestedItemsArray.map((item, index) => {
116
+ return React.cloneElement(rowTemplate({ item, index }), {
117
+ searchValue,
118
+ headers,
119
+ spacing,
120
+ disableContextMenuColumn,
121
+ item,
122
+ nestedRowProps: {
123
+ rowTemplate,
124
+ enableNestedRow,
125
+ nestedItemsKey,
126
+ indentationLevel: indentationLevel + 1,
127
+ indentationLevels:
128
+ indentationLevels && nestedItemsArray
129
+ ? [...indentationLevels, getIndentationLevel(index)]
130
+ : [getIndentationLevel(index)]
131
+ }
132
+ });
133
+ });
134
+ }
135
+ };
136
+
137
+ return {
138
+ renderNestedRow,
139
+ renderNestedRowConnectors,
140
+ getNestedChildSpacing,
141
+ hasNestedChildren
142
+ };
143
+ };
@@ -87,6 +87,9 @@ export const DataGridFilterPopover = ({
87
87
  }
88
88
  }, [isOpen]);
89
89
 
90
+ const columnMetadata = columnsMetadata.find(({ name }) => name === column);
91
+ const disableAddNew = columnMetadata?.disableAddNew;
92
+
90
93
  return (
91
94
  <Popover
92
95
  tabIndex={-1}
@@ -153,16 +156,18 @@ export const DataGridFilterPopover = ({
153
156
  )
154
157
  }
155
158
  selectProps={{
156
- addNew: {
157
- label: addNewValueLabel,
158
- onAddNew: value => {
159
- if (value) {
160
- setValues(prev => [...prev, value]);
161
- setPickedValues(prev => [...prev, value]);
162
- }
163
- },
164
- btnProps: { title: addNewValueButtonTitle, type: "button" }
165
- },
159
+ addNew: disableAddNew
160
+ ? undefined
161
+ : {
162
+ label: addNewValueLabel,
163
+ onAddNew: value => {
164
+ if (value) {
165
+ setValues(prev => [...prev, value]);
166
+ setPickedValues(prev => [...prev, value]);
167
+ }
168
+ },
169
+ btnProps: { title: addNewValueButtonTitle, type: "button" }
170
+ },
166
171
  search: {
167
172
  enabled: true,
168
173
  renderThreshold: 0
@@ -57,6 +57,7 @@ export interface DataGridColumnMetadata {
57
57
  headline: string;
58
58
  operators?: string[];
59
59
  defaultValues?: string[];
60
+ disableAddNew?: boolean;
60
61
  }
61
62
 
62
63
  export interface PopoverTranslations {