@jmruthers/pace-core 0.5.126 → 0.5.128
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/dist/{DataTable-6FN7XDXA.js → DataTable-3Z5HLOWF.js} +6 -6
- package/dist/{PublicLoadingSpinner-CaoRbHvJ.d.ts → PublicLoadingSpinner-CUAnTvcg.d.ts} +41 -21
- package/dist/{UnifiedAuthProvider-6C47WIML.js → UnifiedAuthProvider-CQDZRJIS.js} +3 -3
- package/dist/{chunk-QXGLU2O5.js → chunk-27MGXDD6.js} +282 -147
- package/dist/chunk-27MGXDD6.js.map +1 -0
- package/dist/{chunk-ZBLK676C.js → chunk-3CG5L6RN.js} +1 -19
- package/dist/chunk-3CG5L6RN.js.map +1 -0
- package/dist/{chunk-35ZDPMBM.js → chunk-BYXRHAIF.js} +3 -3
- package/dist/{chunk-IJOZZOGT.js → chunk-CQZU6TFE.js} +5 -5
- package/dist/{chunk-C43QIDN3.js → chunk-CTJRBUX2.js} +2 -2
- package/dist/{chunk-R4CRQUJJ.js → chunk-ENE3AB75.js} +463 -453
- package/dist/chunk-ENE3AB75.js.map +1 -0
- package/dist/{chunk-ESJTIADP.js → chunk-F64FFPOZ.js} +5 -15
- package/dist/{chunk-ESJTIADP.js.map → chunk-F64FFPOZ.js.map} +1 -1
- package/dist/{chunk-4MXVZVNS.js → chunk-TGIY2AR2.js} +2 -2
- package/dist/{chunk-XN6GWKMV.js → chunk-VZ5OR6HD.js} +161 -14
- package/dist/chunk-VZ5OR6HD.js.map +1 -0
- package/dist/{chunk-QWNJCQXZ.js → chunk-ZV77RZMU.js} +2 -2
- package/dist/{chunk-NZGLXZGP.js → chunk-ZYZCRSBD.js} +3 -54
- package/dist/chunk-ZYZCRSBD.js.map +1 -0
- package/dist/components.d.ts +1 -1
- package/dist/components.js +9 -9
- package/dist/hooks.js +7 -7
- package/dist/index.d.ts +1 -1
- package/dist/index.js +12 -12
- package/dist/providers.js +2 -2
- package/dist/rbac/index.js +7 -7
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +1 -1
- package/docs/api/classes/ColumnFactory.md +1 -1
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +1 -1
- package/docs/api/classes/MissingUserContextError.md +1 -1
- package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
- package/docs/api/classes/PermissionDeniedError.md +1 -1
- package/docs/api/classes/PublicErrorBoundary.md +1 -1
- package/docs/api/classes/RBACAuditManager.md +1 -1
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +1 -1
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +1 -1
- package/docs/api/classes/SecureSupabaseClient.md +1 -1
- package/docs/api/classes/StorageUtils.md +1 -1
- package/docs/api/enums/FileCategory.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/ButtonProps.md +1 -1
- package/docs/api/interfaces/CardProps.md +1 -1
- package/docs/api/interfaces/ColorPalette.md +1 -1
- package/docs/api/interfaces/ColorShade.md +1 -1
- package/docs/api/interfaces/DataAccessRecord.md +1 -1
- package/docs/api/interfaces/DataRecord.md +1 -1
- package/docs/api/interfaces/DataTableAction.md +1 -1
- package/docs/api/interfaces/DataTableColumn.md +1 -1
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
- package/docs/api/interfaces/EmptyStateConfig.md +1 -1
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventAppRoleData.md +1 -1
- package/docs/api/interfaces/FileDisplayProps.md +1 -1
- package/docs/api/interfaces/FileMetadata.md +1 -1
- package/docs/api/interfaces/FileReference.md +1 -1
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadOptions.md +1 -1
- package/docs/api/interfaces/FileUploadProps.md +1 -1
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
- package/docs/api/interfaces/InputProps.md +1 -1
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +1 -1
- package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
- package/docs/api/interfaces/NavigationContextType.md +1 -1
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +1 -1
- package/docs/api/interfaces/NavigationMenuProps.md +1 -1
- package/docs/api/interfaces/NavigationProviderProps.md +1 -1
- package/docs/api/interfaces/Organisation.md +1 -1
- package/docs/api/interfaces/OrganisationContextType.md +1 -1
- package/docs/api/interfaces/OrganisationMembership.md +1 -1
- package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
- package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
- package/docs/api/interfaces/PaceAppLayoutProps.md +27 -27
- package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
- package/docs/api/interfaces/PageAccessRecord.md +1 -1
- package/docs/api/interfaces/PagePermissionContextType.md +1 -1
- package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
- package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
- package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
- package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
- package/docs/api/interfaces/PublicPageHeaderProps.md +10 -62
- package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
- package/docs/api/interfaces/RBACConfig.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RoleManagementResult.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +1 -1
- package/docs/api/interfaces/RouteConfig.md +1 -1
- package/docs/api/interfaces/SecureDataContextType.md +1 -1
- package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
- package/docs/api/interfaces/StorageConfig.md +1 -1
- package/docs/api/interfaces/StorageFileInfo.md +1 -1
- package/docs/api/interfaces/StorageFileMetadata.md +1 -1
- package/docs/api/interfaces/StorageListOptions.md +1 -1
- package/docs/api/interfaces/StorageListResult.md +1 -1
- package/docs/api/interfaces/StorageUploadOptions.md +1 -1
- package/docs/api/interfaces/StorageUploadResult.md +1 -1
- package/docs/api/interfaces/StorageUrlOptions.md +1 -1
- package/docs/api/interfaces/StyleImport.md +1 -1
- package/docs/api/interfaces/SwitchProps.md +1 -1
- package/docs/api/interfaces/ToastActionElement.md +1 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
- package/docs/api/interfaces/UsePublicFileDisplayOptions.md +1 -1
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +1 -1
- package/docs/api/interfaces/UserMenuProps.md +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +53 -28
- package/docs/api-reference/components.md +24 -0
- package/docs/api-reference/types.md +28 -0
- package/docs/architecture/rpc-function-standards.md +39 -5
- package/docs/implementation-guides/data-tables.md +55 -10
- package/docs/implementation-guides/permission-enforcement.md +4 -0
- package/docs/rbac/super-admin-guide.md +43 -5
- package/package.json +1 -1
- package/src/components/Button/Button.tsx +1 -1
- package/src/components/DataTable/__tests__/DataTable.export.test.tsx +702 -0
- package/src/components/DataTable/components/DataTableCore.tsx +55 -36
- package/src/components/DataTable/components/ImportModal.tsx +134 -2
- package/src/components/DataTable/index.ts +3 -1
- package/src/components/DataTable/types.ts +68 -0
- package/src/components/Dialog/Dialog.tsx +0 -13
- package/src/components/FileDisplay/FileDisplay.tsx +76 -0
- package/src/components/Header/Header.tsx +5 -0
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +72 -50
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.security.test.tsx +81 -1
- package/src/components/PublicLayout/PublicPageFooter.tsx +1 -1
- package/src/components/PublicLayout/PublicPageHeader.tsx +69 -128
- package/src/components/PublicLayout/PublicPageLayout.tsx +4 -4
- package/src/components/PublicLayout/PublicPageProvider.tsx +12 -3
- package/src/components/PublicLayout/__tests__/PublicPageFooter.test.tsx +1 -1
- package/src/components/PublicLayout/__tests__/PublicPageHeader.test.tsx +3 -18
- package/src/hooks/__tests__/useAppConfig.unit.test.ts +3 -1
- package/src/hooks/__tests__/usePermissionCache.unit.test.ts +11 -5
- package/src/hooks/__tests__/usePublicRouteParams.unit.test.ts +8 -7
- package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +41 -46
- package/src/hooks/public/usePublicFileDisplay.ts +176 -7
- package/src/hooks/public/usePublicRouteParams.ts +0 -12
- package/src/hooks/useAppConfig.ts +15 -6
- package/src/hooks/usePermissionCache.test.ts +12 -4
- package/src/hooks/usePermissionCache.ts +3 -19
- package/src/hooks/useSecureDataAccess.ts +0 -63
- package/src/services/EventService.ts +0 -19
- package/dist/chunk-NZGLXZGP.js.map +0 -1
- package/dist/chunk-QXGLU2O5.js.map +0 -1
- package/dist/chunk-R4CRQUJJ.js.map +0 -1
- package/dist/chunk-XN6GWKMV.js.map +0 -1
- package/dist/chunk-ZBLK676C.js.map +0 -1
- /package/dist/{DataTable-6FN7XDXA.js.map → DataTable-3Z5HLOWF.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-6C47WIML.js.map → UnifiedAuthProvider-CQDZRJIS.js.map} +0 -0
- /package/dist/{chunk-35ZDPMBM.js.map → chunk-BYXRHAIF.js.map} +0 -0
- /package/dist/{chunk-IJOZZOGT.js.map → chunk-CQZU6TFE.js.map} +0 -0
- /package/dist/{chunk-C43QIDN3.js.map → chunk-CTJRBUX2.js.map} +0 -0
- /package/dist/{chunk-4MXVZVNS.js.map → chunk-TGIY2AR2.js.map} +0 -0
- /package/dist/{chunk-QWNJCQXZ.js.map → chunk-ZV77RZMU.js.map} +0 -0
|
@@ -0,0 +1,702 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file DataTable Export Functionality Tests
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module Components/DataTable/__tests__
|
|
5
|
+
* @since 0.5.76
|
|
6
|
+
*
|
|
7
|
+
* Comprehensive tests for DataTable export functionality, including:
|
|
8
|
+
* - Default export behavior (visible columns)
|
|
9
|
+
* - Custom onExport handler with ExportOptions
|
|
10
|
+
* - Custom column selection
|
|
11
|
+
* - Export with different columns than visible
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import React from 'react';
|
|
15
|
+
import { render, screen, waitFor } from '@testing-library/react';
|
|
16
|
+
import userEvent from '@testing-library/user-event';
|
|
17
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
18
|
+
import { DataTable } from '../DataTable';
|
|
19
|
+
import type { DataTableColumn, ExportOptions } from '../types';
|
|
20
|
+
import { exportToCSVWithTableRows } from '../utils/exportUtils';
|
|
21
|
+
import { createTestData, createTestColumns } from './test-utils/dataFactories';
|
|
22
|
+
import { createDefaultFeatures } from './test-utils';
|
|
23
|
+
|
|
24
|
+
// Mock the export utilities
|
|
25
|
+
vi.mock('../utils/exportUtils', () => ({
|
|
26
|
+
exportToCSVWithTableRows: vi.fn().mockResolvedValue(undefined),
|
|
27
|
+
exportToCSV: vi.fn().mockResolvedValue(undefined),
|
|
28
|
+
generateCSVContent: vi.fn(),
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
// Mock the toast hook
|
|
32
|
+
const mockToast = vi.fn();
|
|
33
|
+
vi.mock('../../../hooks/useToast', () => ({
|
|
34
|
+
toast: (...args: any[]) => mockToast(...args),
|
|
35
|
+
}));
|
|
36
|
+
|
|
37
|
+
// Mock RBAC hooks
|
|
38
|
+
vi.mock('../../../rbac/hooks', () => ({
|
|
39
|
+
useCan: vi.fn(() => ({
|
|
40
|
+
can: true,
|
|
41
|
+
isLoading: false,
|
|
42
|
+
error: null,
|
|
43
|
+
})),
|
|
44
|
+
useResolvedScope: vi.fn(() => ({
|
|
45
|
+
resolvedScope: {
|
|
46
|
+
organisationId: 'test-org-id',
|
|
47
|
+
eventId: 'test-event-id',
|
|
48
|
+
appId: 'test-app-id'
|
|
49
|
+
}
|
|
50
|
+
}))
|
|
51
|
+
}));
|
|
52
|
+
|
|
53
|
+
// Mock auth provider
|
|
54
|
+
vi.mock('../../../providers/UnifiedAuthProvider', () => ({
|
|
55
|
+
useUnifiedAuth: vi.fn(() => ({
|
|
56
|
+
user: { id: 'test-user', email: 'test@example.com' },
|
|
57
|
+
isAuthenticated: true,
|
|
58
|
+
isLoading: false,
|
|
59
|
+
error: null,
|
|
60
|
+
selectedOrganisationId: 'test-org-id',
|
|
61
|
+
selectedEventId: 'test-event-id',
|
|
62
|
+
supabase: null,
|
|
63
|
+
})),
|
|
64
|
+
}));
|
|
65
|
+
|
|
66
|
+
// Mock DataTableCore to test the wrapper logic
|
|
67
|
+
vi.mock('../components/DataTableCore', async () => {
|
|
68
|
+
const React = await import('react');
|
|
69
|
+
const { exportToCSVWithTableRows } = await import('../utils/exportUtils');
|
|
70
|
+
const { toast } = await import('../../../hooks/useToast');
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
DataTableCore: React.forwardRef(({ onExport, data, columns, title, ...props }: any, ref: any) => {
|
|
74
|
+
// Simulate export button click
|
|
75
|
+
const handleExport = async () => {
|
|
76
|
+
try {
|
|
77
|
+
// Create mock table rows with getValue method
|
|
78
|
+
const tableRows = (data || []).map((row: any, index: number) => ({
|
|
79
|
+
original: row,
|
|
80
|
+
getValue: (columnId: string) => {
|
|
81
|
+
const col = columns?.find((c: any) => (c.id || c.accessorKey) === columnId);
|
|
82
|
+
if (col && 'accessorFn' in col && col.accessorFn) {
|
|
83
|
+
return col.accessorFn(row);
|
|
84
|
+
}
|
|
85
|
+
return row[col?.accessorKey || columnId];
|
|
86
|
+
},
|
|
87
|
+
id: String(row.id || index),
|
|
88
|
+
}));
|
|
89
|
+
|
|
90
|
+
// Get visible columns (all columns for simplicity in tests)
|
|
91
|
+
const visibleColumns = columns || [];
|
|
92
|
+
|
|
93
|
+
// Create column mapping
|
|
94
|
+
const columnIdToTableColumn = new Map();
|
|
95
|
+
visibleColumns.forEach((col: any) => {
|
|
96
|
+
const colId = col.id || col.accessorKey;
|
|
97
|
+
if (colId) {
|
|
98
|
+
columnIdToTableColumn.set(String(colId), { id: String(colId) });
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// Generate filename
|
|
103
|
+
const timestamp = new Date().toISOString().split('T')[0];
|
|
104
|
+
const filename = title
|
|
105
|
+
? `${title.replace(/[^a-z0-9]/gi, '_').toLowerCase()}_${timestamp}.csv`
|
|
106
|
+
: `data_export_${timestamp}.csv`;
|
|
107
|
+
|
|
108
|
+
// Create export options
|
|
109
|
+
const exportOptions = {
|
|
110
|
+
tableRows,
|
|
111
|
+
allColumns: columns || [],
|
|
112
|
+
visibleColumns,
|
|
113
|
+
columnIdToTableColumn,
|
|
114
|
+
data: data || [],
|
|
115
|
+
filename,
|
|
116
|
+
table: {}, // Mock table instance
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// If custom handler provided, call it with options
|
|
120
|
+
if (onExport) {
|
|
121
|
+
await onExport(exportOptions);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Default export behavior
|
|
126
|
+
const exportColumns = visibleColumns.map((col: any) => ({
|
|
127
|
+
header: col.header || col.accessorKey,
|
|
128
|
+
id: col.id || col.accessorKey,
|
|
129
|
+
accessorKey: col.accessorKey,
|
|
130
|
+
accessorFn: 'accessorFn' in col ? col.accessorFn : undefined,
|
|
131
|
+
}));
|
|
132
|
+
|
|
133
|
+
await exportToCSVWithTableRows(
|
|
134
|
+
tableRows,
|
|
135
|
+
exportColumns,
|
|
136
|
+
columnIdToTableColumn,
|
|
137
|
+
filename
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
toast({
|
|
141
|
+
title: "Export Successful",
|
|
142
|
+
description: `Data exported to ${filename}`,
|
|
143
|
+
variant: "default"
|
|
144
|
+
});
|
|
145
|
+
} catch (error) {
|
|
146
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
|
|
147
|
+
toast({
|
|
148
|
+
title: "Export Failed",
|
|
149
|
+
description: `Failed to export data: ${errorMessage}`,
|
|
150
|
+
variant: "destructive"
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
return (
|
|
156
|
+
<div data-testid="data-table-core">
|
|
157
|
+
{props.features?.export && (
|
|
158
|
+
<button
|
|
159
|
+
data-testid="export-button"
|
|
160
|
+
onClick={handleExport}
|
|
161
|
+
>
|
|
162
|
+
Export
|
|
163
|
+
</button>
|
|
164
|
+
)}
|
|
165
|
+
<div data-testid="table-content">{props.children}</div>
|
|
166
|
+
</div>
|
|
167
|
+
);
|
|
168
|
+
}),
|
|
169
|
+
};
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
interface TestData {
|
|
173
|
+
id: string;
|
|
174
|
+
name: string;
|
|
175
|
+
email: string;
|
|
176
|
+
role: string;
|
|
177
|
+
status: 'active' | 'inactive';
|
|
178
|
+
createdAt: string;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
describe('DataTable Export Functionality', () => {
|
|
182
|
+
const mockData: TestData[] = [
|
|
183
|
+
{ id: '1', name: 'John Doe', email: 'john@example.com', role: 'Admin', status: 'active', createdAt: '2024-01-01' },
|
|
184
|
+
{ id: '2', name: 'Jane Smith', email: 'jane@example.com', role: 'User', status: 'active', createdAt: '2024-01-02' },
|
|
185
|
+
{ id: '3', name: 'Bob Johnson', email: 'bob@example.com', role: 'User', status: 'inactive', createdAt: '2024-01-03' },
|
|
186
|
+
];
|
|
187
|
+
|
|
188
|
+
const mockColumns: DataTableColumn<TestData>[] = [
|
|
189
|
+
{ id: 'id', accessorKey: 'id', header: 'ID' },
|
|
190
|
+
{ id: 'name', accessorKey: 'name', header: 'Name' },
|
|
191
|
+
{ id: 'email', accessorKey: 'email', header: 'Email' },
|
|
192
|
+
{ id: 'role', accessorKey: 'role', header: 'Role' },
|
|
193
|
+
{ id: 'status', accessorKey: 'status', header: 'Status' },
|
|
194
|
+
{ id: 'createdAt', accessorKey: 'createdAt', header: 'Created At' },
|
|
195
|
+
];
|
|
196
|
+
|
|
197
|
+
const mockRBAC = { pageId: 'test-page' };
|
|
198
|
+
|
|
199
|
+
beforeEach(() => {
|
|
200
|
+
vi.clearAllMocks();
|
|
201
|
+
delete (window as any).__testOnExport;
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
describe('Default Export Behavior', () => {
|
|
205
|
+
it('exports visible columns when no custom handler is provided', async () => {
|
|
206
|
+
const features = createDefaultFeatures();
|
|
207
|
+
features.export = true;
|
|
208
|
+
|
|
209
|
+
render(
|
|
210
|
+
<DataTable
|
|
211
|
+
data={mockData}
|
|
212
|
+
columns={mockColumns}
|
|
213
|
+
rbac={mockRBAC}
|
|
214
|
+
features={features}
|
|
215
|
+
title="Test Table"
|
|
216
|
+
/>
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
const exportButton = screen.getByTestId('export-button');
|
|
220
|
+
await userEvent.click(exportButton);
|
|
221
|
+
|
|
222
|
+
await waitFor(() => {
|
|
223
|
+
expect(exportToCSVWithTableRows).toHaveBeenCalled();
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
// Verify default export was called with visible columns
|
|
227
|
+
const callArgs = vi.mocked(exportToCSVWithTableRows).mock.calls[0];
|
|
228
|
+
expect(callArgs).toBeDefined();
|
|
229
|
+
expect(callArgs[2]).toBeDefined(); // columnIdToTableColumn
|
|
230
|
+
expect(callArgs[3]).toMatch(/test_table_\d{4}-\d{2}-\d{2}\.csv/); // filename
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it('generates filename from title', async () => {
|
|
234
|
+
const features = createDefaultFeatures();
|
|
235
|
+
features.export = true;
|
|
236
|
+
|
|
237
|
+
render(
|
|
238
|
+
<DataTable
|
|
239
|
+
data={mockData}
|
|
240
|
+
columns={mockColumns}
|
|
241
|
+
rbac={mockRBAC}
|
|
242
|
+
features={features}
|
|
243
|
+
title="My Custom Table"
|
|
244
|
+
/>
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
const exportButton = screen.getByTestId('export-button');
|
|
248
|
+
await userEvent.click(exportButton);
|
|
249
|
+
|
|
250
|
+
await waitFor(() => {
|
|
251
|
+
expect(exportToCSVWithTableRows).toHaveBeenCalled();
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
const callArgs = vi.mocked(exportToCSVWithTableRows).mock.calls[0];
|
|
255
|
+
expect(callArgs[3]).toMatch(/my_custom_table_\d{4}-\d{2}-\d{2}\.csv/);
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it('uses default filename when no title is provided', async () => {
|
|
259
|
+
const features = createDefaultFeatures();
|
|
260
|
+
features.export = true;
|
|
261
|
+
|
|
262
|
+
render(
|
|
263
|
+
<DataTable
|
|
264
|
+
data={mockData}
|
|
265
|
+
columns={mockColumns}
|
|
266
|
+
rbac={mockRBAC}
|
|
267
|
+
features={features}
|
|
268
|
+
/>
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
const exportButton = screen.getByTestId('export-button');
|
|
272
|
+
await userEvent.click(exportButton);
|
|
273
|
+
|
|
274
|
+
await waitFor(() => {
|
|
275
|
+
expect(exportToCSVWithTableRows).toHaveBeenCalled();
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
const callArgs = vi.mocked(exportToCSVWithTableRows).mock.calls[0];
|
|
279
|
+
expect(callArgs[3]).toMatch(/data_export_\d{4}-\d{2}-\d{2}\.csv/);
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
describe('Custom onExport Handler', () => {
|
|
284
|
+
it('calls custom onExport handler with ExportOptions', async () => {
|
|
285
|
+
const features = createDefaultFeatures();
|
|
286
|
+
features.export = true;
|
|
287
|
+
const mockOnExport = vi.fn().mockResolvedValue(undefined);
|
|
288
|
+
|
|
289
|
+
render(
|
|
290
|
+
<DataTable
|
|
291
|
+
data={mockData}
|
|
292
|
+
columns={mockColumns}
|
|
293
|
+
rbac={mockRBAC}
|
|
294
|
+
features={features}
|
|
295
|
+
onExport={mockOnExport}
|
|
296
|
+
/>
|
|
297
|
+
);
|
|
298
|
+
|
|
299
|
+
const exportButton = screen.getByTestId('export-button');
|
|
300
|
+
await userEvent.click(exportButton);
|
|
301
|
+
|
|
302
|
+
await waitFor(() => {
|
|
303
|
+
expect(mockOnExport).toHaveBeenCalled();
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
// Verify ExportOptions structure
|
|
307
|
+
const exportOptions = mockOnExport.mock.calls[0][0] as ExportOptions<TestData>;
|
|
308
|
+
expect(exportOptions).toBeDefined();
|
|
309
|
+
expect(exportOptions.tableRows).toBeDefined();
|
|
310
|
+
expect(exportOptions.allColumns).toEqual(mockColumns);
|
|
311
|
+
expect(exportOptions.visibleColumns).toBeDefined();
|
|
312
|
+
expect(exportOptions.columnIdToTableColumn).toBeDefined();
|
|
313
|
+
expect(exportOptions.data).toEqual(mockData);
|
|
314
|
+
expect(exportOptions.filename).toBeDefined();
|
|
315
|
+
expect(exportOptions.table).toBeDefined();
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
it('provides all columns in ExportOptions.allColumns', async () => {
|
|
319
|
+
const features = createDefaultFeatures();
|
|
320
|
+
features.export = true;
|
|
321
|
+
const mockOnExport = vi.fn().mockResolvedValue(undefined);
|
|
322
|
+
|
|
323
|
+
render(
|
|
324
|
+
<DataTable
|
|
325
|
+
data={mockData}
|
|
326
|
+
columns={mockColumns}
|
|
327
|
+
rbac={mockRBAC}
|
|
328
|
+
features={features}
|
|
329
|
+
onExport={mockOnExport}
|
|
330
|
+
/>
|
|
331
|
+
);
|
|
332
|
+
|
|
333
|
+
const exportButton = screen.getByTestId('export-button');
|
|
334
|
+
await userEvent.click(exportButton);
|
|
335
|
+
|
|
336
|
+
await waitFor(() => {
|
|
337
|
+
expect(mockOnExport).toHaveBeenCalled();
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
const exportOptions = mockOnExport.mock.calls[0][0] as ExportOptions<TestData>;
|
|
341
|
+
expect(exportOptions.allColumns).toHaveLength(mockColumns.length);
|
|
342
|
+
expect(exportOptions.allColumns).toEqual(mockColumns);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
it('provides visible columns in ExportOptions.visibleColumns', async () => {
|
|
346
|
+
const features = createDefaultFeatures();
|
|
347
|
+
features.export = true;
|
|
348
|
+
const mockOnExport = vi.fn().mockResolvedValue(undefined);
|
|
349
|
+
|
|
350
|
+
render(
|
|
351
|
+
<DataTable
|
|
352
|
+
data={mockData}
|
|
353
|
+
columns={mockColumns}
|
|
354
|
+
rbac={mockRBAC}
|
|
355
|
+
features={features}
|
|
356
|
+
onExport={mockOnExport}
|
|
357
|
+
/>
|
|
358
|
+
);
|
|
359
|
+
|
|
360
|
+
const exportButton = screen.getByTestId('export-button');
|
|
361
|
+
await userEvent.click(exportButton);
|
|
362
|
+
|
|
363
|
+
await waitFor(() => {
|
|
364
|
+
expect(mockOnExport).toHaveBeenCalled();
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
const exportOptions = mockOnExport.mock.calls[0][0] as ExportOptions<TestData>;
|
|
368
|
+
expect(exportOptions.visibleColumns).toBeDefined();
|
|
369
|
+
expect(Array.isArray(exportOptions.visibleColumns)).toBe(true);
|
|
370
|
+
// Visible columns should be a subset of all columns
|
|
371
|
+
expect(exportOptions.visibleColumns.length).toBeLessThanOrEqual(exportOptions.allColumns.length);
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
it('provides tableRows with getValue method', async () => {
|
|
375
|
+
const features = createDefaultFeatures();
|
|
376
|
+
features.export = true;
|
|
377
|
+
const mockOnExport = vi.fn().mockResolvedValue(undefined);
|
|
378
|
+
|
|
379
|
+
render(
|
|
380
|
+
<DataTable
|
|
381
|
+
data={mockData}
|
|
382
|
+
columns={mockColumns}
|
|
383
|
+
rbac={mockRBAC}
|
|
384
|
+
features={features}
|
|
385
|
+
onExport={mockOnExport}
|
|
386
|
+
/>
|
|
387
|
+
);
|
|
388
|
+
|
|
389
|
+
const exportButton = screen.getByTestId('export-button');
|
|
390
|
+
await userEvent.click(exportButton);
|
|
391
|
+
|
|
392
|
+
await waitFor(() => {
|
|
393
|
+
expect(mockOnExport).toHaveBeenCalled();
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
const exportOptions = mockOnExport.mock.calls[0][0] as ExportOptions<TestData>;
|
|
397
|
+
expect(exportOptions.tableRows).toBeDefined();
|
|
398
|
+
expect(Array.isArray(exportOptions.tableRows)).toBe(true);
|
|
399
|
+
expect(exportOptions.tableRows.length).toBeGreaterThan(0);
|
|
400
|
+
|
|
401
|
+
// Verify tableRows have getValue method
|
|
402
|
+
const firstRow = exportOptions.tableRows[0];
|
|
403
|
+
expect(firstRow).toHaveProperty('getValue');
|
|
404
|
+
expect(firstRow).toHaveProperty('original');
|
|
405
|
+
expect(firstRow).toHaveProperty('id');
|
|
406
|
+
expect(typeof firstRow.getValue).toBe('function');
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
it('provides columnIdToTableColumn mapping', async () => {
|
|
410
|
+
const features = createDefaultFeatures();
|
|
411
|
+
features.export = true;
|
|
412
|
+
const mockOnExport = vi.fn().mockResolvedValue(undefined);
|
|
413
|
+
|
|
414
|
+
render(
|
|
415
|
+
<DataTable
|
|
416
|
+
data={mockData}
|
|
417
|
+
columns={mockColumns}
|
|
418
|
+
rbac={mockRBAC}
|
|
419
|
+
features={features}
|
|
420
|
+
onExport={mockOnExport}
|
|
421
|
+
/>
|
|
422
|
+
);
|
|
423
|
+
|
|
424
|
+
const exportButton = screen.getByTestId('export-button');
|
|
425
|
+
await userEvent.click(exportButton);
|
|
426
|
+
|
|
427
|
+
await waitFor(() => {
|
|
428
|
+
expect(mockOnExport).toHaveBeenCalled();
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
const exportOptions = mockOnExport.mock.calls[0][0] as ExportOptions<TestData>;
|
|
432
|
+
expect(exportOptions.columnIdToTableColumn).toBeDefined();
|
|
433
|
+
expect(exportOptions.columnIdToTableColumn instanceof Map).toBe(true);
|
|
434
|
+
});
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
describe('Custom Column Selection', () => {
|
|
438
|
+
it('allows exporting different columns than visible', async () => {
|
|
439
|
+
const features = createDefaultFeatures();
|
|
440
|
+
features.export = true;
|
|
441
|
+
const mockOnExport = vi.fn(async (options: ExportOptions<TestData>) => {
|
|
442
|
+
// Export only name and email columns (different from all visible columns)
|
|
443
|
+
const exportColumns = options.allColumns
|
|
444
|
+
.filter(col => ['name', 'email'].includes(col.accessorKey || ''))
|
|
445
|
+
.map(col => ({
|
|
446
|
+
header: col.header || col.accessorKey,
|
|
447
|
+
id: col.id || col.accessorKey,
|
|
448
|
+
accessorKey: col.accessorKey,
|
|
449
|
+
accessorFn: 'accessorFn' in col ? col.accessorFn : undefined,
|
|
450
|
+
}));
|
|
451
|
+
|
|
452
|
+
await exportToCSVWithTableRows(
|
|
453
|
+
options.tableRows,
|
|
454
|
+
exportColumns,
|
|
455
|
+
options.columnIdToTableColumn,
|
|
456
|
+
'custom-export.csv'
|
|
457
|
+
);
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
render(
|
|
461
|
+
<DataTable
|
|
462
|
+
data={mockData}
|
|
463
|
+
columns={mockColumns}
|
|
464
|
+
rbac={mockRBAC}
|
|
465
|
+
features={features}
|
|
466
|
+
onExport={mockOnExport}
|
|
467
|
+
/>
|
|
468
|
+
);
|
|
469
|
+
|
|
470
|
+
const exportButton = screen.getByTestId('export-button');
|
|
471
|
+
await userEvent.click(exportButton);
|
|
472
|
+
|
|
473
|
+
await waitFor(() => {
|
|
474
|
+
expect(mockOnExport).toHaveBeenCalled();
|
|
475
|
+
expect(exportToCSVWithTableRows).toHaveBeenCalled();
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
// Verify custom export was called with only name and email columns
|
|
479
|
+
const callArgs = vi.mocked(exportToCSVWithTableRows).mock.calls[0];
|
|
480
|
+
const exportedColumns = callArgs[1];
|
|
481
|
+
expect(exportedColumns).toHaveLength(2);
|
|
482
|
+
expect(exportedColumns.map((c: any) => c.accessorKey)).toEqual(['name', 'email']);
|
|
483
|
+
expect(callArgs[3]).toBe('custom-export.csv');
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
it('allows exporting all columns including hidden ones', async () => {
|
|
487
|
+
const features = createDefaultFeatures();
|
|
488
|
+
features.export = true;
|
|
489
|
+
const mockOnExport = vi.fn(async (options: ExportOptions<TestData>) => {
|
|
490
|
+
// Export all columns (including potentially hidden ones)
|
|
491
|
+
const exportColumns = options.allColumns.map(col => ({
|
|
492
|
+
header: col.header || col.accessorKey,
|
|
493
|
+
id: col.id || col.accessorKey,
|
|
494
|
+
accessorKey: col.accessorKey,
|
|
495
|
+
accessorFn: 'accessorFn' in col ? col.accessorFn : undefined,
|
|
496
|
+
}));
|
|
497
|
+
|
|
498
|
+
await exportToCSVWithTableRows(
|
|
499
|
+
options.tableRows,
|
|
500
|
+
exportColumns,
|
|
501
|
+
options.columnIdToTableColumn,
|
|
502
|
+
'all-columns-export.csv'
|
|
503
|
+
);
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
render(
|
|
507
|
+
<DataTable
|
|
508
|
+
data={mockData}
|
|
509
|
+
columns={mockColumns}
|
|
510
|
+
rbac={mockRBAC}
|
|
511
|
+
features={features}
|
|
512
|
+
onExport={mockOnExport}
|
|
513
|
+
/>
|
|
514
|
+
);
|
|
515
|
+
|
|
516
|
+
const exportButton = screen.getByTestId('export-button');
|
|
517
|
+
await userEvent.click(exportButton);
|
|
518
|
+
|
|
519
|
+
await waitFor(() => {
|
|
520
|
+
expect(mockOnExport).toHaveBeenCalled();
|
|
521
|
+
expect(exportToCSVWithTableRows).toHaveBeenCalled();
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
// Verify all columns were exported
|
|
525
|
+
const callArgs = vi.mocked(exportToCSVWithTableRows).mock.calls[0];
|
|
526
|
+
const exportedColumns = callArgs[1];
|
|
527
|
+
expect(exportedColumns).toHaveLength(mockColumns.length);
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
it('allows custom filename in export handler', async () => {
|
|
531
|
+
const features = createDefaultFeatures();
|
|
532
|
+
features.export = true;
|
|
533
|
+
const customFilename = 'my-custom-export-2024.csv';
|
|
534
|
+
const mockOnExport = vi.fn(async (options: ExportOptions<TestData>) => {
|
|
535
|
+
const exportColumns = options.visibleColumns.map(col => ({
|
|
536
|
+
header: col.header || col.accessorKey,
|
|
537
|
+
id: col.id || col.accessorKey,
|
|
538
|
+
accessorKey: col.accessorKey,
|
|
539
|
+
accessorFn: 'accessorFn' in col ? col.accessorFn : undefined,
|
|
540
|
+
}));
|
|
541
|
+
|
|
542
|
+
await exportToCSVWithTableRows(
|
|
543
|
+
options.tableRows,
|
|
544
|
+
exportColumns,
|
|
545
|
+
options.columnIdToTableColumn,
|
|
546
|
+
customFilename
|
|
547
|
+
);
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
render(
|
|
551
|
+
<DataTable
|
|
552
|
+
data={mockData}
|
|
553
|
+
columns={mockColumns}
|
|
554
|
+
rbac={mockRBAC}
|
|
555
|
+
features={features}
|
|
556
|
+
onExport={mockOnExport}
|
|
557
|
+
title="Test Table"
|
|
558
|
+
/>
|
|
559
|
+
);
|
|
560
|
+
|
|
561
|
+
const exportButton = screen.getByTestId('export-button');
|
|
562
|
+
await userEvent.click(exportButton);
|
|
563
|
+
|
|
564
|
+
await waitFor(() => {
|
|
565
|
+
expect(mockOnExport).toHaveBeenCalled();
|
|
566
|
+
expect(exportToCSVWithTableRows).toHaveBeenCalled();
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
const callArgs = vi.mocked(exportToCSVWithTableRows).mock.calls[0];
|
|
570
|
+
expect(callArgs[3]).toBe(customFilename);
|
|
571
|
+
});
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
describe('Export with accessorFn', () => {
|
|
575
|
+
it('handles columns with accessorFn in custom export', async () => {
|
|
576
|
+
const columnsWithAccessorFn: DataTableColumn<TestData>[] = [
|
|
577
|
+
{ id: 'name', accessorKey: 'name', header: 'Name' },
|
|
578
|
+
{
|
|
579
|
+
id: 'fullInfo',
|
|
580
|
+
header: 'Full Info',
|
|
581
|
+
accessorFn: (row) => `${row.name} (${row.email})`,
|
|
582
|
+
},
|
|
583
|
+
];
|
|
584
|
+
|
|
585
|
+
const features = createDefaultFeatures();
|
|
586
|
+
features.export = true;
|
|
587
|
+
const mockOnExport = vi.fn(async (options: ExportOptions<TestData>) => {
|
|
588
|
+
const exportColumns = options.allColumns.map(col => ({
|
|
589
|
+
header: col.header || col.accessorKey,
|
|
590
|
+
id: col.id || col.accessorKey,
|
|
591
|
+
accessorKey: col.accessorKey,
|
|
592
|
+
accessorFn: 'accessorFn' in col ? col.accessorFn : undefined,
|
|
593
|
+
}));
|
|
594
|
+
|
|
595
|
+
await exportToCSVWithTableRows(
|
|
596
|
+
options.tableRows,
|
|
597
|
+
exportColumns,
|
|
598
|
+
options.columnIdToTableColumn,
|
|
599
|
+
'export-with-accessorfn.csv'
|
|
600
|
+
);
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
render(
|
|
604
|
+
<DataTable
|
|
605
|
+
data={mockData}
|
|
606
|
+
columns={columnsWithAccessorFn}
|
|
607
|
+
rbac={mockRBAC}
|
|
608
|
+
features={features}
|
|
609
|
+
onExport={mockOnExport}
|
|
610
|
+
/>
|
|
611
|
+
);
|
|
612
|
+
|
|
613
|
+
const exportButton = screen.getByTestId('export-button');
|
|
614
|
+
await userEvent.click(exportButton);
|
|
615
|
+
|
|
616
|
+
await waitFor(() => {
|
|
617
|
+
expect(mockOnExport).toHaveBeenCalled();
|
|
618
|
+
expect(exportToCSVWithTableRows).toHaveBeenCalled();
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
// Verify export was called with columns including accessorFn
|
|
622
|
+
const callArgs = vi.mocked(exportToCSVWithTableRows).mock.calls[0];
|
|
623
|
+
const exportedColumns = callArgs[1];
|
|
624
|
+
const fullInfoColumn = exportedColumns.find((c: any) => c.id === 'fullInfo');
|
|
625
|
+
expect(fullInfoColumn).toBeDefined();
|
|
626
|
+
expect(fullInfoColumn.accessorFn).toBeDefined();
|
|
627
|
+
});
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
describe('Error Handling', () => {
|
|
631
|
+
it('handles errors in custom export handler gracefully', async () => {
|
|
632
|
+
const features = createDefaultFeatures();
|
|
633
|
+
features.export = true;
|
|
634
|
+
const mockOnExport = vi.fn().mockRejectedValue(new Error('Export failed'));
|
|
635
|
+
|
|
636
|
+
render(
|
|
637
|
+
<DataTable
|
|
638
|
+
data={mockData}
|
|
639
|
+
columns={mockColumns}
|
|
640
|
+
rbac={mockRBAC}
|
|
641
|
+
features={features}
|
|
642
|
+
onExport={mockOnExport}
|
|
643
|
+
/>
|
|
644
|
+
);
|
|
645
|
+
|
|
646
|
+
const exportButton = screen.getByTestId('export-button');
|
|
647
|
+
await userEvent.click(exportButton);
|
|
648
|
+
|
|
649
|
+
await waitFor(() => {
|
|
650
|
+
expect(mockOnExport).toHaveBeenCalled();
|
|
651
|
+
});
|
|
652
|
+
|
|
653
|
+
// Verify error toast was shown
|
|
654
|
+
await waitFor(() => {
|
|
655
|
+
expect(mockToast).toHaveBeenCalledWith(
|
|
656
|
+
expect.objectContaining({
|
|
657
|
+
title: 'Export Failed',
|
|
658
|
+
variant: 'destructive',
|
|
659
|
+
})
|
|
660
|
+
);
|
|
661
|
+
});
|
|
662
|
+
});
|
|
663
|
+
|
|
664
|
+
it('handles errors in default export gracefully', async () => {
|
|
665
|
+
vi.mocked(exportToCSVWithTableRows).mockRejectedValueOnce(new Error('Export failed'));
|
|
666
|
+
|
|
667
|
+
const features = createDefaultFeatures();
|
|
668
|
+
features.export = true;
|
|
669
|
+
|
|
670
|
+
render(
|
|
671
|
+
<DataTable
|
|
672
|
+
data={mockData}
|
|
673
|
+
columns={mockColumns}
|
|
674
|
+
rbac={mockRBAC}
|
|
675
|
+
features={features}
|
|
676
|
+
/>
|
|
677
|
+
);
|
|
678
|
+
|
|
679
|
+
const exportButton = screen.getByTestId('export-button');
|
|
680
|
+
await userEvent.click(exportButton);
|
|
681
|
+
|
|
682
|
+
await waitFor(() => {
|
|
683
|
+
expect(mockToast).toHaveBeenCalledWith(
|
|
684
|
+
expect.objectContaining({
|
|
685
|
+
title: 'Export Failed',
|
|
686
|
+
variant: 'destructive',
|
|
687
|
+
})
|
|
688
|
+
);
|
|
689
|
+
});
|
|
690
|
+
});
|
|
691
|
+
});
|
|
692
|
+
|
|
693
|
+
describe('RBAC Integration', () => {
|
|
694
|
+
it('skips RBAC test - tested in DataTableCore tests', () => {
|
|
695
|
+
// RBAC integration is tested in DataTableCore.test.tsx
|
|
696
|
+
// This test file focuses on the export functionality itself
|
|
697
|
+
// The mock DataTableCore doesn't implement RBAC checks, so we skip here
|
|
698
|
+
expect(true).toBe(true);
|
|
699
|
+
});
|
|
700
|
+
});
|
|
701
|
+
});
|
|
702
|
+
|