@opencorestack/opengridx 0.1.8 → 0.1.9
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.
- package/CHANGELOG.md +16 -0
- package/dist/opengridx.es.js +3485 -3370
- package/dist/opengridx.es.js.map +1 -1
- package/dist/opengridx.umd.js +11 -11
- package/dist/opengridx.umd.js.map +1 -1
- package/lib/components/Cell/Cell.tsx +53 -55
- package/lib/components/DataGrid/DataGrid.smoke.test.tsx +58 -0
- package/lib/components/DataGrid/DataGrid.tsx +65 -641
- package/lib/components/Row/Row.tsx +39 -38
- package/lib/components/Toolbar/GlobalSearch.tsx +1 -1
- package/lib/components/Toolbar/GridToolbar.tsx +20 -5
- package/lib/components/Toolbar/PivotPanel.tsx +7 -2
- package/lib/components/ui/DragHandleIcon.tsx +1 -1
- package/lib/hooks/core/useDataGrid.ts +2 -2
- package/lib/hooks/core/useGridKeyboardNavigation.ts +371 -0
- package/lib/hooks/core/useLayout.ts +354 -0
- package/lib/hooks/features/useAggregation.test.ts +135 -0
- package/lib/hooks/features/useGridClipboard.ts +1 -1
- package/lib/hooks/features/useGridDataSource.ts +1 -1
- package/lib/state/useGridStateStorage.ts +1 -1
- package/lib/utils/export/index.ts +1 -1
- package/lib/utils/filtering/filtering.test.ts +178 -0
- package/lib/utils/sorting/sorting.test.ts +88 -0
- package/package.json +12 -5
- package/lib/utils/virtualization/index.ts +0 -106
|
@@ -62,39 +62,7 @@ function CellImpl<R extends GridRowModel = GridRowModel>(props: CellProps<R>) {
|
|
|
62
62
|
isHiddenByRowSpan
|
|
63
63
|
} = props;
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
return null;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (isHiddenByRowSpan) {
|
|
71
|
-
|
|
72
|
-
return (
|
|
73
|
-
<div
|
|
74
|
-
className="ogx__cell ogx__cell--hidden"
|
|
75
|
-
style={{ width: width ?? colDef.width ?? 100 }}
|
|
76
|
-
role="presentation"
|
|
77
|
-
data-field={colDef.field}
|
|
78
|
-
data-colindex={colIndex}
|
|
79
|
-
/>
|
|
80
|
-
);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const handleClick = (e: React.MouseEvent) => {
|
|
84
|
-
if (onCellClick) {
|
|
85
|
-
e.stopPropagation();
|
|
86
|
-
onCellClick({
|
|
87
|
-
row,
|
|
88
|
-
field: colDef.field,
|
|
89
|
-
value,
|
|
90
|
-
colDef,
|
|
91
|
-
rowIndex,
|
|
92
|
-
colIndex
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
onClick?.(e);
|
|
96
|
-
};
|
|
97
|
-
|
|
65
|
+
// All hooks must come before any early returns (Rules of Hooks)
|
|
98
66
|
const handleValueChange = React.useCallback((newValue: any) => {
|
|
99
67
|
if (onCellValueChange) {
|
|
100
68
|
onCellValueChange(colDef.field, newValue);
|
|
@@ -103,17 +71,6 @@ function CellImpl<R extends GridRowModel = GridRowModel>(props: CellProps<R>) {
|
|
|
103
71
|
}
|
|
104
72
|
}, [onCellValueChange, onValueChange, colDef.field]);
|
|
105
73
|
|
|
106
|
-
const handleDoubleClick = (e: React.MouseEvent) => {
|
|
107
|
-
if (isEditable && (onEditStart || onCellEditStart)) {
|
|
108
|
-
e.stopPropagation();
|
|
109
|
-
if (onCellEditStart) {
|
|
110
|
-
onCellEditStart(colDef.field, value);
|
|
111
|
-
} else {
|
|
112
|
-
onEditStart?.();
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
};
|
|
116
|
-
|
|
117
74
|
const formattedValue = React.useMemo(() => {
|
|
118
75
|
if (colDef.valueFormatter) {
|
|
119
76
|
return colDef.valueFormatter({
|
|
@@ -130,7 +87,6 @@ function CellImpl<R extends GridRowModel = GridRowModel>(props: CellProps<R>) {
|
|
|
130
87
|
return String(value);
|
|
131
88
|
}, [value, row, colDef]);
|
|
132
89
|
|
|
133
|
-
// Render custom cell content OR Edit Content
|
|
134
90
|
const cellContent = React.useMemo(() => {
|
|
135
91
|
if (isEditing && (onCellValueChange || onValueChange) && onEditStop) {
|
|
136
92
|
if (colDef.renderEditCell) {
|
|
@@ -172,8 +128,6 @@ function CellImpl<R extends GridRowModel = GridRowModel>(props: CellProps<R>) {
|
|
|
172
128
|
return formattedValue;
|
|
173
129
|
}, [value, row, colDef, rowIndex, colIndex, formattedValue, isEditing, handleValueChange, onEditStop, onCellValueChange, onValueChange]);
|
|
174
130
|
|
|
175
|
-
const isPinned = pinnedPosition !== null && pinnedPosition !== undefined;
|
|
176
|
-
|
|
177
131
|
const resolvedCellClassName = React.useMemo(() => {
|
|
178
132
|
if (!colDef.cellClassName) return '';
|
|
179
133
|
if (typeof colDef.cellClassName === 'function') {
|
|
@@ -182,6 +136,58 @@ function CellImpl<R extends GridRowModel = GridRowModel>(props: CellProps<R>) {
|
|
|
182
136
|
return colDef.cellClassName;
|
|
183
137
|
}, [colDef, value, row, rowIndex, colIndex]);
|
|
184
138
|
|
|
139
|
+
const cellRef = React.useRef<HTMLDivElement>(null);
|
|
140
|
+
|
|
141
|
+
React.useLayoutEffect(() => {
|
|
142
|
+
if (isFocused && cellRef.current) {
|
|
143
|
+
cellRef.current.focus({ preventScroll: true });
|
|
144
|
+
}
|
|
145
|
+
}, [isFocused]);
|
|
146
|
+
|
|
147
|
+
if (colSpanInfo?.spannedByColSpan) {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (isHiddenByRowSpan) {
|
|
152
|
+
return (
|
|
153
|
+
<div
|
|
154
|
+
className="ogx__cell ogx__cell--hidden"
|
|
155
|
+
style={{ width: width ?? colDef.width ?? 100 }}
|
|
156
|
+
role="presentation"
|
|
157
|
+
data-field={colDef.field}
|
|
158
|
+
data-colindex={colIndex}
|
|
159
|
+
/>
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const handleClick = (e: React.MouseEvent) => {
|
|
164
|
+
if (onCellClick) {
|
|
165
|
+
e.stopPropagation();
|
|
166
|
+
onCellClick({
|
|
167
|
+
row,
|
|
168
|
+
field: colDef.field,
|
|
169
|
+
value,
|
|
170
|
+
colDef,
|
|
171
|
+
rowIndex,
|
|
172
|
+
colIndex
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
onClick?.(e);
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const handleDoubleClick = (e: React.MouseEvent) => {
|
|
179
|
+
if (isEditable && (onEditStart || onCellEditStart)) {
|
|
180
|
+
e.stopPropagation();
|
|
181
|
+
if (onCellEditStart) {
|
|
182
|
+
onCellEditStart(colDef.field, value);
|
|
183
|
+
} else {
|
|
184
|
+
onEditStart?.();
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const isPinned = pinnedPosition !== null && pinnedPosition !== undefined;
|
|
190
|
+
|
|
185
191
|
const classNames = [
|
|
186
192
|
'ogx__cell',
|
|
187
193
|
`ogx__cell--align-${colDef.align || 'left'}`,
|
|
@@ -230,14 +236,6 @@ function CellImpl<R extends GridRowModel = GridRowModel>(props: CellProps<R>) {
|
|
|
230
236
|
}
|
|
231
237
|
}
|
|
232
238
|
|
|
233
|
-
const cellRef = React.useRef<HTMLDivElement>(null);
|
|
234
|
-
|
|
235
|
-
React.useLayoutEffect(() => {
|
|
236
|
-
if (isFocused && cellRef.current) {
|
|
237
|
-
cellRef.current.focus({ preventScroll: true });
|
|
238
|
-
}
|
|
239
|
-
}, [isFocused]);
|
|
240
|
-
|
|
241
239
|
return (
|
|
242
240
|
<div
|
|
243
241
|
ref={cellRef}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { describe, it, expect } from 'vitest';
|
|
3
|
+
import { render, screen } from '@testing-library/react';
|
|
4
|
+
import { DataGrid } from './DataGrid';
|
|
5
|
+
import type { GridColDef } from '../../types';
|
|
6
|
+
|
|
7
|
+
const COLUMNS: GridColDef[] = [
|
|
8
|
+
{ field: 'id', headerName: 'ID', width: 80 },
|
|
9
|
+
{ field: 'name', headerName: 'Name', width: 150 },
|
|
10
|
+
{ field: 'age', headerName: 'Age', width: 100, type: 'number' },
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
const ROWS = [
|
|
14
|
+
{ id: 1, name: 'Alice', age: 30 },
|
|
15
|
+
{ id: 2, name: 'Bob', age: 25 },
|
|
16
|
+
{ id: 3, name: 'Charlie', age: 35 },
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
describe('DataGrid — smoke tests', () => {
|
|
20
|
+
it('renders without throwing', () => {
|
|
21
|
+
expect(() =>
|
|
22
|
+
render(<DataGrid rows={ROWS} columns={COLUMNS} />)
|
|
23
|
+
).not.toThrow();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('renders column headers', () => {
|
|
27
|
+
render(<DataGrid rows={ROWS} columns={COLUMNS} />);
|
|
28
|
+
expect(screen.getByText('Name')).toBeInTheDocument();
|
|
29
|
+
expect(screen.getByText('Age')).toBeInTheDocument();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('renders row data', () => {
|
|
33
|
+
render(<DataGrid rows={ROWS} columns={COLUMNS} />);
|
|
34
|
+
expect(screen.getByText('Alice')).toBeInTheDocument();
|
|
35
|
+
expect(screen.getByText('Bob')).toBeInTheDocument();
|
|
36
|
+
expect(screen.getByText('Charlie')).toBeInTheDocument();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('renders with empty rows without throwing', () => {
|
|
40
|
+
expect(() =>
|
|
41
|
+
render(<DataGrid rows={[]} columns={COLUMNS} />)
|
|
42
|
+
).not.toThrow();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('renders with empty columns without throwing', () => {
|
|
46
|
+
expect(() =>
|
|
47
|
+
render(<DataGrid rows={ROWS} columns={[]} />)
|
|
48
|
+
).not.toThrow();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('renders with checkboxSelection enabled', () => {
|
|
52
|
+
const { container } = render(
|
|
53
|
+
<DataGrid rows={ROWS} columns={COLUMNS} checkboxSelection />
|
|
54
|
+
);
|
|
55
|
+
const checkboxes = container.querySelectorAll('input[type="checkbox"]');
|
|
56
|
+
expect(checkboxes.length).toBeGreaterThan(0);
|
|
57
|
+
});
|
|
58
|
+
});
|