@jmruthers/pace-core 0.5.114 → 0.5.116
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/{AuthService-CVgsgtaZ.d.ts → AuthService-D4646R4b.d.ts} +9 -4
- package/dist/{DataTable-3JRLZXER.js → DataTable-ZOAKQ3SU.js} +10 -9
- package/dist/{UnifiedAuthProvider-KZZUO27W.js → UnifiedAuthProvider-YFN7YGVN.js} +4 -3
- package/dist/{api-PKU4PUBO.js → api-TNIBJWLM.js} +3 -3
- package/dist/{audit-H4YJJF7R.js → audit-T36HM7IM.js} +2 -2
- package/dist/{chunk-4OX5PXHX.js → chunk-2GJ5GL77.js} +4 -5
- package/dist/chunk-2GJ5GL77.js.map +1 -0
- package/dist/{chunk-5YIZFEUQ.js → chunk-2LM4QQGH.js} +31 -35
- package/dist/chunk-2LM4QQGH.js.map +1 -0
- package/dist/{chunk-3OGQLOJM.js → chunk-3DBFLLLU.js} +30 -1
- package/dist/chunk-3DBFLLLU.js.map +1 -0
- package/dist/{chunk-KTHLNIMA.js → chunk-ECOVPXYS.js} +13 -62
- package/dist/chunk-ECOVPXYS.js.map +1 -0
- package/dist/{chunk-OO3V7W4H.js → chunk-KA3PSVNV.js} +87 -40
- package/dist/chunk-KA3PSVNV.js.map +1 -0
- package/dist/{chunk-HKWQN44G.js → chunk-KMPWND3F.js} +15 -15
- package/dist/{chunk-L36JW4KV.js → chunk-LFS45U62.js} +2 -2
- package/dist/{chunk-NEONKMTU.js → chunk-LZYHAL7Y.js} +9 -4
- package/dist/{chunk-NEONKMTU.js.map → chunk-LZYHAL7Y.js.map} +1 -1
- package/dist/{chunk-BUN7NMV7.js → chunk-O3FTRYEU.js} +2 -2
- package/dist/{chunk-F6QB26OS.js → chunk-P3PUOL6B.js} +80 -8
- package/dist/chunk-P3PUOL6B.js.map +1 -0
- package/dist/{chunk-ZPXWJA4H.js → chunk-PHDAXDHB.js} +131 -5
- package/dist/chunk-PHDAXDHB.js.map +1 -0
- package/dist/chunk-UJI6WSMD.js +201 -0
- package/dist/{chunk-5CDJCTOO.js.map → chunk-UJI6WSMD.js.map} +1 -1
- package/dist/{chunk-JHWQNJP3.js → chunk-UKZWNQMB.js} +65 -19
- package/dist/{chunk-JHWQNJP3.js.map → chunk-UKZWNQMB.js.map} +1 -1
- package/dist/{chunk-7H75SHXZ.js → chunk-VN3OOE35.js} +2 -2
- package/dist/{chunk-QKIVSZ2O.js → chunk-WP5I5GLN.js} +2 -2
- package/dist/components.d.ts +1 -1
- package/dist/components.js +12 -11
- package/dist/components.js.map +1 -1
- package/dist/hooks.d.ts +1 -1
- package/dist/hooks.js +10 -9
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +19 -16
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +2 -2
- package/dist/providers.js +3 -2
- package/dist/rbac/index.d.ts +82 -1
- package/dist/rbac/index.js +13 -10
- package/dist/{useToast-DRah6K-g.d.ts → useToast-Cs_g32bg.d.ts} +8 -6
- package/dist/utils.js +6 -4
- package/dist/utils.js.map +1 -1
- package/dist/validation.js +3 -1
- package/dist/validation.js.map +1 -1
- package/docs/README.md +4 -0
- 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 +35 -12
- 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 +71 -0
- 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 +122 -0
- 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 +1 -1
- 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 +100 -0
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RoleManagementResult.md +52 -0
- 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 +43 -16
- package/docs/architecture/rpc-function-standards.md +193 -0
- package/package.json +1 -1
- package/src/__tests__/TEST_STANDARD.md +244 -2
- package/src/components/DataTable/__tests__/a11y.basic.test.tsx +46 -16
- package/src/components/DataTable/__tests__/keyboard.test.tsx +276 -217
- package/src/components/DataTable/components/DataTableCore.tsx +32 -17
- package/src/components/DataTable/components/DataTableToolbar.tsx +3 -2
- package/src/components/DataTable/components/EditableRow.tsx +18 -1
- package/src/components/DataTable/components/ImportModal.tsx +25 -2
- package/src/components/DataTable/components/ViewRowModal.tsx +1 -1
- package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +735 -0
- package/src/components/DataTable/components/__tests__/BulkOperationsDropdown.test.tsx +572 -0
- package/src/components/DataTable/components/__tests__/ColumnVisibilityDropdown.test.tsx +708 -0
- package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +451 -0
- package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +456 -0
- package/src/components/DataTable/components/__tests__/EditableRow.test.tsx +454 -0
- package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +462 -0
- package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +423 -0
- package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +393 -0
- package/src/components/DataTable/components/__tests__/GroupingDropdown.test.tsx +617 -0
- package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +734 -0
- package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +412 -0
- package/src/components/DataTable/hooks/useTableHandlers.ts +4 -0
- package/src/components/EventSelector/EventSelector.tsx +5 -25
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +12 -7
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +4 -0
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.accessibility.test.tsx +7 -2
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.integration.test.tsx +13 -8
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.performance.test.tsx +109 -100
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.security.test.tsx +18 -13
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.unit.test.tsx +17 -12
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +2 -0
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +11 -1
- package/src/components/PasswordReset/PasswordChangeForm.test.tsx +2 -2
- package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +648 -0
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +10 -7
- package/src/components/PublicLayout/__tests__/PublicErrorBoundary.test.tsx +4 -12
- package/src/components/Select/Select.tsx +8 -0
- package/src/components/Toast/Toast.test.tsx +8 -7
- package/src/components/Toast/Toast.tsx +4 -4
- package/src/hooks/__tests__/usePublicEvent.simple.test.ts +367 -3
- package/src/hooks/__tests__/usePublicFileDisplay.test.ts +916 -0
- package/src/hooks/useEventTheme.ts +49 -18
- package/src/hooks/usePermissionCache.ts +5 -3
- package/src/hooks/useSecureDataAccess.ts +11 -1
- package/src/hooks/useToast.ts +11 -12
- package/src/providers/services/EventServiceProvider.tsx +15 -8
- package/src/rbac/__tests__/cache-invalidation.test.ts +385 -0
- package/src/rbac/audit.test.ts +206 -0
- package/src/rbac/audit.ts +37 -2
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +26 -23
- package/src/rbac/errors.test.ts +340 -0
- package/src/rbac/hooks/index.ts +9 -0
- package/src/rbac/hooks/useResolvedScope.test.ts +1063 -0
- package/src/rbac/hooks/useRoleManagement.test.ts +908 -0
- package/src/rbac/hooks/useRoleManagement.ts +255 -0
- package/src/services/AuthService.ts +10 -0
- package/src/services/EventService.ts +111 -50
- package/src/services/__tests__/AuthService.test.ts +1 -1
- package/src/services/__tests__/EventService.test.ts +60 -45
- package/src/services/interfaces/IEventService.ts +1 -1
- package/src/utils/__tests__/deviceFingerprint.unit.test.ts +320 -0
- package/src/utils/__tests__/logger.unit.test.ts +398 -0
- package/src/utils/__tests__/validation.unit.test.ts +225 -1
- package/src/utils/file-reference.test.ts +214 -0
- package/dist/chunk-3OGQLOJM.js.map +0 -1
- package/dist/chunk-4OX5PXHX.js.map +0 -1
- package/dist/chunk-5CDJCTOO.js +0 -190
- package/dist/chunk-5YIZFEUQ.js.map +0 -1
- package/dist/chunk-F6QB26OS.js.map +0 -1
- package/dist/chunk-KTHLNIMA.js.map +0 -1
- package/dist/chunk-OO3V7W4H.js.map +0 -1
- package/dist/chunk-ZPXWJA4H.js.map +0 -1
- package/src/rbac/audit-enhanced.ts +0 -351
- /package/dist/{DataTable-3JRLZXER.js.map → DataTable-ZOAKQ3SU.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-KZZUO27W.js.map → UnifiedAuthProvider-YFN7YGVN.js.map} +0 -0
- /package/dist/{api-PKU4PUBO.js.map → api-TNIBJWLM.js.map} +0 -0
- /package/dist/{audit-H4YJJF7R.js.map → audit-T36HM7IM.js.map} +0 -0
- /package/dist/{chunk-HKWQN44G.js.map → chunk-KMPWND3F.js.map} +0 -0
- /package/dist/{chunk-L36JW4KV.js.map → chunk-LFS45U62.js.map} +0 -0
- /package/dist/{chunk-BUN7NMV7.js.map → chunk-O3FTRYEU.js.map} +0 -0
- /package/dist/{chunk-7H75SHXZ.js.map → chunk-VN3OOE35.js.map} +0 -0
- /package/dist/{chunk-QKIVSZ2O.js.map → chunk-WP5I5GLN.js.map} +0 -0
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file DataTable Modals Component Tests
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module Components/DataTable/Components/__tests__
|
|
5
|
+
* @since 0.4.0
|
|
6
|
+
*
|
|
7
|
+
* Comprehensive test suite for DataTableModals component following testing guidelines.
|
|
8
|
+
* Tests cover all major functionality, edge cases, and user interactions.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import React from 'react';
|
|
12
|
+
import { render, screen, waitFor } from '@testing-library/react';
|
|
13
|
+
import userEvent from '@testing-library/user-event';
|
|
14
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
15
|
+
import { DataTableModals } from '../DataTableModals';
|
|
16
|
+
|
|
17
|
+
// Mock ImportModal
|
|
18
|
+
vi.mock('../ImportModal', () => ({
|
|
19
|
+
ImportModal: ({ isOpen, onClose, onImport, config }: any) => (
|
|
20
|
+
isOpen ? (
|
|
21
|
+
<div data-testid="import-modal">
|
|
22
|
+
<div data-testid="import-modal-title">{config?.title || 'Import Data'}</div>
|
|
23
|
+
<button onClick={onClose} data-testid="import-modal-close">Close</button>
|
|
24
|
+
<button
|
|
25
|
+
onClick={async () => {
|
|
26
|
+
try {
|
|
27
|
+
const mockData = [{ name: 'Test', email: 'test@example.com' }];
|
|
28
|
+
const result = onImport(mockData);
|
|
29
|
+
if (result && typeof result.then === 'function') {
|
|
30
|
+
await result;
|
|
31
|
+
}
|
|
32
|
+
} catch (error) {
|
|
33
|
+
// Errors are expected in error handling tests
|
|
34
|
+
// The component should handle them
|
|
35
|
+
}
|
|
36
|
+
}}
|
|
37
|
+
data-testid="import-modal-import"
|
|
38
|
+
>
|
|
39
|
+
Import
|
|
40
|
+
</button>
|
|
41
|
+
</div>
|
|
42
|
+
) : null
|
|
43
|
+
),
|
|
44
|
+
}));
|
|
45
|
+
|
|
46
|
+
interface TestData extends Record<string, unknown> {
|
|
47
|
+
name: string;
|
|
48
|
+
email: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
describe('[component] DataTableModals', () => {
|
|
52
|
+
const defaultProps = {
|
|
53
|
+
showImportModal: false,
|
|
54
|
+
onCloseImportModal: vi.fn(),
|
|
55
|
+
onImport: vi.fn(),
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
beforeEach(() => {
|
|
59
|
+
vi.clearAllMocks();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
afterEach(() => {
|
|
63
|
+
vi.clearAllMocks();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
describe('Rendering', () => {
|
|
67
|
+
it('renders nothing when no modals are open', () => {
|
|
68
|
+
const { container } = render(<DataTableModals {...defaultProps} />);
|
|
69
|
+
expect(container.firstChild).toBeNull();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('renders ImportModal when showImportModal is true', () => {
|
|
73
|
+
render(
|
|
74
|
+
<DataTableModals
|
|
75
|
+
{...defaultProps}
|
|
76
|
+
showImportModal={true}
|
|
77
|
+
/>
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
expect(screen.getByTestId('import-modal')).toBeInTheDocument();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('does not render ImportModal when showImportModal is false', () => {
|
|
84
|
+
render(<DataTableModals {...defaultProps} />);
|
|
85
|
+
|
|
86
|
+
expect(screen.queryByTestId('import-modal')).not.toBeInTheDocument();
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe('Import Modal Integration', () => {
|
|
91
|
+
it('passes isOpen prop to ImportModal', () => {
|
|
92
|
+
render(
|
|
93
|
+
<DataTableModals
|
|
94
|
+
{...defaultProps}
|
|
95
|
+
showImportModal={true}
|
|
96
|
+
/>
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
expect(screen.getByTestId('import-modal')).toBeInTheDocument();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('passes onClose prop to ImportModal', async () => {
|
|
103
|
+
const user = userEvent.setup();
|
|
104
|
+
const onCloseImportModal = vi.fn();
|
|
105
|
+
|
|
106
|
+
render(
|
|
107
|
+
<DataTableModals
|
|
108
|
+
{...defaultProps}
|
|
109
|
+
showImportModal={true}
|
|
110
|
+
onCloseImportModal={onCloseImportModal}
|
|
111
|
+
/>
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
const closeButton = screen.getByTestId('import-modal-close');
|
|
115
|
+
await user.click(closeButton);
|
|
116
|
+
|
|
117
|
+
expect(onCloseImportModal).toHaveBeenCalledTimes(1);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('passes config to ImportModal', () => {
|
|
121
|
+
const config = {
|
|
122
|
+
title: 'Custom Import Title',
|
|
123
|
+
description: 'Custom description',
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
render(
|
|
127
|
+
<DataTableModals
|
|
128
|
+
{...defaultProps}
|
|
129
|
+
showImportModal={true}
|
|
130
|
+
importModalConfig={config}
|
|
131
|
+
/>
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
expect(screen.getByTestId('import-modal-title')).toHaveTextContent('Custom Import Title');
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
describe('CSV Column Mapping', () => {
|
|
139
|
+
it('maps CSV columns to table columns using column definitions', async () => {
|
|
140
|
+
const user = userEvent.setup();
|
|
141
|
+
const onImport = vi.fn();
|
|
142
|
+
const columns = [
|
|
143
|
+
{ id: 'name', header: 'Name', accessorKey: 'name' },
|
|
144
|
+
{ id: 'email', header: 'Email', accessorKey: 'email' },
|
|
145
|
+
];
|
|
146
|
+
|
|
147
|
+
render(
|
|
148
|
+
<DataTableModals
|
|
149
|
+
{...defaultProps}
|
|
150
|
+
showImportModal={true}
|
|
151
|
+
onImport={onImport}
|
|
152
|
+
columns={columns}
|
|
153
|
+
/>
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
const importButton = screen.getByTestId('import-modal-import');
|
|
157
|
+
await user.click(importButton);
|
|
158
|
+
|
|
159
|
+
await waitFor(() => {
|
|
160
|
+
expect(onImport).toHaveBeenCalled();
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('uses editAccessorKey when available for column mapping', async () => {
|
|
165
|
+
const user = userEvent.setup();
|
|
166
|
+
const onImport = vi.fn();
|
|
167
|
+
const columns = [
|
|
168
|
+
{
|
|
169
|
+
id: 'type',
|
|
170
|
+
header: 'Type',
|
|
171
|
+
accessorKey: 'type',
|
|
172
|
+
editAccessorKey: 'typeId',
|
|
173
|
+
},
|
|
174
|
+
];
|
|
175
|
+
|
|
176
|
+
render(
|
|
177
|
+
<DataTableModals
|
|
178
|
+
{...defaultProps}
|
|
179
|
+
showImportModal={true}
|
|
180
|
+
onImport={onImport}
|
|
181
|
+
columns={columns}
|
|
182
|
+
/>
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
const importButton = screen.getByTestId('import-modal-import');
|
|
186
|
+
await user.click(importButton);
|
|
187
|
+
|
|
188
|
+
await waitFor(() => {
|
|
189
|
+
expect(onImport).toHaveBeenCalled();
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('uses raw data when no columns provided', async () => {
|
|
194
|
+
const user = userEvent.setup();
|
|
195
|
+
const onImport = vi.fn();
|
|
196
|
+
|
|
197
|
+
render(
|
|
198
|
+
<DataTableModals
|
|
199
|
+
{...defaultProps}
|
|
200
|
+
showImportModal={true}
|
|
201
|
+
onImport={onImport}
|
|
202
|
+
/>
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
const importButton = screen.getByTestId('import-modal-import');
|
|
206
|
+
await user.click(importButton);
|
|
207
|
+
|
|
208
|
+
await waitFor(() => {
|
|
209
|
+
expect(onImport).toHaveBeenCalled();
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
describe('Focus Management', () => {
|
|
215
|
+
it('calls onStoreFocus when import modal opens', () => {
|
|
216
|
+
const onStoreFocus = vi.fn();
|
|
217
|
+
|
|
218
|
+
const { rerender } = render(
|
|
219
|
+
<DataTableModals
|
|
220
|
+
{...defaultProps}
|
|
221
|
+
showImportModal={false}
|
|
222
|
+
onStoreFocus={onStoreFocus}
|
|
223
|
+
/>
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
rerender(
|
|
227
|
+
<DataTableModals
|
|
228
|
+
{...defaultProps}
|
|
229
|
+
showImportModal={true}
|
|
230
|
+
onStoreFocus={onStoreFocus}
|
|
231
|
+
/>
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
expect(onStoreFocus).toHaveBeenCalledTimes(1);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it('calls onRestoreFocus when import modal closes', async () => {
|
|
238
|
+
const onRestoreFocus = vi.fn();
|
|
239
|
+
|
|
240
|
+
const { rerender } = render(
|
|
241
|
+
<DataTableModals
|
|
242
|
+
{...defaultProps}
|
|
243
|
+
showImportModal={true}
|
|
244
|
+
onRestoreFocus={onRestoreFocus}
|
|
245
|
+
/>
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
rerender(
|
|
249
|
+
<DataTableModals
|
|
250
|
+
{...defaultProps}
|
|
251
|
+
showImportModal={false}
|
|
252
|
+
onRestoreFocus={onRestoreFocus}
|
|
253
|
+
/>
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
await waitFor(() => {
|
|
257
|
+
expect(onRestoreFocus).toHaveBeenCalled();
|
|
258
|
+
}, { timeout: 200 });
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
it('does not call focus handlers when not provided', () => {
|
|
262
|
+
const { rerender } = render(
|
|
263
|
+
<DataTableModals
|
|
264
|
+
{...defaultProps}
|
|
265
|
+
showImportModal={false}
|
|
266
|
+
/>
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
expect(() => {
|
|
270
|
+
rerender(
|
|
271
|
+
<DataTableModals
|
|
272
|
+
{...defaultProps}
|
|
273
|
+
showImportModal={true}
|
|
274
|
+
/>
|
|
275
|
+
);
|
|
276
|
+
}).not.toThrow();
|
|
277
|
+
});
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
describe('Error Handling', () => {
|
|
281
|
+
it('handles import errors gracefully', async () => {
|
|
282
|
+
const user = userEvent.setup();
|
|
283
|
+
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
284
|
+
const onImport = vi.fn(() => {
|
|
285
|
+
throw new Error('Import failed');
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
render(
|
|
289
|
+
<DataTableModals
|
|
290
|
+
{...defaultProps}
|
|
291
|
+
showImportModal={true}
|
|
292
|
+
onImport={onImport}
|
|
293
|
+
/>
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
const importButton = screen.getByTestId('import-modal-import');
|
|
297
|
+
|
|
298
|
+
// DataTableModals re-throws errors to ImportModal, so we expect the error to propagate
|
|
299
|
+
// The error should be logged before being re-thrown
|
|
300
|
+
await user.click(importButton);
|
|
301
|
+
|
|
302
|
+
await waitFor(() => {
|
|
303
|
+
expect(consoleErrorSpy).toHaveBeenCalled();
|
|
304
|
+
expect(onImport).toHaveBeenCalled();
|
|
305
|
+
}, { timeout: 500 });
|
|
306
|
+
|
|
307
|
+
consoleErrorSpy.mockRestore();
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
it('handles async import errors', async () => {
|
|
311
|
+
const user = userEvent.setup();
|
|
312
|
+
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
313
|
+
const onImport = vi.fn(() => Promise.reject(new Error('Async import failed')));
|
|
314
|
+
|
|
315
|
+
render(
|
|
316
|
+
<DataTableModals
|
|
317
|
+
{...defaultProps}
|
|
318
|
+
showImportModal={true}
|
|
319
|
+
onImport={onImport}
|
|
320
|
+
/>
|
|
321
|
+
);
|
|
322
|
+
|
|
323
|
+
const importButton = screen.getByTestId('import-modal-import');
|
|
324
|
+
|
|
325
|
+
// DataTableModals re-throws errors to ImportModal, so we expect the error to propagate
|
|
326
|
+
// The error should be logged before being re-thrown
|
|
327
|
+
await user.click(importButton);
|
|
328
|
+
|
|
329
|
+
await waitFor(() => {
|
|
330
|
+
expect(consoleErrorSpy).toHaveBeenCalled();
|
|
331
|
+
expect(onImport).toHaveBeenCalled();
|
|
332
|
+
}, { timeout: 500 });
|
|
333
|
+
|
|
334
|
+
consoleErrorSpy.mockRestore();
|
|
335
|
+
});
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
describe('Edge Cases', () => {
|
|
339
|
+
it('handles empty columns array', async () => {
|
|
340
|
+
const user = userEvent.setup();
|
|
341
|
+
const onImport = vi.fn();
|
|
342
|
+
|
|
343
|
+
render(
|
|
344
|
+
<DataTableModals
|
|
345
|
+
{...defaultProps}
|
|
346
|
+
showImportModal={true}
|
|
347
|
+
onImport={onImport}
|
|
348
|
+
columns={[]}
|
|
349
|
+
/>
|
|
350
|
+
);
|
|
351
|
+
|
|
352
|
+
const importButton = screen.getByTestId('import-modal-import');
|
|
353
|
+
await user.click(importButton);
|
|
354
|
+
|
|
355
|
+
await waitFor(() => {
|
|
356
|
+
expect(onImport).toHaveBeenCalled();
|
|
357
|
+
});
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
it('handles columns with missing headers', async () => {
|
|
361
|
+
const user = userEvent.setup();
|
|
362
|
+
const onImport = vi.fn();
|
|
363
|
+
const columns = [
|
|
364
|
+
{ id: 'name', accessorKey: 'name' },
|
|
365
|
+
{ id: 'email', accessorKey: 'email' },
|
|
366
|
+
];
|
|
367
|
+
|
|
368
|
+
render(
|
|
369
|
+
<DataTableModals
|
|
370
|
+
{...defaultProps}
|
|
371
|
+
showImportModal={true}
|
|
372
|
+
onImport={onImport}
|
|
373
|
+
columns={columns}
|
|
374
|
+
/>
|
|
375
|
+
);
|
|
376
|
+
|
|
377
|
+
const importButton = screen.getByTestId('import-modal-import');
|
|
378
|
+
await user.click(importButton);
|
|
379
|
+
|
|
380
|
+
await waitFor(() => {
|
|
381
|
+
expect(onImport).toHaveBeenCalled();
|
|
382
|
+
});
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
it('handles missing onImport callback', async () => {
|
|
386
|
+
const user = userEvent.setup();
|
|
387
|
+
|
|
388
|
+
render(
|
|
389
|
+
<DataTableModals
|
|
390
|
+
{...defaultProps}
|
|
391
|
+
showImportModal={true}
|
|
392
|
+
onImport={undefined as any}
|
|
393
|
+
/>
|
|
394
|
+
);
|
|
395
|
+
|
|
396
|
+
const importButton = screen.getByTestId('import-modal-import');
|
|
397
|
+
|
|
398
|
+
// Should handle gracefully
|
|
399
|
+
expect(() => {
|
|
400
|
+
user.click(importButton);
|
|
401
|
+
}).not.toThrow();
|
|
402
|
+
});
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
describe('Data Transformation', () => {
|
|
406
|
+
it('transforms CSV data to match table column structure', async () => {
|
|
407
|
+
const user = userEvent.setup();
|
|
408
|
+
const onImport = vi.fn();
|
|
409
|
+
const columns = [
|
|
410
|
+
{ id: 'name', header: 'Name', accessorKey: 'name' },
|
|
411
|
+
{ id: 'email', header: 'Email Address', accessorKey: 'email' },
|
|
412
|
+
];
|
|
413
|
+
|
|
414
|
+
render(
|
|
415
|
+
<DataTableModals
|
|
416
|
+
{...defaultProps}
|
|
417
|
+
showImportModal={true}
|
|
418
|
+
onImport={onImport}
|
|
419
|
+
columns={columns}
|
|
420
|
+
/>
|
|
421
|
+
);
|
|
422
|
+
|
|
423
|
+
const importButton = screen.getByTestId('import-modal-import');
|
|
424
|
+
await user.click(importButton);
|
|
425
|
+
|
|
426
|
+
await waitFor(() => {
|
|
427
|
+
expect(onImport).toHaveBeenCalled();
|
|
428
|
+
});
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
it('handles case-insensitive column header matching', async () => {
|
|
432
|
+
const user = userEvent.setup();
|
|
433
|
+
const onImport = vi.fn();
|
|
434
|
+
const columns = [
|
|
435
|
+
{ id: 'name', header: 'Name', accessorKey: 'name' },
|
|
436
|
+
];
|
|
437
|
+
|
|
438
|
+
render(
|
|
439
|
+
<DataTableModals
|
|
440
|
+
{...defaultProps}
|
|
441
|
+
showImportModal={true}
|
|
442
|
+
onImport={onImport}
|
|
443
|
+
columns={columns}
|
|
444
|
+
/>
|
|
445
|
+
);
|
|
446
|
+
|
|
447
|
+
const importButton = screen.getByTestId('import-modal-import');
|
|
448
|
+
await user.click(importButton);
|
|
449
|
+
|
|
450
|
+
await waitFor(() => {
|
|
451
|
+
expect(onImport).toHaveBeenCalled();
|
|
452
|
+
});
|
|
453
|
+
});
|
|
454
|
+
});
|
|
455
|
+
});
|
|
456
|
+
|