@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
|
@@ -55,6 +55,10 @@ import { cn } from '../../utils/core/cn';
|
|
|
55
55
|
// BASE INPUT COMPONENT
|
|
56
56
|
// ============================================================================
|
|
57
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Props for the Input component.
|
|
60
|
+
* Extends standard input HTML attributes.
|
|
61
|
+
*/
|
|
58
62
|
export interface InputProps
|
|
59
63
|
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'> {
|
|
60
64
|
/**
|
|
@@ -101,36 +105,34 @@ export interface InputProps
|
|
|
101
105
|
* />
|
|
102
106
|
* ```
|
|
103
107
|
*/
|
|
104
|
-
|
|
105
|
-
(
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}
|
|
133
|
-
);
|
|
108
|
+
function Input({ className, variant = 'default', size = 'md', error, type, ref, ...props }: InputProps & { ref?: React.Ref<HTMLInputElement> }) {
|
|
109
|
+
return (
|
|
110
|
+
<input
|
|
111
|
+
type={type}
|
|
112
|
+
className={cn(
|
|
113
|
+
// Base styles
|
|
114
|
+
'flex 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',
|
|
115
|
+
|
|
116
|
+
// Variant styles
|
|
117
|
+
{
|
|
118
|
+
'border-input': variant === 'default' && !error,
|
|
119
|
+
'border-destructive focus-visible:ring-destructive': variant === 'destructive' || error,
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
// Size styles
|
|
123
|
+
{
|
|
124
|
+
'h-8 px-2 py-1 text-xs': size === 'sm',
|
|
125
|
+
'h-9 px-3 py-2 text-sm': size === 'md',
|
|
126
|
+
'h-10 px-4 py-3 text-base': size === 'lg',
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
className
|
|
130
|
+
)}
|
|
131
|
+
ref={ref}
|
|
132
|
+
{...props}
|
|
133
|
+
/>
|
|
134
|
+
);
|
|
135
|
+
}
|
|
134
136
|
|
|
135
137
|
Input.displayName = 'Input';
|
|
136
138
|
|
|
@@ -138,7 +140,7 @@ Input.displayName = 'Input';
|
|
|
138
140
|
// INPUT GROUP COMPONENT
|
|
139
141
|
// ============================================================================
|
|
140
142
|
|
|
141
|
-
export interface InputGroupProps extends React.HTMLAttributes<
|
|
143
|
+
export interface InputGroupProps extends React.HTMLAttributes<HTMLFieldSetElement> {
|
|
142
144
|
/** Child elements to be rendered in the group */
|
|
143
145
|
children: React.ReactNode;
|
|
144
146
|
/** Layout orientation of the input group */
|
|
@@ -167,7 +169,7 @@ export interface InputGroupProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
|
167
169
|
* </InputGroup>
|
|
168
170
|
* ```
|
|
169
171
|
*/
|
|
170
|
-
export const InputGroup = React.forwardRef<
|
|
172
|
+
export const InputGroup = React.forwardRef<HTMLFieldSetElement, InputGroupProps>(
|
|
171
173
|
({ className, children, orientation = 'vertical', spacing = 'md', ...props }, ref) => {
|
|
172
174
|
const spacingClasses = {
|
|
173
175
|
sm: orientation === 'horizontal' ? 'space-x-2' : 'space-y-2',
|
|
@@ -176,7 +178,7 @@ export const InputGroup = React.forwardRef<HTMLDivElement, InputGroupProps>(
|
|
|
176
178
|
};
|
|
177
179
|
|
|
178
180
|
return (
|
|
179
|
-
<
|
|
181
|
+
<fieldset
|
|
180
182
|
ref={ref}
|
|
181
183
|
className={cn(
|
|
182
184
|
'flex',
|
|
@@ -187,7 +189,7 @@ export const InputGroup = React.forwardRef<HTMLDivElement, InputGroupProps>(
|
|
|
187
189
|
{...props}
|
|
188
190
|
>
|
|
189
191
|
{children}
|
|
190
|
-
</
|
|
192
|
+
</fieldset>
|
|
191
193
|
);
|
|
192
194
|
}
|
|
193
195
|
);
|
|
@@ -382,10 +382,10 @@ describe('LoadingSpinner Component', () => {
|
|
|
382
382
|
|
|
383
383
|
it('works within a card or container', () => {
|
|
384
384
|
renderWithProviders(
|
|
385
|
-
<
|
|
385
|
+
<section className="card">
|
|
386
386
|
<h2>Loading Content</h2>
|
|
387
387
|
<LoadingSpinner size="lg" />
|
|
388
|
-
</
|
|
388
|
+
</section>
|
|
389
389
|
);
|
|
390
390
|
|
|
391
391
|
const spinner = screen.getByRole('status');
|
|
@@ -397,11 +397,11 @@ describe('LoadingSpinner Component', () => {
|
|
|
397
397
|
|
|
398
398
|
it('works with multiple instances', () => {
|
|
399
399
|
renderWithProviders(
|
|
400
|
-
|
|
400
|
+
<>
|
|
401
401
|
<LoadingSpinner size="sm" />
|
|
402
402
|
<LoadingSpinner size="md" />
|
|
403
403
|
<LoadingSpinner size="lg" />
|
|
404
|
-
|
|
404
|
+
</>
|
|
405
405
|
);
|
|
406
406
|
|
|
407
407
|
const spinners = screen.getAllByRole('status');
|
|
@@ -105,13 +105,13 @@ vi.mock('../../utils/core/cn', () => ({
|
|
|
105
105
|
}));
|
|
106
106
|
|
|
107
107
|
describe('LoginForm Component', () => {
|
|
108
|
-
const
|
|
108
|
+
const baseProps = {
|
|
109
109
|
onSignIn: vi.fn(),
|
|
110
110
|
};
|
|
111
111
|
|
|
112
112
|
describe('Rendering', () => {
|
|
113
113
|
it('renders with default props', () => {
|
|
114
|
-
renderWithProviders(<LoginForm {...
|
|
114
|
+
renderWithProviders(<LoginForm {...baseProps} />);
|
|
115
115
|
|
|
116
116
|
expect(screen.getByTestId('login-form')).toBeInTheDocument();
|
|
117
117
|
expect(screen.getByTestId('card')).toBeInTheDocument();
|
|
@@ -121,7 +121,7 @@ describe('LoginForm Component', () => {
|
|
|
121
121
|
it('renders with custom title and subtitle', () => {
|
|
122
122
|
renderWithProviders(
|
|
123
123
|
<LoginForm
|
|
124
|
-
{...
|
|
124
|
+
{...baseProps}
|
|
125
125
|
title="Welcome Back"
|
|
126
126
|
subtitle="Please enter your credentials"
|
|
127
127
|
/>
|
|
@@ -134,7 +134,7 @@ describe('LoginForm Component', () => {
|
|
|
134
134
|
it('renders with app name in title', () => {
|
|
135
135
|
renderWithProviders(
|
|
136
136
|
<LoginForm
|
|
137
|
-
{...
|
|
137
|
+
{...baseProps}
|
|
138
138
|
appName="My App"
|
|
139
139
|
/>
|
|
140
140
|
);
|
|
@@ -145,7 +145,7 @@ describe('LoginForm Component', () => {
|
|
|
145
145
|
it('renders with custom className', () => {
|
|
146
146
|
renderWithProviders(
|
|
147
147
|
<LoginForm
|
|
148
|
-
{...
|
|
148
|
+
{...baseProps}
|
|
149
149
|
className="custom-login-form"
|
|
150
150
|
/>
|
|
151
151
|
);
|
|
@@ -154,7 +154,7 @@ describe('LoginForm Component', () => {
|
|
|
154
154
|
});
|
|
155
155
|
|
|
156
156
|
it('renders form inputs with correct attributes', () => {
|
|
157
|
-
renderWithProviders(<LoginForm {...
|
|
157
|
+
renderWithProviders(<LoginForm {...baseProps} />);
|
|
158
158
|
|
|
159
159
|
const emailInput = screen.getByLabelText('Email');
|
|
160
160
|
const passwordInput = screen.getByLabelText('Password');
|
|
@@ -169,13 +169,13 @@ describe('LoginForm Component', () => {
|
|
|
169
169
|
});
|
|
170
170
|
|
|
171
171
|
it('renders submit button with correct text', () => {
|
|
172
|
-
renderWithProviders(<LoginForm {...
|
|
172
|
+
renderWithProviders(<LoginForm {...baseProps} />);
|
|
173
173
|
|
|
174
174
|
expect(screen.getByRole('button', { name: 'Sign In' })).toBeInTheDocument();
|
|
175
175
|
});
|
|
176
176
|
|
|
177
177
|
it('renders sign-up link when showSignUp is true without onSignUp', () => {
|
|
178
|
-
renderWithProviders(<LoginForm {...
|
|
178
|
+
renderWithProviders(<LoginForm {...baseProps} showSignUp={true} />);
|
|
179
179
|
|
|
180
180
|
expect(screen.getByText("Don't have an account?")).toBeInTheDocument();
|
|
181
181
|
expect(screen.getByRole('link', { name: 'Sign up' })).toBeInTheDocument();
|
|
@@ -186,7 +186,7 @@ describe('LoginForm Component', () => {
|
|
|
186
186
|
const onSignUp = vi.fn();
|
|
187
187
|
renderWithProviders(
|
|
188
188
|
<LoginForm
|
|
189
|
-
{...
|
|
189
|
+
{...baseProps}
|
|
190
190
|
showSignUp={true}
|
|
191
191
|
onSignUp={onSignUp}
|
|
192
192
|
/>
|
|
@@ -199,7 +199,7 @@ describe('LoginForm Component', () => {
|
|
|
199
199
|
|
|
200
200
|
describe('Form Validation', () => {
|
|
201
201
|
it('disables submit button when form is empty', () => {
|
|
202
|
-
renderWithProviders(<LoginForm {...
|
|
202
|
+
renderWithProviders(<LoginForm {...baseProps} />);
|
|
203
203
|
|
|
204
204
|
const submitButton = screen.getByRole('button', { name: 'Sign In' });
|
|
205
205
|
expect(submitButton).toBeDisabled();
|
|
@@ -207,7 +207,7 @@ describe('LoginForm Component', () => {
|
|
|
207
207
|
|
|
208
208
|
it('enables submit button when both fields have values', async () => {
|
|
209
209
|
const user = userEvent.setup();
|
|
210
|
-
renderWithProviders(<LoginForm {...
|
|
210
|
+
renderWithProviders(<LoginForm {...baseProps} />);
|
|
211
211
|
|
|
212
212
|
const emailInput = screen.getByLabelText('Email');
|
|
213
213
|
const passwordInput = screen.getByLabelText('Password');
|
|
@@ -224,7 +224,7 @@ describe('LoginForm Component', () => {
|
|
|
224
224
|
|
|
225
225
|
it('disables submit button when only email is filled', async () => {
|
|
226
226
|
const user = userEvent.setup();
|
|
227
|
-
renderWithProviders(<LoginForm {...
|
|
227
|
+
renderWithProviders(<LoginForm {...baseProps} />);
|
|
228
228
|
|
|
229
229
|
const emailInput = screen.getByLabelText('Email');
|
|
230
230
|
const submitButton = screen.getByRole('button', { name: 'Sign In' });
|
|
@@ -235,7 +235,7 @@ describe('LoginForm Component', () => {
|
|
|
235
235
|
|
|
236
236
|
it('disables submit button when only password is filled', async () => {
|
|
237
237
|
const user = userEvent.setup();
|
|
238
|
-
renderWithProviders(<LoginForm {...
|
|
238
|
+
renderWithProviders(<LoginForm {...baseProps} />);
|
|
239
239
|
|
|
240
240
|
const passwordInput = screen.getByLabelText('Password');
|
|
241
241
|
const submitButton = screen.getByRole('button', { name: 'Sign In' });
|
|
@@ -248,7 +248,7 @@ describe('LoginForm Component', () => {
|
|
|
248
248
|
describe('User Interactions', () => {
|
|
249
249
|
it('updates email input value when typed', async () => {
|
|
250
250
|
const user = userEvent.setup();
|
|
251
|
-
renderWithProviders(<LoginForm {...
|
|
251
|
+
renderWithProviders(<LoginForm {...baseProps} />);
|
|
252
252
|
|
|
253
253
|
const emailInput = screen.getByLabelText('Email');
|
|
254
254
|
await user.type(emailInput, 'test@example.com');
|
|
@@ -258,7 +258,7 @@ describe('LoginForm Component', () => {
|
|
|
258
258
|
|
|
259
259
|
it('updates password input value when typed', async () => {
|
|
260
260
|
const user = userEvent.setup();
|
|
261
|
-
renderWithProviders(<LoginForm {...
|
|
261
|
+
renderWithProviders(<LoginForm {...baseProps} />);
|
|
262
262
|
|
|
263
263
|
const passwordInput = screen.getByLabelText('Password');
|
|
264
264
|
await user.type(passwordInput, 'password123');
|
|
@@ -269,7 +269,7 @@ describe('LoginForm Component', () => {
|
|
|
269
269
|
it('calls onSignIn with form data when submitted', async () => {
|
|
270
270
|
const user = userEvent.setup();
|
|
271
271
|
const onSignIn = vi.fn().mockResolvedValue(undefined);
|
|
272
|
-
renderWithProviders(<LoginForm {...
|
|
272
|
+
renderWithProviders(<LoginForm {...baseProps} onSignIn={onSignIn} />);
|
|
273
273
|
|
|
274
274
|
const emailInput = screen.getByLabelText('Email');
|
|
275
275
|
const passwordInput = screen.getByLabelText('Password');
|
|
@@ -290,7 +290,7 @@ describe('LoginForm Component', () => {
|
|
|
290
290
|
const onSignUp = vi.fn();
|
|
291
291
|
renderWithProviders(
|
|
292
292
|
<LoginForm
|
|
293
|
-
{...
|
|
293
|
+
{...baseProps}
|
|
294
294
|
showSignUp={true}
|
|
295
295
|
onSignUp={onSignUp}
|
|
296
296
|
/>
|
|
@@ -305,7 +305,7 @@ describe('LoginForm Component', () => {
|
|
|
305
305
|
it('prevents form submission when form is invalid', async () => {
|
|
306
306
|
const user = userEvent.setup();
|
|
307
307
|
const onSignIn = vi.fn();
|
|
308
|
-
renderWithProviders(<LoginForm {...
|
|
308
|
+
renderWithProviders(<LoginForm {...baseProps} onSignIn={onSignIn} />);
|
|
309
309
|
|
|
310
310
|
const submitButton = screen.getByRole('button', { name: 'Sign In' });
|
|
311
311
|
await user.click(submitButton);
|
|
@@ -318,7 +318,7 @@ describe('LoginForm Component', () => {
|
|
|
318
318
|
const onSignIn = vi.fn();
|
|
319
319
|
renderWithProviders(
|
|
320
320
|
<LoginForm
|
|
321
|
-
{...
|
|
321
|
+
{...baseProps}
|
|
322
322
|
onSignIn={onSignIn}
|
|
323
323
|
isLoading={true}
|
|
324
324
|
/>
|
|
@@ -338,13 +338,13 @@ describe('LoginForm Component', () => {
|
|
|
338
338
|
|
|
339
339
|
describe('Loading States', () => {
|
|
340
340
|
it('shows loading text on submit button when loading', () => {
|
|
341
|
-
renderWithProviders(<LoginForm {...
|
|
341
|
+
renderWithProviders(<LoginForm {...baseProps} isLoading={true} />);
|
|
342
342
|
|
|
343
343
|
expect(screen.getByRole('button', { name: 'Signing in...' })).toBeInTheDocument();
|
|
344
344
|
});
|
|
345
345
|
|
|
346
346
|
it('disables form inputs when loading', () => {
|
|
347
|
-
renderWithProviders(<LoginForm {...
|
|
347
|
+
renderWithProviders(<LoginForm {...baseProps} isLoading={true} />);
|
|
348
348
|
|
|
349
349
|
const emailInput = screen.getByLabelText('Email');
|
|
350
350
|
const passwordInput = screen.getByLabelText('Password');
|
|
@@ -354,7 +354,7 @@ describe('LoginForm Component', () => {
|
|
|
354
354
|
});
|
|
355
355
|
|
|
356
356
|
it('disables submit button when loading', () => {
|
|
357
|
-
renderWithProviders(<LoginForm {...
|
|
357
|
+
renderWithProviders(<LoginForm {...baseProps} isLoading={true} />);
|
|
358
358
|
|
|
359
359
|
const submitButton = screen.getByRole('button', { name: 'Signing in...' });
|
|
360
360
|
expect(submitButton).toBeDisabled();
|
|
@@ -363,7 +363,7 @@ describe('LoginForm Component', () => {
|
|
|
363
363
|
|
|
364
364
|
describe('Error Handling', () => {
|
|
365
365
|
it('displays error message when provided', () => {
|
|
366
|
-
renderWithProviders(<LoginForm {...
|
|
366
|
+
renderWithProviders(<LoginForm {...baseProps} />);
|
|
367
367
|
|
|
368
368
|
// Simulate error state by triggering form submission with error
|
|
369
369
|
const form = screen.getByTestId('login-form');
|
|
@@ -381,7 +381,7 @@ describe('LoginForm Component', () => {
|
|
|
381
381
|
|
|
382
382
|
renderWithProviders(
|
|
383
383
|
<LoginForm
|
|
384
|
-
{...
|
|
384
|
+
{...baseProps}
|
|
385
385
|
onSignIn={onSignIn}
|
|
386
386
|
onError={onError}
|
|
387
387
|
/>
|
|
@@ -406,7 +406,7 @@ describe('LoginForm Component', () => {
|
|
|
406
406
|
|
|
407
407
|
renderWithProviders(
|
|
408
408
|
<LoginForm
|
|
409
|
-
{...
|
|
409
|
+
{...baseProps}
|
|
410
410
|
onSignIn={onSignIn}
|
|
411
411
|
/>
|
|
412
412
|
);
|
|
@@ -431,7 +431,7 @@ describe('LoginForm Component', () => {
|
|
|
431
431
|
|
|
432
432
|
renderWithProviders(
|
|
433
433
|
<LoginForm
|
|
434
|
-
{...
|
|
434
|
+
{...baseProps}
|
|
435
435
|
onSignIn={onSignIn}
|
|
436
436
|
onError={onError}
|
|
437
437
|
/>
|
|
@@ -459,7 +459,7 @@ describe('LoginForm Component', () => {
|
|
|
459
459
|
|
|
460
460
|
renderWithProviders(
|
|
461
461
|
<LoginForm
|
|
462
|
-
{...
|
|
462
|
+
{...baseProps}
|
|
463
463
|
onSignIn={onSignIn}
|
|
464
464
|
/>
|
|
465
465
|
);
|
|
@@ -493,7 +493,7 @@ describe('LoginForm Component', () => {
|
|
|
493
493
|
|
|
494
494
|
renderWithProviders(
|
|
495
495
|
<LoginForm
|
|
496
|
-
{...
|
|
496
|
+
{...baseProps}
|
|
497
497
|
onSignIn={onSignIn}
|
|
498
498
|
onSuccess={onSuccess}
|
|
499
499
|
/>
|
|
@@ -518,7 +518,7 @@ describe('LoginForm Component', () => {
|
|
|
518
518
|
|
|
519
519
|
renderWithProviders(
|
|
520
520
|
<LoginForm
|
|
521
|
-
{...
|
|
521
|
+
{...baseProps}
|
|
522
522
|
onSignIn={onSignIn}
|
|
523
523
|
/>
|
|
524
524
|
);
|
|
@@ -540,7 +540,7 @@ describe('LoginForm Component', () => {
|
|
|
540
540
|
|
|
541
541
|
describe('Accessibility', () => {
|
|
542
542
|
it('has proper form structure', () => {
|
|
543
|
-
renderWithProviders(<LoginForm {...
|
|
543
|
+
renderWithProviders(<LoginForm {...baseProps} />);
|
|
544
544
|
|
|
545
545
|
const form = screen.getByTestId('login-form');
|
|
546
546
|
expect(form).toBeInTheDocument();
|
|
@@ -548,7 +548,7 @@ describe('LoginForm Component', () => {
|
|
|
548
548
|
});
|
|
549
549
|
|
|
550
550
|
it('has proper label associations', () => {
|
|
551
|
-
renderWithProviders(<LoginForm {...
|
|
551
|
+
renderWithProviders(<LoginForm {...baseProps} />);
|
|
552
552
|
|
|
553
553
|
const emailInput = screen.getByLabelText('Email');
|
|
554
554
|
const passwordInput = screen.getByLabelText('Password');
|
|
@@ -558,7 +558,7 @@ describe('LoginForm Component', () => {
|
|
|
558
558
|
});
|
|
559
559
|
|
|
560
560
|
it('has proper heading structure', () => {
|
|
561
|
-
renderWithProviders(<LoginForm {...
|
|
561
|
+
renderWithProviders(<LoginForm {...baseProps} />);
|
|
562
562
|
|
|
563
563
|
const title = screen.getByRole('heading', { level: 2 });
|
|
564
564
|
expect(title).toBeInTheDocument();
|
|
@@ -571,7 +571,7 @@ describe('LoginForm Component', () => {
|
|
|
571
571
|
|
|
572
572
|
renderWithProviders(
|
|
573
573
|
<LoginForm
|
|
574
|
-
{...
|
|
574
|
+
{...baseProps}
|
|
575
575
|
onSignIn={onSignIn}
|
|
576
576
|
/>
|
|
577
577
|
);
|
|
@@ -594,7 +594,7 @@ describe('LoginForm Component', () => {
|
|
|
594
594
|
|
|
595
595
|
it('supports keyboard navigation', async () => {
|
|
596
596
|
const user = userEvent.setup();
|
|
597
|
-
renderWithProviders(<LoginForm {...
|
|
597
|
+
renderWithProviders(<LoginForm {...baseProps} />);
|
|
598
598
|
|
|
599
599
|
const emailInput = screen.getByLabelText('Email');
|
|
600
600
|
const passwordInput = screen.getByLabelText('Password');
|
|
@@ -627,7 +627,7 @@ describe('LoginForm Component', () => {
|
|
|
627
627
|
describe('Edge Cases', () => {
|
|
628
628
|
it('handles empty string values', async () => {
|
|
629
629
|
const user = userEvent.setup();
|
|
630
|
-
renderWithProviders(<LoginForm {...
|
|
630
|
+
renderWithProviders(<LoginForm {...baseProps} />);
|
|
631
631
|
|
|
632
632
|
const emailInput = screen.getByLabelText('Email');
|
|
633
633
|
const passwordInput = screen.getByLabelText('Password');
|
|
@@ -646,7 +646,7 @@ describe('LoginForm Component', () => {
|
|
|
646
646
|
const longEmail = 'a'.repeat(100) + '@example.com';
|
|
647
647
|
const longPassword = 'p'.repeat(100);
|
|
648
648
|
|
|
649
|
-
renderWithProviders(<LoginForm {...
|
|
649
|
+
renderWithProviders(<LoginForm {...baseProps} />);
|
|
650
650
|
|
|
651
651
|
const emailInput = screen.getByLabelText('Email');
|
|
652
652
|
const passwordInput = screen.getByLabelText('Password');
|
|
@@ -663,7 +663,7 @@ describe('LoginForm Component', () => {
|
|
|
663
663
|
const specialEmail = 'test+tag@example.co.uk';
|
|
664
664
|
const specialPassword = 'P@ssw0rd!@#$%^&*()';
|
|
665
665
|
|
|
666
|
-
renderWithProviders(<LoginForm {...
|
|
666
|
+
renderWithProviders(<LoginForm {...baseProps} />);
|
|
667
667
|
|
|
668
668
|
const emailInput = screen.getByLabelText('Email');
|
|
669
669
|
const passwordInput = screen.getByLabelText('Password');
|
|
@@ -683,7 +683,7 @@ describe('LoginForm Component', () => {
|
|
|
683
683
|
|
|
684
684
|
renderWithProviders(
|
|
685
685
|
<LoginForm
|
|
686
|
-
{...
|
|
686
|
+
{...baseProps}
|
|
687
687
|
onSignIn={onSignIn}
|
|
688
688
|
/>
|
|
689
689
|
);
|
|
@@ -732,12 +732,12 @@ describe('LoginForm Component', () => {
|
|
|
732
732
|
|
|
733
733
|
it('handles prop changes efficiently', () => {
|
|
734
734
|
const { rerender } = renderWithProviders(
|
|
735
|
-
<LoginForm {...
|
|
735
|
+
<LoginForm {...baseProps} appName="App 1" />
|
|
736
736
|
);
|
|
737
737
|
|
|
738
738
|
expect(screen.getByText('Sign in to App 1')).toBeInTheDocument();
|
|
739
739
|
|
|
740
|
-
rerender(<LoginForm {...
|
|
740
|
+
rerender(<LoginForm {...baseProps} appName="App 2" />);
|
|
741
741
|
|
|
742
742
|
expect(screen.getByText('Sign in to App 2')).toBeInTheDocument();
|
|
743
743
|
});
|
|
@@ -751,7 +751,7 @@ describe('LoginForm Component', () => {
|
|
|
751
751
|
|
|
752
752
|
renderWithProviders(
|
|
753
753
|
<LoginForm
|
|
754
|
-
{...
|
|
754
|
+
{...baseProps}
|
|
755
755
|
onSignIn={onSignIn}
|
|
756
756
|
onSuccess={onSuccess}
|
|
757
757
|
/>
|
|
@@ -790,7 +790,7 @@ describe('LoginForm Component', () => {
|
|
|
790
790
|
|
|
791
791
|
renderWithProviders(
|
|
792
792
|
<LoginForm
|
|
793
|
-
{...
|
|
793
|
+
{...baseProps}
|
|
794
794
|
onSignIn={onSignIn}
|
|
795
795
|
onError={onError}
|
|
796
796
|
/>
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
* - Minimal re-renders
|
|
90
90
|
*
|
|
91
91
|
* @dependencies
|
|
92
|
-
* - React
|
|
92
|
+
* - React 19+ - Hooks and memo
|
|
93
93
|
* - Button component
|
|
94
94
|
* - Input component
|
|
95
95
|
* - Label component
|
|
@@ -106,6 +106,10 @@ import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle }
|
|
|
106
106
|
import { Alert, AlertDescription } from '../Alert/Alert';
|
|
107
107
|
import { cn } from '../../utils/core/cn';
|
|
108
108
|
|
|
109
|
+
/**
|
|
110
|
+
* Props for the LoginForm component.
|
|
111
|
+
* Configures login form behavior, validation, and callbacks.
|
|
112
|
+
*/
|
|
109
113
|
export interface LoginFormProps {
|
|
110
114
|
/** Callback invoked when the form is submitted */
|
|
111
115
|
onSignIn: (data: { email: string; password: string }) => Promise<void>;
|
|
@@ -167,14 +171,14 @@ export const LoginForm = React.memo<LoginFormProps>(({
|
|
|
167
171
|
return formData.email.length > 0 && formData.password.length > 0;
|
|
168
172
|
}, [formData.email, formData.password]);
|
|
169
173
|
|
|
170
|
-
//
|
|
171
|
-
const handleEmailChange =
|
|
174
|
+
// React Compiler handles memoization automatically
|
|
175
|
+
const handleEmailChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
172
176
|
setFormData(prev => ({ ...prev, email: e.target.value }));
|
|
173
|
-
}
|
|
177
|
+
};
|
|
174
178
|
|
|
175
|
-
const handlePasswordChange =
|
|
179
|
+
const handlePasswordChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
176
180
|
setFormData(prev => ({ ...prev, password: e.target.value }));
|
|
177
|
-
}
|
|
181
|
+
};
|
|
178
182
|
|
|
179
183
|
const handleSubmit = useCallback(async (e: React.FormEvent) => {
|
|
180
184
|
e.preventDefault();
|
|
@@ -190,9 +194,9 @@ export const LoginForm = React.memo<LoginFormProps>(({
|
|
|
190
194
|
}
|
|
191
195
|
}, [formData, isFormValid, isLoading, onSignIn, onSuccess, onError]);
|
|
192
196
|
|
|
193
|
-
const handleSignUpClick =
|
|
197
|
+
const handleSignUpClick = () => {
|
|
194
198
|
onSignUp?.();
|
|
195
|
-
}
|
|
199
|
+
};
|
|
196
200
|
|
|
197
201
|
const displayTitle = useMemo(() => title || (appName ? `Sign in to ${appName}` : 'Sign In'), [title, appName]);
|
|
198
202
|
const displaySubtitle = useMemo(() => subtitle || 'Enter your credentials to continue.', [subtitle]);
|