@jmruthers/pace-core 0.5.193 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +62 -0
- package/README.md +7 -1
- package/cursor-rules/00-pace-core-compliance.mdc +299 -0
- package/cursor-rules/01-standards-compliance.mdc +244 -0
- package/cursor-rules/02-project-structure.mdc +200 -0
- package/cursor-rules/03-solid-principles.mdc +222 -0
- package/cursor-rules/04-testing-standards.mdc +268 -0
- package/cursor-rules/05-bug-reports-and-features.mdc +246 -0
- package/cursor-rules/06-code-quality.mdc +309 -0
- package/cursor-rules/07-tech-stack-compliance.mdc +214 -0
- package/cursor-rules/08-markup-quality.mdc +452 -0
- package/cursor-rules/CHANGELOG.md +119 -0
- package/cursor-rules/README.md +192 -0
- package/dist/{AuthService-DjnJHDtC.d.ts → AuthService-BPvc3Ka0.d.ts} +54 -0
- package/dist/{DataTable-Be6dH_dR.d.ts → DataTable-BMRU8a1j.d.ts} +34 -2
- package/dist/{DataTable-5FU7IESH.js → DataTable-TPTKCX4D.js} +10 -9
- package/dist/{PublicPageProvider-C0Sm_e5k.d.ts → PublicPageProvider-DC6kCaqf.d.ts} +385 -261
- package/dist/{UnifiedAuthProvider-RGJTDE2C.js → UnifiedAuthProvider-CH6Z342H.js} +3 -3
- package/dist/{UnifiedAuthProvider-185Ih4dj.d.ts → UnifiedAuthProvider-CVcTjx-d.d.ts} +29 -0
- package/dist/{api-N774RPUA.js → api-MVVQZLJI.js} +2 -2
- package/dist/{chunk-KNC55RTG.js → chunk-24UVZUZG.js} +90 -54
- package/dist/chunk-24UVZUZG.js.map +1 -0
- package/dist/{chunk-HWIIPPNI.js → chunk-2UOI2FG5.js} +20 -20
- package/dist/chunk-2UOI2FG5.js.map +1 -0
- package/dist/{chunk-E3SPN4VZ 5.js → chunk-3XC4CPTD.js} +4345 -3986
- package/dist/chunk-3XC4CPTD.js.map +1 -0
- package/dist/{chunk-7EQTDTTJ.js → chunk-6J4GEEJR.js} +172 -45
- package/dist/chunk-6J4GEEJR.js.map +1 -0
- package/dist/{chunk-6C4YBBJM 5.js → chunk-6SOIHG6Z.js} +1 -1
- package/dist/chunk-6SOIHG6Z.js.map +1 -0
- package/dist/{chunk-7FLMSG37.js → chunk-EHMR7VYL.js} +25 -25
- package/dist/chunk-EHMR7VYL.js.map +1 -0
- package/dist/{chunk-I7PSE6JW.js → chunk-F2IMUDXZ.js} +2 -75
- package/dist/chunk-F2IMUDXZ.js.map +1 -0
- package/dist/{chunk-QWWZ5CAQ.js → chunk-FFQEQTNW.js} +7 -9
- package/dist/chunk-FFQEQTNW.js.map +1 -0
- package/dist/chunk-FMUCXFII.js +76 -0
- package/dist/chunk-FMUCXFII.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-SQGMNID3.js → chunk-L4OXEN46.js} +4 -5
- package/dist/chunk-L4OXEN46.js.map +1 -0
- package/dist/{chunk-R77UEZ4E 3.js → chunk-M43Y4SSO.js} +1 -1
- package/dist/chunk-M43Y4SSO.js.map +1 -0
- package/dist/{chunk-IIELH4DL.js → chunk-MMZ7JXPU.js} +60 -223
- package/dist/chunk-MMZ7JXPU.js.map +1 -0
- package/dist/{chunk-NOAYCWCX 5.js → chunk-NECFR5MM.js} +394 -312
- package/dist/chunk-NECFR5MM.js.map +1 -0
- package/dist/{chunk-BC4IJKSL.js → chunk-SFZUDBL5.js} +40 -4
- package/dist/chunk-SFZUDBL5.js.map +1 -0
- package/dist/{chunk-XNXXZ43G.js → chunk-XWQCNGTQ.js} +748 -364
- package/dist/chunk-XWQCNGTQ.js.map +1 -0
- package/dist/components.d.ts +6 -6
- package/dist/components.js +15 -12
- package/dist/components.js.map +1 -1
- package/dist/{functions-D_kgHktt.d.ts → functions-DHebl8-F.d.ts} +1 -1
- package/dist/hooks.d.ts +59 -126
- package/dist/hooks.js +19 -28
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +63 -16
- package/dist/index.js +23 -24
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +21 -3
- package/dist/providers.js +2 -2
- package/dist/rbac/index.d.ts +146 -115
- package/dist/rbac/index.js +8 -11
- package/dist/theming/runtime.d.ts +1 -13
- package/dist/theming/runtime.js +1 -1
- package/dist/{timezone-_pgH8qrY.d.ts → timezone-CHhWg6b4.d.ts} +3 -10
- package/dist/{types-UU913iLA.d.ts → types-BeoeWV5I.d.ts} +8 -0
- package/dist/{types-CEpcvwwF.d.ts → types-CkbwOr4Y.d.ts} +6 -0
- package/dist/types.d.ts +2 -2
- package/dist/{usePublicRouteParams-TZe0gy-4.d.ts → usePublicRouteParams-1oMokgLF.d.ts} +34 -4
- package/dist/{useToast-C8gR5ir4.d.ts → useToast-AyaT-x7p.d.ts} +2 -2
- package/dist/utils.d.ts +4 -5
- package/dist/utils.js +15 -15
- package/dist/utils.js.map +1 -1
- package/docs/api/README.md +7 -1
- package/docs/api/classes/ColumnFactory.md +8 -8
- package/docs/api/classes/InvalidScopeError.md +4 -4
- package/docs/api/classes/Logger.md +1 -1
- package/docs/api/classes/MissingUserContextError.md +4 -4
- package/docs/api/classes/OrganisationContextRequiredError.md +4 -4
- package/docs/api/classes/PermissionDeniedError.md +4 -4
- package/docs/api/classes/RBACAuditManager.md +1 -1
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +1 -1
- package/docs/api/classes/RBACError.md +4 -4
- package/docs/api/classes/RBACNotInitializedError.md +4 -4
- package/docs/api/classes/SecureSupabaseClient.md +18 -15
- 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 +4 -4
- package/docs/api/interfaces/AutocompleteOptions.md +1 -1
- package/docs/api/interfaces/AvatarProps.md +1 -1
- package/docs/api/interfaces/BadgeProps.md +9 -2
- package/docs/api/interfaces/ButtonProps.md +7 -4
- package/docs/api/interfaces/CalendarProps.md +8 -5
- package/docs/api/interfaces/CardProps.md +8 -5
- 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 +9 -9
- package/docs/api/interfaces/DataRecord.md +1 -1
- package/docs/api/interfaces/DataTableAction.md +24 -21
- package/docs/api/interfaces/DataTableColumn.md +31 -31
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +7 -7
- package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
- package/docs/api/interfaces/DatabaseIssue.md +1 -1
- package/docs/api/interfaces/EmptyStateConfig.md +5 -5
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/ErrorBoundaryProps.md +147 -0
- package/docs/api/interfaces/ErrorBoundaryProviderProps.md +36 -0
- package/docs/api/interfaces/ErrorBoundaryState.md +75 -0
- package/docs/api/interfaces/EventAppRoleData.md +1 -1
- package/docs/api/interfaces/ExportColumn.md +1 -1
- package/docs/api/interfaces/ExportOptions.md +8 -8
- 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 +26 -23
- package/docs/api/interfaces/FooterProps.md +10 -8
- package/docs/api/interfaces/FormFieldProps.md +10 -10
- 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 +7 -4
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoggerConfig.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +14 -11
- package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
- package/docs/api/interfaces/NavigationContextType.md +1 -1
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +11 -11
- package/docs/api/interfaces/NavigationMenuProps.md +15 -15
- 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 +30 -27
- package/docs/api/interfaces/PaceLoginPageProps.md +6 -4
- package/docs/api/interfaces/PageAccessRecord.md +1 -1
- package/docs/api/interfaces/PagePermissionContextType.md +1 -1
- package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/ParsedAddress.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
- package/docs/api/interfaces/ProgressProps.md +1 -1
- package/docs/api/interfaces/ProtectedRouteProps.md +7 -26
- package/docs/api/interfaces/PublicPageFooterProps.md +9 -9
- package/docs/api/interfaces/PublicPageHeaderProps.md +10 -10
- package/docs/api/interfaces/PublicPageLayoutProps.md +7 -20
- 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 +1 -1
- 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 +1 -1
- 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 +1 -1
- package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
- package/docs/api/interfaces/RBACRoleRevokeParams.md +1 -1
- package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
- package/docs/api/interfaces/RBACRoleValidateParams.md +1 -1
- package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
- package/docs/api/interfaces/RBACRolesListParams.md +1 -1
- package/docs/api/interfaces/RBACRolesListResult.md +1 -1
- 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 +1 -1
- package/docs/api/interfaces/RouteConfig.md +1 -1
- package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
- package/docs/api/interfaces/SecureDataContextType.md +9 -9
- package/docs/api/interfaces/SecureDataProviderProps.md +8 -8
- package/docs/api/interfaces/SessionRestorationLoaderProps.md +3 -3
- 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 +3 -3
- package/docs/api/interfaces/TextareaProps.md +1 -1
- package/docs/api/interfaces/ToastActionElement.md +4 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +58 -55
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +15 -13
- package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
- package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +11 -9
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +8 -8
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +6 -6
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +9 -6
- package/docs/api/interfaces/UsePublicEventOptions.md +3 -3
- package/docs/api/interfaces/UsePublicEventReturn.md +8 -5
- package/docs/api/interfaces/UsePublicFileDisplayOptions.md +4 -4
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +12 -9
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +10 -7
- package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
- package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +14 -11
- package/docs/api/interfaces/UserMenuProps.md +8 -6
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +575 -634
- package/docs/architecture/database-schema-requirements.md +161 -0
- package/docs/core-concepts/rbac-system.md +3 -3
- package/docs/documentation-index.md +2 -4
- package/docs/getting-started/cursor-rules.md +263 -0
- package/docs/getting-started/installation-guide.md +6 -1
- package/docs/getting-started/quick-start.md +6 -1
- package/docs/migration/DOCUMENTATION_STRUCTURE.md +441 -0
- package/docs/migration/MIGRATION_GUIDE.md +6 -28
- package/docs/migration/README.md +52 -6
- package/docs/migration/V0.5.190_TO_V0.6.1_MIGRATION.md +1153 -0
- package/docs/migration/V0.6.0_REACT_19_MIGRATION.md +227 -0
- package/docs/migration/database-changes-december-2025.md +3 -3
- package/docs/rbac/event-based-apps.md +1 -1
- package/docs/rbac/getting-started.md +1 -1
- package/docs/rbac/quick-start.md +1 -1
- package/docs/standards/README.md +40 -0
- package/docs/troubleshooting/migration.md +4 -4
- package/examples/PublicPages/PublicEventPage.tsx +1 -1
- package/package.json +12 -6
- package/scripts/audit/core/checks/accessibility.cjs +197 -0
- package/scripts/audit/core/checks/api-usage.cjs +191 -0
- package/scripts/audit/core/checks/bundle.cjs +142 -0
- package/scripts/{check-pace-core-compliance.cjs → audit/core/checks/compliance.cjs} +737 -691
- package/scripts/audit/core/checks/config.cjs +54 -0
- package/scripts/audit/core/checks/coverage.cjs +84 -0
- package/scripts/audit/core/checks/dependencies.cjs +454 -0
- package/scripts/audit/core/checks/documentation.cjs +203 -0
- package/scripts/audit/core/checks/environment.cjs +128 -0
- package/scripts/audit/core/checks/error-handling.cjs +299 -0
- package/scripts/audit/core/checks/forms.cjs +172 -0
- package/scripts/audit/core/checks/heuristics.cjs +68 -0
- package/scripts/audit/core/checks/hooks.cjs +334 -0
- package/scripts/audit/core/checks/imports.cjs +244 -0
- package/scripts/audit/core/checks/performance.cjs +325 -0
- package/scripts/audit/core/checks/routes.cjs +117 -0
- package/scripts/audit/core/checks/state.cjs +130 -0
- package/scripts/audit/core/checks/structure.cjs +65 -0
- package/scripts/audit/core/checks/style.cjs +584 -0
- package/scripts/audit/core/checks/testing.cjs +122 -0
- package/scripts/audit/core/checks/typescript.cjs +61 -0
- package/scripts/audit/core/scanner.cjs +199 -0
- package/scripts/audit/core/utils.cjs +137 -0
- package/scripts/audit/index.cjs +223 -0
- package/scripts/audit/reporters/console.cjs +151 -0
- package/scripts/audit/reporters/json.cjs +54 -0
- package/scripts/audit/reporters/markdown.cjs +124 -0
- package/scripts/audit-consuming-app.cjs +86 -0
- package/scripts/build-docs/build-decision.js +240 -0
- package/scripts/build-docs/cache-utils.js +105 -0
- package/scripts/build-docs/content-normalization.js +150 -0
- package/scripts/build-docs/file-utils.js +105 -0
- package/scripts/build-docs/git-utils.js +86 -0
- package/scripts/build-docs/hash-utils.js +116 -0
- package/scripts/build-docs/typedoc-runner.js +220 -0
- package/scripts/build-docs-incremental.js +77 -913
- package/scripts/install-cursor-rules.cjs +236 -0
- package/scripts/utils/command-runner.js +16 -11
- package/scripts/validate-formats.js +61 -56
- package/scripts/validate-master.js +74 -69
- package/scripts/validate-pre-publish.js +70 -65
- package/src/__tests__/helpers/test-providers.tsx +1 -1
- package/src/__tests__/helpers/test-utils.tsx +1 -1
- package/src/__tests__/hooks/usePermissions.test.ts +2 -2
- package/src/components/Alert/Alert.test.tsx +12 -18
- package/src/components/Alert/Alert.tsx +5 -7
- package/src/components/Avatar/Avatar.test.tsx +4 -4
- package/src/components/Badge/Badge.tsx +16 -4
- package/src/components/Button/Button.tsx +27 -4
- package/src/components/Calendar/Calendar.tsx +9 -3
- package/src/components/Card/Card.tsx +4 -0
- package/src/components/Checkbox/Checkbox.test.tsx +12 -12
- package/src/components/Checkbox/Checkbox.tsx +2 -2
- package/src/components/DataTable/DataTable.test.tsx +57 -93
- package/src/components/DataTable/DataTable.tsx +40 -6
- package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +5 -6
- package/src/components/DataTable/__tests__/pagination.modes.test.tsx +29 -7
- package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx +12 -12
- package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +2 -3
- package/src/components/DataTable/components/AccessDeniedPage.tsx +17 -26
- package/src/components/DataTable/components/ActionButtons.tsx +10 -7
- package/src/components/DataTable/components/BulkOperationsDropdown.tsx +2 -2
- package/src/components/DataTable/components/ColumnFilter.tsx +10 -0
- package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +12 -0
- package/src/components/DataTable/components/DataTableBody.tsx +8 -0
- package/src/components/DataTable/components/DataTableCore.tsx +200 -561
- package/src/components/DataTable/components/DataTableErrorBoundary.tsx +11 -0
- package/src/components/DataTable/components/DataTableLayout.tsx +559 -0
- package/src/components/DataTable/components/DataTableModals.tsx +9 -1
- package/src/components/DataTable/components/DataTableToolbar.tsx +8 -0
- package/src/components/DataTable/components/DraggableColumnHeader.tsx +12 -0
- package/src/components/DataTable/components/EditFields.tsx +307 -0
- package/src/components/DataTable/components/EditableRow.tsx +9 -1
- package/src/components/DataTable/components/EmptyState.tsx +10 -0
- package/src/components/DataTable/components/FilterRow.tsx +12 -0
- package/src/components/DataTable/components/GroupHeader.tsx +12 -0
- package/src/components/DataTable/components/GroupingDropdown.tsx +12 -0
- package/src/components/DataTable/components/ImportModal.tsx +7 -0
- package/src/components/DataTable/components/LoadingState.tsx +6 -0
- package/src/components/DataTable/components/PaginationControls.tsx +16 -1
- package/src/components/DataTable/components/RowComponent.tsx +391 -0
- package/src/components/DataTable/components/UnifiedTableBody.tsx +62 -852
- package/src/components/DataTable/components/VirtualizedDataTable.tsx +16 -4
- package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +4 -2
- 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/components/cellValueUtils.ts +40 -0
- package/src/components/DataTable/components/hooks/useImportModalFocus.ts +53 -0
- package/src/components/DataTable/components/hooks/usePermissionTracking.ts +126 -0
- package/src/components/DataTable/context/DataTableContext.tsx +50 -0
- package/src/components/DataTable/core/ColumnFactory.ts +31 -0
- package/src/components/DataTable/core/DataTableContext.tsx +32 -1
- package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +10 -0
- package/src/components/DataTable/hooks/useColumnReordering.ts +14 -2
- package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +10 -0
- package/src/components/DataTable/hooks/useDataTableDataPipeline.ts +16 -0
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +124 -32
- package/src/components/DataTable/hooks/useDataTableState.ts +35 -1
- package/src/components/DataTable/hooks/useEffectiveColumnOrder.ts +12 -0
- package/src/components/DataTable/hooks/useKeyboardNavigation.ts +2 -2
- package/src/components/DataTable/hooks/useServerSideDataEffect.ts +11 -0
- package/src/components/DataTable/hooks/useTableColumns.ts +8 -0
- package/src/components/DataTable/hooks/useTableHandlers.ts +14 -0
- package/src/components/DataTable/styles.ts +6 -6
- package/src/components/DataTable/types.ts +6 -10
- package/src/components/DataTable/utils/a11yUtils.ts +7 -0
- package/src/components/DataTable/utils/debugTools.ts +18 -113
- package/src/components/DataTable/utils/errorHandling.ts +12 -0
- package/src/components/DataTable/utils/exportUtils.ts +9 -0
- package/src/components/DataTable/utils/flexibleImport.ts +12 -48
- package/src/components/DataTable/utils/paginationUtils.ts +8 -0
- package/src/components/DataTable/utils/performanceUtils.ts +5 -1
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +8 -14
- package/src/components/Dialog/Dialog.tsx +8 -7
- package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +180 -1
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +46 -6
- package/src/components/ErrorBoundary/ErrorBoundaryContext.tsx +129 -0
- package/src/components/ErrorBoundary/index.ts +27 -2
- package/src/components/EventSelector/EventSelector.tsx +4 -1
- package/src/components/FileDisplay/FileDisplay.test.tsx +2 -2
- package/src/components/FileDisplay/FileDisplay.tsx +32 -18
- package/src/components/FileUpload/FileUpload.tsx +22 -2
- package/src/components/Footer/Footer.test.tsx +16 -16
- package/src/components/Footer/Footer.tsx +15 -12
- package/src/components/Form/Form.test.tsx +36 -15
- package/src/components/Form/Form.tsx +31 -26
- package/src/components/Header/Header.tsx +22 -11
- package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +40 -40
- package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +1 -1
- package/src/components/Input/Input.test.tsx +2 -2
- package/src/components/Input/Input.tsx +36 -34
- package/src/components/Label/Label.tsx +1 -1
- package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +4 -4
- package/src/components/LoadingSpinner/LoadingSpinner.tsx +1 -1
- package/src/components/LoginForm/LoginForm.test.tsx +42 -42
- package/src/components/LoginForm/LoginForm.tsx +12 -8
- package/src/components/NavigationMenu/NavigationMenu.tsx +15 -514
- package/src/components/NavigationMenu/types.ts +56 -0
- package/src/components/NavigationMenu/useNavigationFiltering.ts +390 -0
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +3 -0
- package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +1 -1
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +54 -52
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +33 -12
- package/src/components/PaceAppLayout/README.md +1 -1
- package/src/components/PaceAppLayout/test-setup.tsx +1 -2
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +4 -1
- package/src/components/PasswordChange/PasswordChangeForm.test.tsx +33 -33
- package/src/components/PasswordChange/PasswordChangeForm.tsx +10 -1
- package/src/components/Progress/Progress.tsx +1 -1
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +3 -9
- package/src/components/PublicLayout/PublicPageLayout.tsx +3 -6
- package/src/components/PublicLayout/PublicPageProvider.tsx +4 -0
- package/src/components/Select/Select.tsx +95 -438
- package/src/components/Select/context.ts +23 -0
- package/src/components/Select/hooks/useSelectEvents.ts +87 -0
- package/src/components/Select/hooks/useSelectSearch.ts +91 -0
- package/src/components/Select/hooks/useSelectState.ts +104 -0
- package/src/components/Select/index.ts +9 -1
- package/src/components/Select/types.ts +123 -0
- package/src/components/Select/utils/text.ts +26 -0
- package/src/components/SessionRestorationLoader/SessionRestorationLoader.tsx +5 -6
- package/src/components/Switch/Switch.tsx +4 -4
- package/src/components/Table/Table.tsx +1 -1
- package/src/components/Tabs/Tabs.tsx +1 -1
- package/src/components/Textarea/Textarea.tsx +27 -29
- package/src/components/Toast/Toast.tsx +5 -1
- package/src/components/Tooltip/Tooltip.tsx +3 -3
- package/src/components/UserMenu/UserMenu.test.tsx +24 -11
- package/src/components/UserMenu/UserMenu.tsx +22 -19
- package/src/components/index.ts +2 -2
- package/src/hooks/__tests__/hooks.integration.test.tsx +80 -55
- package/src/hooks/__tests__/index.unit.test.ts +2 -5
- package/src/hooks/__tests__/useStorage.unit.test.ts +36 -36
- package/src/hooks/index.ts +1 -2
- package/src/hooks/public/usePublicEvent.ts +5 -1
- package/src/hooks/public/usePublicEventLogo.ts +5 -1
- package/src/hooks/public/usePublicFileDisplay.ts +4 -0
- package/src/hooks/public/usePublicRouteParams.ts +5 -1
- package/src/hooks/services/useAuth.ts +32 -0
- package/src/hooks/services/useCurrentEvent.ts +6 -0
- package/src/hooks/services/useCurrentOrganisation.ts +6 -0
- package/src/hooks/useDataTableState.ts +8 -18
- package/src/hooks/useDebounce.ts +9 -0
- package/src/hooks/useEventTheme.ts +6 -0
- package/src/hooks/useFileDisplay.ts +4 -0
- package/src/hooks/useFileReference.ts +25 -7
- package/src/hooks/useFileUrl.ts +11 -1
- package/src/hooks/useFocusManagement.ts +16 -2
- package/src/hooks/useFocusTrap.ts +7 -4
- package/src/hooks/useFormDialog.ts +8 -7
- package/src/hooks/useInactivityTracker.ts +4 -1
- package/src/hooks/useKeyboardShortcuts.ts +4 -0
- package/src/hooks/useOrganisationPermissions.ts +4 -0
- package/src/hooks/useOrganisationSecurity.ts +4 -0
- package/src/hooks/usePerformanceMonitor.ts +4 -0
- package/src/hooks/usePermissionCache.ts +8 -1
- package/src/hooks/useQueryCache.ts +12 -1
- package/src/hooks/useSessionRestoration.ts +4 -0
- package/src/hooks/useStorage.ts +4 -0
- package/src/hooks/useToast.ts +3 -3
- package/src/index.ts +2 -1
- package/src/providers/__tests__/OrganisationProvider.test.tsx +115 -49
- package/src/providers/__tests__/ProviderLifecycle.test.tsx +21 -6
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +10 -10
- package/src/providers/services/AuthServiceProvider.tsx +18 -0
- package/src/providers/services/EventServiceProvider.tsx +18 -0
- package/src/providers/services/InactivityServiceProvider.tsx +18 -0
- package/src/providers/services/OrganisationServiceProvider.tsx +18 -0
- package/src/providers/services/UnifiedAuthProvider.tsx +58 -22
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +33 -7
- package/src/rbac/README.md +1 -1
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +26 -26
- package/src/rbac/__tests__/scenarios.user-role.test.tsx +4 -5
- package/src/rbac/adapters.tsx +14 -5
- package/src/rbac/api.ts +100 -67
- package/src/rbac/components/EnhancedNavigationMenu.tsx +1 -1
- package/src/rbac/components/NavigationGuard.tsx +1 -1
- package/src/rbac/components/NavigationProvider.tsx +5 -2
- package/src/rbac/components/PagePermissionGuard.tsx +158 -18
- package/src/rbac/components/PagePermissionProvider.tsx +1 -1
- package/src/rbac/components/PermissionEnforcer.tsx +1 -1
- package/src/rbac/components/RoleBasedRouter.tsx +6 -2
- package/src/rbac/components/SecureDataProvider.test.tsx +84 -49
- package/src/rbac/components/SecureDataProvider.tsx +21 -6
- package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +24 -14
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +7 -0
- package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +14 -6
- package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +15 -4
- package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +148 -24
- package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +81 -15
- package/src/rbac/engine.ts +38 -14
- package/src/rbac/hooks/permissions/index.ts +7 -0
- package/src/rbac/hooks/permissions/useAccessLevel.ts +105 -0
- package/src/rbac/hooks/permissions/useCachedPermissions.ts +79 -0
- package/src/rbac/hooks/permissions/useCan.ts +347 -0
- package/src/rbac/hooks/permissions/useHasAllPermissions.ts +90 -0
- package/src/rbac/hooks/permissions/useHasAnyPermission.ts +90 -0
- package/src/rbac/hooks/permissions/useMultiplePermissions.ts +93 -0
- package/src/rbac/hooks/permissions/usePermissions.ts +253 -0
- package/src/rbac/hooks/useCan.test.ts +71 -64
- package/src/rbac/hooks/usePermissions.ts +14 -995
- package/src/rbac/hooks/useResourcePermissions.test.ts +54 -18
- package/src/rbac/hooks/useResourcePermissions.ts +14 -4
- package/src/rbac/hooks/useSecureSupabase.ts +33 -13
- package/src/rbac/permissions.ts +0 -30
- package/src/rbac/secureClient.ts +212 -61
- package/src/rbac/types.ts +8 -0
- package/src/theming/__tests__/parseEventColours.test.ts +6 -9
- package/src/theming/parseEventColours.ts +5 -19
- package/src/types/vitest-globals.d.ts +51 -26
- package/src/utils/__mocks__/supabaseMock.ts +1 -3
- package/src/utils/__tests__/formatting.unit.test.ts +4 -4
- package/src/utils/__tests__/index.unit.test.ts +2 -2
- package/src/utils/audit/audit.ts +0 -3
- package/src/utils/core/cn.ts +1 -1
- package/src/utils/file-reference/index.ts +53 -1
- package/src/utils/formatting/formatting.ts +8 -18
- package/src/utils/index.ts +0 -1
- package/src/utils/security/secureDataAccess.test.ts +31 -20
- package/src/utils/security/secureDataAccess.ts +4 -3
- package/dist/chunk-6C4YBBJM.js +0 -628
- package/dist/chunk-6C4YBBJM.js.map +0 -1
- package/dist/chunk-7D4SUZUM.js 2.map +0 -1
- package/dist/chunk-7EQTDTTJ.js 2.map +0 -1
- package/dist/chunk-7EQTDTTJ.js.map +0 -1
- package/dist/chunk-7FLMSG37.js 2.map +0 -1
- package/dist/chunk-7FLMSG37.js.map +0 -1
- package/dist/chunk-BC4IJKSL.js.map +0 -1
- package/dist/chunk-E3SPN4VZ.js +0 -12917
- package/dist/chunk-E3SPN4VZ.js.map +0 -1
- package/dist/chunk-E66EQZE6 5.js +0 -37
- package/dist/chunk-E66EQZE6.js 2.map +0 -1
- package/dist/chunk-HWIIPPNI.js.map +0 -1
- package/dist/chunk-I7PSE6JW 5.js +0 -191
- package/dist/chunk-I7PSE6JW.js 2.map +0 -1
- package/dist/chunk-I7PSE6JW.js.map +0 -1
- package/dist/chunk-IIELH4DL.js.map +0 -1
- package/dist/chunk-KNC55RTG.js 5.map +0 -1
- package/dist/chunk-KNC55RTG.js.map +0 -1
- package/dist/chunk-KQCRWDSA.js 5.map +0 -1
- package/dist/chunk-LFNCN2SP.js +0 -412
- package/dist/chunk-LFNCN2SP.js 2.map +0 -1
- package/dist/chunk-LFNCN2SP.js.map +0 -1
- package/dist/chunk-LMC26NLJ 2.js +0 -84
- package/dist/chunk-NOAYCWCX.js +0 -4993
- package/dist/chunk-NOAYCWCX.js.map +0 -1
- package/dist/chunk-QWWZ5CAQ.js 3.map +0 -1
- package/dist/chunk-QWWZ5CAQ.js.map +0 -1
- package/dist/chunk-QXHPKYJV 3.js +0 -113
- package/dist/chunk-R77UEZ4E.js +0 -68
- package/dist/chunk-R77UEZ4E.js.map +0 -1
- package/dist/chunk-SQGMNID3.js.map +0 -1
- package/dist/chunk-VBXEHIUJ.js 6.map +0 -1
- package/dist/chunk-XNXXZ43G.js.map +0 -1
- package/dist/chunk-ZSAAAMVR 6.js +0 -25
- package/dist/components.js 5.map +0 -1
- package/dist/styles/index 2.js +0 -12
- package/dist/styles/index.js 5.map +0 -1
- package/dist/theming/runtime 5.js +0 -19
- package/dist/theming/runtime.js 5.map +0 -1
- package/docs/api/classes/ErrorBoundary.md +0 -144
- package/docs/migration/quick-migration-guide.md +0 -356
- package/docs/migration/service-architecture.md +0 -281
- package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +0 -680
- package/src/hooks/useSecureDataAccess.test.ts +0 -559
- package/src/hooks/useSecureDataAccess.ts +0 -666
- /package/dist/{DataTable-5FU7IESH.js.map → DataTable-TPTKCX4D.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-RGJTDE2C.js.map → UnifiedAuthProvider-CH6Z342H.js.map} +0 -0
- /package/dist/{api-N774RPUA.js.map → api-MVVQZLJI.js.map} +0 -0
- /package/docs/migration/{organisation-context-timing-fix.md → V0.3.44_organisation-context-timing-fix.md} +0 -0
- /package/docs/migration/{rbac-migration.md → V0.4.0_rbac-migration.md} +0 -0
- /package/docs/migration/{person-scoped-profiles-migration-guide.md → V0.5.190_person-scoped-profiles-migration-guide.md} +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
|
@@ -8,10 +8,12 @@
|
|
|
8
8
|
* Tests form rendering, validation, submission, and accessibility.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
import React from 'react';
|
|
11
12
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
12
13
|
import { render, screen, waitFor, act } from '@testing-library/react';
|
|
13
14
|
import userEvent from '@testing-library/user-event';
|
|
14
15
|
import { z } from 'zod';
|
|
16
|
+
import { useWatch, useFormState, useFormContext } from 'react-hook-form';
|
|
15
17
|
import { Form, FormField } from './index';
|
|
16
18
|
import { renderWithProviders } from '../../__tests__/helpers/test-utils';
|
|
17
19
|
|
|
@@ -108,27 +110,46 @@ describe('Form Component', () => {
|
|
|
108
110
|
|
|
109
111
|
it('shares live form state with render prop children', async () => {
|
|
110
112
|
const onSubmit = vi.fn();
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
113
|
+
const InnerComponent = () => {
|
|
114
|
+
// useWatch and useFormState must be called inside FormProvider context
|
|
115
|
+
// useFormState subscribes to form state changes and triggers re-renders
|
|
116
|
+
const { control } = useFormContext();
|
|
117
|
+
const nameValue = useWatch({ name: 'name', control });
|
|
118
|
+
const { isDirty } = useFormState({ control });
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<>
|
|
122
|
+
<span data-testid="dirty-flag">{isDirty ? 'dirty' : 'pristine'}</span>
|
|
123
|
+
<span data-testid="watched-value">{nameValue || ''}</span>
|
|
124
|
+
<FormField name="name" label="Name" />
|
|
125
|
+
</>
|
|
126
|
+
);
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const TestComponent = () => {
|
|
130
|
+
return (
|
|
131
|
+
<Form onSubmit={onSubmit} defaultValues={{ name: '' }}>
|
|
132
|
+
<InnerComponent />
|
|
133
|
+
</Form>
|
|
134
|
+
);
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
renderWithProviders(<TestComponent />);
|
|
123
138
|
|
|
124
139
|
const input = screen.getByLabelText('Name');
|
|
125
140
|
expect(screen.getByTestId('dirty-flag')).toHaveTextContent('pristine');
|
|
141
|
+
|
|
142
|
+
// Type into the input field
|
|
143
|
+
await user.clear(input);
|
|
126
144
|
await user.type(input, 'Jane');
|
|
127
145
|
|
|
146
|
+
// Wait for both the watched value and dirty state to update
|
|
128
147
|
await waitFor(() => {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
148
|
+
const watchedValue = screen.getByTestId('watched-value');
|
|
149
|
+
const dirtyFlag = screen.getByTestId('dirty-flag');
|
|
150
|
+
expect(watchedValue).toHaveTextContent('Jane');
|
|
151
|
+
expect(dirtyFlag).toHaveTextContent('dirty');
|
|
152
|
+
}, { timeout: 5000 });
|
|
132
153
|
});
|
|
133
154
|
});
|
|
134
155
|
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
* - react-hook-form - Form state management
|
|
69
69
|
* - @hookform/resolvers/zod - Zod validation integration
|
|
70
70
|
* - zod - Schema validation
|
|
71
|
-
* - React
|
|
71
|
+
* - React 19+ - Hooks and context
|
|
72
72
|
*/
|
|
73
73
|
|
|
74
74
|
import React from 'react';
|
|
@@ -153,6 +153,7 @@ export function Form<TFieldValues extends FieldValues = FieldValues>({
|
|
|
153
153
|
resolver: schema ? zodResolver(schema) : undefined,
|
|
154
154
|
defaultValues,
|
|
155
155
|
mode,
|
|
156
|
+
shouldUnregister: false,
|
|
156
157
|
});
|
|
157
158
|
|
|
158
159
|
const handleSubmit = methods.handleSubmit(onSubmit, onError);
|
|
@@ -224,7 +225,7 @@ export interface FormFieldProps<
|
|
|
224
225
|
field: ControllerRenderProps<TFieldValues, TName>;
|
|
225
226
|
fieldState: ControllerFieldState;
|
|
226
227
|
formState: UseFormStateReturn<TFieldValues>;
|
|
227
|
-
}) => React.ReactElement
|
|
228
|
+
}) => React.ReactElement<any>;
|
|
228
229
|
|
|
229
230
|
/**
|
|
230
231
|
* Test ID
|
|
@@ -318,13 +319,7 @@ export function FormField<
|
|
|
318
319
|
'data-testid': testId,
|
|
319
320
|
className,
|
|
320
321
|
}: FormFieldProps<TFieldValues, TName>) {
|
|
321
|
-
const { control
|
|
322
|
-
const fieldError = errors[name];
|
|
323
|
-
|
|
324
|
-
// Safely extract error message
|
|
325
|
-
const errorMessage = fieldError && typeof fieldError === 'object' && 'message' in fieldError
|
|
326
|
-
? String(fieldError.message)
|
|
327
|
-
: undefined;
|
|
322
|
+
const { control } = useFormContext<TFieldValues>();
|
|
328
323
|
|
|
329
324
|
return (
|
|
330
325
|
<div className={cn("space-y-2", className)}>
|
|
@@ -344,32 +339,42 @@ export function FormField<
|
|
|
344
339
|
control={control}
|
|
345
340
|
rules={validation}
|
|
346
341
|
render={(props) => {
|
|
342
|
+
const { field, fieldState } = props;
|
|
343
|
+
const fieldError = fieldState.error;
|
|
344
|
+
|
|
345
|
+
// Safely extract error message
|
|
346
|
+
const errorMessage = fieldError && typeof fieldError === 'object' && 'message' in fieldError
|
|
347
|
+
? String(fieldError.message)
|
|
348
|
+
: undefined;
|
|
349
|
+
|
|
347
350
|
if (render) {
|
|
348
351
|
return render(props);
|
|
349
352
|
}
|
|
350
353
|
|
|
351
354
|
return (
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
355
|
+
<>
|
|
356
|
+
<input
|
|
357
|
+
{...field}
|
|
358
|
+
id={name}
|
|
359
|
+
type={type}
|
|
360
|
+
placeholder={placeholder}
|
|
361
|
+
data-testid={testId}
|
|
362
|
+
aria-label={label || placeholder || name}
|
|
363
|
+
className={cn(
|
|
364
|
+
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
|
365
|
+
fieldError && "border-destructive focus-visible:ring-destructive"
|
|
366
|
+
)}
|
|
367
|
+
{...inputProps}
|
|
368
|
+
/>
|
|
369
|
+
{errorMessage && (
|
|
370
|
+
<p className="text-destructive" role="alert">
|
|
371
|
+
{errorMessage}
|
|
372
|
+
</p>
|
|
361
373
|
)}
|
|
362
|
-
|
|
363
|
-
/>
|
|
374
|
+
</>
|
|
364
375
|
);
|
|
365
376
|
}}
|
|
366
377
|
/>
|
|
367
|
-
|
|
368
|
-
{errorMessage && (
|
|
369
|
-
<p className="text-destructive" role="alert">
|
|
370
|
-
{errorMessage}
|
|
371
|
-
</p>
|
|
372
|
-
)}
|
|
373
378
|
</div>
|
|
374
379
|
);
|
|
375
380
|
}
|
|
@@ -42,10 +42,10 @@
|
|
|
42
42
|
* ]}
|
|
43
43
|
* currentPath="/dashboard"
|
|
44
44
|
* actions={
|
|
45
|
-
*
|
|
45
|
+
* <>
|
|
46
46
|
* <Button variant="outline">Export</Button>
|
|
47
47
|
* <Button>New Item</Button>
|
|
48
|
-
*
|
|
48
|
+
* </>
|
|
49
49
|
* }
|
|
50
50
|
* user={currentUser}
|
|
51
51
|
* onSignOut={handleSignOut}
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
*
|
|
80
80
|
* @dependencies
|
|
81
81
|
* - @supabase/supabase-js - User authentication
|
|
82
|
-
* - React
|
|
82
|
+
* - React 19+ - Component framework
|
|
83
83
|
* - Tailwind CSS - Styling
|
|
84
84
|
* - NavigationMenu component
|
|
85
85
|
* - UserMenu component
|
|
@@ -199,10 +199,10 @@ export interface HeaderProps {
|
|
|
199
199
|
*
|
|
200
200
|
* function HeaderWithActions() {
|
|
201
201
|
* const customActions = (
|
|
202
|
-
*
|
|
202
|
+
* <>
|
|
203
203
|
* <Button variant="outline" size="sm">Export</Button>
|
|
204
204
|
* <Button size="sm">New Item</Button>
|
|
205
|
-
*
|
|
205
|
+
* </>
|
|
206
206
|
* );
|
|
207
207
|
*
|
|
208
208
|
* return (
|
|
@@ -238,6 +238,13 @@ export interface HeaderProps {
|
|
|
238
238
|
*
|
|
239
239
|
* @since 0.1.0
|
|
240
240
|
*/
|
|
241
|
+
/**
|
|
242
|
+
* Header component for application layouts.
|
|
243
|
+
* Provides navigation, user menu, organisation/event selectors, and customizable branding.
|
|
244
|
+
*
|
|
245
|
+
* @param props - Header configuration
|
|
246
|
+
* @returns The rendered header
|
|
247
|
+
*/
|
|
241
248
|
export function Header({
|
|
242
249
|
logoUrl,
|
|
243
250
|
logoAlt = 'Logo',
|
|
@@ -278,7 +285,7 @@ export function Header({
|
|
|
278
285
|
"w-full border-b border-main-200 h-16 shadow-sm bg-main-100 ",
|
|
279
286
|
className
|
|
280
287
|
)} role="banner">
|
|
281
|
-
<nav className="px-4 w-[min(var(--app-width),100%)] mx-auto
|
|
288
|
+
<nav className="px-4 w-[min(var(--app-width),100%)] mx-auto grid grid-cols-[auto_1fr_auto_auto_auto_auto] items-center gap-4 h-full">
|
|
282
289
|
{/* Logo */}
|
|
283
290
|
{logo ? (
|
|
284
291
|
logoHref ? (
|
|
@@ -333,10 +340,7 @@ export function Header({
|
|
|
333
340
|
itemsPreFiltered={true}
|
|
334
341
|
/>
|
|
335
342
|
)}
|
|
336
|
-
|
|
337
343
|
|
|
338
|
-
{/* Right side: Organisation Selector, Event Selector, Actions, and User Menu */}
|
|
339
|
-
<div className="flex items-center gap-4 ml-auto">
|
|
340
344
|
{/* Organisation Selector - Only show if user has organisations */}
|
|
341
345
|
{showOrgSelector ? (
|
|
342
346
|
<OrganisationSelectorConditional />
|
|
@@ -346,7 +350,15 @@ export function Header({
|
|
|
346
350
|
{showEventSelector ? (
|
|
347
351
|
<EventSelector
|
|
348
352
|
placeholder="Select event"
|
|
349
|
-
className=
|
|
353
|
+
className={cn(
|
|
354
|
+
"w-96",
|
|
355
|
+
// If both org selector and actions exist, EventSelector uses 1 column
|
|
356
|
+
// If only one exists, EventSelector spans 2 columns
|
|
357
|
+
// If neither exists, EventSelector spans 3 columns
|
|
358
|
+
showOrgSelector && actions ? "col-span-1" :
|
|
359
|
+
showOrgSelector || actions ? "col-span-2" :
|
|
360
|
+
"col-span-3"
|
|
361
|
+
)}
|
|
350
362
|
data-testid="event-selector"
|
|
351
363
|
/>
|
|
352
364
|
) : null}
|
|
@@ -367,7 +379,6 @@ export function Header({
|
|
|
367
379
|
/>
|
|
368
380
|
)
|
|
369
381
|
)}
|
|
370
|
-
</div>
|
|
371
382
|
|
|
372
383
|
</nav>
|
|
373
384
|
</header>
|
|
@@ -65,7 +65,7 @@ vi.mock('lucide-react', () => ({
|
|
|
65
65
|
}));
|
|
66
66
|
|
|
67
67
|
describe('InactivityWarningModal Component', () => {
|
|
68
|
-
const
|
|
68
|
+
const baseProps = {
|
|
69
69
|
isOpen: true,
|
|
70
70
|
timeRemaining: 45,
|
|
71
71
|
onStaySignedIn: vi.fn(),
|
|
@@ -74,20 +74,20 @@ describe('InactivityWarningModal Component', () => {
|
|
|
74
74
|
|
|
75
75
|
describe('Rendering', () => {
|
|
76
76
|
it('renders when isOpen is true', () => {
|
|
77
|
-
renderWithProviders(<InactivityWarningModal {...
|
|
77
|
+
renderWithProviders(<InactivityWarningModal {...baseProps} />);
|
|
78
78
|
|
|
79
79
|
expect(screen.getByTestId('dialog')).toBeInTheDocument();
|
|
80
80
|
expect(screen.getByTestId('inactivity-warning-modal')).toBeInTheDocument();
|
|
81
81
|
});
|
|
82
82
|
|
|
83
83
|
it('does not render when isOpen is false', () => {
|
|
84
|
-
renderWithProviders(<InactivityWarningModal {...
|
|
84
|
+
renderWithProviders(<InactivityWarningModal {...baseProps} isOpen={false} />);
|
|
85
85
|
|
|
86
86
|
expect(screen.queryByTestId('dialog')).not.toBeInTheDocument();
|
|
87
87
|
});
|
|
88
88
|
|
|
89
89
|
it('renders with default title and description', () => {
|
|
90
|
-
renderWithProviders(<InactivityWarningModal {...
|
|
90
|
+
renderWithProviders(<InactivityWarningModal {...baseProps} />);
|
|
91
91
|
|
|
92
92
|
expect(screen.getByText('Session Timeout Warning')).toBeInTheDocument();
|
|
93
93
|
expect(screen.getByText("You've been inactive for a while. Your session will expire soon for security reasons.")).toBeInTheDocument();
|
|
@@ -95,7 +95,7 @@ describe('InactivityWarningModal Component', () => {
|
|
|
95
95
|
|
|
96
96
|
it('renders with custom title and description', () => {
|
|
97
97
|
const customProps = {
|
|
98
|
-
...
|
|
98
|
+
...baseProps,
|
|
99
99
|
title: 'Custom Warning',
|
|
100
100
|
description: 'Custom description text',
|
|
101
101
|
};
|
|
@@ -107,58 +107,58 @@ describe('InactivityWarningModal Component', () => {
|
|
|
107
107
|
});
|
|
108
108
|
|
|
109
109
|
it('renders with custom className', () => {
|
|
110
|
-
renderWithProviders(<InactivityWarningModal {...
|
|
110
|
+
renderWithProviders(<InactivityWarningModal {...baseProps} className="custom-modal" />);
|
|
111
111
|
|
|
112
112
|
const modal = screen.getByTestId('inactivity-warning-modal');
|
|
113
113
|
expect(modal).toHaveClass('custom-modal');
|
|
114
114
|
});
|
|
115
115
|
|
|
116
116
|
it('renders countdown timer with correct time format', () => {
|
|
117
|
-
renderWithProviders(<InactivityWarningModal {...
|
|
117
|
+
renderWithProviders(<InactivityWarningModal {...baseProps} timeRemaining={125} />);
|
|
118
118
|
|
|
119
119
|
expect(screen.getByText('02:05')).toBeInTheDocument();
|
|
120
120
|
});
|
|
121
121
|
|
|
122
122
|
it('renders countdown timer with single digit minutes', () => {
|
|
123
|
-
renderWithProviders(<InactivityWarningModal {...
|
|
123
|
+
renderWithProviders(<InactivityWarningModal {...baseProps} timeRemaining={65} />);
|
|
124
124
|
|
|
125
125
|
expect(screen.getByText('01:05')).toBeInTheDocument();
|
|
126
126
|
});
|
|
127
127
|
|
|
128
128
|
it('renders countdown timer with zero seconds', () => {
|
|
129
|
-
renderWithProviders(<InactivityWarningModal {...
|
|
129
|
+
renderWithProviders(<InactivityWarningModal {...baseProps} timeRemaining={60} />);
|
|
130
130
|
|
|
131
131
|
expect(screen.getByText('01:00')).toBeInTheDocument();
|
|
132
132
|
});
|
|
133
133
|
|
|
134
134
|
it('renders countdown timer with single digit seconds', () => {
|
|
135
|
-
renderWithProviders(<InactivityWarningModal {...
|
|
135
|
+
renderWithProviders(<InactivityWarningModal {...baseProps} timeRemaining={5} />);
|
|
136
136
|
|
|
137
137
|
expect(screen.getByText('00:05')).toBeInTheDocument();
|
|
138
138
|
});
|
|
139
139
|
|
|
140
140
|
it('renders countdown timer with zero time', () => {
|
|
141
|
-
renderWithProviders(<InactivityWarningModal {...
|
|
141
|
+
renderWithProviders(<InactivityWarningModal {...baseProps} timeRemaining={0} />);
|
|
142
142
|
|
|
143
143
|
expect(screen.getByText('00:00')).toBeInTheDocument();
|
|
144
144
|
});
|
|
145
145
|
|
|
146
146
|
it('renders action buttons with correct text', () => {
|
|
147
|
-
renderWithProviders(<InactivityWarningModal {...
|
|
147
|
+
renderWithProviders(<InactivityWarningModal {...baseProps} />);
|
|
148
148
|
|
|
149
149
|
expect(screen.getByRole('button', { name: 'Stay Signed In' })).toBeInTheDocument();
|
|
150
150
|
expect(screen.getByRole('button', { name: 'Sign Out Now' })).toBeInTheDocument();
|
|
151
151
|
});
|
|
152
152
|
|
|
153
153
|
it('renders icons correctly', () => {
|
|
154
|
-
renderWithProviders(<InactivityWarningModal {...
|
|
154
|
+
renderWithProviders(<InactivityWarningModal {...baseProps} />);
|
|
155
155
|
|
|
156
156
|
expect(screen.getByTestId('alert-triangle-icon')).toBeInTheDocument();
|
|
157
157
|
expect(screen.getByTestId('clock-icon')).toBeInTheDocument();
|
|
158
158
|
});
|
|
159
159
|
|
|
160
160
|
it('renders additional security information', () => {
|
|
161
|
-
renderWithProviders(<InactivityWarningModal {...
|
|
161
|
+
renderWithProviders(<InactivityWarningModal {...baseProps} />);
|
|
162
162
|
|
|
163
163
|
expect(screen.getByText("For security reasons, you'll be automatically signed out after 30 minutes of inactivity.")).toBeInTheDocument();
|
|
164
164
|
});
|
|
@@ -178,7 +178,7 @@ describe('InactivityWarningModal Component', () => {
|
|
|
178
178
|
|
|
179
179
|
testCases.forEach(({ input, expected }) => {
|
|
180
180
|
const { unmount } = renderWithProviders(
|
|
181
|
-
<InactivityWarningModal {...
|
|
181
|
+
<InactivityWarningModal {...baseProps} timeRemaining={input} />
|
|
182
182
|
);
|
|
183
183
|
|
|
184
184
|
expect(screen.getByText(expected)).toBeInTheDocument();
|
|
@@ -188,12 +188,12 @@ describe('InactivityWarningModal Component', () => {
|
|
|
188
188
|
|
|
189
189
|
it('updates display time when timeRemaining prop changes', () => {
|
|
190
190
|
const { rerender } = renderWithProviders(
|
|
191
|
-
<InactivityWarningModal {...
|
|
191
|
+
<InactivityWarningModal {...baseProps} timeRemaining={60} />
|
|
192
192
|
);
|
|
193
193
|
|
|
194
194
|
expect(screen.getByText('01:00')).toBeInTheDocument();
|
|
195
195
|
|
|
196
|
-
rerender(<InactivityWarningModal {...
|
|
196
|
+
rerender(<InactivityWarningModal {...baseProps} timeRemaining={30} />);
|
|
197
197
|
|
|
198
198
|
expect(screen.getByText('00:30')).toBeInTheDocument();
|
|
199
199
|
});
|
|
@@ -205,7 +205,7 @@ describe('InactivityWarningModal Component', () => {
|
|
|
205
205
|
const onStaySignedIn = vi.fn();
|
|
206
206
|
|
|
207
207
|
renderWithProviders(
|
|
208
|
-
<InactivityWarningModal {...
|
|
208
|
+
<InactivityWarningModal {...baseProps} onStaySignedIn={onStaySignedIn} />
|
|
209
209
|
);
|
|
210
210
|
|
|
211
211
|
await user.click(screen.getByRole('button', { name: 'Stay Signed In' }));
|
|
@@ -218,7 +218,7 @@ describe('InactivityWarningModal Component', () => {
|
|
|
218
218
|
const onSignOutNow = vi.fn();
|
|
219
219
|
|
|
220
220
|
renderWithProviders(
|
|
221
|
-
<InactivityWarningModal {...
|
|
221
|
+
<InactivityWarningModal {...baseProps} onSignOutNow={onSignOutNow} />
|
|
222
222
|
);
|
|
223
223
|
|
|
224
224
|
await user.click(screen.getByRole('button', { name: 'Sign Out Now' }));
|
|
@@ -231,7 +231,7 @@ describe('InactivityWarningModal Component', () => {
|
|
|
231
231
|
const onStaySignedIn = vi.fn();
|
|
232
232
|
|
|
233
233
|
renderWithProviders(
|
|
234
|
-
<InactivityWarningModal {...
|
|
234
|
+
<InactivityWarningModal {...baseProps} onStaySignedIn={onStaySignedIn} />
|
|
235
235
|
);
|
|
236
236
|
|
|
237
237
|
// Simulate dialog close (this would normally happen via the Dialog component)
|
|
@@ -244,7 +244,7 @@ describe('InactivityWarningModal Component', () => {
|
|
|
244
244
|
|
|
245
245
|
describe('Button Styling and Props', () => {
|
|
246
246
|
it('applies correct styling to Stay Signed In button', () => {
|
|
247
|
-
renderWithProviders(<InactivityWarningModal {...
|
|
247
|
+
renderWithProviders(<InactivityWarningModal {...baseProps} />);
|
|
248
248
|
|
|
249
249
|
const stayButton = screen.getByRole('button', { name: 'Stay Signed In' });
|
|
250
250
|
expect(stayButton).toHaveClass('bg-main-600', 'hover:bg-main-700', 'text-main-50');
|
|
@@ -252,7 +252,7 @@ describe('InactivityWarningModal Component', () => {
|
|
|
252
252
|
});
|
|
253
253
|
|
|
254
254
|
it('applies correct styling to Sign Out Now button', () => {
|
|
255
|
-
renderWithProviders(<InactivityWarningModal {...
|
|
255
|
+
renderWithProviders(<InactivityWarningModal {...baseProps} />);
|
|
256
256
|
|
|
257
257
|
const signOutButton = screen.getByRole('button', { name: 'Sign Out Now' });
|
|
258
258
|
expect(signOutButton).toHaveAttribute('data-variant', 'outline');
|
|
@@ -263,7 +263,7 @@ describe('InactivityWarningModal Component', () => {
|
|
|
263
263
|
|
|
264
264
|
describe('Accessibility', () => {
|
|
265
265
|
it('has proper ARIA attributes', () => {
|
|
266
|
-
renderWithProviders(<InactivityWarningModal {...
|
|
266
|
+
renderWithProviders(<InactivityWarningModal {...baseProps} />);
|
|
267
267
|
|
|
268
268
|
const dialog = screen.getByTestId('dialog');
|
|
269
269
|
expect(dialog).toHaveAttribute('role', 'dialog');
|
|
@@ -277,14 +277,14 @@ describe('InactivityWarningModal Component', () => {
|
|
|
277
277
|
});
|
|
278
278
|
|
|
279
279
|
it('has accessible button labels', () => {
|
|
280
|
-
renderWithProviders(<InactivityWarningModal {...
|
|
280
|
+
renderWithProviders(<InactivityWarningModal {...baseProps} />);
|
|
281
281
|
|
|
282
282
|
expect(screen.getByRole('button', { name: 'Stay Signed In' })).toBeInTheDocument();
|
|
283
283
|
expect(screen.getByRole('button', { name: 'Sign Out Now' })).toBeInTheDocument();
|
|
284
284
|
});
|
|
285
285
|
|
|
286
286
|
it('has proper heading structure', () => {
|
|
287
|
-
renderWithProviders(<InactivityWarningModal {...
|
|
287
|
+
renderWithProviders(<InactivityWarningModal {...baseProps} />);
|
|
288
288
|
|
|
289
289
|
const title = screen.getByRole('heading', { level: 2 });
|
|
290
290
|
expect(title).toBeInTheDocument();
|
|
@@ -292,7 +292,7 @@ describe('InactivityWarningModal Component', () => {
|
|
|
292
292
|
});
|
|
293
293
|
|
|
294
294
|
it('provides descriptive text for screen readers', () => {
|
|
295
|
-
renderWithProviders(<InactivityWarningModal {...
|
|
295
|
+
renderWithProviders(<InactivityWarningModal {...baseProps} />);
|
|
296
296
|
|
|
297
297
|
expect(screen.getByText('Time remaining before automatic logout')).toBeInTheDocument();
|
|
298
298
|
expect(screen.getByText("For security reasons, you'll be automatically signed out after 30 minutes of inactivity.")).toBeInTheDocument();
|
|
@@ -301,7 +301,7 @@ describe('InactivityWarningModal Component', () => {
|
|
|
301
301
|
|
|
302
302
|
describe('Dialog Configuration', () => {
|
|
303
303
|
it('configures dialog with correct props', () => {
|
|
304
|
-
renderWithProviders(<InactivityWarningModal {...
|
|
304
|
+
renderWithProviders(<InactivityWarningModal {...baseProps} />);
|
|
305
305
|
|
|
306
306
|
const modal = screen.getByTestId('inactivity-warning-modal');
|
|
307
307
|
expect(modal).toHaveClass('sm:max-w-md');
|
|
@@ -311,7 +311,7 @@ describe('InactivityWarningModal Component', () => {
|
|
|
311
311
|
|
|
312
312
|
it('applies custom className to dialog content', () => {
|
|
313
313
|
renderWithProviders(
|
|
314
|
-
<InactivityWarningModal {...
|
|
314
|
+
<InactivityWarningModal {...baseProps} className="custom-dialog-class" />
|
|
315
315
|
);
|
|
316
316
|
|
|
317
317
|
const modal = screen.getByTestId('inactivity-warning-modal');
|
|
@@ -321,14 +321,14 @@ describe('InactivityWarningModal Component', () => {
|
|
|
321
321
|
|
|
322
322
|
describe('Edge Cases', () => {
|
|
323
323
|
it('handles negative time values gracefully', () => {
|
|
324
|
-
renderWithProviders(<InactivityWarningModal {...
|
|
324
|
+
renderWithProviders(<InactivityWarningModal {...baseProps} timeRemaining={-5} />);
|
|
325
325
|
|
|
326
326
|
// Should still render, but with negative time formatted as the component actually does it
|
|
327
327
|
expect(screen.getByText('-1:-5')).toBeInTheDocument();
|
|
328
328
|
});
|
|
329
329
|
|
|
330
330
|
it('handles very large time values', () => {
|
|
331
|
-
renderWithProviders(<InactivityWarningModal {...
|
|
331
|
+
renderWithProviders(<InactivityWarningModal {...baseProps} timeRemaining={999999} />);
|
|
332
332
|
|
|
333
333
|
expect(screen.getByText('16666:39')).toBeInTheDocument();
|
|
334
334
|
});
|
|
@@ -353,7 +353,7 @@ describe('InactivityWarningModal Component', () => {
|
|
|
353
353
|
it('handles empty string title and description', () => {
|
|
354
354
|
renderWithProviders(
|
|
355
355
|
<InactivityWarningModal
|
|
356
|
-
{...
|
|
356
|
+
{...baseProps}
|
|
357
357
|
title=""
|
|
358
358
|
description=""
|
|
359
359
|
/>
|
|
@@ -370,19 +370,19 @@ describe('InactivityWarningModal Component', () => {
|
|
|
370
370
|
describe('Integration', () => {
|
|
371
371
|
it('works with rapid prop changes', () => {
|
|
372
372
|
const { rerender } = renderWithProviders(
|
|
373
|
-
<InactivityWarningModal {...
|
|
373
|
+
<InactivityWarningModal {...baseProps} timeRemaining={60} />
|
|
374
374
|
);
|
|
375
375
|
|
|
376
376
|
expect(screen.getByText('01:00')).toBeInTheDocument();
|
|
377
377
|
|
|
378
378
|
// Rapid changes
|
|
379
|
-
rerender(<InactivityWarningModal {...
|
|
379
|
+
rerender(<InactivityWarningModal {...baseProps} timeRemaining={30} />);
|
|
380
380
|
expect(screen.getByText('00:30')).toBeInTheDocument();
|
|
381
381
|
|
|
382
|
-
rerender(<InactivityWarningModal {...
|
|
382
|
+
rerender(<InactivityWarningModal {...baseProps} timeRemaining={15} />);
|
|
383
383
|
expect(screen.getByText('00:15')).toBeInTheDocument();
|
|
384
384
|
|
|
385
|
-
rerender(<InactivityWarningModal {...
|
|
385
|
+
rerender(<InactivityWarningModal {...baseProps} timeRemaining={0} />);
|
|
386
386
|
expect(screen.getByText('00:00')).toBeInTheDocument();
|
|
387
387
|
});
|
|
388
388
|
|
|
@@ -393,7 +393,7 @@ describe('InactivityWarningModal Component', () => {
|
|
|
393
393
|
|
|
394
394
|
renderWithProviders(
|
|
395
395
|
<InactivityWarningModal
|
|
396
|
-
{...
|
|
396
|
+
{...baseProps}
|
|
397
397
|
onStaySignedIn={onStaySignedIn}
|
|
398
398
|
onSignOutNow={onSignOutNow}
|
|
399
399
|
/>
|
|
@@ -413,17 +413,17 @@ describe('InactivityWarningModal Component', () => {
|
|
|
413
413
|
|
|
414
414
|
it('maintains state consistency during re-renders', () => {
|
|
415
415
|
const { rerender } = renderWithProviders(
|
|
416
|
-
<InactivityWarningModal {...
|
|
416
|
+
<InactivityWarningModal {...baseProps} timeRemaining={45} />
|
|
417
417
|
);
|
|
418
418
|
|
|
419
419
|
expect(screen.getByText('00:45')).toBeInTheDocument();
|
|
420
420
|
|
|
421
421
|
// Re-render with same props
|
|
422
|
-
rerender(<InactivityWarningModal {...
|
|
422
|
+
rerender(<InactivityWarningModal {...baseProps} timeRemaining={45} />);
|
|
423
423
|
expect(screen.getByText('00:45')).toBeInTheDocument();
|
|
424
424
|
|
|
425
425
|
// Re-render with different props
|
|
426
|
-
rerender(<InactivityWarningModal {...
|
|
426
|
+
rerender(<InactivityWarningModal {...baseProps} timeRemaining={30} />);
|
|
427
427
|
expect(screen.getByText('00:30')).toBeInTheDocument();
|
|
428
428
|
});
|
|
429
429
|
});
|
|
@@ -447,7 +447,7 @@ describe('InactivityWarningModal Component', () => {
|
|
|
447
447
|
|
|
448
448
|
it('handles invalid timeRemaining values', () => {
|
|
449
449
|
const invalidTimeProps = {
|
|
450
|
-
...
|
|
450
|
+
...baseProps,
|
|
451
451
|
timeRemaining: NaN,
|
|
452
452
|
};
|
|
453
453
|
|
|
@@ -420,14 +420,14 @@ describe('InputGroup Component', () => {
|
|
|
420
420
|
// Accessibility tests
|
|
421
421
|
describe('Accessibility', () => {
|
|
422
422
|
it('forwards ref correctly', () => {
|
|
423
|
-
const ref = React.createRef<
|
|
423
|
+
const ref = React.createRef<HTMLFieldSetElement>();
|
|
424
424
|
renderWithProviders(
|
|
425
425
|
<InputGroup ref={ref}>
|
|
426
426
|
<Input placeholder="Input 1" />
|
|
427
427
|
</InputGroup>
|
|
428
428
|
);
|
|
429
429
|
|
|
430
|
-
expect(ref.current).toBeInstanceOf(
|
|
430
|
+
expect(ref.current).toBeInstanceOf(HTMLFieldSetElement);
|
|
431
431
|
});
|
|
432
432
|
|
|
433
433
|
it('maintains proper focus order', async () => {
|