@equinor/eds-data-grid-react 0.8.1 → 0.9.1

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.
@@ -513,13 +513,42 @@ function TableRow({
513
513
  onCellClick,
514
514
  onClick,
515
515
  onDoubleClick,
516
- onContextMenu
516
+ onContextMenu,
517
+ rowVirtualizer,
518
+ virtualItem
517
519
  }) {
518
520
  const {
519
521
  rowClass,
520
522
  rowStyle
521
523
  } = useTableContext();
524
+ const isMountedRef = react.useRef(true);
525
+
526
+ // Set mounted flag to false on unmount to prevent measurements during cleanup
527
+ react.useEffect(() => {
528
+ return () => {
529
+ isMountedRef.current = false;
530
+ };
531
+ }, []);
532
+
533
+ // Create a stable ref callback that guards against calls during unmount
534
+ const measureRef = react.useCallback(node => {
535
+ // Only measure if we have a node, the component is still mounted, and we have a virtualizer
536
+ if (node && isMountedRef.current && rowVirtualizer) {
537
+ try {
538
+ rowVirtualizer.measureElement(node);
539
+ } catch (error) {
540
+ // Silently catch any errors during measurement to prevent crashes
541
+ // This can happen if the virtualizer is in an inconsistent state during unmount
542
+ if (process.env.NODE_ENV === 'development') {
543
+ console.warn('Failed to measure element during virtualization:', error);
544
+ }
545
+ }
546
+ }
547
+ }, [rowVirtualizer]);
522
548
  return /*#__PURE__*/jsxRuntime.jsx(StyledTableRow, {
549
+ "data-index": virtualItem?.index,
550
+ ref: measureRef //measure dynamic row height safely
551
+ ,
523
552
  style: {
524
553
  ...(rowStyle?.(row) ?? {})
525
554
  },
@@ -578,6 +607,12 @@ function logDevelopmentWarningOfPropUse(deprecatedProps) {
578
607
  }
579
608
  }
580
609
  }
610
+ const isFirefox = () => {
611
+ return typeof window !== 'undefined' && navigator.userAgent.indexOf('Firefox') !== -1;
612
+ };
613
+ const getMeasureElementHandler = () => {
614
+ return isFirefox() ? undefined : element => element?.getBoundingClientRect().height;
615
+ };
581
616
 
582
617
  /* eslint-disable @typescript-eslint/no-non-null-assertion */
583
618
  function EdsDataGridInner({
@@ -890,7 +925,9 @@ function EdsDataGridInner({
890
925
  const virtualizer = reactVirtual.useVirtualizer({
891
926
  count: table.getRowModel().rows.length,
892
927
  getScrollElement: () => parentRef.current,
893
- estimateSize
928
+ estimateSize,
929
+ //measure dynamic row height, except in firefox because it measures table border height incorrectly
930
+ measureElement: getMeasureElementHandler()
894
931
  });
895
932
  if (rowVirtualizerInstanceRef) rowVirtualizerInstanceRef.current = virtualizer;
896
933
 
@@ -977,6 +1014,8 @@ function EdsDataGridInner({
977
1014
  }), virtualRows.map(virtualItem => {
978
1015
  const row = table.getRowModel().rows[virtualItem.index];
979
1016
  return /*#__PURE__*/jsxRuntime.jsx(TableRow, {
1017
+ virtualItem: virtualItem,
1018
+ rowVirtualizer: virtualizer,
980
1019
  row: row,
981
1020
  onContextMenu: onRowContextMenu ? event => onRowContextMenu(row, event) : undefined,
982
1021
  onClick: onRowClick ? event => onRowClick(row, event) : undefined,
@@ -7,7 +7,7 @@ import { TableProvider } from './EdsDataGridContext.js';
7
7
  import { TableHeaderRow } from './components/TableHeaderRow.js';
8
8
  import { TableFooterRow } from './components/TableFooterRow.js';
9
9
  import { TableRow } from './components/TableRow.js';
10
- import { addPxSuffixIfInputHasNoPrefix, logDevelopmentWarningOfPropUse } from './utils.js';
10
+ import { addPxSuffixIfInputHasNoPrefix, logDevelopmentWarningOfPropUse, getMeasureElementHandler } from './utils.js';
11
11
  import { mergeRefs } from '@equinor/eds-utils';
12
12
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
13
13
 
@@ -322,7 +322,9 @@ function EdsDataGridInner({
322
322
  const virtualizer = useVirtualizer({
323
323
  count: table.getRowModel().rows.length,
324
324
  getScrollElement: () => parentRef.current,
325
- estimateSize
325
+ estimateSize,
326
+ //measure dynamic row height, except in firefox because it measures table border height incorrectly
327
+ measureElement: getMeasureElementHandler()
326
328
  });
327
329
  if (rowVirtualizerInstanceRef) rowVirtualizerInstanceRef.current = virtualizer;
328
330
 
@@ -409,6 +411,8 @@ function EdsDataGridInner({
409
411
  }), virtualRows.map(virtualItem => {
410
412
  const row = table.getRowModel().rows[virtualItem.index];
411
413
  return /*#__PURE__*/jsx(TableRow, {
414
+ virtualItem: virtualItem,
415
+ rowVirtualizer: virtualizer,
412
416
  row: row,
413
417
  onContextMenu: onRowContextMenu ? event => onRowContextMenu(row, event) : undefined,
414
418
  onClick: onRowClick ? event => onRowClick(row, event) : undefined,
@@ -1,4 +1,5 @@
1
1
  import { Table } from '@equinor/eds-core-react';
2
+ import { useRef, useEffect, useCallback } from 'react';
2
3
  import styled from 'styled-components';
3
4
  import { useTableContext } from '../EdsDataGridContext.js';
4
5
  import { TableBodyCell } from './TableBodyCell.js';
@@ -9,13 +10,42 @@ function TableRow({
9
10
  onCellClick,
10
11
  onClick,
11
12
  onDoubleClick,
12
- onContextMenu
13
+ onContextMenu,
14
+ rowVirtualizer,
15
+ virtualItem
13
16
  }) {
14
17
  const {
15
18
  rowClass,
16
19
  rowStyle
17
20
  } = useTableContext();
21
+ const isMountedRef = useRef(true);
22
+
23
+ // Set mounted flag to false on unmount to prevent measurements during cleanup
24
+ useEffect(() => {
25
+ return () => {
26
+ isMountedRef.current = false;
27
+ };
28
+ }, []);
29
+
30
+ // Create a stable ref callback that guards against calls during unmount
31
+ const measureRef = useCallback(node => {
32
+ // Only measure if we have a node, the component is still mounted, and we have a virtualizer
33
+ if (node && isMountedRef.current && rowVirtualizer) {
34
+ try {
35
+ rowVirtualizer.measureElement(node);
36
+ } catch (error) {
37
+ // Silently catch any errors during measurement to prevent crashes
38
+ // This can happen if the virtualizer is in an inconsistent state during unmount
39
+ if (process.env.NODE_ENV === 'development') {
40
+ console.warn('Failed to measure element during virtualization:', error);
41
+ }
42
+ }
43
+ }
44
+ }, [rowVirtualizer]);
18
45
  return /*#__PURE__*/jsx(StyledTableRow, {
46
+ "data-index": virtualItem?.index,
47
+ ref: measureRef //measure dynamic row height safely
48
+ ,
19
49
  style: {
20
50
  ...(rowStyle?.(row) ?? {})
21
51
  },
package/dist/esm/utils.js CHANGED
@@ -36,5 +36,11 @@ function logDevelopmentWarningOfPropUse(deprecatedProps) {
36
36
  }
37
37
  }
38
38
  }
39
+ const isFirefox = () => {
40
+ return typeof window !== 'undefined' && navigator.userAgent.indexOf('Firefox') !== -1;
41
+ };
42
+ const getMeasureElementHandler = () => {
43
+ return isFirefox() ? undefined : element => element?.getBoundingClientRect().height;
44
+ };
39
45
 
40
- export { addPxSuffixIfInputHasNoPrefix, isNumberOnlyString, logDevelopmentWarningOfPropUse };
46
+ export { addPxSuffixIfInputHasNoPrefix, getMeasureElementHandler, isFirefox, isNumberOnlyString, logDevelopmentWarningOfPropUse };
@@ -0,0 +1,12 @@
1
+ import React, { ButtonHTMLAttributes } from 'react';
2
+ export type ClickableCellProps = {
3
+ /** Cell content */
4
+ children: React.ReactNode;
5
+ /** Click handler */
6
+ onClick: () => void;
7
+ /** Accessible label for screen readers */
8
+ ariaLabel: string;
9
+ /** Indicates if the cell is selected */
10
+ isSelected?: boolean;
11
+ } & Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'onClick' | 'children'>;
12
+ export declare function ClickableCell({ children, onClick, ariaLabel, isSelected, ...rest }: ClickableCellProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,3 @@
1
+ import type { ComponentToken } from '@equinor/eds-tokens';
2
+ export type ClickableCellToken = ComponentToken;
3
+ export declare const clickableCell: ClickableCellToken;
@@ -0,0 +1 @@
1
+ export { ClickableCell, type ClickableCellProps } from './ClickableCell';
@@ -1,9 +1,12 @@
1
1
  import { Row } from '@tanstack/react-table';
2
2
  import { HTMLAttributes } from 'react';
3
3
  import { EdsDataGridProps } from '../EdsDataGridProps';
4
+ import { VirtualItem, Virtualizer } from '@tanstack/react-virtual';
4
5
  type Props<T> = {
5
6
  row: Row<T>;
6
7
  onCellClick?: EdsDataGridProps<T>['onCellClick'];
8
+ rowVirtualizer?: Virtualizer<HTMLDivElement, HTMLTableRowElement>;
9
+ virtualItem?: VirtualItem;
7
10
  } & HTMLAttributes<HTMLTableRowElement>;
8
- export declare function TableRow<T>({ row, onCellClick, onClick, onDoubleClick, onContextMenu, }: Props<T>): import("react/jsx-runtime").JSX.Element;
11
+ export declare function TableRow<T>({ row, onCellClick, onClick, onDoubleClick, onContextMenu, rowVirtualizer, virtualItem, }: Props<T>): import("react/jsx-runtime").JSX.Element;
9
12
  export {};
@@ -20,3 +20,5 @@ export declare function logDevelopmentWarningOfPropUse(deprecatedProps: Record<s
20
20
  value: unknown;
21
21
  mitigationInfo?: string;
22
22
  }>): void;
23
+ export declare const isFirefox: () => boolean;
24
+ export declare const getMeasureElementHandler: () => (element: HTMLTableRowElement) => number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@equinor/eds-data-grid-react",
3
- "version": "0.8.1",
3
+ "version": "0.9.1",
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",
@@ -21,52 +21,48 @@
21
21
  },
22
22
  "dependencies": {
23
23
  "@tanstack/react-table": "^8.21.3",
24
- "@tanstack/react-virtual": "^3.13.8",
24
+ "@tanstack/react-virtual": "^3.13.12",
25
25
  "@equinor/eds-icons": "^0.22.0",
26
- "@equinor/eds-tokens": "0.9.2",
27
- "@equinor/eds-utils": "^0.8.7"
26
+ "@equinor/eds-utils": "^0.9.0",
27
+ "@equinor/eds-tokens": "0.10.0"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@rollup/plugin-babel": "^6.0.4",
31
- "@rollup/plugin-commonjs": "^28.0.3",
31
+ "@rollup/plugin-commonjs": "^28.0.6",
32
32
  "@rollup/plugin-node-resolve": "^16.0.1",
33
- "@storybook/addon-a11y": "^8.6.12",
34
- "@storybook/addon-actions": "^8.6.12",
35
- "@storybook/addon-docs": "^8.6.12",
36
- "@storybook/addon-essentials": "^8.6.12",
37
- "@storybook/addon-links": "^8.6.12",
38
- "@storybook/blocks": "^8.6.12",
39
- "@storybook/preview-api": "^8.6.12",
40
- "@storybook/react": "^8.6.12",
41
- "@storybook/react-vite": "^8.6.12",
42
- "@testing-library/dom": "^10.4.0",
43
- "@testing-library/jest-dom": "^6.6.3",
33
+ "@storybook/addon-a11y": "^9.1.5",
34
+ "@storybook/addon-docs": "^9.1.5",
35
+ "@storybook/addon-links": "^9.1.5",
36
+ "@storybook/react-vite": "^9.1.5",
37
+ "@testing-library/dom": "^10.4.1",
38
+ "@testing-library/jest-dom": "^6.8.0",
44
39
  "@testing-library/react": "16.3.0",
45
40
  "@testing-library/user-event": "^14.6.1",
46
41
  "@types/jest": "^29.5.14",
47
- "@types/node": "^22.15.3",
42
+ "@types/node": "^22.18.0",
48
43
  "@types/ramda": "^0.30.2",
49
- "@types/react": "^18.3.20",
50
- "@types/react-dom": "^18.3.6",
44
+ "@types/react": "^18.3.24",
45
+ "@types/react-dom": "^18.3.7",
51
46
  "babel-plugin-styled-components": "^2.1.4",
52
47
  "jest": "29.7.0",
53
48
  "jest-environment-jsdom": "29.7.0",
54
49
  "jest-styled-components": "^7.2.0",
55
50
  "js-file-download": "^0.4.12",
56
- "postcss": "^8.5.3",
57
- "ramda": "^0.30.1",
51
+ "postcss": "^8.5.6",
52
+ "ramda": "^0.31.3",
58
53
  "react": "^18.3.1",
59
54
  "react-dom": "^18.3.1",
60
- "react-hook-form": "^7.56.1",
61
- "rollup": "^4.40.1",
55
+ "react-hook-form": "^7.62.0",
56
+ "rollup": "^4.50.0",
62
57
  "rollup-plugin-delete": "^2.2.0",
63
58
  "rollup-plugin-postcss": "^4.0.2",
64
- "storybook": "^8.6.14",
65
- "styled-components": "6.1.18",
66
- "ts-jest": "29.3.3",
59
+ "storybook": "^9.1.5",
60
+ "styled-components": "6.1.19",
61
+ "ts-jest": "29.4.0",
67
62
  "ts-node": "10.9.2",
68
- "tsc-watch": "^6.2.1",
69
- "typescript": "~5.8.3"
63
+ "tsc-watch": "^6.3.1",
64
+ "typescript": "~5.8.3",
65
+ "eslint-plugin-storybook": "9.1.5"
70
66
  },
71
67
  "homepage": "https://eds.equinor.com",
72
68
  "repository": {
@@ -86,7 +82,7 @@
86
82
  ],
87
83
  "scripts": {
88
84
  "build": "rollup -c --bundleConfigAsCjs && tsc -p tsconfig.build.json",
89
- "test": "tsc -p tsconfig.spec.json && jest",
85
+ "test": "tsc -p tsconfig.test.json && jest",
90
86
  "test:watch": "tsc-watch -p tsconfig.test.json --onFirstSuccess \"jest --watch\"",
91
87
  "test:update-snapshots": "jest --updateSnapshot",
92
88
  "storybook": "storybook dev -p 9000 --ci",