@equinor/eds-data-grid-react 0.7.7 → 0.8.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.
@@ -59,7 +59,6 @@ function DebouncedInput({
59
59
  label: props.placeholder,
60
60
  children: /*#__PURE__*/jsxRuntime.jsx(edsCoreReact.Input, {
61
61
  type: 'number',
62
- placeholder: '0',
63
62
  value: value,
64
63
  onChange: e => setValue(e.target.valueAsNumber)
65
64
  })
@@ -161,6 +160,34 @@ function Filter({
161
160
  });
162
161
  }
163
162
 
163
+ const ResizeInner = styled__default.default.div.withConfig({
164
+ displayName: "Resizer__ResizeInner",
165
+ componentId: "sc-plcbjs-0"
166
+ })(["width:2px;opacity:0;height:100%;"]);
167
+ const Resizer = styled__default.default.div.withConfig({
168
+ displayName: "Resizer",
169
+ componentId: "sc-plcbjs-1"
170
+ })(["transform:", ";", "{opacity:", ";}position:absolute;right:0;top:0;height:100%;width:5px;cursor:col-resize;user-select:none;touch-action:none;display:flex;justify-content:flex-end;z-index:1;"], props => props.$columnResizeMode === 'onEnd' ? 'translateX(0px)' : 'none', ResizeInner, props => props.$isResizing ? 1 : 0);
171
+
172
+ const FilterVisibility = styled__default.default.div.withConfig({
173
+ displayName: "TableCell__FilterVisibility",
174
+ componentId: "sc-1g0k23m-0"
175
+ })([""]);
176
+ const TableCell = styled__default.default(edsCoreReact.Table.Cell).withConfig({
177
+ displayName: "TableCell",
178
+ componentId: "sc-1g0k23m-1"
179
+ })(["font-weight:bold;position:", ";top:0;", " ", ";&:hover ", "{background:", ";opacity:1;}", ":not(:focus-within){opacity:", ";}&:hover ", "{opacity:1;}", ":focus-within{opacity:1;}"], p => p.$sticky || p.$pinned ? 'sticky' : 'relative', p => {
180
+ if (p.$pinned) {
181
+ return `${p.$pinned}: ${p.$offset}px;`;
182
+ }
183
+ return '';
184
+ }, p => {
185
+ if (p.$sticky && p.$pinned) return 'z-index: 13';
186
+ if (p.$sticky || p.$pinned) return 'z-index: 12';
187
+ }, ResizeInner, edsTokens.tokens.colors.interactive.primary__hover.rgba, FilterVisibility, ({
188
+ $activeFilter
189
+ }) => $activeFilter ? 1 : 0, FilterVisibility, FilterVisibility);
190
+
164
191
  /* istanbul ignore file */
165
192
 
166
193
  function FilterWrapper({
@@ -189,7 +216,8 @@ function FilterWrapper({
189
216
  return value;
190
217
  };
191
218
  const onChange = react.useCallback(value => column.setFilterValue(value), [column]);
192
- return /*#__PURE__*/jsxRuntime.jsxs("span", {
219
+ return /*#__PURE__*/jsxRuntime.jsxs(FilterVisibility, {
220
+ onClick: e => e.stopPropagation(),
193
221
  children: [/*#__PURE__*/jsxRuntime.jsx(edsCoreReact.Button, {
194
222
  "aria-haspopup": true,
195
223
  "aria-expanded": open,
@@ -238,28 +266,6 @@ const SortIndicator = ({
238
266
  }[column.getIsSorted()] ?? null;
239
267
  };
240
268
 
241
- const ResizeInner = styled__default.default.div.withConfig({
242
- displayName: "Resizer__ResizeInner",
243
- componentId: "sc-plcbjs-0"
244
- })(["width:2px;opacity:0;height:100%;"]);
245
- const Resizer = styled__default.default.div.withConfig({
246
- displayName: "Resizer",
247
- componentId: "sc-plcbjs-1"
248
- })(["transform:", ";", "{opacity:", ";}position:absolute;right:0;top:0;height:100%;width:5px;cursor:col-resize;user-select:none;touch-action:none;display:flex;justify-content:flex-end;z-index:1;"], props => props.$columnResizeMode === 'onEnd' ? 'translateX(0px)' : 'none', ResizeInner, props => props.$isResizing ? 1 : 0);
249
-
250
- const TableCell = styled__default.default(edsCoreReact.Table.Cell).withConfig({
251
- displayName: "TableCell",
252
- componentId: "sc-1g0k23m-0"
253
- })(["font-weight:bold;position:", ";top:0;", " ", ";&:hover ", "{background:", ";opacity:1;}"], p => p.$sticky || p.$pinned ? 'sticky' : 'relative', p => {
254
- if (p.$pinned) {
255
- return `${p.$pinned}: ${p.$offset}px;`;
256
- }
257
- return '';
258
- }, p => {
259
- if (p.$sticky && p.$pinned) return 'z-index: 13';
260
- if (p.$sticky || p.$pinned) return 'z-index: 12';
261
- }, ResizeInner, edsTokens.tokens.colors.interactive.primary__hover.rgba);
262
-
263
269
  const SortButton = styled__default.default.button.withConfig({
264
270
  displayName: "TableHeaderCell__SortButton",
265
271
  componentId: "sc-1n0j3v0-0"
@@ -281,6 +287,15 @@ function TableHeaderCell({
281
287
  const ctx = useTableContext();
282
288
  const table = ctx.table;
283
289
  const pinned = header.column.getIsPinned();
290
+ const isFiltered = header.column.getIsFiltered();
291
+ const filterValue = header.column.getFilterValue();
292
+ const hasActiveFilters = react.useMemo(() => {
293
+ if (!isFiltered) return false;
294
+ if (Array.isArray(filterValue)) {
295
+ return filterValue.length > 0 && filterValue.some(v => !!v || v === 0); // avoid empty strings counting
296
+ }
297
+ return !!filterValue;
298
+ }, [isFiltered, filterValue]);
284
299
  const canSort = header.column.getCanSort();
285
300
  const canFilter = header.column.getCanFilter();
286
301
  const offset = react.useMemo(() => {
@@ -311,6 +326,7 @@ function TableHeaderCell({
311
326
  $sticky: ctx.stickyHeader,
312
327
  $offset: offset,
313
328
  $pinned: pinned,
329
+ $activeFilter: hasActiveFilters,
314
330
  className: ctx.headerClass ? ctx.headerClass(header.column) : '',
315
331
  "aria-sort": getSortLabel(header.column.getIsSorted()),
316
332
  colSpan: header.colSpan,
@@ -333,8 +349,15 @@ function TableHeaderCell({
333
349
  }) : /*#__PURE__*/jsxRuntime.jsx(TableHeaderCellLabel, {
334
350
  className: "table-header-cell-label",
335
351
  children: reactTable.flexRender(header.column.columnDef.header, header.getContext())
336
- }), canFilter && !header.column.columnDef.meta?.customFilterInput ? /*#__PURE__*/jsxRuntime.jsx(FilterWrapper, {
337
- column: header.column
352
+ }), canFilter && !header.column.columnDef.meta?.customFilterInput ?
353
+ /*#__PURE__*/
354
+ // Supressing this warning - div is not interactive, but prevents propagation of events to avoid unintended sorting
355
+ // eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions
356
+ jsxRuntime.jsx(FilterVisibility, {
357
+ onClick: e => e.stopPropagation(),
358
+ children: /*#__PURE__*/jsxRuntime.jsx(FilterWrapper, {
359
+ column: header.column
360
+ })
338
361
  }) : null, columnResizeMode && /*#__PURE__*/jsxRuntime.jsx(Resizer, {
339
362
  onMouseDown: header.getResizeHandler(),
340
363
  onTouchStart: header.getResizeHandler(),
@@ -561,6 +584,8 @@ function EdsDataGridInner({
561
584
  selectedRows,
562
585
  rowSelectionState,
563
586
  enableColumnFiltering,
587
+ columnFiltersState,
588
+ onColumnFiltersChange,
564
589
  debug,
565
590
  enablePagination,
566
591
  enableSorting,
@@ -647,6 +672,9 @@ function EdsDataGridInner({
647
672
  react.useEffect(() => {
648
673
  setVisible(columnVisibility ?? {});
649
674
  }, [columnVisibility, setVisible]);
675
+ react.useEffect(() => {
676
+ setColumnFilters(columnFiltersState);
677
+ }, [columnFiltersState]);
650
678
  react.useEffect(() => {
651
679
  setColumnPin(s => columnPinState ?? s);
652
680
  }, [columnPinState]);
@@ -727,6 +755,7 @@ function EdsDataGridInner({
727
755
  },
728
756
  state: {
729
757
  sorting,
758
+ columnFilters: columnFilters,
730
759
  columnPinning: columnPin,
731
760
  rowSelection: internalRowSelectionState,
732
761
  columnOrder: columnOrderState,
@@ -783,7 +812,7 @@ function EdsDataGridInner({
783
812
  if (enableColumnFiltering) {
784
813
  options.state.columnFilters = columnFilters;
785
814
  options.state.globalFilter = globalFilter;
786
- options.onColumnFiltersChange = setColumnFilters;
815
+ options.onColumnFiltersChange = onColumnFiltersChange ?? setColumnFilters;
787
816
  options.onGlobalFilterChange = setGlobalFilter;
788
817
  options.getFacetedRowModel = reactTable.getFacetedRowModel();
789
818
  options.getFacetedUniqueValues = reactTable.getFacetedUniqueValues();
@@ -24,6 +24,8 @@ function EdsDataGridInner({
24
24
  selectedRows,
25
25
  rowSelectionState,
26
26
  enableColumnFiltering,
27
+ columnFiltersState,
28
+ onColumnFiltersChange,
27
29
  debug,
28
30
  enablePagination,
29
31
  enableSorting,
@@ -110,6 +112,9 @@ function EdsDataGridInner({
110
112
  useEffect(() => {
111
113
  setVisible(columnVisibility ?? {});
112
114
  }, [columnVisibility, setVisible]);
115
+ useEffect(() => {
116
+ setColumnFilters(columnFiltersState);
117
+ }, [columnFiltersState]);
113
118
  useEffect(() => {
114
119
  setColumnPin(s => columnPinState ?? s);
115
120
  }, [columnPinState]);
@@ -190,6 +195,7 @@ function EdsDataGridInner({
190
195
  },
191
196
  state: {
192
197
  sorting,
198
+ columnFilters: columnFilters,
193
199
  columnPinning: columnPin,
194
200
  rowSelection: internalRowSelectionState,
195
201
  columnOrder: columnOrderState,
@@ -246,7 +252,7 @@ function EdsDataGridInner({
246
252
  if (enableColumnFiltering) {
247
253
  options.state.columnFilters = columnFilters;
248
254
  options.state.globalFilter = globalFilter;
249
- options.onColumnFiltersChange = setColumnFilters;
255
+ options.onColumnFiltersChange = onColumnFiltersChange ?? setColumnFilters;
250
256
  options.onGlobalFilterChange = setGlobalFilter;
251
257
  options.getFacetedRowModel = getFacetedRowModel();
252
258
  options.getFacetedUniqueValues = getFacetedUniqueValues();
@@ -28,7 +28,6 @@ function DebouncedInput({
28
28
  label: props.placeholder,
29
29
  children: /*#__PURE__*/jsx(Input, {
30
30
  type: 'number',
31
- placeholder: '0',
32
31
  value: value,
33
32
  onChange: e => setValue(e.target.valueAsNumber)
34
33
  })
@@ -4,6 +4,7 @@ import { filter_alt_active, filter_alt } from '@equinor/eds-icons';
4
4
  import { tokens } from '@equinor/eds-tokens';
5
5
  import { Filter } from './Filter.js';
6
6
  import { useTableContext } from '../EdsDataGridContext.js';
7
+ import { FilterVisibility } from './TableCell.js';
7
8
  import { jsxs, jsx } from 'react/jsx-runtime';
8
9
 
9
10
  /* istanbul ignore file */
@@ -34,7 +35,8 @@ function FilterWrapper({
34
35
  return value;
35
36
  };
36
37
  const onChange = useCallback(value => column.setFilterValue(value), [column]);
37
- return /*#__PURE__*/jsxs("span", {
38
+ return /*#__PURE__*/jsxs(FilterVisibility, {
39
+ onClick: e => e.stopPropagation(),
38
40
  children: [/*#__PURE__*/jsx(Button, {
39
41
  "aria-haspopup": true,
40
42
  "aria-expanded": open,
@@ -3,10 +3,14 @@ import { tokens } from '@equinor/eds-tokens';
3
3
  import styled from 'styled-components';
4
4
  import { ResizeInner } from './Resizer.js';
5
5
 
6
+ const FilterVisibility = styled.div.withConfig({
7
+ displayName: "TableCell__FilterVisibility",
8
+ componentId: "sc-1g0k23m-0"
9
+ })([""]);
6
10
  const TableCell = styled(Table.Cell).withConfig({
7
11
  displayName: "TableCell",
8
- componentId: "sc-1g0k23m-0"
9
- })(["font-weight:bold;position:", ";top:0;", " ", ";&:hover ", "{background:", ";opacity:1;}"], p => p.$sticky || p.$pinned ? 'sticky' : 'relative', p => {
12
+ componentId: "sc-1g0k23m-1"
13
+ })(["font-weight:bold;position:", ";top:0;", " ", ";&:hover ", "{background:", ";opacity:1;}", ":not(:focus-within){opacity:", ";}&:hover ", "{opacity:1;}", ":focus-within{opacity:1;}"], p => p.$sticky || p.$pinned ? 'sticky' : 'relative', p => {
10
14
  if (p.$pinned) {
11
15
  return `${p.$pinned}: ${p.$offset}px;`;
12
16
  }
@@ -14,6 +18,8 @@ const TableCell = styled(Table.Cell).withConfig({
14
18
  }, p => {
15
19
  if (p.$sticky && p.$pinned) return 'z-index: 13';
16
20
  if (p.$sticky || p.$pinned) return 'z-index: 12';
17
- }, ResizeInner, tokens.colors.interactive.primary__hover.rgba);
21
+ }, ResizeInner, tokens.colors.interactive.primary__hover.rgba, FilterVisibility, ({
22
+ $activeFilter
23
+ }) => $activeFilter ? 1 : 0, FilterVisibility, FilterVisibility);
18
24
 
19
- export { TableCell };
25
+ export { FilterVisibility, TableCell };
@@ -4,7 +4,7 @@ import { useMemo } from 'react';
4
4
  import { FilterWrapper } from './FilterWrapper.js';
5
5
  import { SortIndicator } from './SortIndicator.js';
6
6
  import { Resizer, ResizeInner } from './Resizer.js';
7
- import { TableCell } from './TableCell.js';
7
+ import { TableCell, FilterVisibility } from './TableCell.js';
8
8
  import styled from 'styled-components';
9
9
  import { jsx, jsxs } from 'react/jsx-runtime';
10
10
 
@@ -29,6 +29,15 @@ function TableHeaderCell({
29
29
  const ctx = useTableContext();
30
30
  const table = ctx.table;
31
31
  const pinned = header.column.getIsPinned();
32
+ const isFiltered = header.column.getIsFiltered();
33
+ const filterValue = header.column.getFilterValue();
34
+ const hasActiveFilters = useMemo(() => {
35
+ if (!isFiltered) return false;
36
+ if (Array.isArray(filterValue)) {
37
+ return filterValue.length > 0 && filterValue.some(v => !!v || v === 0); // avoid empty strings counting
38
+ }
39
+ return !!filterValue;
40
+ }, [isFiltered, filterValue]);
32
41
  const canSort = header.column.getCanSort();
33
42
  const canFilter = header.column.getCanFilter();
34
43
  const offset = useMemo(() => {
@@ -59,6 +68,7 @@ function TableHeaderCell({
59
68
  $sticky: ctx.stickyHeader,
60
69
  $offset: offset,
61
70
  $pinned: pinned,
71
+ $activeFilter: hasActiveFilters,
62
72
  className: ctx.headerClass ? ctx.headerClass(header.column) : '',
63
73
  "aria-sort": getSortLabel(header.column.getIsSorted()),
64
74
  colSpan: header.colSpan,
@@ -81,8 +91,15 @@ function TableHeaderCell({
81
91
  }) : /*#__PURE__*/jsx(TableHeaderCellLabel, {
82
92
  className: "table-header-cell-label",
83
93
  children: flexRender(header.column.columnDef.header, header.getContext())
84
- }), canFilter && !header.column.columnDef.meta?.customFilterInput ? /*#__PURE__*/jsx(FilterWrapper, {
85
- column: header.column
94
+ }), canFilter && !header.column.columnDef.meta?.customFilterInput ?
95
+ /*#__PURE__*/
96
+ // Supressing this warning - div is not interactive, but prevents propagation of events to avoid unintended sorting
97
+ // eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions
98
+ jsx(FilterVisibility, {
99
+ onClick: e => e.stopPropagation(),
100
+ children: /*#__PURE__*/jsx(FilterWrapper, {
101
+ column: header.column
102
+ })
86
103
  }) : null, columnResizeMode && /*#__PURE__*/jsx(Resizer, {
87
104
  onMouseDown: header.getResizeHandler(),
88
105
  onTouchStart: header.getResizeHandler(),
@@ -1,6 +1,6 @@
1
1
  import { HTMLAttributes, ForwardedRef } from 'react';
2
2
  import { EdsDataGridProps } from './EdsDataGridProps';
3
- declare function EdsDataGridInner<T>({ rows, columns, columnResizeMode, pageSize, rowSelection, enableRowSelection, enableMultiRowSelection, enableSubRowSelection, selectedRows, rowSelectionState, enableColumnFiltering, debug, enablePagination, enableSorting, stickyHeader, stickyFooter, onSelectRow, onRowSelectionChange, caption, enableVirtual, virtualHeight, columnVisibility, columnVisibilityChange, emptyMessage, columnOrder, cellClass, cellStyle, rowClass, rowStyle, headerClass, headerStyle, footerClass, footerStyle, externalPaginator, onSortingChange, manualSorting, sortingState, columnPinState, scrollbarHorizontal, width, minWidth, height, getRowId, rowVirtualizerInstanceRef, tableInstanceRef, columnSizing, onColumnResize, expansionState, setExpansionState, getSubRows, defaultColumn, onRowContextMenu, onRowClick, onRowDoubleClick, onCellClick, enableFooter, enableSortingRemoval, ...rest }: EdsDataGridProps<T> & HTMLAttributes<HTMLDivElement>, ref: ForwardedRef<HTMLDivElement>): import("react/jsx-runtime").JSX.Element;
3
+ declare function EdsDataGridInner<T>({ rows, columns, columnResizeMode, pageSize, rowSelection, enableRowSelection, enableMultiRowSelection, enableSubRowSelection, selectedRows, rowSelectionState, enableColumnFiltering, columnFiltersState, onColumnFiltersChange, debug, enablePagination, enableSorting, stickyHeader, stickyFooter, onSelectRow, onRowSelectionChange, caption, enableVirtual, virtualHeight, columnVisibility, columnVisibilityChange, emptyMessage, columnOrder, cellClass, cellStyle, rowClass, rowStyle, headerClass, headerStyle, footerClass, footerStyle, externalPaginator, onSortingChange, manualSorting, sortingState, columnPinState, scrollbarHorizontal, width, minWidth, height, getRowId, rowVirtualizerInstanceRef, tableInstanceRef, columnSizing, onColumnResize, expansionState, setExpansionState, getSubRows, defaultColumn, onRowContextMenu, onRowClick, onRowDoubleClick, onCellClick, enableFooter, enableSortingRemoval, ...rest }: EdsDataGridProps<T> & HTMLAttributes<HTMLDivElement>, ref: ForwardedRef<HTMLDivElement>): import("react/jsx-runtime").JSX.Element;
4
4
  export declare const EdsDataGrid: <T>(props: EdsDataGridProps<T> & HTMLAttributes<HTMLDivElement> & {
5
5
  ref?: ForwardedRef<HTMLDivElement>;
6
6
  }) => ReturnType<typeof EdsDataGridInner>;
@@ -1,4 +1,4 @@
1
- import { Cell, Column, ColumnDef, ColumnPinningState, ColumnResizeMode, ColumnSizingState, ExpandedState, OnChangeFn, Row, RowSelectionState, SortingState, Table, TableOptions } from '@tanstack/react-table';
1
+ import { Cell, Column, ColumnDef, ColumnFilter, ColumnPinningState, ColumnResizeMode, ColumnSizingState, ExpandedState, OnChangeFn, Row, RowSelectionState, SortingState, Table, TableOptions } from '@tanstack/react-table';
2
2
  import { Virtualizer } from '@tanstack/react-virtual';
3
3
  import { CSSProperties, MouseEvent, MutableRefObject, ReactElement } from 'react';
4
4
  type BaseProps<T> = {
@@ -184,6 +184,16 @@ type FilterProps = {
184
184
  * @default false
185
185
  */
186
186
  enableColumnFiltering?: boolean;
187
+ /**
188
+ * Initial filter state
189
+ * @default undefined
190
+ */
191
+ columnFiltersState?: Array<ColumnFilter>;
192
+ /**
193
+ * Callback for when the filter state changes
194
+ * @default undefined
195
+ */
196
+ onColumnFiltersChange?: OnChangeFn<Array<ColumnFilter>>;
187
197
  };
188
198
  type HandlersProps<T> = {
189
199
  /**
@@ -1,4 +1,5 @@
1
1
  import { ColumnPinningPosition } from '@tanstack/react-table';
2
+ export declare const FilterVisibility: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
2
3
  export declare const TableCell: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components/dist/types").Substitute<(Omit<{
3
4
  variant?: import("@equinor/eds-core-react/dist/types/components/Table/Table.types").Variants;
4
5
  color?: import("@equinor/eds-core-react/dist/types/components/Table/Table.types").Colors;
@@ -15,4 +16,5 @@ export declare const TableCell: import("styled-components/dist/types").IStyledCo
15
16
  $sticky: boolean;
16
17
  $pinned: ColumnPinningPosition;
17
18
  $offset: number;
19
+ $activeFilter?: boolean;
18
20
  }>> & string & Omit<import("react").ForwardRefExoticComponent<import("@equinor/eds-core-react").CellProps & import("react").RefAttributes<HTMLTableCellElement>>, keyof import("react").Component<any, {}, any>>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@equinor/eds-data-grid-react",
3
- "version": "0.7.7",
3
+ "version": "0.8.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",
@@ -63,7 +63,7 @@
63
63
  "rollup-plugin-postcss": "^4.0.2",
64
64
  "storybook": "^8.6.12",
65
65
  "styled-components": "6.1.17",
66
- "ts-jest": "29.3.1",
66
+ "ts-jest": "29.3.2",
67
67
  "ts-node": "10.9.2",
68
68
  "tsc-watch": "^6.2.1",
69
69
  "typescript": "~5.8.3"