@equinor/eds-data-grid-react 0.3.0 → 0.5.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.
@@ -1,13 +1,13 @@
1
1
  'use strict';
2
2
 
3
- var edsCoreReact = require('@equinor/eds-core-react');
4
3
  var reactTable = require('@tanstack/react-table');
4
+ var edsCoreReact = require('@equinor/eds-core-react');
5
5
  var reactVirtual = require('@tanstack/react-virtual');
6
6
  var react = require('react');
7
- var edsIcons = require('@equinor/eds-icons');
8
- var jsxRuntime = require('react/jsx-runtime');
9
7
  var styled = require('styled-components');
8
+ var jsxRuntime = require('react/jsx-runtime');
10
9
  var edsTokens = require('@equinor/eds-tokens');
10
+ var edsIcons = require('@equinor/eds-icons');
11
11
 
12
12
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
13
13
 
@@ -71,7 +71,6 @@ function DebouncedInput({
71
71
  /* eslint-disable-next-line @typescript-eslint/ban-ts-comment */,
72
72
  label: `Select ${label ?? ''}`,
73
73
  placeholder: props.placeholder ?? 'Search',
74
- disablePortal: false /*TODO: Check with Oddbjørn re. sizing/position*/,
75
74
  selectedOptions: value,
76
75
  onOptionsChange: c => setValue(c.selectedItems),
77
76
  multiline: true
@@ -110,12 +109,6 @@ function Filter({
110
109
  table
111
110
  }) {
112
111
  const firstValue = table.getPreFilteredRowModel().flatRows[0]?.getValue(column.id);
113
- const [open, setOpen] = react.useState(false);
114
- const filterIconRef = react.useRef();
115
- const togglePopover = event => {
116
- event.stopPropagation();
117
- setOpen(!open);
118
- };
119
112
  const columnText = react.useMemo(() => {
120
113
  let header;
121
114
  try {
@@ -136,6 +129,53 @@ function Filter({
136
129
  const sortedUniqueValues = react.useMemo(() => typeof firstValue === 'number' ? [] : Array.from(column.getFacetedUniqueValues().keys()).sort().map(v => v ?? 'NULL_OR_UNDEFINED'),
137
130
  // eslint-disable-next-line react-hooks/exhaustive-deps
138
131
  [column.getFacetedUniqueValues()]);
132
+ return typeof firstValue === 'number' ? /*#__PURE__*/jsxRuntime.jsxs(NumberContainer, {
133
+ children: [/*#__PURE__*/jsxRuntime.jsx(DebouncedInput, {
134
+ type: "number",
135
+ values: sortedUniqueValues,
136
+ min: Number(column.getFacetedMinMaxValues()?.[0] ?? ''),
137
+ max: Number(column.getFacetedMinMaxValues()?.[1] ?? ''),
138
+ value: columnFilterValue?.[0] ?? '',
139
+ onChange: value => column.setFilterValue(old => [value, old?.[1]]),
140
+ placeholder: `Min ${column.getFacetedMinMaxValues()?.[0] ? `(${column.getFacetedMinMaxValues()?.[0]})` : ''}`
141
+ }), /*#__PURE__*/jsxRuntime.jsx(DebouncedInput, {
142
+ type: "number",
143
+ values: sortedUniqueValues,
144
+ min: Number(column.getFacetedMinMaxValues()?.[0] ?? ''),
145
+ max: Number(column.getFacetedMinMaxValues()?.[1] ?? ''),
146
+ value: columnFilterValue?.[1] ?? '',
147
+ onChange: value => column.setFilterValue(old => [old?.[0], value]),
148
+ placeholder: `Max ${column.getFacetedMinMaxValues()?.[1] ? `(${column.getFacetedMinMaxValues()?.[1]})` : ''}`
149
+ })]
150
+ }) : /*#__PURE__*/jsxRuntime.jsx(DebouncedInput, {
151
+ type: "text",
152
+ label: columnText,
153
+ values: sortedUniqueValues,
154
+ debounce: 100,
155
+ value: columnFilterValue ?? [],
156
+ onChange: value => column.setFilterValue(value),
157
+ placeholder: `${(columnFilterValue ?? []).length} / ${column.getFacetedUniqueValues().size} selected`,
158
+ list: column.id + 'list'
159
+ });
160
+ }
161
+
162
+ /* istanbul ignore file */
163
+
164
+ function FilterWrapper({
165
+ column,
166
+ CustomComponent
167
+ }) {
168
+ const {
169
+ table
170
+ } = useTableContext();
171
+ const firstValue = table.getPreFilteredRowModel().flatRows[0]?.getValue(column.id);
172
+ const [open, setOpen] = react.useState(false);
173
+ const filterIconRef = react.useRef();
174
+ const togglePopover = event => {
175
+ event.stopPropagation();
176
+ setOpen(!open);
177
+ };
178
+ const columnFilterValue = column.getFilterValue();
139
179
  const hasActiveFilters = value => {
140
180
  if (Array.isArray(value)) {
141
181
  if (typeof firstValue === 'number') {
@@ -146,6 +186,7 @@ function Filter({
146
186
  }
147
187
  return value;
148
188
  };
189
+ const onChange = react.useCallback(value => column.setFilterValue(value), [column]);
149
190
  return /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
150
191
  children: [/*#__PURE__*/jsxRuntime.jsx(edsCoreReact.Button, {
151
192
  "aria-haspopup": true,
@@ -154,6 +195,7 @@ function Filter({
154
195
  ref: filterIconRef,
155
196
  onClick: togglePopover,
156
197
  variant: 'ghost_icon',
198
+ "aria-label": 'Show column filters',
157
199
  children: /*#__PURE__*/jsxRuntime.jsx(edsCoreReact.Icon, {
158
200
  color: edsTokens.tokens.colors.text.static_icons__default.hex,
159
201
  data: hasActiveFilters(columnFilterValue) ? edsIcons.filter_alt_active : edsIcons.filter_alt
@@ -169,39 +211,31 @@ function Filter({
169
211
  style: {
170
212
  width: typeof firstValue === 'number' ? '180px' : '310px'
171
213
  },
172
- children: typeof firstValue === 'number' ? /*#__PURE__*/jsxRuntime.jsxs(NumberContainer, {
173
- children: [/*#__PURE__*/jsxRuntime.jsx(DebouncedInput, {
174
- type: "number",
175
- values: sortedUniqueValues,
176
- min: Number(column.getFacetedMinMaxValues()?.[0] ?? ''),
177
- max: Number(column.getFacetedMinMaxValues()?.[1] ?? ''),
178
- value: columnFilterValue?.[0] ?? '',
179
- onChange: value => column.setFilterValue(old => [value, old?.[1]]),
180
- placeholder: `Min ${column.getFacetedMinMaxValues()?.[0] ? `(${column.getFacetedMinMaxValues()?.[0]})` : ''}`
181
- }), /*#__PURE__*/jsxRuntime.jsx(DebouncedInput, {
182
- type: "number",
183
- values: sortedUniqueValues,
184
- min: Number(column.getFacetedMinMaxValues()?.[0] ?? ''),
185
- max: Number(column.getFacetedMinMaxValues()?.[1] ?? ''),
186
- value: columnFilterValue?.[1] ?? '',
187
- onChange: value => column.setFilterValue(old => [old?.[0], value]),
188
- placeholder: `Max ${column.getFacetedMinMaxValues()?.[1] ? `(${column.getFacetedMinMaxValues()?.[1]})` : ''}`
189
- })]
190
- }) : /*#__PURE__*/jsxRuntime.jsx(DebouncedInput, {
191
- type: "text",
192
- label: columnText,
193
- values: sortedUniqueValues,
194
- debounce: 100,
195
- value: columnFilterValue ?? [],
196
- onChange: value => column.setFilterValue(value),
197
- placeholder: `${(columnFilterValue ?? []).length} / ${column.getFacetedUniqueValues().size} selected`,
198
- list: column.id + 'list'
214
+ children: CustomComponent ? /*#__PURE__*/jsxRuntime.jsx(CustomComponent, {
215
+ onChange: onChange,
216
+ value: columnFilterValue
217
+ }) : /*#__PURE__*/jsxRuntime.jsx(Filter, {
218
+ column: column,
219
+ table: table
199
220
  })
200
221
  })
201
222
  })]
202
223
  });
203
224
  }
204
225
 
226
+ const SortIndicator = ({
227
+ column
228
+ }) => {
229
+ return {
230
+ asc: /*#__PURE__*/jsxRuntime.jsx(edsCoreReact.Icon, {
231
+ data: edsIcons.arrow_up
232
+ }),
233
+ desc: /*#__PURE__*/jsxRuntime.jsx(edsCoreReact.Icon, {
234
+ data: edsIcons.arrow_down
235
+ })
236
+ }[column.getIsSorted()] ?? null;
237
+ };
238
+
205
239
  const getSortLabel = sorted => {
206
240
  if (sorted) {
207
241
  return `${sorted}ending`;
@@ -219,7 +253,7 @@ const Resizer = styled__default.default.div.withConfig({
219
253
  const Cell = styled__default.default(edsCoreReact.Table.Cell).withConfig({
220
254
  displayName: "TableHeaderCell__Cell",
221
255
  componentId: "sc-1n0j3v0-2"
222
- })(["font-weight:bold;height:30px;position:", ";top:0;", " ", ";&:hover ", "{background:", ";opacity:1;}"], p => p.$sticky || p.$pinned ? 'sticky' : 'relative', p => {
256
+ })(["font-weight:bold;position:", ";top:0;", " ", ";&:hover ", "{background:", ";opacity:1;}"], p => p.$sticky || p.$pinned ? 'sticky' : 'relative', p => {
223
257
  if (p.$pinned) {
224
258
  return `${p.$pinned}: ${p.$offset}px;`;
225
259
  }
@@ -257,7 +291,6 @@ function TableHeaderCell({
257
291
  className: ctx.headerClass ? ctx.headerClass(header.column) : '',
258
292
  "aria-sort": getSortLabel(header.column.getIsSorted()),
259
293
  onClick: header.column.getToggleSortingHandler(),
260
- key: header.id,
261
294
  colSpan: header.colSpan,
262
295
  style: {
263
296
  width: header.getSize(),
@@ -274,22 +307,16 @@ function TableHeaderCell({
274
307
  className: "table-header-cell-label",
275
308
  children: reactTable.flexRender(header.column.columnDef.header, header.getContext())
276
309
  })
277
- }), {
278
- asc: /*#__PURE__*/jsxRuntime.jsx(edsCoreReact.Icon, {
279
- data: edsIcons.arrow_up
280
- }),
281
- desc: /*#__PURE__*/jsxRuntime.jsx(edsCoreReact.Icon, {
282
- data: edsIcons.arrow_down
283
- })
284
- }[header.column.getIsSorted()] ?? null, header.column.getCanFilter() ?
285
-
310
+ }), header.column.columnDef.meta?.customFilterInput && /*#__PURE__*/jsxRuntime.jsx(SortIndicator, {
311
+ column: header.column
312
+ }), header.column.getCanFilter() && !header.column.columnDef.meta?.customFilterInput ?
313
+ /*#__PURE__*/
286
314
  // Supressing this warning - div is not interactive, but prevents propagation of events to avoid unintended sorting
287
315
  // eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions
288
316
  jsxRuntime.jsx("div", {
289
317
  onClick: e => e.stopPropagation(),
290
- children: /*#__PURE__*/jsxRuntime.jsx(Filter, {
291
- column: header.column,
292
- table: table
318
+ children: /*#__PURE__*/jsxRuntime.jsx(FilterWrapper, {
319
+ column: header.column
293
320
  })
294
321
  }) : null]
295
322
  }), columnResizeMode && /*#__PURE__*/jsxRuntime.jsx(Resizer, {
@@ -302,7 +329,7 @@ function TableHeaderCell({
302
329
  "data-testid": 'resize-handle',
303
330
  children: /*#__PURE__*/jsxRuntime.jsx(ResizeInner, {})
304
331
  })]
305
- });
332
+ }, header.id);
306
333
  }
307
334
 
308
335
  function TableHeaderRow({
@@ -350,18 +377,19 @@ function TableBodyCell({
350
377
  $pinned: pinned,
351
378
  $offset: pinnedOffset,
352
379
  className: cellClass ? cellClass(cell.row, cell.column.id) : '',
353
- key: cell.id,
354
380
  style: {
355
381
  width: cell.column.getSize(),
356
382
  maxWidth: cell.column.getSize(),
357
383
  ...(cellStyle?.(cell.row, cell.column.id) ?? {})
358
384
  },
359
385
  children: reactTable.flexRender(cell.column.columnDef.cell, cell.getContext())
360
- });
386
+ }, cell.id);
361
387
  }
362
388
 
363
389
  function TableRow({
364
- row
390
+ row,
391
+ onCellClick,
392
+ onClick
365
393
  }) {
366
394
  const {
367
395
  rowClass,
@@ -369,13 +397,13 @@ function TableRow({
369
397
  } = useTableContext();
370
398
  return /*#__PURE__*/jsxRuntime.jsx(StyledTableRow, {
371
399
  style: {
372
- cursor: row.getCanSelect() ? 'pointer' : 'inherit',
373
400
  ...(rowStyle?.(row) ?? {})
374
401
  },
375
402
  className: `${row.getIsSelected() ? 'selected' : ''} ${rowClass?.(row)}`,
376
- onClick: () => row.getCanSelect() ? row.toggleSelected() : null,
403
+ onClick: onClick,
377
404
  children: row.getVisibleCells().map(cell => /*#__PURE__*/jsxRuntime.jsx(TableBodyCell, {
378
- cell: cell
405
+ cell: cell,
406
+ onClick: onCellClick ? event => onCellClick(cell, event) : undefined
379
407
  }, cell.id))
380
408
  });
381
409
  }
@@ -411,6 +439,19 @@ function addPxSuffixIfInputHasNoPrefix(size) {
411
439
  }
412
440
  return size;
413
441
  }
442
+ function logDevelopmentWarningOfPropUse(deprecatedProps) {
443
+ if (process.env.NODE_ENV !== 'development') {
444
+ return;
445
+ }
446
+ for (const [key, {
447
+ value,
448
+ mitigationInfo
449
+ }] of Object.entries(deprecatedProps)) {
450
+ if (typeof value !== 'undefined') {
451
+ console.warn(`The prop '${key}' is deprecated and will be removed in a future release. ${mitigationInfo}`);
452
+ }
453
+ }
454
+ }
414
455
 
415
456
  /* eslint-disable @typescript-eslint/no-non-null-assertion */
416
457
  function EdsDataGrid({
@@ -419,13 +460,17 @@ function EdsDataGrid({
419
460
  columnResizeMode,
420
461
  pageSize,
421
462
  rowSelection,
463
+ enableRowSelection,
464
+ enableMultiRowSelection,
422
465
  selectedRows,
466
+ rowSelectionState,
423
467
  enableColumnFiltering,
424
468
  debug,
425
469
  enablePagination,
426
470
  enableSorting,
427
471
  stickyHeader,
428
472
  onSelectRow,
473
+ onRowSelectionChange,
429
474
  caption,
430
475
  enableVirtual,
431
476
  virtualHeight,
@@ -451,10 +496,34 @@ function EdsDataGrid({
451
496
  getRowId,
452
497
  rowVirtualizerInstanceRef,
453
498
  columnSizing,
454
- onColumnResize
499
+ onColumnResize,
500
+ expansionState,
501
+ setExpansionState,
502
+ getSubRows,
503
+ defaultColumn,
504
+ onRowClick,
505
+ onCellClick
455
506
  }) {
507
+ logDevelopmentWarningOfPropUse({
508
+ virtualHeight: {
509
+ value: virtualHeight,
510
+ mitigationInfo: "Use 'height' instead."
511
+ },
512
+ rowSelection: {
513
+ value: rowSelection,
514
+ mitigationInfo: "Use 'enableRowSelection' instead."
515
+ },
516
+ onSelectRow: {
517
+ value: onSelectRow,
518
+ mitigationInfo: "Use 'onRowSelectionChange' instead."
519
+ },
520
+ selectedRows: {
521
+ value: selectedRows,
522
+ mitigationInfo: "Use 'rowSelectionState' instead."
523
+ }
524
+ });
456
525
  const [sorting, setSorting] = react.useState(sortingState ?? []);
457
- const [selection, setSelection] = react.useState(selectedRows ?? {});
526
+ const [internalRowSelectionState, setInternalRowSelectionState] = react.useState(rowSelectionState ?? selectedRows ?? {});
458
527
  const [columnPin, setColumnPin] = react.useState(columnPinState ?? {});
459
528
  const [columnFilters, setColumnFilters] = react.useState([]);
460
529
  const [internalColumnSize, setInternalColumnSize] = react.useState(columnSizing ?? {});
@@ -465,6 +534,11 @@ function EdsDataGrid({
465
534
  pageIndex: 0,
466
535
  pageSize: pageSize ?? 25
467
536
  });
537
+ react.useEffect(() => {
538
+ if (virtualHeight) {
539
+ console.warn(`virtualHeight prop is deprecated and will be removed in a later version. Please update your code to use height instead.`);
540
+ }
541
+ }, [virtualHeight]);
468
542
  react.useEffect(() => {
469
543
  setVisible(columnVisibility ?? {});
470
544
  }, [columnVisibility, setVisible]);
@@ -475,8 +549,8 @@ function EdsDataGrid({
475
549
  setSorting(sortingState);
476
550
  }, [sortingState]);
477
551
  react.useEffect(() => {
478
- setSelection(selectedRows ?? {});
479
- }, [selectedRows]);
552
+ setInternalRowSelectionState(rowSelectionState ?? selectedRows ?? {});
553
+ }, [rowSelectionState, selectedRows]);
480
554
 
481
555
  /**
482
556
  * By default, the filter-function accepts single-value filters. This adds multi-filter functionality out of the box.
@@ -520,7 +594,7 @@ function EdsDataGrid({
520
594
  const options = {
521
595
  data: rows,
522
596
  columns: _columns,
523
- defaultColumn: {
597
+ defaultColumn: defaultColumn ?? {
524
598
  size: 150,
525
599
  cell: context => {
526
600
  return /*#__PURE__*/jsxRuntime.jsx(edsCoreReact.Typography, {
@@ -549,10 +623,14 @@ function EdsDataGrid({
549
623
  state: {
550
624
  sorting,
551
625
  columnPinning: columnPin,
552
- rowSelection: selection,
626
+ rowSelection: internalRowSelectionState,
553
627
  columnOrder: columnOrderState,
554
- columnSizing: columnSizing ?? internalColumnSize
628
+ columnSizing: columnSizing ?? internalColumnSize,
629
+ expanded: expansionState
555
630
  },
631
+ getSubRows: getSubRows,
632
+ onExpandedChange: setExpansionState,
633
+ getExpandedRowModel: reactTable.getExpandedRowModel(),
556
634
  onSortingChange: changes => {
557
635
  if (onSortingChange) {
558
636
  onSortingChange(changes);
@@ -569,7 +647,8 @@ function EdsDataGrid({
569
647
  debugTable: debug,
570
648
  debugHeaders: debug,
571
649
  debugColumns: debug,
572
- enableRowSelection: rowSelection ?? false,
650
+ enableRowSelection: enableRowSelection ?? rowSelection ?? false,
651
+ enableMultiRowSelection: enableMultiRowSelection ?? false,
573
652
  enableColumnPinning: true,
574
653
  enablePinning: true,
575
654
  getRowId
@@ -583,12 +662,11 @@ function EdsDataGrid({
583
662
  /**
584
663
  * Set up handlers for rowSelection
585
664
  */
586
- if (rowSelection ?? false) {
665
+ if (enableRowSelection ?? rowSelection ?? false) {
587
666
  options.onRowSelectionChange = updaterOrValue => {
588
- if (onSelectRow) {
589
- onSelectRow(updaterOrValue);
590
- }
591
- setSelection(updaterOrValue);
667
+ onSelectRow?.(updaterOrValue);
668
+ onRowSelectionChange?.(updaterOrValue);
669
+ setInternalRowSelectionState(updaterOrValue);
592
670
  };
593
671
  }
594
672
 
@@ -733,9 +811,14 @@ function EdsDataGrid({
733
811
  height: `${paddingTop}px`
734
812
  }
735
813
  })
736
- }), virtualRows.map(row => /*#__PURE__*/jsxRuntime.jsx(TableRow, {
737
- row: table.getRowModel().rows[row.index]
738
- }, row.index)), paddingBottom > 0 && /*#__PURE__*/jsxRuntime.jsx(edsCoreReact.Table.Row, {
814
+ }), virtualRows.map(virtualItem => {
815
+ const row = table.getRowModel().rows[virtualItem.index];
816
+ return /*#__PURE__*/jsxRuntime.jsx(TableRow, {
817
+ row: row,
818
+ onClick: onRowClick ? event => onRowClick(row, event) : undefined,
819
+ onCellClick: onCellClick
820
+ }, virtualItem.index);
821
+ }), paddingBottom > 0 && /*#__PURE__*/jsxRuntime.jsx(edsCoreReact.Table.Row, {
739
822
  "data-testid": "virtual-padding-bottom",
740
823
  className: 'virtual-padding-bottom',
741
824
  style: {
@@ -748,7 +831,9 @@ function EdsDataGrid({
748
831
  })
749
832
  })]
750
833
  }), !enableVirtual && table.getRowModel().rows.map(row => /*#__PURE__*/jsxRuntime.jsx(TableRow, {
751
- row: row
834
+ row: row,
835
+ onClick: onRowClick ? event => onRowClick(row, event) : undefined,
836
+ onCellClick: onCellClick
752
837
  }, row.id))]
753
838
  })]
754
839
  }), externalPaginator ? externalPaginator : enablePagination && /*#__PURE__*/jsxRuntime.jsx("div", {
@@ -784,4 +869,10 @@ const TableWrapper = styled__default.default.div.withConfig({
784
869
  $width
785
870
  }) => Boolean($height) && Boolean($width) ? 'strict' : 'unset');
786
871
 
872
+ Object.defineProperty(exports, "createColumnHelper", {
873
+ enumerable: true,
874
+ get: function () { return reactTable.createColumnHelper; }
875
+ });
787
876
  exports.EdsDataGrid = EdsDataGrid;
877
+ exports.FilterWrapper = FilterWrapper;
878
+ exports.SortIndicator = SortIndicator;
@@ -1,12 +1,12 @@
1
1
  import { Typography, useEds, Table, Pagination } from '@equinor/eds-core-react';
2
- import { getCoreRowModel, getSortedRowModel, getFacetedRowModel, getFacetedUniqueValues, getFacetedMinMaxValues, getFilteredRowModel, getPaginationRowModel, useReactTable } from '@tanstack/react-table';
2
+ import { getExpandedRowModel, getCoreRowModel, getSortedRowModel, getFacetedRowModel, getFacetedUniqueValues, getFacetedMinMaxValues, getFilteredRowModel, getPaginationRowModel, useReactTable } from '@tanstack/react-table';
3
3
  import { useVirtualizer } from '@tanstack/react-virtual';
4
4
  import { useState, useEffect, useMemo, useRef, useCallback } from 'react';
5
+ import styled from 'styled-components';
6
+ import { TableProvider } from './EdsDataGridContext.js';
5
7
  import { TableHeaderRow } from './components/TableHeaderRow.js';
6
8
  import { TableRow } from './components/TableRow.js';
7
- import { TableProvider } from './EdsDataGridContext.js';
8
- import styled from 'styled-components';
9
- import { addPxSuffixIfInputHasNoPrefix } from './utils.js';
9
+ import { addPxSuffixIfInputHasNoPrefix, logDevelopmentWarningOfPropUse } from './utils.js';
10
10
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
11
11
 
12
12
  /* eslint-disable @typescript-eslint/no-non-null-assertion */
@@ -16,13 +16,17 @@ function EdsDataGrid({
16
16
  columnResizeMode,
17
17
  pageSize,
18
18
  rowSelection,
19
+ enableRowSelection,
20
+ enableMultiRowSelection,
19
21
  selectedRows,
22
+ rowSelectionState,
20
23
  enableColumnFiltering,
21
24
  debug,
22
25
  enablePagination,
23
26
  enableSorting,
24
27
  stickyHeader,
25
28
  onSelectRow,
29
+ onRowSelectionChange,
26
30
  caption,
27
31
  enableVirtual,
28
32
  virtualHeight,
@@ -48,10 +52,34 @@ function EdsDataGrid({
48
52
  getRowId,
49
53
  rowVirtualizerInstanceRef,
50
54
  columnSizing,
51
- onColumnResize
55
+ onColumnResize,
56
+ expansionState,
57
+ setExpansionState,
58
+ getSubRows,
59
+ defaultColumn,
60
+ onRowClick,
61
+ onCellClick
52
62
  }) {
63
+ logDevelopmentWarningOfPropUse({
64
+ virtualHeight: {
65
+ value: virtualHeight,
66
+ mitigationInfo: "Use 'height' instead."
67
+ },
68
+ rowSelection: {
69
+ value: rowSelection,
70
+ mitigationInfo: "Use 'enableRowSelection' instead."
71
+ },
72
+ onSelectRow: {
73
+ value: onSelectRow,
74
+ mitigationInfo: "Use 'onRowSelectionChange' instead."
75
+ },
76
+ selectedRows: {
77
+ value: selectedRows,
78
+ mitigationInfo: "Use 'rowSelectionState' instead."
79
+ }
80
+ });
53
81
  const [sorting, setSorting] = useState(sortingState ?? []);
54
- const [selection, setSelection] = useState(selectedRows ?? {});
82
+ const [internalRowSelectionState, setInternalRowSelectionState] = useState(rowSelectionState ?? selectedRows ?? {});
55
83
  const [columnPin, setColumnPin] = useState(columnPinState ?? {});
56
84
  const [columnFilters, setColumnFilters] = useState([]);
57
85
  const [internalColumnSize, setInternalColumnSize] = useState(columnSizing ?? {});
@@ -62,6 +90,11 @@ function EdsDataGrid({
62
90
  pageIndex: 0,
63
91
  pageSize: pageSize ?? 25
64
92
  });
93
+ useEffect(() => {
94
+ if (virtualHeight) {
95
+ console.warn(`virtualHeight prop is deprecated and will be removed in a later version. Please update your code to use height instead.`);
96
+ }
97
+ }, [virtualHeight]);
65
98
  useEffect(() => {
66
99
  setVisible(columnVisibility ?? {});
67
100
  }, [columnVisibility, setVisible]);
@@ -72,8 +105,8 @@ function EdsDataGrid({
72
105
  setSorting(sortingState);
73
106
  }, [sortingState]);
74
107
  useEffect(() => {
75
- setSelection(selectedRows ?? {});
76
- }, [selectedRows]);
108
+ setInternalRowSelectionState(rowSelectionState ?? selectedRows ?? {});
109
+ }, [rowSelectionState, selectedRows]);
77
110
 
78
111
  /**
79
112
  * By default, the filter-function accepts single-value filters. This adds multi-filter functionality out of the box.
@@ -117,7 +150,7 @@ function EdsDataGrid({
117
150
  const options = {
118
151
  data: rows,
119
152
  columns: _columns,
120
- defaultColumn: {
153
+ defaultColumn: defaultColumn ?? {
121
154
  size: 150,
122
155
  cell: context => {
123
156
  return /*#__PURE__*/jsx(Typography, {
@@ -146,10 +179,14 @@ function EdsDataGrid({
146
179
  state: {
147
180
  sorting,
148
181
  columnPinning: columnPin,
149
- rowSelection: selection,
182
+ rowSelection: internalRowSelectionState,
150
183
  columnOrder: columnOrderState,
151
- columnSizing: columnSizing ?? internalColumnSize
184
+ columnSizing: columnSizing ?? internalColumnSize,
185
+ expanded: expansionState
152
186
  },
187
+ getSubRows: getSubRows,
188
+ onExpandedChange: setExpansionState,
189
+ getExpandedRowModel: getExpandedRowModel(),
153
190
  onSortingChange: changes => {
154
191
  if (onSortingChange) {
155
192
  onSortingChange(changes);
@@ -166,7 +203,8 @@ function EdsDataGrid({
166
203
  debugTable: debug,
167
204
  debugHeaders: debug,
168
205
  debugColumns: debug,
169
- enableRowSelection: rowSelection ?? false,
206
+ enableRowSelection: enableRowSelection ?? rowSelection ?? false,
207
+ enableMultiRowSelection: enableMultiRowSelection ?? false,
170
208
  enableColumnPinning: true,
171
209
  enablePinning: true,
172
210
  getRowId
@@ -180,12 +218,11 @@ function EdsDataGrid({
180
218
  /**
181
219
  * Set up handlers for rowSelection
182
220
  */
183
- if (rowSelection ?? false) {
221
+ if (enableRowSelection ?? rowSelection ?? false) {
184
222
  options.onRowSelectionChange = updaterOrValue => {
185
- if (onSelectRow) {
186
- onSelectRow(updaterOrValue);
187
- }
188
- setSelection(updaterOrValue);
223
+ onSelectRow?.(updaterOrValue);
224
+ onRowSelectionChange?.(updaterOrValue);
225
+ setInternalRowSelectionState(updaterOrValue);
189
226
  };
190
227
  }
191
228
 
@@ -330,9 +367,14 @@ function EdsDataGrid({
330
367
  height: `${paddingTop}px`
331
368
  }
332
369
  })
333
- }), virtualRows.map(row => /*#__PURE__*/jsx(TableRow, {
334
- row: table.getRowModel().rows[row.index]
335
- }, row.index)), paddingBottom > 0 && /*#__PURE__*/jsx(Table.Row, {
370
+ }), virtualRows.map(virtualItem => {
371
+ const row = table.getRowModel().rows[virtualItem.index];
372
+ return /*#__PURE__*/jsx(TableRow, {
373
+ row: row,
374
+ onClick: onRowClick ? event => onRowClick(row, event) : undefined,
375
+ onCellClick: onCellClick
376
+ }, virtualItem.index);
377
+ }), paddingBottom > 0 && /*#__PURE__*/jsx(Table.Row, {
336
378
  "data-testid": "virtual-padding-bottom",
337
379
  className: 'virtual-padding-bottom',
338
380
  style: {
@@ -345,7 +387,9 @@ function EdsDataGrid({
345
387
  })
346
388
  })]
347
389
  }), !enableVirtual && table.getRowModel().rows.map(row => /*#__PURE__*/jsx(TableRow, {
348
- row: row
390
+ row: row,
391
+ onClick: onRowClick ? event => onRowClick(row, event) : undefined,
392
+ onCellClick: onCellClick
349
393
  }, row.id))]
350
394
  })]
351
395
  }), externalPaginator ? externalPaginator : enablePagination && /*#__PURE__*/jsx("div", {
@@ -1,4 +1,4 @@
1
- import { createContext, useContext } from 'react';
1
+ import { useContext, createContext } from 'react';
2
2
  import { jsx } from 'react/jsx-runtime';
3
3
 
4
4
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -42,7 +42,6 @@ function DebouncedInput({
42
42
  /* eslint-disable-next-line @typescript-eslint/ban-ts-comment */,
43
43
  label: `Select ${label ?? ''}`,
44
44
  placeholder: props.placeholder ?? 'Search',
45
- disablePortal: false /*TODO: Check with Oddbjørn re. sizing/position*/,
46
45
  selectedOptions: value,
47
46
  onOptionsChange: c => setValue(c.selectedItems),
48
47
  multiline: true
@@ -1,10 +1,7 @@
1
- import { useState, useRef, useMemo } from 'react';
1
+ import { useMemo } from 'react';
2
2
  import { DebouncedInput } from './DebouncedInput.js';
3
- import { Button, Icon, Popover } from '@equinor/eds-core-react';
4
- import { filter_alt_active, filter_alt } from '@equinor/eds-icons';
5
3
  import styled from 'styled-components';
6
- import { tokens } from '@equinor/eds-tokens';
7
- import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
4
+ import { jsxs, jsx } from 'react/jsx-runtime';
8
5
 
9
6
  /* istanbul ignore file */
10
7
 
@@ -17,12 +14,6 @@ function Filter({
17
14
  table
18
15
  }) {
19
16
  const firstValue = table.getPreFilteredRowModel().flatRows[0]?.getValue(column.id);
20
- const [open, setOpen] = useState(false);
21
- const filterIconRef = useRef();
22
- const togglePopover = event => {
23
- event.stopPropagation();
24
- setOpen(!open);
25
- };
26
17
  const columnText = useMemo(() => {
27
18
  let header;
28
19
  try {
@@ -43,69 +34,33 @@ function Filter({
43
34
  const sortedUniqueValues = useMemo(() => typeof firstValue === 'number' ? [] : Array.from(column.getFacetedUniqueValues().keys()).sort().map(v => v ?? 'NULL_OR_UNDEFINED'),
44
35
  // eslint-disable-next-line react-hooks/exhaustive-deps
45
36
  [column.getFacetedUniqueValues()]);
46
- const hasActiveFilters = value => {
47
- if (Array.isArray(value)) {
48
- if (typeof firstValue === 'number') {
49
- return value.some(v => !isNaN(v) && !!v);
50
- } else {
51
- return value.filter(v => !!v).length > 0;
52
- }
53
- }
54
- return value;
55
- };
56
- return /*#__PURE__*/jsxs(Fragment, {
57
- children: [/*#__PURE__*/jsx(Button, {
58
- "aria-haspopup": true,
59
- "aria-expanded": open,
60
- "data-testid": 'open-filters',
61
- ref: filterIconRef,
62
- onClick: togglePopover,
63
- variant: 'ghost_icon',
64
- children: /*#__PURE__*/jsx(Icon, {
65
- color: tokens.colors.text.static_icons__default.hex,
66
- data: hasActiveFilters(columnFilterValue) ? filter_alt_active : filter_alt
67
- })
68
- }), /*#__PURE__*/jsx(Popover, {
69
- style: {
70
- width: typeof firstValue === 'number' ? '220px' : '340px'
71
- },
72
- anchorEl: filterIconRef.current,
73
- open: open,
74
- onClose: () => setOpen(false),
75
- children: /*#__PURE__*/jsx(Popover.Content, {
76
- style: {
77
- width: typeof firstValue === 'number' ? '180px' : '310px'
78
- },
79
- children: typeof firstValue === 'number' ? /*#__PURE__*/jsxs(NumberContainer, {
80
- children: [/*#__PURE__*/jsx(DebouncedInput, {
81
- type: "number",
82
- values: sortedUniqueValues,
83
- min: Number(column.getFacetedMinMaxValues()?.[0] ?? ''),
84
- max: Number(column.getFacetedMinMaxValues()?.[1] ?? ''),
85
- value: columnFilterValue?.[0] ?? '',
86
- onChange: value => column.setFilterValue(old => [value, old?.[1]]),
87
- placeholder: `Min ${column.getFacetedMinMaxValues()?.[0] ? `(${column.getFacetedMinMaxValues()?.[0]})` : ''}`
88
- }), /*#__PURE__*/jsx(DebouncedInput, {
89
- type: "number",
90
- values: sortedUniqueValues,
91
- min: Number(column.getFacetedMinMaxValues()?.[0] ?? ''),
92
- max: Number(column.getFacetedMinMaxValues()?.[1] ?? ''),
93
- value: columnFilterValue?.[1] ?? '',
94
- onChange: value => column.setFilterValue(old => [old?.[0], value]),
95
- placeholder: `Max ${column.getFacetedMinMaxValues()?.[1] ? `(${column.getFacetedMinMaxValues()?.[1]})` : ''}`
96
- })]
97
- }) : /*#__PURE__*/jsx(DebouncedInput, {
98
- type: "text",
99
- label: columnText,
100
- values: sortedUniqueValues,
101
- debounce: 100,
102
- value: columnFilterValue ?? [],
103
- onChange: value => column.setFilterValue(value),
104
- placeholder: `${(columnFilterValue ?? []).length} / ${column.getFacetedUniqueValues().size} selected`,
105
- list: column.id + 'list'
106
- })
107
- })
37
+ return typeof firstValue === 'number' ? /*#__PURE__*/jsxs(NumberContainer, {
38
+ children: [/*#__PURE__*/jsx(DebouncedInput, {
39
+ type: "number",
40
+ values: sortedUniqueValues,
41
+ min: Number(column.getFacetedMinMaxValues()?.[0] ?? ''),
42
+ max: Number(column.getFacetedMinMaxValues()?.[1] ?? ''),
43
+ value: columnFilterValue?.[0] ?? '',
44
+ onChange: value => column.setFilterValue(old => [value, old?.[1]]),
45
+ placeholder: `Min ${column.getFacetedMinMaxValues()?.[0] ? `(${column.getFacetedMinMaxValues()?.[0]})` : ''}`
46
+ }), /*#__PURE__*/jsx(DebouncedInput, {
47
+ type: "number",
48
+ values: sortedUniqueValues,
49
+ min: Number(column.getFacetedMinMaxValues()?.[0] ?? ''),
50
+ max: Number(column.getFacetedMinMaxValues()?.[1] ?? ''),
51
+ value: columnFilterValue?.[1] ?? '',
52
+ onChange: value => column.setFilterValue(old => [old?.[0], value]),
53
+ placeholder: `Max ${column.getFacetedMinMaxValues()?.[1] ? `(${column.getFacetedMinMaxValues()?.[1]})` : ''}`
108
54
  })]
55
+ }) : /*#__PURE__*/jsx(DebouncedInput, {
56
+ type: "text",
57
+ label: columnText,
58
+ values: sortedUniqueValues,
59
+ debounce: 100,
60
+ value: columnFilterValue ?? [],
61
+ onChange: value => column.setFilterValue(value),
62
+ placeholder: `${(columnFilterValue ?? []).length} / ${column.getFacetedUniqueValues().size} selected`,
63
+ list: column.id + 'list'
109
64
  });
110
65
  }
111
66
 
@@ -0,0 +1,73 @@
1
+ import { useState, useRef, useCallback } from 'react';
2
+ import { Button, Icon, Popover } from '@equinor/eds-core-react';
3
+ import { filter_alt_active, filter_alt } from '@equinor/eds-icons';
4
+ import { tokens } from '@equinor/eds-tokens';
5
+ import { Filter } from './Filter.js';
6
+ import { useTableContext } from '../EdsDataGridContext.js';
7
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
8
+
9
+ /* istanbul ignore file */
10
+
11
+ function FilterWrapper({
12
+ column,
13
+ CustomComponent
14
+ }) {
15
+ const {
16
+ table
17
+ } = useTableContext();
18
+ const firstValue = table.getPreFilteredRowModel().flatRows[0]?.getValue(column.id);
19
+ const [open, setOpen] = useState(false);
20
+ const filterIconRef = useRef();
21
+ const togglePopover = event => {
22
+ event.stopPropagation();
23
+ setOpen(!open);
24
+ };
25
+ const columnFilterValue = column.getFilterValue();
26
+ const hasActiveFilters = value => {
27
+ if (Array.isArray(value)) {
28
+ if (typeof firstValue === 'number') {
29
+ return value.some(v => !isNaN(v) && !!v);
30
+ } else {
31
+ return value.filter(v => !!v).length > 0;
32
+ }
33
+ }
34
+ return value;
35
+ };
36
+ const onChange = useCallback(value => column.setFilterValue(value), [column]);
37
+ return /*#__PURE__*/jsxs(Fragment, {
38
+ children: [/*#__PURE__*/jsx(Button, {
39
+ "aria-haspopup": true,
40
+ "aria-expanded": open,
41
+ "data-testid": 'open-filters',
42
+ ref: filterIconRef,
43
+ onClick: togglePopover,
44
+ variant: 'ghost_icon',
45
+ "aria-label": 'Show column filters',
46
+ children: /*#__PURE__*/jsx(Icon, {
47
+ color: tokens.colors.text.static_icons__default.hex,
48
+ data: hasActiveFilters(columnFilterValue) ? filter_alt_active : filter_alt
49
+ })
50
+ }), /*#__PURE__*/jsx(Popover, {
51
+ style: {
52
+ width: typeof firstValue === 'number' ? '220px' : '340px'
53
+ },
54
+ anchorEl: filterIconRef.current,
55
+ open: open,
56
+ onClose: () => setOpen(false),
57
+ children: /*#__PURE__*/jsx(Popover.Content, {
58
+ style: {
59
+ width: typeof firstValue === 'number' ? '180px' : '310px'
60
+ },
61
+ children: CustomComponent ? /*#__PURE__*/jsx(CustomComponent, {
62
+ onChange: onChange,
63
+ value: columnFilterValue
64
+ }) : /*#__PURE__*/jsx(Filter, {
65
+ column: column,
66
+ table: table
67
+ })
68
+ })
69
+ })]
70
+ });
71
+ }
72
+
73
+ export { FilterWrapper };
@@ -0,0 +1,18 @@
1
+ import { Icon } from '@equinor/eds-core-react';
2
+ import { arrow_up, arrow_down } from '@equinor/eds-icons';
3
+ import { jsx } from 'react/jsx-runtime';
4
+
5
+ const SortIndicator = ({
6
+ column
7
+ }) => {
8
+ return {
9
+ asc: /*#__PURE__*/jsx(Icon, {
10
+ data: arrow_up
11
+ }),
12
+ desc: /*#__PURE__*/jsx(Icon, {
13
+ data: arrow_down
14
+ })
15
+ }[column.getIsSorted()] ?? null;
16
+ };
17
+
18
+ export { SortIndicator };
@@ -1,8 +1,8 @@
1
- import { flexRender } from '@tanstack/react-table';
2
1
  import { Table } from '@equinor/eds-core-react';
3
- import { useTableContext } from '../EdsDataGridContext.js';
2
+ import { flexRender } from '@tanstack/react-table';
4
3
  import { useMemo } from 'react';
5
4
  import styled from 'styled-components';
5
+ import { useTableContext } from '../EdsDataGridContext.js';
6
6
  import { jsx } from 'react/jsx-runtime';
7
7
 
8
8
  const StyledCell = styled(Table.Cell).withConfig({
@@ -34,14 +34,13 @@ function TableBodyCell({
34
34
  $pinned: pinned,
35
35
  $offset: pinnedOffset,
36
36
  className: cellClass ? cellClass(cell.row, cell.column.id) : '',
37
- key: cell.id,
38
37
  style: {
39
38
  width: cell.column.getSize(),
40
39
  maxWidth: cell.column.getSize(),
41
40
  ...(cellStyle?.(cell.row, cell.column.id) ?? {})
42
41
  },
43
42
  children: flexRender(cell.column.columnDef.cell, cell.getContext())
44
- });
43
+ }, cell.id);
45
44
  }
46
45
 
47
46
  export { TableBodyCell };
@@ -1,11 +1,11 @@
1
1
  import { flexRender } from '@tanstack/react-table';
2
- import { Table, Icon } from '@equinor/eds-core-react';
3
- import { arrow_up, arrow_down } from '@equinor/eds-icons';
2
+ import { Table } from '@equinor/eds-core-react';
4
3
  import { useTableContext } from '../EdsDataGridContext.js';
5
- import { Filter } from './Filter.js';
6
4
  import styled from 'styled-components';
7
5
  import { tokens } from '@equinor/eds-tokens';
8
6
  import { useMemo } from 'react';
7
+ import { FilterWrapper } from './FilterWrapper.js';
8
+ import { SortIndicator } from './SortIndicator.js';
9
9
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
10
10
 
11
11
  const getSortLabel = sorted => {
@@ -25,7 +25,7 @@ const Resizer = styled.div.withConfig({
25
25
  const Cell = styled(Table.Cell).withConfig({
26
26
  displayName: "TableHeaderCell__Cell",
27
27
  componentId: "sc-1n0j3v0-2"
28
- })(["font-weight:bold;height:30px;position:", ";top:0;", " ", ";&:hover ", "{background:", ";opacity:1;}"], p => p.$sticky || p.$pinned ? 'sticky' : 'relative', p => {
28
+ })(["font-weight:bold;position:", ";top:0;", " ", ";&:hover ", "{background:", ";opacity:1;}"], p => p.$sticky || p.$pinned ? 'sticky' : 'relative', p => {
29
29
  if (p.$pinned) {
30
30
  return `${p.$pinned}: ${p.$offset}px;`;
31
31
  }
@@ -63,7 +63,6 @@ function TableHeaderCell({
63
63
  className: ctx.headerClass ? ctx.headerClass(header.column) : '',
64
64
  "aria-sort": getSortLabel(header.column.getIsSorted()),
65
65
  onClick: header.column.getToggleSortingHandler(),
66
- key: header.id,
67
66
  colSpan: header.colSpan,
68
67
  style: {
69
68
  width: header.getSize(),
@@ -80,22 +79,16 @@ function TableHeaderCell({
80
79
  className: "table-header-cell-label",
81
80
  children: flexRender(header.column.columnDef.header, header.getContext())
82
81
  })
83
- }), {
84
- asc: /*#__PURE__*/jsx(Icon, {
85
- data: arrow_up
86
- }),
87
- desc: /*#__PURE__*/jsx(Icon, {
88
- data: arrow_down
89
- })
90
- }[header.column.getIsSorted()] ?? null, header.column.getCanFilter() ?
91
-
82
+ }), header.column.columnDef.meta?.customFilterInput && /*#__PURE__*/jsx(SortIndicator, {
83
+ column: header.column
84
+ }), header.column.getCanFilter() && !header.column.columnDef.meta?.customFilterInput ?
85
+ /*#__PURE__*/
92
86
  // Supressing this warning - div is not interactive, but prevents propagation of events to avoid unintended sorting
93
87
  // eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions
94
88
  jsx("div", {
95
89
  onClick: e => e.stopPropagation(),
96
- children: /*#__PURE__*/jsx(Filter, {
97
- column: header.column,
98
- table: table
90
+ children: /*#__PURE__*/jsx(FilterWrapper, {
91
+ column: header.column
99
92
  })
100
93
  }) : null]
101
94
  }), columnResizeMode && /*#__PURE__*/jsx(Resizer, {
@@ -108,7 +101,7 @@ function TableHeaderCell({
108
101
  "data-testid": 'resize-handle',
109
102
  children: /*#__PURE__*/jsx(ResizeInner, {})
110
103
  })]
111
- });
104
+ }, header.id);
112
105
  }
113
106
 
114
107
  export { TableHeaderCell };
@@ -1,11 +1,13 @@
1
1
  import { Table } from '@equinor/eds-core-react';
2
- import { TableBodyCell } from './TableBodyCell.js';
3
- import { useTableContext } from '../EdsDataGridContext.js';
4
2
  import styled from 'styled-components';
3
+ import { useTableContext } from '../EdsDataGridContext.js';
4
+ import { TableBodyCell } from './TableBodyCell.js';
5
5
  import { jsx } from 'react/jsx-runtime';
6
6
 
7
7
  function TableRow({
8
- row
8
+ row,
9
+ onCellClick,
10
+ onClick
9
11
  }) {
10
12
  const {
11
13
  rowClass,
@@ -13,13 +15,13 @@ function TableRow({
13
15
  } = useTableContext();
14
16
  return /*#__PURE__*/jsx(StyledTableRow, {
15
17
  style: {
16
- cursor: row.getCanSelect() ? 'pointer' : 'inherit',
17
18
  ...(rowStyle?.(row) ?? {})
18
19
  },
19
20
  className: `${row.getIsSelected() ? 'selected' : ''} ${rowClass?.(row)}`,
20
- onClick: () => row.getCanSelect() ? row.toggleSelected() : null,
21
+ onClick: onClick,
21
22
  children: row.getVisibleCells().map(cell => /*#__PURE__*/jsx(TableBodyCell, {
22
- cell: cell
23
+ cell: cell,
24
+ onClick: onCellClick ? event => onCellClick(cell, event) : undefined
23
25
  }, cell.id))
24
26
  });
25
27
  }
package/dist/esm/index.js CHANGED
@@ -1 +1,4 @@
1
+ export { createColumnHelper } from '@tanstack/react-table';
1
2
  export { EdsDataGrid } from './EdsDataGrid.js';
3
+ export { FilterWrapper } from './components/FilterWrapper.js';
4
+ export { SortIndicator } from './components/SortIndicator.js';
package/dist/esm/utils.js CHANGED
@@ -23,5 +23,18 @@ function addPxSuffixIfInputHasNoPrefix(size) {
23
23
  }
24
24
  return size;
25
25
  }
26
+ function logDevelopmentWarningOfPropUse(deprecatedProps) {
27
+ if (process.env.NODE_ENV !== 'development') {
28
+ return;
29
+ }
30
+ for (const [key, {
31
+ value,
32
+ mitigationInfo
33
+ }] of Object.entries(deprecatedProps)) {
34
+ if (typeof value !== 'undefined') {
35
+ console.warn(`The prop '${key}' is deprecated and will be removed in a future release. ${mitigationInfo}`);
36
+ }
37
+ }
38
+ }
26
39
 
27
- export { addPxSuffixIfInputHasNoPrefix, isNumberOnlyString };
40
+ export { addPxSuffixIfInputHasNoPrefix, isNumberOnlyString, logDevelopmentWarningOfPropUse };
@@ -1,2 +1,2 @@
1
1
  import { EdsDataGridProps } from './EdsDataGridProps';
2
- export declare function EdsDataGrid<T>({ rows, columns, columnResizeMode, pageSize, rowSelection, selectedRows, enableColumnFiltering, debug, enablePagination, enableSorting, stickyHeader, onSelectRow, caption, enableVirtual, virtualHeight, columnVisibility, columnVisibilityChange, emptyMessage, columnOrder, cellClass, cellStyle, rowClass, rowStyle, headerClass, headerStyle, externalPaginator, onSortingChange, manualSorting, sortingState, columnPinState, scrollbarHorizontal, width, minWidth, height, getRowId, rowVirtualizerInstanceRef, columnSizing, onColumnResize, }: EdsDataGridProps<T>): import("react/jsx-runtime").JSX.Element;
2
+ export declare function EdsDataGrid<T>({ rows, columns, columnResizeMode, pageSize, rowSelection, enableRowSelection, enableMultiRowSelection, selectedRows, rowSelectionState, enableColumnFiltering, debug, enablePagination, enableSorting, stickyHeader, onSelectRow, onRowSelectionChange, caption, enableVirtual, virtualHeight, columnVisibility, columnVisibilityChange, emptyMessage, columnOrder, cellClass, cellStyle, rowClass, rowStyle, headerClass, headerStyle, externalPaginator, onSortingChange, manualSorting, sortingState, columnPinState, scrollbarHorizontal, width, minWidth, height, getRowId, rowVirtualizerInstanceRef, columnSizing, onColumnResize, expansionState, setExpansionState, getSubRows, defaultColumn, onRowClick, onCellClick, }: EdsDataGridProps<T>): import("react/jsx-runtime").JSX.Element;
@@ -1,6 +1,6 @@
1
- import { Column, ColumnDef, ColumnPinningState, ColumnResizeMode, ColumnSizingState, OnChangeFn, Row, RowSelectionState, SortingState, TableOptions } from '@tanstack/react-table';
1
+ import { Cell, Column, ColumnDef, ColumnPinningState, ColumnResizeMode, ColumnSizingState, ExpandedState, OnChangeFn, Row, RowSelectionState, SortingState, TableOptions } from '@tanstack/react-table';
2
2
  import { Virtualizer } from '@tanstack/react-virtual';
3
- import { CSSProperties, MutableRefObject, ReactElement } from 'react';
3
+ import { CSSProperties, MouseEvent, MutableRefObject, ReactElement } from 'react';
4
4
  type BaseProps<T> = {
5
5
  /**
6
6
  * The rows to display in the table
@@ -9,22 +9,13 @@ type BaseProps<T> = {
9
9
  /**
10
10
  * Definition of the columns to display in the table
11
11
  */
12
- columns: ColumnDef<T>[];
12
+ columns: TableOptions<T>['columns'];
13
13
  /**
14
14
  * The mode of column resizing. If not set, column resizing is disabled.
15
15
  * Can be either 'onChange' or 'onEnd'
16
16
  * @default undefined
17
17
  */
18
18
  columnResizeMode?: ColumnResizeMode;
19
- /**
20
- * Set this to enable rowSelection. If a function is provided, it will be called for each row to determine if it is selectable.
21
- * @default false
22
- */
23
- rowSelection?: boolean | ((row: Row<T>) => boolean);
24
- /**
25
- * Callback for when row-selection changes
26
- */
27
- onSelectRow?: OnChangeFn<RowSelectionState>;
28
19
  /**
29
20
  * Enable debug mode. See https://tanstack.com/table/v8/docs/api/core/table#debugall
30
21
  * @default false
@@ -45,11 +36,6 @@ type BaseProps<T> = {
45
36
  * @default undefined
46
37
  */
47
38
  emptyMessage?: string;
48
- /**
49
- * The currently selected rows
50
- * @default {}
51
- */
52
- selectedRows?: Record<string | number, boolean>;
53
39
  /**
54
40
  * Whether there should be horizontal scrolling.
55
41
  * This must be true for column pinning to work
@@ -84,6 +70,51 @@ type BaseProps<T> = {
84
70
  * @link [Guide](https://tanstack.com/table/v8/docs/guide/tables)
85
71
  */
86
72
  getRowId?: TableOptions<T>['getRowId'];
73
+ /**
74
+ * Optional prop to override the default column setup (cell, header, size etc.)
75
+ * @link [API Docs](https://tanstack.com/table/v8/docs/api/core/table#defaultcolumn)
76
+ */
77
+ defaultColumn?: Partial<ColumnDef<T, unknown>>;
78
+ };
79
+ type RowSelectionProps<T> = {
80
+ /**
81
+ * Set this to enable rowSelection. If a function is provided, it will be called for each row to determine if it is selectable.
82
+ * @default false
83
+ */
84
+ enableRowSelection?: boolean | ((row: Row<T>) => boolean);
85
+ /**
86
+ * Only used if row selection has been enabled via `enableRowSelection`
87
+ * Enables/disables multiple row selection for all rows in the table OR
88
+ * A function that given a row, returns whether to enable/disable multiple row selection for that row's children/grandchildren
89
+ * @default false
90
+ */
91
+ enableMultiRowSelection?: boolean | ((row: Row<T>) => boolean);
92
+ /**
93
+ * The currently selected rows
94
+ * @deprecated Use `rowSelectionState`
95
+ * @default {}
96
+ */
97
+ selectedRows?: Record<string | number, boolean>;
98
+ /**
99
+ * The currently selected rows
100
+ * @default {}
101
+ */
102
+ rowSelectionState?: RowSelectionState;
103
+ /**
104
+ * Set this to enable rowSelection. If a function is provided, it will be called for each row to determine if it is selectable.
105
+ * @deprecated Use `enableRowSelection`
106
+ * @default false
107
+ */
108
+ rowSelection?: boolean | ((row: Row<T>) => boolean);
109
+ /**
110
+ * Callback for when row-selection changes
111
+ * @deprecated Use `onRowSelectionChange`
112
+ */
113
+ onSelectRow?: OnChangeFn<RowSelectionState>;
114
+ /**
115
+ * Callback for when row-selection changes
116
+ */
117
+ onRowSelectionChange?: OnChangeFn<RowSelectionState>;
87
118
  };
88
119
  type StyleProps<T> = {
89
120
  /**
@@ -129,6 +160,24 @@ type FilterProps = {
129
160
  */
130
161
  enableColumnFiltering?: boolean;
131
162
  };
163
+ type HandlersProps<T> = {
164
+ /**
165
+ * Row click handler.
166
+ *
167
+ * @param row The current row
168
+ * @param event The click event
169
+ * @returns
170
+ */
171
+ onRowClick?: (row: Row<T>, event: MouseEvent<HTMLTableRowElement>) => unknown;
172
+ /**
173
+ * Cell click handler.
174
+ *
175
+ * @param cell The current cell
176
+ * @param event The click event
177
+ * @returns
178
+ */
179
+ onCellClick?: (cell: Cell<T, unknown>, event: MouseEvent<HTMLTableCellElement>) => unknown;
180
+ };
132
181
  type PagingProps = {
133
182
  /**
134
183
  * Whether pagination should be enabled.
@@ -155,6 +204,7 @@ type VirtualProps = {
155
204
  enableVirtual?: boolean;
156
205
  /**
157
206
  * The height of the virtualized table in pixels.
207
+ * @deprecated Use `height` prop over virtualHeight, this will be removed in a later version
158
208
  * @default 500
159
209
  */
160
210
  virtualHeight?: number;
@@ -187,7 +237,12 @@ type ColumnProps = {
187
237
  type RefProps = {
188
238
  rowVirtualizerInstanceRef?: MutableRefObject<Virtualizer<HTMLDivElement, Element> | null>;
189
239
  };
190
- export type EdsDataGridProps<T> = BaseProps<T> & StyleProps<T> & SortProps & FilterProps & PagingProps & ColumnProps & VirtualProps & RefProps & {
240
+ type ExpansionProps<T> = {
241
+ expansionState?: ExpandedState;
242
+ setExpansionState?: React.Dispatch<React.SetStateAction<ExpandedState>>;
243
+ getSubRows?: (row: T, rowIndex: number) => Array<T>;
244
+ };
245
+ export type EdsDataGridProps<T> = BaseProps<T> & RowSelectionProps<T> & StyleProps<T> & HandlersProps<T> & SortProps & FilterProps & PagingProps & ColumnProps & VirtualProps & RefProps & ExpansionProps<T> & {
191
246
  /**
192
247
  * Which columns are visible. If not set, all columns are visible. undefined means that the column is visible.
193
248
  * @default undefined
@@ -0,0 +1,11 @@
1
+ import { Column } from '@tanstack/react-table';
2
+ import { FC } from 'react';
3
+ type FilterProps<T = unknown, U = unknown> = {
4
+ column: Column<T>;
5
+ CustomComponent?: FC<{
6
+ onChange: (value: U) => void;
7
+ value: U;
8
+ }>;
9
+ };
10
+ export declare function FilterWrapper<T = unknown>({ column, CustomComponent, }: FilterProps<T>): import("react/jsx-runtime").JSX.Element;
11
+ export {};
@@ -0,0 +1,4 @@
1
+ import { Column } from '@tanstack/react-table';
2
+ export declare const SortIndicator: <T>({ column, }: {
3
+ column: Column<T, unknown>;
4
+ }) => import("react/jsx-runtime").JSX.Element;
@@ -1,6 +1,7 @@
1
1
  import { Cell } from '@tanstack/react-table';
2
+ import { HTMLAttributes } from 'react';
2
3
  type Props<T> = {
3
4
  cell: Cell<T, unknown>;
4
- };
5
+ } & HTMLAttributes<HTMLTableCellElement>;
5
6
  export declare function TableBodyCell<T>({ cell }: Props<T>): import("react/jsx-runtime").JSX.Element;
6
7
  export {};
@@ -1,7 +1,9 @@
1
1
  import { Row } from '@tanstack/react-table';
2
2
  import { HTMLAttributes } from 'react';
3
+ import { EdsDataGridProps } from '../EdsDataGridProps';
3
4
  type Props<T> = {
4
5
  row: Row<T>;
6
+ onCellClick?: EdsDataGridProps<T>['onCellClick'];
5
7
  } & HTMLAttributes<HTMLTableRowElement>;
6
- export declare function TableRow<T>({ row }: Props<T>): import("react/jsx-runtime").JSX.Element;
8
+ export declare function TableRow<T>({ row, onCellClick, onClick }: Props<T>): import("react/jsx-runtime").JSX.Element;
7
9
  export {};
@@ -1,3 +1,6 @@
1
+ export { createColumnHelper } from '@tanstack/react-table';
1
2
  export * from './EdsDataGrid';
2
3
  export * from './EdsDataGridProps';
4
+ export * from './components/FilterWrapper';
5
+ export * from './components/SortIndicator';
3
6
  export * from './types';
@@ -1,3 +1,3 @@
1
1
  import type { Cell, CellContext, ColumnDef, ColumnPinningState, ColumnResizeMode, ColumnSort, ExpandedState, HeaderContext, OnChangeFn, Row, RowSelectionState, SortingState, Table, VisibilityState } from '@tanstack/react-table';
2
2
  import type { Virtualizer } from '@tanstack/react-virtual';
3
- export type { Cell, CellContext, ColumnDef, ColumnPinningState, ColumnResizeMode, ColumnSort, ExpandedState, HeaderContext, OnChangeFn, Row, RowSelectionState, SortingState, Table, VisibilityState, Virtualizer, };
3
+ export type { Cell, CellContext, ColumnDef, ColumnPinningState, ColumnResizeMode, ColumnSort, ExpandedState, HeaderContext, OnChangeFn, Row, RowSelectionState, SortingState, Table, Virtualizer, VisibilityState, };
@@ -16,3 +16,7 @@
16
16
  */
17
17
  export declare function isNumberOnlyString(number: string): boolean;
18
18
  export declare function addPxSuffixIfInputHasNoPrefix(size: number | string): string;
19
+ export declare function logDevelopmentWarningOfPropUse(deprecatedProps: Record<string, {
20
+ value: unknown;
21
+ mitigationInfo?: string;
22
+ }>): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@equinor/eds-data-grid-react",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "A feature-rich data-grid written in React, implementing the Equinor Design System",
5
5
  "license": "MIT",
6
6
  "types": "dist/types/index.d.ts",
@@ -17,37 +17,35 @@
17
17
  "@equinor/eds-core-react": ">=0.36.0",
18
18
  "react": ">=16.8",
19
19
  "react-dom": ">=16.8",
20
- "styled-components": ">=4.2"
20
+ "styled-components": ">=5.1"
21
21
  },
22
22
  "dependencies": {
23
- "@tanstack/react-table": "^8.10.7",
24
- "@tanstack/react-virtual": "^3.0.4",
23
+ "@tanstack/react-table": "^8.16.0",
24
+ "@tanstack/react-virtual": "^3.5.0",
25
25
  "@equinor/eds-icons": "^0.21.0",
26
- "@equinor/eds-utils": "^0.8.4",
26
+ "@equinor/eds-utils": "^0.8.5",
27
27
  "@equinor/eds-tokens": "0.9.2"
28
28
  },
29
29
  "devDependencies": {
30
- "@mdx-js/react": "2.3.0",
31
30
  "@rollup/plugin-babel": "^6.0.4",
32
31
  "@rollup/plugin-commonjs": "^25.0.7",
33
32
  "@rollup/plugin-node-resolve": "^15.2.3",
34
- "@storybook/addon-a11y": "^7.5.1",
35
- "@storybook/addon-actions": "^7.5.1",
36
- "@storybook/addon-docs": "^7.5.1",
37
- "@storybook/addon-essentials": "^7.5.1",
38
- "@storybook/addon-links": "^7.5.1",
39
- "@storybook/blocks": "^7.5.1",
40
- "@storybook/builder-vite": "^7.5.1",
41
- "@storybook/client-api": "^7.5.1",
42
- "@storybook/react": "^7.5.1",
43
- "@storybook/react-vite": "^7.5.1",
33
+ "@storybook/addon-a11y": "^8.0.2",
34
+ "@storybook/addon-actions": "^8.0.2",
35
+ "@storybook/addon-docs": "^8.0.2",
36
+ "@storybook/addon-essentials": "^8.0.2",
37
+ "@storybook/addon-links": "^8.0.2",
38
+ "@storybook/blocks": "^8.0.2",
39
+ "@storybook/preview-api": "^8.0.2",
40
+ "@storybook/react": "^8.0.2",
41
+ "@storybook/react-vite": "^8.0.2",
44
42
  "@testing-library/dom": "^9.3.3",
45
43
  "@testing-library/jest-dom": "^6.1.4",
46
44
  "@testing-library/react": "14.0.0",
47
45
  "@testing-library/user-event": "^14.5.1",
48
46
  "@types/jest": "^29.5.6",
49
47
  "@types/node": "20.8.9",
50
- "@types/ramda": "^0.29.7",
48
+ "@types/ramda": "^0.30.0",
51
49
  "@types/react": "^18.2.33",
52
50
  "@types/react-dom": "^18.2.14",
53
51
  "babel-loader": "^9.1.3",
@@ -57,16 +55,15 @@
57
55
  "jest-styled-components": "^7.2.0",
58
56
  "js-file-download": "^0.4.12",
59
57
  "postcss": "^8.4.31",
60
- "ramda": "^0.29.1",
58
+ "ramda": "^0.30.0",
61
59
  "react": "^18.2.0",
62
60
  "react-dom": "^18.2.0",
63
61
  "react-hook-form": "^7.47.0",
64
- "remark-gfm": "^4.0.0",
65
62
  "rollup": "^4.2.0",
66
63
  "rollup-plugin-delete": "^2.0.0",
67
64
  "rollup-plugin-postcss": "^4.0.2",
68
- "storybook": "^7.5.1",
69
- "styled-components": "6.1.0",
65
+ "storybook": "^8.0.2",
66
+ "styled-components": "6.1.11",
70
67
  "ts-jest": "29.1.1",
71
68
  "ts-node": "10.9.1",
72
69
  "tsc-watch": "^6.0.4",