@jmruthers/pace-core 0.5.191 → 0.6.1
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 +29 -0
- package/README.md +7 -1
- package/cursor-rules/00-pace-core-compliance.mdc +372 -0
- package/cursor-rules/01-standards-compliance.mdc +275 -0
- package/cursor-rules/02-project-structure.mdc +200 -0
- package/cursor-rules/03-solid-principles.mdc +341 -0
- package/cursor-rules/04-testing-standards.mdc +315 -0
- package/cursor-rules/05-bug-reports-and-features.mdc +246 -0
- package/cursor-rules/06-code-quality.mdc +392 -0
- package/cursor-rules/07-tech-stack-compliance.mdc +309 -0
- package/cursor-rules/CHANGELOG.md +101 -0
- package/cursor-rules/README.md +191 -0
- package/dist/{AuthService-CbP_utw2.d.ts → AuthService-DjnJHDtC.d.ts} +1 -0
- package/dist/{DataTable-Be6dH_dR.d.ts → DataTable-CH1U5Tpy.d.ts} +1 -1
- package/dist/{DataTable-WKRZD47S.js → DataTable-DQ7RSOHE.js} +8 -7
- package/dist/{PublicPageProvider-ULXC_u6U.d.ts → PublicPageProvider-ce4xlHYA.d.ts} +37 -156
- package/dist/{UnifiedAuthProvider-BYA9qB-o.d.ts → UnifiedAuthProvider-185Ih4dj.d.ts} +2 -0
- package/dist/{UnifiedAuthProvider-FTSG5XH7.js → UnifiedAuthProvider-ATAP5UTR.js} +3 -3
- package/dist/{api-IHKALJZD.js → api-N774RPUA.js} +2 -2
- package/dist/{chunk-6C4YBBJM.js → chunk-3QRJFVBR.js} +1 -1
- package/dist/chunk-3QRJFVBR.js.map +1 -0
- package/dist/{chunk-OETXORNB.js → chunk-3XTALGJF.js} +211 -136
- package/dist/chunk-3XTALGJF.js.map +1 -0
- package/dist/{chunk-6TQDD426.js → chunk-4N5C5XZU.js} +47 -228
- package/dist/chunk-4N5C5XZU.js.map +1 -0
- package/dist/{chunk-LOMZXPSN.js → chunk-4ZC4GX36.js} +47 -74
- package/dist/chunk-4ZC4GX36.js.map +1 -0
- package/dist/{chunk-6LTQQAT6.js → chunk-BYFSK72L.js} +357 -158
- package/dist/chunk-BYFSK72L.js.map +1 -0
- package/dist/{chunk-XYXSXPUK.js → chunk-EXUD6RNJ.js} +50 -10
- package/dist/chunk-EXUD6RNJ.js.map +1 -0
- package/dist/{chunk-VKB2CO4Z.js → chunk-GLK6VM3F.js} +244 -249
- package/dist/chunk-GLK6VM3F.js.map +1 -0
- package/dist/{chunk-HW3OVDUF.js → chunk-J36DSWQK.js} +1 -1
- package/dist/{chunk-HW3OVDUF.js.map → chunk-J36DSWQK.js.map} +1 -1
- package/dist/{chunk-XNYQOL3Z.js → chunk-JBKQ3SAO.js} +9 -18
- package/dist/chunk-JBKQ3SAO.js.map +1 -0
- package/dist/{chunk-ROXMHMY2.js → chunk-KNC55RTG.js} +13 -3
- package/dist/{chunk-ROXMHMY2.js.map → chunk-KNC55RTG.js.map} +1 -1
- package/dist/{chunk-QWWZ5CAQ.js → chunk-LXQLPRQ2.js} +2 -2
- package/dist/{chunk-ULHIJK66.js → chunk-T33XF5ZC.js} +255 -140
- package/dist/chunk-T33XF5ZC.js.map +1 -0
- package/dist/{chunk-VRGWKHDB.js → chunk-XM25TVIE.js} +100 -33
- package/dist/chunk-XM25TVIE.js.map +1 -0
- package/dist/components.d.ts +4 -4
- package/dist/components.js +9 -9
- package/dist/hooks.d.ts +6 -6
- package/dist/hooks.js +20 -25
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +11 -11
- package/dist/index.js +18 -21
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +3 -3
- package/dist/providers.js +2 -2
- package/dist/rbac/index.d.ts +2 -20
- package/dist/rbac/index.js +7 -9
- package/dist/{usePublicRouteParams-TZe0gy-4.d.ts → usePublicRouteParams-BJAlWfuJ.d.ts} +3 -3
- package/dist/{useToast-C8gR5ir4.d.ts → useToast-AyaT-x7p.d.ts} +2 -2
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +3 -3
- 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/Logger.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 +2 -2
- package/docs/api/classes/RBACAuditManager.md +2 -2
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +2 -2
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +1 -1
- package/docs/api/classes/SecureSupabaseClient.md +10 -10
- package/docs/api/classes/StorageUtils.md +1 -1
- package/docs/api/enums/FileCategory.md +1 -1
- package/docs/api/enums/LogLevel.md +1 -1
- package/docs/api/enums/RBACErrorCode.md +1 -1
- package/docs/api/enums/RPCFunction.md +1 -1
- package/docs/api/interfaces/AddressFieldProps.md +1 -1
- package/docs/api/interfaces/AddressFieldRef.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/AutocompleteOptions.md +1 -1
- package/docs/api/interfaces/AvatarProps.md +1 -1
- package/docs/api/interfaces/BadgeProps.md +1 -1
- package/docs/api/interfaces/ButtonProps.md +1 -1
- package/docs/api/interfaces/CalendarProps.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/ComplianceResult.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/DatabaseComplianceResult.md +1 -1
- package/docs/api/interfaces/DatabaseIssue.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/ExportColumn.md +1 -1
- package/docs/api/interfaces/ExportOptions.md +1 -1
- package/docs/api/interfaces/FileDisplayProps.md +24 -11
- 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/FormFieldProps.md +1 -1
- package/docs/api/interfaces/FormProps.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/LoggerConfig.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +1 -1
- package/docs/api/interfaces/NavigationAccessRecord.md +2 -2
- 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 +1 -1
- 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 +2 -2
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/ParsedAddress.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +4 -4
- package/docs/api/interfaces/ProgressProps.md +1 -1
- package/docs/api/interfaces/ProtectedRouteProps.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/QuickFix.md +1 -1
- package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
- package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
- package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
- package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
- package/docs/api/interfaces/RBACConfig.md +2 -2
- package/docs/api/interfaces/RBACContext.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
- package/docs/api/interfaces/RBACPerformanceMetrics.md +1 -1
- package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
- package/docs/api/interfaces/RBACPermissionCheckResult.md +2 -2
- package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
- package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
- package/docs/api/interfaces/RBACResult.md +1 -1
- package/docs/api/interfaces/RBACRoleGrantParams.md +2 -2
- package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
- package/docs/api/interfaces/RBACRoleRevokeParams.md +2 -2
- package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
- package/docs/api/interfaces/RBACRoleValidateParams.md +2 -2
- package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
- package/docs/api/interfaces/RBACRolesListParams.md +1 -1
- package/docs/api/interfaces/RBACRolesListResult.md +2 -2
- package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
- package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
- package/docs/api/interfaces/ResourcePermissions.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 +2 -2
- package/docs/api/interfaces/RouteConfig.md +2 -2
- package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
- package/docs/api/interfaces/SecureDataContextType.md +1 -1
- package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
- package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
- package/docs/api/interfaces/SetupIssue.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/TabsContentProps.md +1 -1
- package/docs/api/interfaces/TabsListProps.md +1 -1
- package/docs/api/interfaces/TabsProps.md +1 -1
- package/docs/api/interfaces/TabsTriggerProps.md +1 -1
- package/docs/api/interfaces/TextareaProps.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 +60 -38
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +13 -13
- package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
- package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +2 -2
- package/docs/api/interfaces/UsePublicEventLogoReturn.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 +2 -2
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeOptions.md +2 -2
- package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
- package/docs/api/interfaces/UseResourcePermissionsOptions.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 +194 -209
- package/docs/getting-started/cursor-rules.md +262 -0
- package/docs/getting-started/installation-guide.md +6 -1
- package/docs/getting-started/quick-start.md +6 -1
- package/docs/migration/MIGRATION_GUIDE.md +4 -4
- package/docs/migration/REACT_19_MIGRATION.md +227 -0
- package/docs/migration/database-changes-december-2025.md +2 -1
- package/docs/rbac/event-based-apps.md +124 -6
- package/docs/standards/README.md +39 -0
- package/docs/troubleshooting/migration.md +4 -4
- package/examples/PublicPages/PublicEventPage.tsx +1 -1
- package/package.json +11 -6
- package/scripts/audit-consuming-app.cjs +961 -0
- package/scripts/check-pace-core-compliance.cjs +315 -61
- package/scripts/install-cursor-rules.cjs +236 -0
- package/src/__tests__/helpers/test-providers.tsx +1 -1
- package/src/__tests__/helpers/test-utils.tsx +1 -1
- package/src/__tests__/rls-policies.test.ts +3 -1
- package/src/components/Badge/Badge.tsx +2 -4
- package/src/components/Button/Button.tsx +5 -4
- package/src/components/Calendar/Calendar.tsx +1 -1
- package/src/components/DataTable/DataTable.test.tsx +57 -93
- package/src/components/DataTable/DataTable.tsx +2 -2
- package/src/components/DataTable/__tests__/DataTable.default-state.test.tsx +172 -45
- package/src/components/DataTable/__tests__/DataTable.grouping-aggregation.test.tsx +121 -28
- package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +9 -8
- package/src/components/DataTable/__tests__/DataTableCore.test.tsx +20 -52
- package/src/components/DataTable/__tests__/a11y.basic.test.tsx +170 -34
- package/src/components/DataTable/__tests__/keyboard.test.tsx +75 -12
- package/src/components/DataTable/__tests__/pagination.modes.test.tsx +88 -16
- package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx +12 -12
- package/src/components/DataTable/components/AccessDeniedPage.tsx +1 -1
- package/src/components/DataTable/components/BulkOperationsDropdown.tsx +1 -1
- package/src/components/DataTable/components/DataTableCore.tsx +4 -7
- package/src/components/DataTable/components/DataTableModals.tsx +1 -1
- package/src/components/DataTable/components/EditableRow.tsx +1 -1
- package/src/components/DataTable/components/UnifiedTableBody.tsx +86 -17
- package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +23 -23
- package/src/components/DataTable/components/__tests__/EditableRow.test.tsx +11 -11
- package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +36 -36
- package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +27 -27
- package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +39 -39
- package/src/components/DataTable/components/__tests__/UnifiedTableBody.test.tsx +33 -33
- package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +29 -29
- package/src/components/DataTable/hooks/useColumnReordering.ts +2 -2
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +75 -10
- package/src/components/DataTable/hooks/useKeyboardNavigation.ts +2 -2
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +8 -14
- package/src/components/Dialog/Dialog.tsx +6 -5
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +1 -1
- package/src/components/EventSelector/EventSelector.tsx +1 -1
- package/src/components/FileDisplay/FileDisplay.test.tsx +4 -3
- package/src/components/FileDisplay/FileDisplay.tsx +16 -4
- package/src/components/Footer/Footer.tsx +1 -1
- package/src/components/Form/Form.test.tsx +36 -15
- package/src/components/Form/Form.tsx +30 -26
- package/src/components/Header/Header.tsx +1 -1
- package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +40 -40
- package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +1 -1
- package/src/components/Input/Input.tsx +28 -30
- package/src/components/Label/Label.tsx +1 -1
- package/src/components/LoadingSpinner/LoadingSpinner.tsx +1 -1
- package/src/components/LoginForm/LoginForm.test.tsx +42 -42
- package/src/components/LoginForm/LoginForm.tsx +8 -8
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +6 -4
- package/src/components/NavigationMenu/NavigationMenu.tsx +2 -11
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +0 -1
- package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +1 -1
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +75 -52
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +98 -69
- package/src/components/PaceAppLayout/README.md +1 -1
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +1 -8
- package/src/components/PasswordChange/PasswordChangeForm.test.tsx +33 -33
- package/src/components/PasswordChange/PasswordChangeForm.tsx +1 -1
- package/src/components/Progress/Progress.tsx +1 -1
- package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +5 -9
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +0 -1
- package/src/components/PublicLayout/PublicPageLayout.tsx +1 -1
- package/src/components/PublicLayout/PublicPageProvider.tsx +0 -1
- package/src/components/Select/Select.tsx +33 -22
- package/src/components/SessionRestorationLoader/SessionRestorationLoader.tsx +1 -1
- package/src/components/Table/Table.tsx +1 -1
- package/src/components/Textarea/Textarea.tsx +27 -29
- package/src/components/Toast/Toast.tsx +1 -1
- package/src/components/Tooltip/Tooltip.tsx +1 -1
- package/src/components/UserMenu/UserMenu.tsx +1 -1
- package/src/hooks/__tests__/hooks.integration.test.tsx +80 -55
- package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +14 -7
- package/src/hooks/__tests__/useStorage.unit.test.ts +36 -36
- package/src/hooks/public/usePublicEvent.ts +1 -1
- package/src/hooks/public/usePublicEventLogo.ts +1 -1
- package/src/hooks/public/usePublicRouteParams.ts +1 -1
- package/src/hooks/services/useAuthService.ts +21 -3
- package/src/hooks/services/useEventService.ts +21 -3
- package/src/hooks/services/useInactivityService.ts +21 -3
- package/src/hooks/services/useOrganisationService.ts +21 -3
- package/src/hooks/useDataTableState.ts +8 -18
- package/src/hooks/useFileDisplay.ts +10 -17
- package/src/hooks/useFocusManagement.ts +2 -2
- package/src/hooks/useFocusTrap.ts +4 -4
- package/src/hooks/useFormDialog.ts +8 -7
- package/src/hooks/useInactivityTracker.ts +1 -1
- package/src/hooks/usePermissionCache.ts +1 -1
- package/src/hooks/useSecureDataAccess.test.ts +16 -9
- package/src/hooks/useSecureDataAccess.ts +22 -6
- package/src/hooks/useToast.ts +2 -2
- package/src/providers/__tests__/OrganisationProvider.test.tsx +57 -13
- package/src/providers/__tests__/ProviderLifecycle.test.tsx +21 -6
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +10 -10
- package/src/providers/services/EventServiceProvider.tsx +0 -8
- package/src/providers/services/UnifiedAuthProvider.tsx +196 -46
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +13 -3
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +34 -40
- package/src/rbac/__tests__/isSuperAdmin.real.test.ts +82 -0
- package/src/rbac/adapters.tsx +3 -22
- package/src/rbac/api.test.ts +2 -2
- package/src/rbac/api.ts +7 -1
- package/src/rbac/components/EnhancedNavigationMenu.tsx +3 -16
- package/src/rbac/components/NavigationGuard.tsx +2 -11
- package/src/rbac/components/NavigationProvider.tsx +1 -2
- package/src/rbac/components/PagePermissionGuard.tsx +1 -1
- package/src/rbac/components/PagePermissionProvider.tsx +1 -1
- package/src/rbac/components/PermissionEnforcer.tsx +46 -13
- package/src/rbac/components/RoleBasedRouter.tsx +1 -1
- package/src/rbac/components/SecureDataProvider.tsx +1 -2
- package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +7 -43
- package/src/rbac/components/__tests__/NavigationGuard.test.tsx +4 -11
- package/src/rbac/components/__tests__/NavigationProvider.test.tsx +3 -3
- package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +1 -1
- package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +1 -1
- package/src/rbac/engine.ts +14 -2
- package/src/rbac/hooks/index.ts +0 -3
- package/src/rbac/hooks/usePermissions.ts +51 -11
- package/src/rbac/hooks/useRBAC.ts +3 -13
- package/src/rbac/hooks/useResolvedScope.test.ts +75 -54
- package/src/rbac/hooks/useResolvedScope.ts +58 -33
- package/src/rbac/hooks/useSecureSupabase.ts +4 -9
- package/src/rbac/secureClient.ts +43 -0
- package/src/services/EventService.ts +4 -57
- package/src/services/InactivityService.ts +127 -34
- package/src/services/OrganisationService.ts +68 -10
- package/src/utils/security/secureDataAccess.test.ts +31 -20
- package/src/utils/security/secureDataAccess.ts +4 -3
- package/dist/chunk-6C4YBBJM.js.map +0 -1
- package/dist/chunk-6LTQQAT6.js.map +0 -1
- package/dist/chunk-6TQDD426.js.map +0 -1
- package/dist/chunk-LOMZXPSN.js.map +0 -1
- package/dist/chunk-OETXORNB.js.map +0 -1
- package/dist/chunk-ULHIJK66.js.map +0 -1
- package/dist/chunk-VKB2CO4Z.js.map +0 -1
- package/dist/chunk-VRGWKHDB.js.map +0 -1
- package/dist/chunk-XNYQOL3Z.js.map +0 -1
- package/dist/chunk-XYXSXPUK.js.map +0 -1
- package/scripts/check-pace-core-compliance.js +0 -512
- package/src/rbac/hooks/useSuperAdminBypass.ts +0 -126
- package/src/utils/context/superAdminOverride.ts +0 -58
- /package/dist/{DataTable-WKRZD47S.js.map → DataTable-DQ7RSOHE.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-FTSG5XH7.js.map → UnifiedAuthProvider-ATAP5UTR.js.map} +0 -0
- /package/dist/{api-IHKALJZD.js.map → api-N774RPUA.js.map} +0 -0
- /package/dist/{chunk-QWWZ5CAQ.js.map → chunk-LXQLPRQ2.js.map} +0 -0
- /package/examples/{rbac → RBAC}/CompleteRBACExample.tsx +0 -0
- /package/examples/{rbac → RBAC}/EventBasedApp.tsx +0 -0
- /package/examples/{rbac → RBAC}/PermissionExample.tsx +0 -0
- /package/examples/{rbac → RBAC}/index.ts +0 -0
|
@@ -78,7 +78,7 @@ const createMockRow = (overrides: Partial<Row<TestData>> = {}): Row<TestData> =>
|
|
|
78
78
|
} as unknown as Row<TestData>);
|
|
79
79
|
|
|
80
80
|
describe('[component] GroupHeader', () => {
|
|
81
|
-
const
|
|
81
|
+
const baseProps = {
|
|
82
82
|
row: createMockRow(),
|
|
83
83
|
groupByColumn: 'category',
|
|
84
84
|
isExpanded: false,
|
|
@@ -96,40 +96,40 @@ describe('[component] GroupHeader', () => {
|
|
|
96
96
|
|
|
97
97
|
describe('Rendering', () => {
|
|
98
98
|
it('renders group header container', () => {
|
|
99
|
-
render(<GroupHeader {...
|
|
99
|
+
render(<GroupHeader {...baseProps} />);
|
|
100
100
|
|
|
101
101
|
const container = screen.getByText(/Category A/i).closest('div');
|
|
102
102
|
expect(container).toBeInTheDocument();
|
|
103
103
|
});
|
|
104
104
|
|
|
105
105
|
it('displays group value', () => {
|
|
106
|
-
render(<GroupHeader {...
|
|
106
|
+
render(<GroupHeader {...baseProps} />);
|
|
107
107
|
|
|
108
108
|
expect(screen.getByText(/Category A/i)).toBeInTheDocument();
|
|
109
109
|
});
|
|
110
110
|
|
|
111
111
|
it('displays subRowsCount', () => {
|
|
112
|
-
render(<GroupHeader {...
|
|
112
|
+
render(<GroupHeader {...baseProps} subRowsCount={10} />);
|
|
113
113
|
|
|
114
114
|
expect(screen.getByText(/\(10 items\)/i)).toBeInTheDocument();
|
|
115
115
|
});
|
|
116
116
|
|
|
117
117
|
it('renders ChevronRight icon when not expanded', () => {
|
|
118
|
-
render(<GroupHeader {...
|
|
118
|
+
render(<GroupHeader {...baseProps} isExpanded={false} />);
|
|
119
119
|
|
|
120
120
|
expect(screen.getByTestId('chevron-right-icon')).toBeInTheDocument();
|
|
121
121
|
expect(screen.queryByTestId('chevron-down-icon')).not.toBeInTheDocument();
|
|
122
122
|
});
|
|
123
123
|
|
|
124
124
|
it('renders ChevronDown icon when expanded', () => {
|
|
125
|
-
render(<GroupHeader {...
|
|
125
|
+
render(<GroupHeader {...baseProps} isExpanded={true} />);
|
|
126
126
|
|
|
127
127
|
expect(screen.getByTestId('chevron-down-icon')).toBeInTheDocument();
|
|
128
128
|
expect(screen.queryByTestId('chevron-right-icon')).not.toBeInTheDocument();
|
|
129
129
|
});
|
|
130
130
|
|
|
131
131
|
it('renders toggle button', () => {
|
|
132
|
-
render(<GroupHeader {...
|
|
132
|
+
render(<GroupHeader {...baseProps} />);
|
|
133
133
|
|
|
134
134
|
const button = screen.getByRole('button');
|
|
135
135
|
expect(button).toBeInTheDocument();
|
|
@@ -147,7 +147,7 @@ describe('[component] GroupHeader', () => {
|
|
|
147
147
|
|
|
148
148
|
render(
|
|
149
149
|
<GroupHeader
|
|
150
|
-
{...
|
|
150
|
+
{...baseProps}
|
|
151
151
|
row={row}
|
|
152
152
|
/>
|
|
153
153
|
);
|
|
@@ -165,7 +165,7 @@ describe('[component] GroupHeader', () => {
|
|
|
165
165
|
|
|
166
166
|
render(
|
|
167
167
|
<GroupHeader
|
|
168
|
-
{...
|
|
168
|
+
{...baseProps}
|
|
169
169
|
row={row}
|
|
170
170
|
/>
|
|
171
171
|
);
|
|
@@ -183,7 +183,7 @@ describe('[component] GroupHeader', () => {
|
|
|
183
183
|
|
|
184
184
|
render(
|
|
185
185
|
<GroupHeader
|
|
186
|
-
{...
|
|
186
|
+
{...baseProps}
|
|
187
187
|
row={row}
|
|
188
188
|
/>
|
|
189
189
|
);
|
|
@@ -201,7 +201,7 @@ describe('[component] GroupHeader', () => {
|
|
|
201
201
|
|
|
202
202
|
render(
|
|
203
203
|
<GroupHeader
|
|
204
|
-
{...
|
|
204
|
+
{...baseProps}
|
|
205
205
|
row={row}
|
|
206
206
|
/>
|
|
207
207
|
);
|
|
@@ -212,19 +212,19 @@ describe('[component] GroupHeader', () => {
|
|
|
212
212
|
|
|
213
213
|
describe('SubRows Count Display', () => {
|
|
214
214
|
it('displays correct count for single item', () => {
|
|
215
|
-
render(<GroupHeader {...
|
|
215
|
+
render(<GroupHeader {...baseProps} subRowsCount={1} />);
|
|
216
216
|
|
|
217
217
|
expect(screen.getByText(/\(1 items\)/i)).toBeInTheDocument();
|
|
218
218
|
});
|
|
219
219
|
|
|
220
220
|
it('displays correct count for multiple items', () => {
|
|
221
|
-
render(<GroupHeader {...
|
|
221
|
+
render(<GroupHeader {...baseProps} subRowsCount={25} />);
|
|
222
222
|
|
|
223
223
|
expect(screen.getByText(/\(25 items\)/i)).toBeInTheDocument();
|
|
224
224
|
});
|
|
225
225
|
|
|
226
226
|
it('displays zero count', () => {
|
|
227
|
-
render(<GroupHeader {...
|
|
227
|
+
render(<GroupHeader {...baseProps} subRowsCount={0} />);
|
|
228
228
|
|
|
229
229
|
expect(screen.getByText(/\(0 items\)/i)).toBeInTheDocument();
|
|
230
230
|
});
|
|
@@ -237,7 +237,7 @@ describe('[component] GroupHeader', () => {
|
|
|
237
237
|
|
|
238
238
|
render(
|
|
239
239
|
<GroupHeader
|
|
240
|
-
{...
|
|
240
|
+
{...baseProps}
|
|
241
241
|
onToggle={handleToggle}
|
|
242
242
|
/>
|
|
243
243
|
);
|
|
@@ -254,7 +254,7 @@ describe('[component] GroupHeader', () => {
|
|
|
254
254
|
|
|
255
255
|
render(
|
|
256
256
|
<GroupHeader
|
|
257
|
-
{...
|
|
257
|
+
{...baseProps}
|
|
258
258
|
onToggle={handleToggle}
|
|
259
259
|
/>
|
|
260
260
|
);
|
|
@@ -270,7 +270,7 @@ describe('[component] GroupHeader', () => {
|
|
|
270
270
|
it('updates icon when expanded state changes', () => {
|
|
271
271
|
const { rerender } = render(
|
|
272
272
|
<GroupHeader
|
|
273
|
-
{...
|
|
273
|
+
{...baseProps}
|
|
274
274
|
isExpanded={false}
|
|
275
275
|
/>
|
|
276
276
|
);
|
|
@@ -279,7 +279,7 @@ describe('[component] GroupHeader', () => {
|
|
|
279
279
|
|
|
280
280
|
rerender(
|
|
281
281
|
<GroupHeader
|
|
282
|
-
{...
|
|
282
|
+
{...baseProps}
|
|
283
283
|
isExpanded={true}
|
|
284
284
|
/>
|
|
285
285
|
);
|
|
@@ -299,7 +299,7 @@ describe('[component] GroupHeader', () => {
|
|
|
299
299
|
|
|
300
300
|
render(
|
|
301
301
|
<GroupHeader
|
|
302
|
-
{...
|
|
302
|
+
{...baseProps}
|
|
303
303
|
row={row}
|
|
304
304
|
/>
|
|
305
305
|
);
|
|
@@ -308,7 +308,7 @@ describe('[component] GroupHeader', () => {
|
|
|
308
308
|
});
|
|
309
309
|
|
|
310
310
|
it('handles very large subRowsCount', () => {
|
|
311
|
-
render(<GroupHeader {...
|
|
311
|
+
render(<GroupHeader {...baseProps} subRowsCount={999999} />);
|
|
312
312
|
|
|
313
313
|
expect(screen.getByText(/\(999999 items\)/i)).toBeInTheDocument();
|
|
314
314
|
});
|
|
@@ -323,7 +323,7 @@ describe('[component] GroupHeader', () => {
|
|
|
323
323
|
|
|
324
324
|
render(
|
|
325
325
|
<GroupHeader
|
|
326
|
-
{...
|
|
326
|
+
{...baseProps}
|
|
327
327
|
row={row}
|
|
328
328
|
groupByColumn="status"
|
|
329
329
|
/>
|
|
@@ -342,7 +342,7 @@ describe('[component] GroupHeader', () => {
|
|
|
342
342
|
|
|
343
343
|
render(
|
|
344
344
|
<GroupHeader
|
|
345
|
-
{...
|
|
345
|
+
{...baseProps}
|
|
346
346
|
row={row}
|
|
347
347
|
/>
|
|
348
348
|
);
|
|
@@ -353,21 +353,21 @@ describe('[component] GroupHeader', () => {
|
|
|
353
353
|
|
|
354
354
|
describe('Styling', () => {
|
|
355
355
|
it('has proper container styling', () => {
|
|
356
|
-
render(<GroupHeader {...
|
|
356
|
+
render(<GroupHeader {...baseProps} />);
|
|
357
357
|
|
|
358
358
|
const container = screen.getByText(/Category A/i).closest('div');
|
|
359
359
|
expect(container).toHaveClass('flex', 'items-center');
|
|
360
360
|
});
|
|
361
361
|
|
|
362
362
|
it('applies background and border styling', () => {
|
|
363
|
-
render(<GroupHeader {...
|
|
363
|
+
render(<GroupHeader {...baseProps} />);
|
|
364
364
|
|
|
365
365
|
const container = screen.getByText(/Category A/i).closest('div');
|
|
366
366
|
expect(container).toHaveClass('bg-muted/50', 'border-b');
|
|
367
367
|
});
|
|
368
368
|
|
|
369
369
|
it('applies font styling', () => {
|
|
370
|
-
render(<GroupHeader {...
|
|
370
|
+
render(<GroupHeader {...baseProps} />);
|
|
371
371
|
|
|
372
372
|
const container = screen.getByText(/Category A/i).closest('div');
|
|
373
373
|
expect(container).toHaveClass('font-medium');
|
|
@@ -376,14 +376,14 @@ describe('[component] GroupHeader', () => {
|
|
|
376
376
|
|
|
377
377
|
describe('Accessibility', () => {
|
|
378
378
|
it('has accessible button for toggling', () => {
|
|
379
|
-
render(<GroupHeader {...
|
|
379
|
+
render(<GroupHeader {...baseProps} />);
|
|
380
380
|
|
|
381
381
|
const button = screen.getByRole('button');
|
|
382
382
|
expect(button).toBeInTheDocument();
|
|
383
383
|
});
|
|
384
384
|
|
|
385
385
|
it('displays group information clearly', () => {
|
|
386
|
-
render(<GroupHeader {...
|
|
386
|
+
render(<GroupHeader {...baseProps} />);
|
|
387
387
|
|
|
388
388
|
expect(screen.getByText(/Category A/i)).toBeInTheDocument();
|
|
389
389
|
expect(screen.getByText(/\(5 items\)/i)).toBeInTheDocument();
|
|
@@ -89,7 +89,7 @@ vi.mock('../../../utils/core/logger', () => ({
|
|
|
89
89
|
}));
|
|
90
90
|
|
|
91
91
|
describe('[component] ImportModal', () => {
|
|
92
|
-
const
|
|
92
|
+
const baseProps = {
|
|
93
93
|
isOpen: true,
|
|
94
94
|
onClose: vi.fn(),
|
|
95
95
|
onImport: vi.fn(),
|
|
@@ -133,13 +133,13 @@ describe('[component] ImportModal', () => {
|
|
|
133
133
|
describe('Rendering', () => {
|
|
134
134
|
it('returns null when modal is closed', () => {
|
|
135
135
|
const { container } = render(
|
|
136
|
-
<ImportModal {...
|
|
136
|
+
<ImportModal {...baseProps} isOpen={false} />
|
|
137
137
|
);
|
|
138
138
|
expect(container.firstChild).toBeNull();
|
|
139
139
|
});
|
|
140
140
|
|
|
141
141
|
it('renders modal when open', () => {
|
|
142
|
-
render(<ImportModal {...
|
|
142
|
+
render(<ImportModal {...baseProps} />);
|
|
143
143
|
|
|
144
144
|
// Dialog renders with role="dialog" from Radix UI
|
|
145
145
|
expect(screen.getByRole('dialog')).toBeInTheDocument();
|
|
@@ -148,7 +148,7 @@ describe('[component] ImportModal', () => {
|
|
|
148
148
|
});
|
|
149
149
|
|
|
150
150
|
it('renders default title', () => {
|
|
151
|
-
render(<ImportModal {...
|
|
151
|
+
render(<ImportModal {...baseProps} />);
|
|
152
152
|
|
|
153
153
|
// DialogTitle renders as h2
|
|
154
154
|
expect(screen.getByRole('heading', { name: 'Import Data' })).toBeInTheDocument();
|
|
@@ -157,7 +157,7 @@ describe('[component] ImportModal', () => {
|
|
|
157
157
|
it('renders custom title from config', () => {
|
|
158
158
|
render(
|
|
159
159
|
<ImportModal
|
|
160
|
-
{...
|
|
160
|
+
{...baseProps}
|
|
161
161
|
config={{ title: 'Custom Import Title' }}
|
|
162
162
|
/>
|
|
163
163
|
);
|
|
@@ -167,7 +167,7 @@ describe('[component] ImportModal', () => {
|
|
|
167
167
|
});
|
|
168
168
|
|
|
169
169
|
it('renders default description', () => {
|
|
170
|
-
render(<ImportModal {...
|
|
170
|
+
render(<ImportModal {...baseProps} />);
|
|
171
171
|
|
|
172
172
|
// DialogDescription renders as p
|
|
173
173
|
expect(screen.getByText('Upload a CSV file to import multiple records at once.')).toBeInTheDocument();
|
|
@@ -176,7 +176,7 @@ describe('[component] ImportModal', () => {
|
|
|
176
176
|
it('renders custom description from config', () => {
|
|
177
177
|
render(
|
|
178
178
|
<ImportModal
|
|
179
|
-
{...
|
|
179
|
+
{...baseProps}
|
|
180
180
|
config={{ description: 'Custom description' }}
|
|
181
181
|
/>
|
|
182
182
|
);
|
|
@@ -186,20 +186,20 @@ describe('[component] ImportModal', () => {
|
|
|
186
186
|
});
|
|
187
187
|
|
|
188
188
|
it('renders file upload area', () => {
|
|
189
|
-
render(<ImportModal {...
|
|
189
|
+
render(<ImportModal {...baseProps} />);
|
|
190
190
|
|
|
191
191
|
expect(screen.getByText(/choose a csv file/i)).toBeInTheDocument();
|
|
192
192
|
expect(screen.getByRole('button', { name: /select file/i })).toBeInTheDocument();
|
|
193
193
|
});
|
|
194
194
|
|
|
195
195
|
it('renders cancel button', () => {
|
|
196
|
-
render(<ImportModal {...
|
|
196
|
+
render(<ImportModal {...baseProps} />);
|
|
197
197
|
|
|
198
198
|
expect(screen.getByRole('button', { name: /cancel/i })).toBeInTheDocument();
|
|
199
199
|
});
|
|
200
200
|
|
|
201
201
|
it('renders import button', () => {
|
|
202
|
-
render(<ImportModal {...
|
|
202
|
+
render(<ImportModal {...baseProps} />);
|
|
203
203
|
|
|
204
204
|
expect(screen.getByRole('button', { name: /import/i })).toBeInTheDocument();
|
|
205
205
|
});
|
|
@@ -211,7 +211,7 @@ describe('[component] ImportModal', () => {
|
|
|
211
211
|
const csvContent = 'name,email\nJohn,john@example.com';
|
|
212
212
|
const file = createCSVFile(csvContent);
|
|
213
213
|
|
|
214
|
-
render(<ImportModal {...
|
|
214
|
+
render(<ImportModal {...baseProps} />);
|
|
215
215
|
|
|
216
216
|
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
217
217
|
await user.upload(fileInput, file);
|
|
@@ -226,7 +226,7 @@ describe('[component] ImportModal', () => {
|
|
|
226
226
|
const csvContent = 'name,email\nJohn,john@example.com';
|
|
227
227
|
const file = createCSVFile(csvContent);
|
|
228
228
|
|
|
229
|
-
const { rerender } = render(<ImportModal {...
|
|
229
|
+
const { rerender } = render(<ImportModal {...baseProps} />);
|
|
230
230
|
|
|
231
231
|
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
232
232
|
await user.upload(fileInput, file);
|
|
@@ -235,8 +235,8 @@ describe('[component] ImportModal', () => {
|
|
|
235
235
|
expect(screen.getByText(`Selected: ${file.name}`)).toBeInTheDocument();
|
|
236
236
|
});
|
|
237
237
|
|
|
238
|
-
rerender(<ImportModal {...
|
|
239
|
-
rerender(<ImportModal {...
|
|
238
|
+
rerender(<ImportModal {...baseProps} isOpen={false} />);
|
|
239
|
+
rerender(<ImportModal {...baseProps} isOpen={true} />);
|
|
240
240
|
|
|
241
241
|
// File should be reset
|
|
242
242
|
expect(screen.queryByText(`Selected: ${file.name}`)).not.toBeInTheDocument();
|
|
@@ -249,7 +249,7 @@ describe('[component] ImportModal', () => {
|
|
|
249
249
|
const csvContent = 'name,email\nJohn,john@example.com\nJane,jane@example.com';
|
|
250
250
|
const file = createCSVFile(csvContent);
|
|
251
251
|
|
|
252
|
-
render(<ImportModal {...
|
|
252
|
+
render(<ImportModal {...baseProps} />);
|
|
253
253
|
|
|
254
254
|
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
255
255
|
await user.upload(fileInput, file);
|
|
@@ -272,7 +272,7 @@ describe('[component] ImportModal', () => {
|
|
|
272
272
|
const csvContent = 'name,email\nJohn,john@example.com\nJane,jane@example.com';
|
|
273
273
|
const file = createCSVFile(csvContent);
|
|
274
274
|
|
|
275
|
-
render(<ImportModal {...
|
|
275
|
+
render(<ImportModal {...baseProps} />);
|
|
276
276
|
|
|
277
277
|
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
278
278
|
await user.upload(fileInput, file);
|
|
@@ -295,7 +295,7 @@ describe('[component] ImportModal', () => {
|
|
|
295
295
|
const csvContent = 'name,email\nJohn,john@example.com\nJane,jane@example.com\nBob,bob@example.com';
|
|
296
296
|
const file = createCSVFile(csvContent);
|
|
297
297
|
|
|
298
|
-
render(<ImportModal {...
|
|
298
|
+
render(<ImportModal {...baseProps} />);
|
|
299
299
|
|
|
300
300
|
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
301
301
|
await user.upload(fileInput, file);
|
|
@@ -317,7 +317,7 @@ describe('[component] ImportModal', () => {
|
|
|
317
317
|
const csvContent = 'name,email\n"John Doe","john@example.com"\n"Jane Smith","jane@example.com"';
|
|
318
318
|
const file = createCSVFile(csvContent);
|
|
319
319
|
|
|
320
|
-
render(<ImportModal {...
|
|
320
|
+
render(<ImportModal {...baseProps} />);
|
|
321
321
|
|
|
322
322
|
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
323
323
|
await user.upload(fileInput, file);
|
|
@@ -339,7 +339,7 @@ describe('[component] ImportModal', () => {
|
|
|
339
339
|
const csvContent = 'name,description\nJohn,"Description, with comma"\nJane,"Another, description"';
|
|
340
340
|
const file = createCSVFile(csvContent);
|
|
341
341
|
|
|
342
|
-
render(<ImportModal {...
|
|
342
|
+
render(<ImportModal {...baseProps} />);
|
|
343
343
|
|
|
344
344
|
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
345
345
|
await user.upload(fileInput, file);
|
|
@@ -365,7 +365,7 @@ describe('[component] ImportModal', () => {
|
|
|
365
365
|
const invalidContent = 'name,email';
|
|
366
366
|
const file = createCSVFile(invalidContent);
|
|
367
367
|
|
|
368
|
-
render(<ImportModal {...
|
|
368
|
+
render(<ImportModal {...baseProps} />);
|
|
369
369
|
|
|
370
370
|
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
371
371
|
await user.upload(fileInput, file);
|
|
@@ -384,7 +384,7 @@ describe('[component] ImportModal', () => {
|
|
|
384
384
|
const csvContent = 'name,email';
|
|
385
385
|
const file = createCSVFile(csvContent);
|
|
386
386
|
|
|
387
|
-
render(<ImportModal {...
|
|
387
|
+
render(<ImportModal {...baseProps} />);
|
|
388
388
|
|
|
389
389
|
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
390
390
|
await user.upload(fileInput, file);
|
|
@@ -399,7 +399,7 @@ describe('[component] ImportModal', () => {
|
|
|
399
399
|
const invalidFile = createCSVFile('invalid');
|
|
400
400
|
const validFile = createCSVFile('name,email\nJohn,john@example.com');
|
|
401
401
|
|
|
402
|
-
render(<ImportModal {...
|
|
402
|
+
render(<ImportModal {...baseProps} />);
|
|
403
403
|
|
|
404
404
|
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
405
405
|
await user.upload(fileInput, invalidFile);
|
|
@@ -425,7 +425,7 @@ describe('[component] ImportModal', () => {
|
|
|
425
425
|
const csvContent = 'name,email\nJohn,john@example.com';
|
|
426
426
|
const file = createCSVFile(csvContent);
|
|
427
427
|
|
|
428
|
-
render(<ImportModal {...
|
|
428
|
+
render(<ImportModal {...baseProps} onImport={onImport} />);
|
|
429
429
|
|
|
430
430
|
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
431
431
|
await user.upload(fileInput, file);
|
|
@@ -457,7 +457,7 @@ describe('[component] ImportModal', () => {
|
|
|
457
457
|
});
|
|
458
458
|
|
|
459
459
|
it('disables import button when no file is selected', () => {
|
|
460
|
-
render(<ImportModal {...
|
|
460
|
+
render(<ImportModal {...baseProps} />);
|
|
461
461
|
|
|
462
462
|
const importButton = screen.getByRole('button', { name: /import/i });
|
|
463
463
|
expect(importButton).toBeDisabled();
|
|
@@ -469,7 +469,7 @@ describe('[component] ImportModal', () => {
|
|
|
469
469
|
const csvContent = 'name,email\nJohn,john@example.com';
|
|
470
470
|
const file = createCSVFile(csvContent);
|
|
471
471
|
|
|
472
|
-
render(<ImportModal {...
|
|
472
|
+
render(<ImportModal {...baseProps} onImport={onImport} />);
|
|
473
473
|
|
|
474
474
|
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
475
475
|
await user.upload(fileInput, file);
|
|
@@ -498,7 +498,7 @@ describe('[component] ImportModal', () => {
|
|
|
498
498
|
const csvContent = 'name,email\nJohn,john@example.com';
|
|
499
499
|
const file = createCSVFile(csvContent);
|
|
500
500
|
|
|
501
|
-
render(<ImportModal {...
|
|
501
|
+
render(<ImportModal {...baseProps} onImport={onImport} />);
|
|
502
502
|
|
|
503
503
|
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
504
504
|
await user.upload(fileInput, file);
|
|
@@ -529,7 +529,7 @@ describe('[component] ImportModal', () => {
|
|
|
529
529
|
const csvContent = 'name,email\nJohn,john@example.com';
|
|
530
530
|
const file = createCSVFile(csvContent);
|
|
531
531
|
|
|
532
|
-
render(<ImportModal {...
|
|
532
|
+
render(<ImportModal {...baseProps} onClose={onClose} />);
|
|
533
533
|
|
|
534
534
|
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
535
535
|
await user.upload(fileInput, file);
|
|
@@ -558,7 +558,7 @@ describe('[component] ImportModal', () => {
|
|
|
558
558
|
const user = userEvent.setup();
|
|
559
559
|
const onClose = vi.fn();
|
|
560
560
|
|
|
561
|
-
render(<ImportModal {...
|
|
561
|
+
render(<ImportModal {...baseProps} onClose={onClose} />);
|
|
562
562
|
|
|
563
563
|
const cancelButton = screen.getByRole('button', { name: /cancel/i });
|
|
564
564
|
await user.click(cancelButton);
|
|
@@ -571,7 +571,7 @@ describe('[component] ImportModal', () => {
|
|
|
571
571
|
const csvContent = 'name,email\nJohn,john@example.com';
|
|
572
572
|
const file = createCSVFile(csvContent);
|
|
573
573
|
|
|
574
|
-
const { rerender } = render(<ImportModal {...
|
|
574
|
+
const { rerender } = render(<ImportModal {...baseProps} />);
|
|
575
575
|
|
|
576
576
|
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
577
577
|
await user.upload(fileInput, file);
|
|
@@ -586,8 +586,8 @@ describe('[component] ImportModal', () => {
|
|
|
586
586
|
expect(screen.getByText('John')).toBeInTheDocument();
|
|
587
587
|
}, { timeout: 2000 });
|
|
588
588
|
|
|
589
|
-
rerender(<ImportModal {...
|
|
590
|
-
rerender(<ImportModal {...
|
|
589
|
+
rerender(<ImportModal {...baseProps} isOpen={false} />);
|
|
590
|
+
rerender(<ImportModal {...baseProps} isOpen={true} />);
|
|
591
591
|
|
|
592
592
|
// State should be reset - wait for modal to reopen
|
|
593
593
|
await waitFor(() => {
|
|
@@ -600,7 +600,7 @@ describe('[component] ImportModal', () => {
|
|
|
600
600
|
it('uses custom button texts from config', () => {
|
|
601
601
|
render(
|
|
602
602
|
<ImportModal
|
|
603
|
-
{...
|
|
603
|
+
{...baseProps}
|
|
604
604
|
config={{
|
|
605
605
|
selectFileButtonText: 'Browse Files',
|
|
606
606
|
importButtonText: 'Import Data',
|
|
@@ -624,7 +624,7 @@ describe('[component] ImportModal', () => {
|
|
|
624
624
|
|
|
625
625
|
render(
|
|
626
626
|
<ImportModal
|
|
627
|
-
{...
|
|
627
|
+
{...baseProps}
|
|
628
628
|
config={{ previewHeaderText: 'Data Preview' }}
|
|
629
629
|
/>
|
|
630
630
|
);
|
|
@@ -645,7 +645,7 @@ describe('[component] ImportModal', () => {
|
|
|
645
645
|
|
|
646
646
|
render(
|
|
647
647
|
<ImportModal
|
|
648
|
-
{...
|
|
648
|
+
{...baseProps}
|
|
649
649
|
config={{ totalRowsText: 'Found {count} records' }}
|
|
650
650
|
/>
|
|
651
651
|
);
|
|
@@ -670,7 +670,7 @@ describe('[component] ImportModal', () => {
|
|
|
670
670
|
const user = userEvent.setup();
|
|
671
671
|
const file = createCSVFile('');
|
|
672
672
|
|
|
673
|
-
render(<ImportModal {...
|
|
673
|
+
render(<ImportModal {...baseProps} />);
|
|
674
674
|
|
|
675
675
|
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
676
676
|
await user.upload(fileInput, file);
|
|
@@ -684,7 +684,7 @@ describe('[component] ImportModal', () => {
|
|
|
684
684
|
const user = userEvent.setup();
|
|
685
685
|
const file = createCSVFile(' \n \n ');
|
|
686
686
|
|
|
687
|
-
render(<ImportModal {...
|
|
687
|
+
render(<ImportModal {...baseProps} />);
|
|
688
688
|
|
|
689
689
|
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
690
690
|
await user.upload(fileInput, file);
|
|
@@ -701,7 +701,7 @@ describe('[component] ImportModal', () => {
|
|
|
701
701
|
const csvContent = headers + rows;
|
|
702
702
|
const file = createCSVFile(csvContent);
|
|
703
703
|
|
|
704
|
-
render(<ImportModal {...
|
|
704
|
+
render(<ImportModal {...baseProps} />);
|
|
705
705
|
|
|
706
706
|
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
707
707
|
await user.upload(fileInput, file);
|
|
@@ -714,7 +714,7 @@ describe('[component] ImportModal', () => {
|
|
|
714
714
|
|
|
715
715
|
describe('Accessibility', () => {
|
|
716
716
|
it('provides accessible file input', () => {
|
|
717
|
-
render(<ImportModal {...
|
|
717
|
+
render(<ImportModal {...baseProps} />);
|
|
718
718
|
|
|
719
719
|
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
|
720
720
|
expect(fileInput).toBeInTheDocument();
|
|
@@ -723,7 +723,7 @@ describe('[component] ImportModal', () => {
|
|
|
723
723
|
});
|
|
724
724
|
|
|
725
725
|
it('provides accessible button labels', () => {
|
|
726
|
-
render(<ImportModal {...
|
|
726
|
+
render(<ImportModal {...baseProps} />);
|
|
727
727
|
|
|
728
728
|
expect(screen.getByRole('button', { name: /select file/i })).toBeInTheDocument();
|
|
729
729
|
expect(screen.getByRole('button', { name: /import/i })).toBeInTheDocument();
|