@jmruthers/pace-core 0.5.76 → 0.5.78
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 +8 -0
- package/dist/{RBACService-C4udt_Zp.d.ts → AuthService-Df3IozMG.d.ts} +10 -118
- package/dist/{DataTable-ntgmhO2W.d.ts → DataTable-BE0OXZKQ.d.ts} +9 -2
- package/dist/{DataTable-4GAVPIEG.js → DataTable-ETGVF4Y5.js} +50 -13
- package/dist/{PublicLoadingSpinner-BiNER8F5.d.ts → PublicLoadingSpinner-CnUaz0vG.d.ts} +5 -2
- package/dist/{UnifiedAuthProvider-Bj6YCf7c.d.ts → UnifiedAuthProvider-B391Aqum.d.ts} +42 -45
- package/dist/{UnifiedAuthProvider-3NKDOSOK.js → UnifiedAuthProvider-P5SOJAQ6.js} +4 -5
- package/dist/{api-DDMUKIUD.js → api-KG4A2X7P.js} +9 -3
- package/dist/{audit-6TOCAMKO.js → audit-65VNHEV2.js} +2 -2
- package/dist/{chunk-K34IM5CT.js → chunk-2OGV6IRV.js} +196 -626
- package/dist/chunk-2OGV6IRV.js.map +1 -0
- package/dist/{chunk-NTNILOBC.js → chunk-5BO3MI5Y.js} +4 -4
- package/dist/{chunk-XLZ7U46Z.js → chunk-CVMVPYAL.js} +9 -60
- package/dist/chunk-CVMVPYAL.js.map +1 -0
- package/dist/{chunk-URUTVZ7N.js → chunk-FL4ZCQLD.js} +2 -2
- package/dist/{chunk-LW7MMEAQ.js → chunk-FT2M4R4F.js} +2 -2
- package/dist/{chunk-5BSLGBYI.js → chunk-JCQZ6LA7.js} +2 -8
- package/dist/{chunk-5BSLGBYI.js.map → chunk-JCQZ6LA7.js.map} +1 -1
- package/dist/{chunk-KHJS6VIA.js → chunk-LRQ6RBJC.js} +157 -112
- package/dist/chunk-LRQ6RBJC.js.map +1 -0
- package/dist/{chunk-WN6XJWOS.js → chunk-MNJXXD6C.js} +274 -743
- package/dist/chunk-MNJXXD6C.js.map +1 -0
- package/dist/{chunk-KK73ZB4E.js → chunk-PTR5PMPE.js} +153 -132
- package/dist/chunk-PTR5PMPE.js.map +1 -0
- package/dist/{chunk-B2WTCLCV.js → chunk-Q7APDV6H.js} +18 -8
- package/dist/chunk-Q7APDV6H.js.map +1 -0
- package/dist/{chunk-A4FUBC7B.js → chunk-QGVSOUJ2.js} +2 -4
- package/dist/{chunk-A4FUBC7B.js.map → chunk-QGVSOUJ2.js.map} +1 -1
- package/dist/{chunk-FGMFQSHX.js → chunk-S63MFSY6.js} +500 -551
- package/dist/chunk-S63MFSY6.js.map +1 -0
- package/dist/{chunk-AFGTSUAD.js → chunk-VSOKOFRF.js} +4 -4
- package/dist/chunk-WUXCWRL6.js +20 -0
- package/dist/chunk-WUXCWRL6.js.map +1 -0
- package/dist/{chunk-Y6TXWPJO.js → chunk-YVVGHRGI.js} +105 -31
- package/dist/chunk-YVVGHRGI.js.map +1 -0
- package/dist/{chunk-M5IWZRBT.js → chunk-ZMNXIJP4.js} +2187 -981
- package/dist/chunk-ZMNXIJP4.js.map +1 -0
- package/dist/components.d.ts +6 -6
- package/dist/components.js +14 -18
- package/dist/components.js.map +1 -1
- package/dist/{database-C3Szpi5J.d.ts → database-BXAfr2Y_.d.ts} +18 -0
- package/dist/hooks.d.ts +5 -5
- package/dist/hooks.js +8 -9
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +19 -27
- package/dist/index.js +21 -29
- package/dist/index.js.map +1 -1
- package/dist/{organisation-BtshODVF.d.ts → organisation-D6qRDtbF.d.ts} +1 -1
- package/dist/providers.d.ts +7 -21
- package/dist/providers.js +3 -10
- package/dist/rbac/index.d.ts +71 -221
- package/dist/rbac/index.js +15 -16
- package/dist/{types-CGX9Vyf5.d.ts → types-BDg1mAGG.d.ts} +36 -6
- package/dist/types.d.ts +3 -3
- package/dist/types.js +61 -18
- package/dist/types.js.map +1 -1
- package/dist/{unified-CM7T0aTK.d.ts → unified-DQ4VcT7H.d.ts} +1 -1
- package/dist/{usePublicRouteParams-B-CumWRc.d.ts → usePublicRouteParams-BlgwXweB.d.ts} +3 -3
- package/dist/utils.d.ts +2 -2
- package/dist/utils.js +52 -9
- package/dist/utils.js.map +1 -1
- package/docs/CONTENT_AUDIT_REPORT.md +253 -0
- package/docs/DOCUMENTATION_AUDIT.md +172 -0
- package/docs/README.md +142 -147
- package/docs/STYLE_GUIDE.md +37 -0
- package/docs/api/classes/ColumnFactory.md +17 -17
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +4 -4
- package/docs/api/classes/MissingUserContextError.md +4 -4
- package/docs/api/classes/OrganisationContextRequiredError.md +4 -4
- package/docs/api/classes/PermissionDeniedError.md +5 -5
- package/docs/api/classes/PublicErrorBoundary.md +1 -1
- package/docs/api/classes/RBACAuditManager.md +8 -8
- package/docs/api/classes/RBACCache.md +35 -5
- package/docs/api/classes/RBACEngine.md +49 -20
- package/docs/api/classes/RBACError.md +4 -4
- package/docs/api/classes/RBACNotInitializedError.md +4 -4
- 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 +4 -4
- 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 +11 -0
- package/docs/api/interfaces/DataTableAction.md +65 -29
- package/docs/api/interfaces/DataTableColumn.md +36 -23
- package/docs/api/interfaces/DataTableProps.md +80 -38
- package/docs/api/interfaces/DataTableToolbarButton.md +7 -7
- package/docs/api/interfaces/EmptyStateConfig.md +5 -5
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventLogoProps.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/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 +11 -11
- package/docs/api/interfaces/NavigationContextType.md +9 -9
- 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 +7 -7
- 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 +16 -3
- 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/PermissionEnforcerProps.md +4 -4
- 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/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +2 -2
- package/docs/api/interfaces/RouteConfig.md +2 -2
- 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 +94 -521
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +16 -16
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
- 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/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 +11 -11
- package/docs/api/interfaces/UserMenuProps.md +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +251 -269
- package/docs/api-reference/components.md +193 -0
- package/docs/api-reference/hooks.md +265 -0
- package/docs/api-reference/providers.md +6 -0
- package/docs/api-reference/types.md +6 -0
- package/docs/api-reference/utilities.md +207 -0
- package/docs/architecture/README.md +6 -0
- package/docs/{database-schema-requirements.md → architecture/database-schema-requirements.md} +6 -0
- package/docs/architecture/rbac-security-architecture.md +258 -0
- package/docs/architecture/services.md +9 -1
- package/docs/best-practices/README.md +6 -0
- package/docs/best-practices/accessibility.md +6 -0
- package/docs/{common-patterns.md → best-practices/common-patterns.md} +6 -0
- package/docs/best-practices/deployment.md +6 -0
- package/docs/best-practices/performance.md +475 -2
- package/docs/best-practices/security.md +6 -0
- package/docs/best-practices/testing.md +6 -0
- package/docs/core-concepts/authentication.md +6 -0
- package/docs/core-concepts/events.md +6 -0
- package/docs/core-concepts/organisations.md +6 -0
- package/docs/core-concepts/permissions.md +6 -0
- package/docs/core-concepts/rbac-system.md +8 -0
- package/docs/documentation-index.md +121 -182
- package/docs/{consuming-app-vite-config.md → getting-started/consuming-app-vite-config.md} +6 -0
- package/docs/getting-started/documentation-index.md +40 -0
- package/docs/getting-started/examples/README.md +878 -35
- package/docs/{faq.md → getting-started/faq.md} +7 -1
- package/docs/getting-started/installation-guide.md +6 -0
- package/docs/{quick-reference.md → getting-started/quick-reference.md} +6 -0
- package/docs/implementation-guides/app-layout.md +6 -0
- package/docs/implementation-guides/authentication.md +1021 -0
- package/docs/implementation-guides/component-styling.md +6 -0
- package/docs/implementation-guides/data-tables.md +1264 -2076
- package/docs/implementation-guides/dynamic-colors.md +6 -0
- package/docs/implementation-guides/event-theming-summary.md +6 -0
- package/docs/{file-reference-system.md → implementation-guides/file-reference-system.md} +6 -0
- package/docs/implementation-guides/file-upload-storage.md +6 -0
- package/docs/implementation-guides/forms.md +6 -0
- package/docs/implementation-guides/inactivity-tracking.md +6 -0
- package/docs/implementation-guides/navigation.md +6 -0
- package/docs/implementation-guides/organisation-security.md +6 -0
- package/docs/implementation-guides/permission-enforcement.md +6 -0
- package/docs/implementation-guides/public-pages-advanced.md +6 -0
- package/docs/implementation-guides/public-pages.md +6 -0
- package/docs/migration/MIGRATION_GUIDE.md +827 -351
- package/docs/migration/README.md +7 -1
- package/docs/migration/organisation-context-timing-fix.md +6 -0
- package/docs/migration/rbac-migration.md +44 -1
- package/docs/migration/service-architecture.md +6 -0
- package/docs/migration/v0.4.15-tailwind-scanning.md +6 -0
- package/docs/migration/v0.4.16-css-first-approach.md +6 -0
- package/docs/migration/v0.4.17-source-path-fix.md +6 -0
- package/docs/rbac/README-rbac-rls-integration.md +6 -0
- package/docs/rbac/README.md +6 -0
- package/docs/rbac/advanced-patterns.md +6 -0
- package/docs/rbac/api-reference.md +7 -1
- package/docs/rbac/breaking-changes-v3.md +222 -0
- package/docs/rbac/examples/rbac-rls-integration-example.md +6 -0
- package/docs/rbac/examples.md +6 -0
- package/docs/rbac/getting-started.md +6 -0
- package/docs/rbac/migration-guide.md +260 -0
- package/docs/rbac/quick-start.md +70 -13
- package/docs/rbac/rbac-rls-integration.md +6 -0
- package/docs/rbac/super-admin-guide.md +6 -0
- package/docs/rbac/troubleshooting.md +6 -0
- package/docs/security/README.md +6 -0
- package/docs/security/checklist.md +6 -0
- package/docs/styles/README.md +7 -1
- package/docs/{usage.md → styles/usage.md} +6 -0
- package/docs/testing/README.md +6 -0
- package/docs/{visual-testing.md → testing/visual-testing.md} +6 -0
- package/docs/troubleshooting/README.md +387 -5
- package/docs/troubleshooting/cake-page-permission-guard-issue-summary.md +6 -0
- package/docs/troubleshooting/common-issues.md +6 -0
- package/docs/troubleshooting/database-view-compatibility.md +6 -0
- package/docs/troubleshooting/organisation-context-setup.md +6 -0
- package/docs/troubleshooting/react-hooks-issue-analysis.md +6 -0
- package/docs/troubleshooting/styling-issues.md +6 -0
- package/docs/troubleshooting/tailwind-content-scanning.md +6 -0
- package/package.json +1 -1
- package/src/__tests__/helpers/__tests__/test-providers.test.tsx +2 -1
- package/src/__tests__/helpers/test-providers.tsx +3 -53
- package/src/components/DataTable/DataTable.test.tsx +319 -0
- package/src/components/DataTable/DataTable.tsx +32 -11
- package/src/components/DataTable/__tests__/{DataTable.comprehensive.test.tsx → DataTable.comprehensive.test.tsx.skip} +6 -4
- package/src/components/DataTable/__tests__/{DataTable.test.tsx → DataTable.test.tsx.skip} +6 -4
- package/src/components/DataTable/__tests__/DataTableCore.test.tsx +31 -9
- package/src/components/DataTable/__tests__/a11y.basic.test.tsx +601 -0
- package/src/components/DataTable/__tests__/keyboard.test.tsx +615 -0
- package/src/components/DataTable/__tests__/pagination.modes.test.tsx +639 -0
- package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx.skip +330 -0
- package/src/components/DataTable/components/AccessDeniedPage.tsx +2 -2
- package/src/components/DataTable/components/ActionButtons.tsx +88 -104
- package/src/components/DataTable/components/DataTableCore.tsx +309 -337
- package/src/components/DataTable/components/DataTableErrorBoundary.tsx +4 -2
- package/src/components/DataTable/components/DataTableModals.tsx +22 -1
- package/src/components/DataTable/components/EditableRow.tsx +69 -84
- package/src/components/DataTable/components/EmptyState.tsx +5 -1
- package/src/components/DataTable/components/ImportModal.tsx +65 -36
- package/src/components/DataTable/components/PaginationControls.tsx +40 -100
- package/src/components/DataTable/components/UnifiedTableBody.tsx +125 -148
- package/src/components/DataTable/context/DataTableContext.tsx +1 -1
- package/src/components/DataTable/core/ColumnFactory.ts +5 -0
- package/src/components/DataTable/examples/HierarchicalActionsExample.tsx +12 -10
- package/src/components/DataTable/examples/HierarchicalExample.tsx +1 -1
- package/src/components/DataTable/examples/InitialPageSizeExample.tsx +1 -0
- package/src/components/DataTable/examples/PerformanceExample.tsx +1 -0
- package/src/components/DataTable/hooks/__tests__/useColumnOrderPersistence.test.ts +1 -5
- package/src/components/DataTable/hooks/__tests__/useColumnVisibilityPersistence.test.ts +167 -0
- package/src/components/DataTable/hooks/index.ts +7 -0
- package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +32 -15
- package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +102 -0
- package/src/components/DataTable/hooks/useDataTableConfiguration.ts +89 -0
- package/src/components/DataTable/hooks/useDataTableDataPipeline.ts +117 -0
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +71 -27
- package/src/components/DataTable/hooks/useDataTableState.ts +39 -11
- package/src/components/DataTable/hooks/useEffectiveColumnOrder.ts +33 -0
- package/src/components/DataTable/hooks/useHierarchicalState.ts +15 -1
- package/src/components/DataTable/hooks/useKeyboardNavigation.ts +447 -0
- package/src/components/DataTable/hooks/useServerSideDataEffect.ts +94 -0
- package/src/components/DataTable/hooks/useTableColumns.ts +10 -7
- package/src/components/DataTable/hooks/useTableHandlers.ts +174 -0
- package/src/components/DataTable/index.ts +12 -3
- package/src/components/DataTable/types.ts +129 -9
- package/src/components/DataTable/utils/__tests__/exportUtils.test.ts +159 -22
- package/src/components/DataTable/utils/__tests__/flexibleImport.test.ts +111 -0
- package/src/components/DataTable/utils/__tests__/rowUtils.test.ts +15 -29
- package/src/components/DataTable/utils/a11yUtils.ts +244 -0
- package/src/components/DataTable/utils/debugTools.ts +609 -0
- package/src/components/DataTable/utils/exportUtils.ts +114 -16
- package/src/components/DataTable/utils/flexibleImport.ts +202 -32
- package/src/components/DataTable/utils/hierarchicalUtils.ts +1 -1
- package/src/components/DataTable/utils/index.ts +2 -0
- package/src/components/DataTable/utils/paginationUtils.ts +350 -0
- package/src/components/DataTable/utils/rowUtils.ts +6 -5
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +19 -24
- package/src/components/NavigationMenu/NavigationMenu.tsx +19 -8
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.security.test.tsx +1 -23
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +56 -6
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +137 -13
- package/src/components/PublicLayout/__tests__/PublicPageHeader.test.tsx +1 -1
- package/src/components/Select/Select.tsx +1 -0
- package/src/components/examples/PermissionExample.tsx +173 -0
- package/src/examples/CorrectPublicPageImplementation.tsx +301 -0
- package/src/examples/PublicEventPage.tsx +274 -0
- package/src/examples/PublicPageApp.tsx +308 -0
- package/src/examples/PublicPageUsageExample.tsx +216 -0
- package/src/hooks/__tests__/useOrganisationPermissions.unit.test.tsx +12 -1
- package/src/hooks/__tests__/useOrganisationSecurity.unit.test.tsx +129 -17
- package/src/hooks/__tests__/useRBAC.unit.test.ts +151 -846
- package/src/hooks/useOrganisationPermissions.test.ts +42 -18
- package/src/hooks/useOrganisationPermissions.ts +12 -6
- package/src/hooks/useOrganisationSecurity.test.ts +138 -85
- package/src/hooks/useOrganisationSecurity.ts +41 -10
- package/src/index.ts +0 -1
- package/src/providers/AuthProvider.simplified.tsx +880 -0
- package/src/providers/UnifiedAuthProvider.test.simple.tsx +8 -8
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +29 -19
- package/src/providers/index.ts +0 -1
- package/src/providers/services/EventServiceProvider.tsx +19 -15
- package/src/providers/services/InactivityServiceProvider.tsx +19 -15
- package/src/providers/services/OrganisationServiceProvider.tsx +19 -15
- package/src/providers/services/UnifiedAuthProvider.tsx +156 -127
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +1 -1
- package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +3 -3
- package/src/rbac/README.md +1 -1
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +25 -27
- package/src/rbac/__tests__/auth-rbac-security.integration.test.tsx +313 -0
- package/src/rbac/__tests__/engine.comprehensive.test.ts +114 -348
- package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +28 -110
- package/src/rbac/__tests__/rbac-engine-simplified.test.ts +33 -85
- package/src/rbac/__tests__/scenarios.user-role.test.tsx +2 -2
- package/src/rbac/adapters.tsx +26 -69
- package/src/rbac/api.test.ts +90 -27
- package/src/rbac/api.ts +61 -10
- package/src/rbac/audit.test.ts +33 -38
- package/src/rbac/audit.ts +21 -6
- package/src/rbac/cache.ts +33 -1
- package/src/rbac/components/NavigationGuard.tsx +11 -11
- package/src/rbac/components/NavigationProvider.test.tsx +11 -5
- package/src/rbac/components/NavigationProvider.tsx +37 -13
- package/src/rbac/components/PagePermissionGuard.tsx +111 -50
- package/src/rbac/components/PagePermissionProvider.tsx +5 -5
- package/src/rbac/components/PermissionEnforcer.tsx +11 -11
- package/src/rbac/components/RoleBasedRouter.tsx +5 -5
- package/src/rbac/components/SecureDataProvider.tsx +5 -5
- package/src/rbac/components/__tests__/NavigationGuard.test.tsx +8 -8
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +14 -14
- package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +12 -12
- package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +6 -6
- package/src/rbac/engine.test.simple.ts +19 -13
- package/src/rbac/engine.test.ts +1 -0
- package/src/rbac/engine.ts +330 -766
- package/src/rbac/errors.ts +156 -0
- package/src/rbac/hooks/usePermissions.ts +32 -10
- package/src/rbac/hooks/useRBAC.test.ts +126 -512
- package/src/rbac/hooks/useRBAC.ts +147 -193
- package/src/rbac/hooks/useResolvedScope.ts +12 -0
- package/src/rbac/index.ts +7 -4
- package/src/rbac/security.ts +109 -18
- package/src/rbac/types.ts +12 -1
- package/src/services/AuthService.ts +2 -15
- package/src/services/EventService.ts +43 -46
- package/src/services/OrganisationService.ts +51 -31
- package/src/services/__tests__/AuthService.test.ts +1 -1
- package/src/services/__tests__/EventService.test.ts +1 -1
- package/src/services/__tests__/OrganisationService.test.ts +1 -1
- package/src/services/base/BaseService.ts +8 -0
- package/src/styles/base.css +208 -0
- package/src/styles/semantic.css +24 -0
- package/src/types/database.generated.ts +7347 -0
- package/src/types/database.ts +20 -0
- package/src/utils/logger.ts +179 -0
- package/src/utils/organisationContext.ts +11 -4
- package/src/utils/storage/__tests__/helpers.unit.test.ts +6 -2
- package/dist/appNameResolver-UURKN7NF.js +0 -22
- package/dist/audit-6TOCAMKO.js.map +0 -1
- package/dist/chunk-B2WTCLCV.js.map +0 -1
- package/dist/chunk-FGMFQSHX.js.map +0 -1
- package/dist/chunk-K34IM5CT.js.map +0 -1
- package/dist/chunk-KHJS6VIA.js.map +0 -1
- package/dist/chunk-KK73ZB4E.js.map +0 -1
- package/dist/chunk-M5IWZRBT.js.map +0 -1
- package/dist/chunk-ULBI5JGB.js +0 -109
- package/dist/chunk-ULBI5JGB.js.map +0 -1
- package/dist/chunk-WN6XJWOS.js.map +0 -1
- package/dist/chunk-XLZ7U46Z.js.map +0 -1
- package/dist/chunk-Y6TXWPJO.js.map +0 -1
- package/docs/DOCUMENTATION_CHECKLIST.md +0 -281
- package/docs/TERMINOLOGY.md +0 -231
- package/docs/api/interfaces/RBACContextType.md +0 -468
- package/docs/api/interfaces/RBACProviderProps.md +0 -107
- package/docs/best-practices/performance-expansion.md +0 -473
- package/docs/breaking-changes.md +0 -179
- package/docs/consuming-app-example.md +0 -290
- package/docs/documentation-templates.md +0 -539
- package/docs/examples/navigation-menu-auth-fix.md +0 -344
- package/docs/getting-started/examples/basic-auth-app.md +0 -520
- package/docs/getting-started/examples/full-featured-app.md +0 -616
- package/docs/getting-started/quick-start.md +0 -376
- package/docs/implementation-guides/datatable-filtering.md +0 -313
- package/docs/implementation-guides/datatable-rbac-usage.md +0 -317
- package/docs/implementation-guides/hierarchical-datatable.md +0 -850
- package/docs/implementation-guides/large-datasets.md +0 -281
- package/docs/implementation-guides/performance.md +0 -403
- package/docs/migration/quick-migration-guide.md +0 -320
- package/docs/migration-guide.md +0 -193
- package/docs/migration-guides/unified-auth-provider-mandatory-timeouts.md +0 -226
- package/docs/performance/README.md +0 -551
- package/docs/style-guide.md +0 -964
- package/docs/troubleshooting/authentication-issues.md +0 -334
- package/docs/troubleshooting/debugging.md +0 -1117
- package/docs/troubleshooting/migration.md +0 -918
- package/src/__tests__/hooks/usePermissions.test.ts +0 -261
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.rbac.test.tsx +0 -574
- package/src/hooks/__tests__/ServiceHooks.test.tsx +0 -613
- package/src/hooks/services/__tests__/useServiceHooks.test.tsx +0 -137
- package/src/hooks/services/usePermissions.ts +0 -70
- package/src/hooks/services/useRBACService.ts +0 -30
- package/src/hooks/usePermissionCheck.ts +0 -150
- package/src/providers/__tests__/ServiceProviders.test.tsx +0 -477
- package/src/providers/services/RBACServiceProvider.tsx +0 -79
- package/src/rbac/__tests__/integration.authflow.test.tsx +0 -119
- package/src/rbac/__tests__/integration.navigation.test.tsx +0 -69
- package/src/rbac/__tests__/integration.securedata.test.tsx +0 -92
- package/src/rbac/__tests__/integration.smoke.test.tsx +0 -73
- package/src/rbac/providers/RBACProvider.tsx +0 -645
- package/src/rbac/providers/__tests__/RBACProvider.integration.test.tsx +0 -688
- package/src/rbac/providers/__tests__/RBACProvider.test.tsx +0 -1186
- package/src/rbac/providers/index.ts +0 -11
- package/src/services/RBACService.ts +0 -522
- package/src/services/__tests__/RBACService.test.ts +0 -492
- package/src/services/interfaces/IRBACService.ts +0 -62
- package/src/utils/appNameResolver.test 2.ts +0 -494
- /package/dist/{DataTable-4GAVPIEG.js.map → DataTable-ETGVF4Y5.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-3NKDOSOK.js.map → UnifiedAuthProvider-P5SOJAQ6.js.map} +0 -0
- /package/dist/{api-DDMUKIUD.js.map → api-KG4A2X7P.js.map} +0 -0
- /package/dist/{appNameResolver-UURKN7NF.js.map → audit-65VNHEV2.js.map} +0 -0
- /package/dist/{chunk-NTNILOBC.js.map → chunk-5BO3MI5Y.js.map} +0 -0
- /package/dist/{chunk-URUTVZ7N.js.map → chunk-FL4ZCQLD.js.map} +0 -0
- /package/dist/{chunk-LW7MMEAQ.js.map → chunk-FT2M4R4F.js.map} +0 -0
- /package/dist/{chunk-AFGTSUAD.js.map → chunk-VSOKOFRF.js.map} +0 -0
- /package/docs/{app.css.example → styles/app.css.example} +0 -0
|
@@ -0,0 +1,615 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file DataTable Keyboard Navigation Tests
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module Components/DataTable/Tests
|
|
5
|
+
* @since 0.4.0
|
|
6
|
+
*
|
|
7
|
+
* Comprehensive keyboard navigation tests for DataTable component.
|
|
8
|
+
* Tests roving tabindex, arrow key navigation, and focus management.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import React from 'react';
|
|
12
|
+
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
|
13
|
+
import userEvent from '@testing-library/user-event';
|
|
14
|
+
import { vi, beforeEach, afterEach } from 'vitest';
|
|
15
|
+
import { DataTable } from '../DataTable';
|
|
16
|
+
import { MockRBACProvider } from './mocks/MockRBACProvider';
|
|
17
|
+
import type { DataTableColumn } from '../types';
|
|
18
|
+
|
|
19
|
+
// Mock the RBAC hooks
|
|
20
|
+
vi.mock('../../../rbac/hooks', () => ({
|
|
21
|
+
useCan: vi.fn(() => ({ can: true, isLoading: false })),
|
|
22
|
+
useResolvedScope: vi.fn(() => ({
|
|
23
|
+
organisationId: 'test-org',
|
|
24
|
+
eventId: 'test-event',
|
|
25
|
+
appId: 'test-app'
|
|
26
|
+
})),
|
|
27
|
+
}));
|
|
28
|
+
|
|
29
|
+
// Mock the UnifiedAuthProvider
|
|
30
|
+
vi.mock('../../../providers/UnifiedAuthProvider', () => ({
|
|
31
|
+
useUnifiedAuth: vi.fn(() => ({
|
|
32
|
+
user: { id: 'test-user', name: 'Test User' },
|
|
33
|
+
isLoading: false,
|
|
34
|
+
error: null,
|
|
35
|
+
})),
|
|
36
|
+
}));
|
|
37
|
+
|
|
38
|
+
// Mock the toast hook
|
|
39
|
+
vi.mock('../../../hooks/useToast', () => ({
|
|
40
|
+
toast: vi.fn(),
|
|
41
|
+
}));
|
|
42
|
+
|
|
43
|
+
// Test data
|
|
44
|
+
interface TestUser {
|
|
45
|
+
id: string;
|
|
46
|
+
name: string;
|
|
47
|
+
email: string;
|
|
48
|
+
role: string;
|
|
49
|
+
status: 'active' | 'inactive';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const testUsers: TestUser[] = [
|
|
53
|
+
{ id: '1', name: 'John Doe', email: 'john@example.com', role: 'Admin', status: 'active' },
|
|
54
|
+
{ id: '2', name: 'Jane Smith', email: 'jane@example.com', role: 'User', status: 'active' },
|
|
55
|
+
{ id: '3', name: 'Bob Johnson', email: 'bob@example.com', role: 'User', status: 'inactive' },
|
|
56
|
+
{ id: '4', name: 'Alice Brown', email: 'alice@example.com', role: 'Manager', status: 'active' },
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
const testColumns: DataTableColumn<TestUser>[] = [
|
|
60
|
+
{
|
|
61
|
+
accessorKey: 'name',
|
|
62
|
+
header: 'Name',
|
|
63
|
+
sortable: true,
|
|
64
|
+
searchable: true,
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
accessorKey: 'email',
|
|
68
|
+
header: 'Email',
|
|
69
|
+
sortable: true,
|
|
70
|
+
searchable: true,
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
accessorKey: 'role',
|
|
74
|
+
header: 'Role',
|
|
75
|
+
sortable: true,
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
accessorKey: 'status',
|
|
79
|
+
header: 'Status',
|
|
80
|
+
sortable: true,
|
|
81
|
+
},
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
const defaultFeatures = {
|
|
85
|
+
search: true,
|
|
86
|
+
pagination: true,
|
|
87
|
+
sorting: true,
|
|
88
|
+
filtering: true,
|
|
89
|
+
export: false,
|
|
90
|
+
import: true,
|
|
91
|
+
selection: true,
|
|
92
|
+
creation: false,
|
|
93
|
+
editing: false,
|
|
94
|
+
deletion: false,
|
|
95
|
+
deleteSelected: false,
|
|
96
|
+
grouping: false,
|
|
97
|
+
columnVisibility: false,
|
|
98
|
+
columnReordering: false,
|
|
99
|
+
hierarchical: false,
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// Test wrapper component
|
|
103
|
+
const TestWrapper: React.FC<{ children: React.ReactNode }> = ({ children }) => (
|
|
104
|
+
<MockRBACProvider>
|
|
105
|
+
{children}
|
|
106
|
+
</MockRBACProvider>
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
describe('DataTable Keyboard Navigation', () => {
|
|
110
|
+
beforeEach(() => {
|
|
111
|
+
// Clear any existing live regions
|
|
112
|
+
const existingLiveRegions = document.querySelectorAll('[aria-live]');
|
|
113
|
+
existingLiveRegions.forEach(region => {
|
|
114
|
+
if (region.parentNode) {
|
|
115
|
+
region.parentNode.removeChild(region);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
afterEach(() => {
|
|
121
|
+
// Clean up live regions after each test
|
|
122
|
+
const liveRegions = document.querySelectorAll('[aria-live]');
|
|
123
|
+
liveRegions.forEach(region => {
|
|
124
|
+
if (region.parentNode) {
|
|
125
|
+
region.parentNode.removeChild(region);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
describe('Header Navigation', () => {
|
|
131
|
+
it('should support Enter key to toggle sort on headers', async () => {
|
|
132
|
+
const user = userEvent.setup();
|
|
133
|
+
|
|
134
|
+
render(
|
|
135
|
+
<TestWrapper>
|
|
136
|
+
<DataTable
|
|
137
|
+
data={testUsers}
|
|
138
|
+
columns={testColumns}
|
|
139
|
+
rbac={{ pageId: 'test-page' }}
|
|
140
|
+
features={defaultFeatures}
|
|
141
|
+
/>
|
|
142
|
+
</TestWrapper>
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
const nameHeader = screen.getByRole('columnheader', { name: /name/i });
|
|
146
|
+
const nameButton = nameHeader.querySelector('button');
|
|
147
|
+
|
|
148
|
+
// Focus the button inside the header
|
|
149
|
+
nameButton?.focus();
|
|
150
|
+
expect(nameButton).toHaveFocus();
|
|
151
|
+
|
|
152
|
+
// Press Enter to sort
|
|
153
|
+
await user.keyboard('{Enter}');
|
|
154
|
+
|
|
155
|
+
await waitFor(() => {
|
|
156
|
+
expect(nameHeader).toHaveAttribute('aria-sort', 'ascending');
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// Press Enter again to sort descending
|
|
160
|
+
await user.keyboard('{Enter}');
|
|
161
|
+
|
|
162
|
+
await waitFor(() => {
|
|
163
|
+
expect(nameHeader).toHaveAttribute('aria-sort', 'descending');
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('should support Space key to toggle sort on headers', async () => {
|
|
168
|
+
const user = userEvent.setup();
|
|
169
|
+
|
|
170
|
+
render(
|
|
171
|
+
<TestWrapper>
|
|
172
|
+
<DataTable
|
|
173
|
+
data={testUsers}
|
|
174
|
+
columns={testColumns}
|
|
175
|
+
rbac={{ pageId: 'test-page' }}
|
|
176
|
+
features={defaultFeatures}
|
|
177
|
+
/>
|
|
178
|
+
</TestWrapper>
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
const nameHeader = screen.getByRole('columnheader', { name: /name/i });
|
|
182
|
+
const nameButton = nameHeader.querySelector('button');
|
|
183
|
+
|
|
184
|
+
// Focus the button inside the header
|
|
185
|
+
nameButton?.focus();
|
|
186
|
+
expect(nameButton).toHaveFocus();
|
|
187
|
+
|
|
188
|
+
// Press Space to sort
|
|
189
|
+
await user.keyboard(' ');
|
|
190
|
+
|
|
191
|
+
await waitFor(() => {
|
|
192
|
+
expect(nameHeader).toHaveAttribute('aria-sort', 'ascending');
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it.skip('should navigate between headers with arrow keys', async () => {
|
|
197
|
+
const user = userEvent.setup();
|
|
198
|
+
|
|
199
|
+
render(
|
|
200
|
+
<TestWrapper>
|
|
201
|
+
<DataTable
|
|
202
|
+
data={testUsers}
|
|
203
|
+
columns={testColumns}
|
|
204
|
+
rbac={{ pageId: 'test-page' }}
|
|
205
|
+
features={defaultFeatures}
|
|
206
|
+
/>
|
|
207
|
+
</TestWrapper>
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
const nameHeader = screen.getByRole('columnheader', { name: /name/i });
|
|
211
|
+
const emailHeader = screen.getByRole('columnheader', { name: /email/i });
|
|
212
|
+
|
|
213
|
+
// Focus the first header
|
|
214
|
+
nameHeader.focus();
|
|
215
|
+
expect(nameHeader).toHaveFocus();
|
|
216
|
+
|
|
217
|
+
// Press Right arrow to move to next header
|
|
218
|
+
await user.keyboard('{ArrowRight}');
|
|
219
|
+
|
|
220
|
+
await waitFor(() => {
|
|
221
|
+
expect(emailHeader).toHaveFocus();
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// Press Left arrow to move back
|
|
225
|
+
await user.keyboard('{ArrowLeft}');
|
|
226
|
+
|
|
227
|
+
await waitFor(() => {
|
|
228
|
+
expect(nameHeader).toHaveFocus();
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it.skip('should navigate to first/last header with Home/End keys', async () => {
|
|
233
|
+
const user = userEvent.setup();
|
|
234
|
+
|
|
235
|
+
render(
|
|
236
|
+
<TestWrapper>
|
|
237
|
+
<DataTable
|
|
238
|
+
data={testUsers}
|
|
239
|
+
columns={testColumns}
|
|
240
|
+
rbac={{ pageId: 'test-page' }}
|
|
241
|
+
features={defaultFeatures}
|
|
242
|
+
/>
|
|
243
|
+
</TestWrapper>
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
const nameHeader = screen.getByRole('columnheader', { name: /name/i });
|
|
247
|
+
const statusHeader = screen.getByRole('columnheader', { name: /status/i });
|
|
248
|
+
|
|
249
|
+
// Focus a middle header
|
|
250
|
+
const emailHeader = screen.getByRole('columnheader', { name: /email/i });
|
|
251
|
+
emailHeader.focus();
|
|
252
|
+
|
|
253
|
+
// Press Home to go to first header
|
|
254
|
+
await user.keyboard('{Home}');
|
|
255
|
+
|
|
256
|
+
await waitFor(() => {
|
|
257
|
+
expect(nameHeader).toHaveFocus();
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
// Press End to go to last header
|
|
261
|
+
await user.keyboard('{End}');
|
|
262
|
+
|
|
263
|
+
await waitFor(() => {
|
|
264
|
+
expect(statusHeader).toHaveFocus();
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
describe('Cell Navigation', () => {
|
|
270
|
+
it.skip('should implement roving tabindex for cells', () => {
|
|
271
|
+
render(
|
|
272
|
+
<TestWrapper>
|
|
273
|
+
<DataTable
|
|
274
|
+
data={testUsers}
|
|
275
|
+
columns={testColumns}
|
|
276
|
+
rbac={{ pageId: 'test-page' }}
|
|
277
|
+
features={defaultFeatures}
|
|
278
|
+
/>
|
|
279
|
+
</TestWrapper>
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
const cells = screen.getAllByRole('cell');
|
|
283
|
+
|
|
284
|
+
// Only one cell should be tabbable (tabindex="0")
|
|
285
|
+
const tabbableCells = cells.filter(cell => cell.getAttribute('tabindex') === '0');
|
|
286
|
+
expect(tabbableCells.length).toBeLessThanOrEqual(1);
|
|
287
|
+
|
|
288
|
+
// All other cells should have tabindex="-1"
|
|
289
|
+
const nonTabbableCells = cells.filter(cell => cell.getAttribute('tabindex') === '-1');
|
|
290
|
+
expect(nonTabbableCells.length).toBeGreaterThan(0);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it.skip('should navigate between cells with arrow keys', async () => {
|
|
294
|
+
const user = userEvent.setup();
|
|
295
|
+
|
|
296
|
+
render(
|
|
297
|
+
<TestWrapper>
|
|
298
|
+
<DataTable
|
|
299
|
+
data={testUsers}
|
|
300
|
+
columns={testColumns}
|
|
301
|
+
rbac={{ pageId: 'test-page' }}
|
|
302
|
+
features={defaultFeatures}
|
|
303
|
+
/>
|
|
304
|
+
</TestWrapper>
|
|
305
|
+
);
|
|
306
|
+
|
|
307
|
+
const cells = screen.getAllByRole('cell');
|
|
308
|
+
const firstCell = cells[0];
|
|
309
|
+
|
|
310
|
+
if (firstCell) {
|
|
311
|
+
// Focus the first cell
|
|
312
|
+
firstCell.focus();
|
|
313
|
+
expect(firstCell).toHaveFocus();
|
|
314
|
+
|
|
315
|
+
// Test arrow key navigation
|
|
316
|
+
await user.keyboard('{ArrowRight}');
|
|
317
|
+
// The next cell should be focused (implementation dependent)
|
|
318
|
+
|
|
319
|
+
await user.keyboard('{ArrowDown}');
|
|
320
|
+
// The cell below should be focused (implementation dependent)
|
|
321
|
+
|
|
322
|
+
await user.keyboard('{ArrowLeft}');
|
|
323
|
+
// The previous cell should be focused (implementation dependent)
|
|
324
|
+
|
|
325
|
+
await user.keyboard('{ArrowUp}');
|
|
326
|
+
// The cell above should be focused (implementation dependent)
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it.skip('should navigate to row start/end with Home/End keys', async () => {
|
|
331
|
+
const user = userEvent.setup();
|
|
332
|
+
|
|
333
|
+
render(
|
|
334
|
+
<TestWrapper>
|
|
335
|
+
<DataTable
|
|
336
|
+
data={testUsers}
|
|
337
|
+
columns={testColumns}
|
|
338
|
+
rbac={{ pageId: 'test-page' }}
|
|
339
|
+
features={defaultFeatures}
|
|
340
|
+
/>
|
|
341
|
+
</TestWrapper>
|
|
342
|
+
);
|
|
343
|
+
|
|
344
|
+
const cells = screen.getAllByRole('cell');
|
|
345
|
+
const middleCell = cells[2]; // Assuming this is not the first or last in row
|
|
346
|
+
|
|
347
|
+
if (middleCell) {
|
|
348
|
+
// Focus a middle cell
|
|
349
|
+
middleCell.focus();
|
|
350
|
+
expect(middleCell).toHaveFocus();
|
|
351
|
+
|
|
352
|
+
// Press Home to go to row start
|
|
353
|
+
await user.keyboard('{Home}');
|
|
354
|
+
// Should focus first cell in row (implementation dependent)
|
|
355
|
+
|
|
356
|
+
// Press End to go to row end
|
|
357
|
+
await user.keyboard('{End}');
|
|
358
|
+
// Should focus last cell in row (implementation dependent)
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
it.skip('should navigate to table start/end with Ctrl+Home/End', async () => {
|
|
363
|
+
const user = userEvent.setup();
|
|
364
|
+
|
|
365
|
+
render(
|
|
366
|
+
<TestWrapper>
|
|
367
|
+
<DataTable
|
|
368
|
+
data={testUsers}
|
|
369
|
+
columns={testColumns}
|
|
370
|
+
rbac={{ pageId: 'test-page' }}
|
|
371
|
+
features={defaultFeatures}
|
|
372
|
+
/>
|
|
373
|
+
</TestWrapper>
|
|
374
|
+
);
|
|
375
|
+
|
|
376
|
+
const cells = screen.getAllByRole('cell');
|
|
377
|
+
const middleCell = cells[Math.floor(cells.length / 2)];
|
|
378
|
+
|
|
379
|
+
if (middleCell) {
|
|
380
|
+
// Focus a middle cell
|
|
381
|
+
middleCell.focus();
|
|
382
|
+
expect(middleCell).toHaveFocus();
|
|
383
|
+
|
|
384
|
+
// Press Ctrl+Home to go to table start
|
|
385
|
+
await user.keyboard('{Control>}{Home}{/Control}');
|
|
386
|
+
// Should focus first cell in table (implementation dependent)
|
|
387
|
+
|
|
388
|
+
// Press Ctrl+End to go to table end
|
|
389
|
+
await user.keyboard('{Control>}{End}{/Control}');
|
|
390
|
+
// Should focus last cell in table (implementation dependent)
|
|
391
|
+
}
|
|
392
|
+
});
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
describe('Focus Management', () => {
|
|
396
|
+
it.skip('should restore focus when modals close', async () => {
|
|
397
|
+
const user = userEvent.setup();
|
|
398
|
+
|
|
399
|
+
render(
|
|
400
|
+
<TestWrapper>
|
|
401
|
+
<DataTable
|
|
402
|
+
data={testUsers}
|
|
403
|
+
columns={testColumns}
|
|
404
|
+
rbac={{ pageId: 'test-page' }}
|
|
405
|
+
features={defaultFeatures}
|
|
406
|
+
/>
|
|
407
|
+
</TestWrapper>
|
|
408
|
+
);
|
|
409
|
+
|
|
410
|
+
// Find and focus a cell
|
|
411
|
+
const cells = screen.getAllByRole('cell');
|
|
412
|
+
const firstCell = cells[0];
|
|
413
|
+
|
|
414
|
+
if (firstCell) {
|
|
415
|
+
firstCell.focus();
|
|
416
|
+
expect(firstCell).toHaveFocus();
|
|
417
|
+
|
|
418
|
+
// Open import modal (if import button exists)
|
|
419
|
+
const importButton = screen.queryByText(/import/i);
|
|
420
|
+
if (importButton) {
|
|
421
|
+
await user.click(importButton);
|
|
422
|
+
|
|
423
|
+
// Focus should be stored and modal should open
|
|
424
|
+
// (Implementation dependent - modal focus management)
|
|
425
|
+
|
|
426
|
+
// Close modal (ESC key or close button)
|
|
427
|
+
await user.keyboard('{Escape}');
|
|
428
|
+
|
|
429
|
+
// Focus should be restored to the original cell
|
|
430
|
+
await waitFor(() => {
|
|
431
|
+
expect(firstCell).toHaveFocus();
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
it('should not show focus warnings in console', () => {
|
|
438
|
+
const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
439
|
+
|
|
440
|
+
render(
|
|
441
|
+
<TestWrapper>
|
|
442
|
+
<DataTable
|
|
443
|
+
data={testUsers}
|
|
444
|
+
columns={testColumns}
|
|
445
|
+
rbac={{ pageId: 'test-page' }}
|
|
446
|
+
features={defaultFeatures}
|
|
447
|
+
/>
|
|
448
|
+
</TestWrapper>
|
|
449
|
+
);
|
|
450
|
+
|
|
451
|
+
// Check that no focus-related warnings were logged
|
|
452
|
+
const focusWarnings = consoleSpy.mock.calls.filter(call =>
|
|
453
|
+
call.some(arg =>
|
|
454
|
+
typeof arg === 'string' &&
|
|
455
|
+
(arg.includes('focus') || arg.includes('tabindex') || arg.includes('keyboard'))
|
|
456
|
+
)
|
|
457
|
+
);
|
|
458
|
+
|
|
459
|
+
expect(focusWarnings).toHaveLength(0);
|
|
460
|
+
|
|
461
|
+
consoleSpy.mockRestore();
|
|
462
|
+
});
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
describe('Unsupported Operations', () => {
|
|
466
|
+
it('should disable keyboard resize when not supported', async () => {
|
|
467
|
+
const user = userEvent.setup();
|
|
468
|
+
|
|
469
|
+
render(
|
|
470
|
+
<TestWrapper>
|
|
471
|
+
<DataTable
|
|
472
|
+
data={testUsers}
|
|
473
|
+
columns={testColumns}
|
|
474
|
+
rbac={{ pageId: 'test-page' }}
|
|
475
|
+
features={{ ...defaultFeatures, columnReordering: false }}
|
|
476
|
+
/>
|
|
477
|
+
</TestWrapper>
|
|
478
|
+
);
|
|
479
|
+
|
|
480
|
+
const nameHeader = screen.getByRole('columnheader', { name: /name/i });
|
|
481
|
+
const nameButton = nameHeader.querySelector('button');
|
|
482
|
+
|
|
483
|
+
// Focus the button inside the header
|
|
484
|
+
nameButton?.focus();
|
|
485
|
+
expect(nameButton).toHaveFocus();
|
|
486
|
+
|
|
487
|
+
// Try to resize column (Ctrl + or Ctrl -)
|
|
488
|
+
await user.keyboard('{Control>}={/Control}');
|
|
489
|
+
|
|
490
|
+
// Should announce that resizing is not supported
|
|
491
|
+
await waitFor(() => {
|
|
492
|
+
const liveRegion = document.querySelector('[aria-live="polite"]');
|
|
493
|
+
expect(liveRegion?.textContent).toContain('not supported');
|
|
494
|
+
}, { timeout: 1000 });
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
it('should disable keyboard reorder when not supported', async () => {
|
|
498
|
+
const user = userEvent.setup();
|
|
499
|
+
|
|
500
|
+
render(
|
|
501
|
+
<TestWrapper>
|
|
502
|
+
<DataTable
|
|
503
|
+
data={testUsers}
|
|
504
|
+
columns={testColumns}
|
|
505
|
+
rbac={{ pageId: 'test-page' }}
|
|
506
|
+
features={{ ...defaultFeatures, columnReordering: false }}
|
|
507
|
+
/>
|
|
508
|
+
</TestWrapper>
|
|
509
|
+
);
|
|
510
|
+
|
|
511
|
+
const nameHeader = screen.getByRole('columnheader', { name: /name/i });
|
|
512
|
+
const nameButton = nameHeader.querySelector('button');
|
|
513
|
+
|
|
514
|
+
// Focus the button inside the header
|
|
515
|
+
nameButton?.focus();
|
|
516
|
+
expect(nameButton).toHaveFocus();
|
|
517
|
+
|
|
518
|
+
// Try to reorder column (Ctrl + R)
|
|
519
|
+
await user.keyboard('{Control>}r{/Control}');
|
|
520
|
+
|
|
521
|
+
// Should announce that reordering is not supported
|
|
522
|
+
await waitFor(() => {
|
|
523
|
+
const liveRegion = document.querySelector('[aria-live="polite"]');
|
|
524
|
+
expect(liveRegion?.textContent).toContain('not supported');
|
|
525
|
+
}, { timeout: 1000 });
|
|
526
|
+
});
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
describe('Tab Navigation', () => {
|
|
530
|
+
it('should allow normal Tab navigation to work', async () => {
|
|
531
|
+
const user = userEvent.setup();
|
|
532
|
+
|
|
533
|
+
render(
|
|
534
|
+
<TestWrapper>
|
|
535
|
+
<DataTable
|
|
536
|
+
data={testUsers}
|
|
537
|
+
columns={testColumns}
|
|
538
|
+
rbac={{ pageId: 'test-page' }}
|
|
539
|
+
features={defaultFeatures}
|
|
540
|
+
/>
|
|
541
|
+
</TestWrapper>
|
|
542
|
+
);
|
|
543
|
+
|
|
544
|
+
// Tab should work normally and not be intercepted
|
|
545
|
+
await user.tab();
|
|
546
|
+
|
|
547
|
+
// Should focus the first tabbable element
|
|
548
|
+
const focusedElement = document.activeElement;
|
|
549
|
+
expect(focusedElement).toBeTruthy();
|
|
550
|
+
|
|
551
|
+
// Tab again
|
|
552
|
+
await user.tab();
|
|
553
|
+
|
|
554
|
+
// Should move to next tabbable element
|
|
555
|
+
const nextFocusedElement = document.activeElement;
|
|
556
|
+
expect(nextFocusedElement).not.toBe(focusedElement);
|
|
557
|
+
});
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
describe('Integration Tests', () => {
|
|
561
|
+
it.skip('should work end-to-end with sorting and navigation', async () => {
|
|
562
|
+
const user = userEvent.setup();
|
|
563
|
+
|
|
564
|
+
render(
|
|
565
|
+
<TestWrapper>
|
|
566
|
+
<DataTable
|
|
567
|
+
data={testUsers}
|
|
568
|
+
columns={testColumns}
|
|
569
|
+
rbac={{ pageId: 'test-page' }}
|
|
570
|
+
features={defaultFeatures}
|
|
571
|
+
/>
|
|
572
|
+
</TestWrapper>
|
|
573
|
+
);
|
|
574
|
+
|
|
575
|
+
// Navigate to name header
|
|
576
|
+
const nameHeader = screen.getByRole('columnheader', { name: /name/i });
|
|
577
|
+
nameHeader.focus();
|
|
578
|
+
|
|
579
|
+
// Sort by name
|
|
580
|
+
await user.keyboard('{Enter}');
|
|
581
|
+
|
|
582
|
+
await waitFor(() => {
|
|
583
|
+
expect(nameHeader).toHaveAttribute('aria-sort', 'ascending');
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
// Navigate to email header
|
|
587
|
+
await user.keyboard('{ArrowRight}');
|
|
588
|
+
|
|
589
|
+
const emailHeader = screen.getByRole('columnheader', { name: /email/i });
|
|
590
|
+
await waitFor(() => {
|
|
591
|
+
expect(emailHeader).toHaveFocus();
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
// Sort by email
|
|
595
|
+
await user.keyboard(' ');
|
|
596
|
+
|
|
597
|
+
await waitFor(() => {
|
|
598
|
+
expect(emailHeader).toHaveAttribute('aria-sort', 'ascending');
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
// Navigate to first cell
|
|
602
|
+
const cells = screen.getAllByRole('cell');
|
|
603
|
+
if (cells[0]) {
|
|
604
|
+
cells[0].focus();
|
|
605
|
+
|
|
606
|
+
// Navigate around cells
|
|
607
|
+
await user.keyboard('{ArrowRight}');
|
|
608
|
+
await user.keyboard('{ArrowDown}');
|
|
609
|
+
await user.keyboard('{Home}');
|
|
610
|
+
|
|
611
|
+
// Should not cause any errors or warnings
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
});
|
|
615
|
+
});
|