@opencorestack/opengridx 1.2.1 → 1.2.2
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 +25 -0
- package/dist/index.d.ts +2 -0
- package/dist/opengridx.es.js +2175 -2164
- package/dist/opengridx.es.js.map +1 -1
- package/dist/opengridx.umd.js +10 -10
- package/dist/opengridx.umd.js.map +1 -1
- package/lib/components/Cell/Cell.tsx +1 -1
- package/lib/components/Cell/CellErrorBoundary.tsx +14 -1
- package/lib/components/DataGrid/DataGrid.tsx +1 -0
- package/lib/hooks/core/useDataGrid.ts +1 -0
- package/lib/types/index.ts +2 -0
- package/lib/utils/export/exportToPdf.test.ts +5 -6
- package/lib/utils/export/exportToPdf.ts +9 -7
- package/package.json +1 -1
|
@@ -241,7 +241,7 @@ function CellImpl<R extends GridRowModel = GridRowModel>(props: CellProps<R>) {
|
|
|
241
241
|
) : colDef.renderCell ? (
|
|
242
242
|
<CellErrorBoundary
|
|
243
243
|
field={colDef.field}
|
|
244
|
-
resetKey={
|
|
244
|
+
resetKey={row}
|
|
245
245
|
renderFn={() => colDef.renderCell!({
|
|
246
246
|
value,
|
|
247
247
|
row,
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
|
|
3
|
+
// Must live at module scope (not inline) so React sees a stable component type.
|
|
4
|
+
// Calling renderFn() here puts the throw one level BELOW CellErrorBoundary,
|
|
5
|
+
// which is required — a component cannot catch errors thrown in its own render().
|
|
6
|
+
function CellRenderTarget({ renderFn }: { renderFn: () => React.ReactNode }) {
|
|
7
|
+
return <>{renderFn()}</>;
|
|
8
|
+
}
|
|
9
|
+
|
|
3
10
|
interface CellErrorBoundaryProps {
|
|
4
11
|
renderFn: () => React.ReactNode;
|
|
5
12
|
field: string;
|
|
@@ -35,6 +42,12 @@ export class CellErrorBoundary extends React.Component<CellErrorBoundaryProps, C
|
|
|
35
42
|
return { hasError: true, error };
|
|
36
43
|
}
|
|
37
44
|
|
|
45
|
+
override componentDidCatch(error: Error, info: React.ErrorInfo) {
|
|
46
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
47
|
+
console.warn(`[CellErrorBoundary] field="${this.props.field}"`, error, info.componentStack);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
38
51
|
override render() {
|
|
39
52
|
if (this.state.hasError) {
|
|
40
53
|
return (
|
|
@@ -48,6 +61,6 @@ export class CellErrorBoundary extends React.Component<CellErrorBoundaryProps, C
|
|
|
48
61
|
</div>
|
|
49
62
|
);
|
|
50
63
|
}
|
|
51
|
-
return this.props.renderFn
|
|
64
|
+
return <CellRenderTarget renderFn={this.props.renderFn} />;
|
|
52
65
|
}
|
|
53
66
|
}
|
|
@@ -467,6 +467,7 @@ export function DataGrid<R extends GridRowModel = GridRowModel>(props: DataGridP
|
|
|
467
467
|
|
|
468
468
|
useEffect(() => {
|
|
469
469
|
gridData.apiRef.current.getVisibleRows = () => pagination ? paginatedUnpinnedRows : sortedUnpinnedRows;
|
|
470
|
+
gridData.apiRef.current.getAllFilteredRows = () => sortedUnpinnedRows;
|
|
470
471
|
gridData.apiRef.current.getVisibleColumns = () => effectiveColumns as unknown as GridColDef[];
|
|
471
472
|
}, [sortedUnpinnedRows, paginatedUnpinnedRows, pagination, effectiveColumns, gridData.apiRef]);
|
|
472
473
|
|
|
@@ -299,6 +299,7 @@ export function useDataGrid<R extends GridRowModel = GridRowModel>(params: UseDa
|
|
|
299
299
|
getRow: (id: GridRowId) => stateRef.current.rows.idRowsLookup.get(id) || null,
|
|
300
300
|
getAllRows: () => Array.from(stateRef.current.rows.idRowsLookup.values()),
|
|
301
301
|
getVisibleRows: () => Array.from(stateRef.current.rows.idRowsLookup.values()),
|
|
302
|
+
getAllFilteredRows: () => Array.from(stateRef.current.rows.idRowsLookup.values()),
|
|
302
303
|
getAggregationResult: () => null,
|
|
303
304
|
getAggregationModel: () => null,
|
|
304
305
|
getColumn: (field: string) => stateRef.current.columns.lookup.get(field) || null,
|
package/lib/types/index.ts
CHANGED
|
@@ -817,6 +817,8 @@ export interface GridApi {
|
|
|
817
817
|
getAllRows: () => GridRowModel[];
|
|
818
818
|
/** Returns all rows currently visible after filtering and sorting. */
|
|
819
819
|
getVisibleRows: () => GridRowModel[];
|
|
820
|
+
/** Returns all filtered and sorted rows, ignoring pagination. Use this for full-dataset exports. */
|
|
821
|
+
getAllFilteredRows: () => GridRowModel[];
|
|
820
822
|
/** Returns the current aggregation results. */
|
|
821
823
|
getAggregationResult: () => Record<string, unknown> | null;
|
|
822
824
|
/** Returns the active aggregation configuration. */
|
|
@@ -15,7 +15,6 @@ const mockAddImage = vi.fn();
|
|
|
15
15
|
const mockSetPage = vi.fn();
|
|
16
16
|
|
|
17
17
|
class MockJsPDF {
|
|
18
|
-
autoTable = mockAutoTable;
|
|
19
18
|
save = mockSave;
|
|
20
19
|
setFontSize = mockSetFontSize;
|
|
21
20
|
setFont = mockSetFont;
|
|
@@ -35,7 +34,7 @@ class MockJsPDF {
|
|
|
35
34
|
}
|
|
36
35
|
|
|
37
36
|
vi.mock('jspdf', () => ({ default: MockJsPDF }));
|
|
38
|
-
vi.mock('jspdf-autotable', () => ({}));
|
|
37
|
+
vi.mock('jspdf-autotable', () => ({ default: mockAutoTable }));
|
|
39
38
|
|
|
40
39
|
const sampleColumns: GridColDef[] = [
|
|
41
40
|
{ field: 'name', headerName: 'Name', width: 150 },
|
|
@@ -72,7 +71,7 @@ describe('exportToPdf', () => {
|
|
|
72
71
|
it('excludes columns with exportable: false and system columns', async () => {
|
|
73
72
|
const { exportToPdf } = await import('./exportToPdf');
|
|
74
73
|
await exportToPdf(sampleRows, sampleColumns);
|
|
75
|
-
const call = mockAutoTable.mock.calls[0][
|
|
74
|
+
const call = mockAutoTable.mock.calls[0][1];
|
|
76
75
|
// head should only have Name and Salary — not Hidden or __check__
|
|
77
76
|
expect(call.head[0]).toEqual(['Name', 'Salary']);
|
|
78
77
|
});
|
|
@@ -80,7 +79,7 @@ describe('exportToPdf', () => {
|
|
|
80
79
|
it('applies valueFormatter to cell values', async () => {
|
|
81
80
|
const { exportToPdf } = await import('./exportToPdf');
|
|
82
81
|
await exportToPdf(sampleRows, sampleColumns);
|
|
83
|
-
const call = mockAutoTable.mock.calls[0][
|
|
82
|
+
const call = mockAutoTable.mock.calls[0][1];
|
|
84
83
|
// salary column should be formatted
|
|
85
84
|
expect(call.body[0][1]).toBe('$90000');
|
|
86
85
|
expect(call.body[1][1]).toBe('$80000');
|
|
@@ -89,7 +88,7 @@ describe('exportToPdf', () => {
|
|
|
89
88
|
it('filters to selectedRows when provided', async () => {
|
|
90
89
|
const { exportToPdf } = await import('./exportToPdf');
|
|
91
90
|
await exportToPdf(sampleRows, sampleColumns, { selectedRows: [1] });
|
|
92
|
-
const call = mockAutoTable.mock.calls[0][
|
|
91
|
+
const call = mockAutoTable.mock.calls[0][1];
|
|
93
92
|
expect(call.body).toHaveLength(1);
|
|
94
93
|
expect(call.body[0][0]).toBe('Alice');
|
|
95
94
|
});
|
|
@@ -100,7 +99,7 @@ describe('exportToPdf', () => {
|
|
|
100
99
|
aggregationResult: { salary: 170000 },
|
|
101
100
|
aggregationModel: { salary: 'sum' },
|
|
102
101
|
});
|
|
103
|
-
const call = mockAutoTable.mock.calls[0][
|
|
102
|
+
const call = mockAutoTable.mock.calls[0][1];
|
|
104
103
|
expect(call.foot).toBeDefined();
|
|
105
104
|
expect(call.foot[0][1]).toBe('$170000');
|
|
106
105
|
});
|
|
@@ -6,9 +6,7 @@ import type {
|
|
|
6
6
|
} from '../../types';
|
|
7
7
|
import { formatAggregationValue } from '../../hooks/features/useAggregation';
|
|
8
8
|
|
|
9
|
-
// jsPDF plugin augments the jsPDF prototype — we type only what we use
|
|
10
9
|
interface JsPDFDoc {
|
|
11
|
-
autoTable: (opts: AutoTableOptions) => void;
|
|
12
10
|
save: (filename: string) => void;
|
|
13
11
|
setFontSize: (size: number) => void;
|
|
14
12
|
setFont: (name: string, style: string) => void;
|
|
@@ -112,10 +110,14 @@ export async function exportToPdf<R extends GridRowModel>(
|
|
|
112
110
|
): Promise<void> {
|
|
113
111
|
// Lazy-load peer deps — provides a clear error if not installed
|
|
114
112
|
let JsPDF: new (opts: Record<string, unknown>) => JsPDFDoc;
|
|
113
|
+
let autoTable: (doc: JsPDFDoc, opts: AutoTableOptions) => void;
|
|
115
114
|
try {
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
115
|
+
const [jspdfMod, autoTableMod] = await Promise.all([
|
|
116
|
+
import('jspdf'),
|
|
117
|
+
import('jspdf-autotable'),
|
|
118
|
+
]);
|
|
119
|
+
JsPDF = jspdfMod.default as unknown as new (opts: Record<string, unknown>) => JsPDFDoc;
|
|
120
|
+
autoTable = autoTableMod.default as unknown as (doc: JsPDFDoc, opts: AutoTableOptions) => void;
|
|
119
121
|
} catch {
|
|
120
122
|
throw new Error(
|
|
121
123
|
"exportToPdf requires 'jspdf' and 'jspdf-autotable'. Run: npm install jspdf jspdf-autotable"
|
|
@@ -235,7 +237,7 @@ export async function exportToPdf<R extends GridRowModel>(
|
|
|
235
237
|
const [hr, hg, hb] = hexToRgb(headerBackgroundColor);
|
|
236
238
|
const [tr, tg, tb] = hexToRgb(headerTextColor);
|
|
237
239
|
|
|
238
|
-
|
|
240
|
+
autoTable(doc, {
|
|
239
241
|
head,
|
|
240
242
|
body,
|
|
241
243
|
...(foot ? { foot } : {}),
|
|
@@ -250,7 +252,7 @@ export async function exportToPdf<R extends GridRowModel>(
|
|
|
250
252
|
? { alternateRowStyles: { fillColor: [248, 250, 252] } }
|
|
251
253
|
: {}),
|
|
252
254
|
...(foot
|
|
253
|
-
? { footStyles: { fillColor: [241, 245, 249], fontStyle: 'bold' } }
|
|
255
|
+
? { footStyles: { fillColor: [241, 245, 249], fontStyle: 'bold', textColor: [0, 0, 0] } }
|
|
254
256
|
: {}),
|
|
255
257
|
columnStyles,
|
|
256
258
|
showFoot: foot ? 'lastPage' : 'never',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opencorestack/opengridx",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.2",
|
|
4
4
|
"description": "OpenGridX: High-performance React data infrastructure. Unlock advanced Row Grouping, Excel Export, and Column Pinning without the usual \"Pro\" gatekeeping. Built for speed, scale, and complete architectural freedom. Fully open, virtualization-ready, and feature-complete.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/opengridx.umd.js",
|