@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
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Define standard folder structure and file organization for consuming apps
|
|
3
|
+
globs: ["**/*.{ts,tsx,js,jsx,md}", "src/**"]
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
paceCoreVersion: "0.6.x"
|
|
6
|
+
rulesVersion: "2025-01-28"
|
|
7
|
+
---
|
|
8
|
+
# Project Structure Standard
|
|
9
|
+
|
|
10
|
+
This guide defines the standard folder structure and file organization for consuming apps in the PACE suite.
|
|
11
|
+
|
|
12
|
+
## MUST: Follow Standard Directory Structure
|
|
13
|
+
|
|
14
|
+
**Consuming apps MUST follow this structure:**
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
your-app/
|
|
18
|
+
├── .cursor/
|
|
19
|
+
│ └── rules/ # Cursor rules (pace-core rules + local rules)
|
|
20
|
+
├── src/
|
|
21
|
+
│ ├── components/ # App-specific components
|
|
22
|
+
│ ├── hooks/ # App-specific hooks
|
|
23
|
+
│ ├── services/ # App-specific services
|
|
24
|
+
│ ├── pages/ # Page components
|
|
25
|
+
│ ├── types/ # TypeScript type definitions
|
|
26
|
+
│ ├── utils/ # App-specific utilities
|
|
27
|
+
│ ├── App.tsx # Main app component
|
|
28
|
+
│ └── main.tsx # Entry point
|
|
29
|
+
├── supabase/
|
|
30
|
+
│ ├── migrations/ # Database migrations
|
|
31
|
+
│ └── functions/ # Edge functions (if used)
|
|
32
|
+
├── public/ # Static assets
|
|
33
|
+
├── tests/ # Integration/E2E tests (optional)
|
|
34
|
+
├── audit/ # Audit reports (generated)
|
|
35
|
+
├── package.json
|
|
36
|
+
├── tsconfig.json
|
|
37
|
+
├── vite.config.ts
|
|
38
|
+
└── vitest.config.ts
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## MUST: Organize Components by Feature
|
|
42
|
+
|
|
43
|
+
**Components SHOULD be organized by feature/domain, not by type:**
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
src/
|
|
47
|
+
├── components/
|
|
48
|
+
│ ├── events/ # Event-related components
|
|
49
|
+
│ │ ├── EventCard.tsx
|
|
50
|
+
│ │ └── EventList.tsx
|
|
51
|
+
│ ├── users/ # User-related components
|
|
52
|
+
│ │ ├── UserProfile.tsx
|
|
53
|
+
│ │ └── UserList.tsx
|
|
54
|
+
│ └── shared/ # Shared app-specific components
|
|
55
|
+
│ ├── Layout.tsx
|
|
56
|
+
│ └── Navigation.tsx
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**MUST NOT:**
|
|
60
|
+
- Organize by component type (`buttons/`, `inputs/`, etc.) - use pace-core instead
|
|
61
|
+
- Create duplicate components that pace-core provides
|
|
62
|
+
|
|
63
|
+
## MUST: Colocate Tests
|
|
64
|
+
|
|
65
|
+
**Tests MUST be colocated with source files:**
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
src/
|
|
69
|
+
├── components/
|
|
70
|
+
│ └── EventCard/
|
|
71
|
+
│ ├── EventCard.tsx
|
|
72
|
+
│ └── EventCard.test.tsx
|
|
73
|
+
├── hooks/
|
|
74
|
+
│ ├── useEventData.ts
|
|
75
|
+
│ └── useEventData.test.ts
|
|
76
|
+
└── utils/
|
|
77
|
+
├── formatEvent.ts
|
|
78
|
+
└── formatEvent.test.ts
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## MUST: Follow Naming Conventions
|
|
82
|
+
|
|
83
|
+
### Files
|
|
84
|
+
- Components: `PascalCase.tsx` (e.g., `EventCard.tsx`)
|
|
85
|
+
- Hooks: `camelCase.ts` with `use` prefix (e.g., `useEventData.ts`)
|
|
86
|
+
- Utilities: `camelCase.ts` (e.g., `formatEvent.ts`)
|
|
87
|
+
- Types: `camelCase.ts` or `types.ts` (e.g., `eventTypes.ts`)
|
|
88
|
+
- Tests: `*.test.ts` or `*.test.tsx`
|
|
89
|
+
- Config: `kebab-case.config.js` (e.g., `vite.config.ts`)
|
|
90
|
+
|
|
91
|
+
### Directories
|
|
92
|
+
- Use `kebab-case` for directories (e.g., `event-management/`)
|
|
93
|
+
- Match feature/domain names
|
|
94
|
+
|
|
95
|
+
## MUST: Place Migrations Correctly
|
|
96
|
+
|
|
97
|
+
**Database migrations MUST be in `supabase/migrations/`:**
|
|
98
|
+
- Format: `YYYYMMDDHHMMSS_description.sql`
|
|
99
|
+
- Example: `20250115143022_add_user_preferences.sql`
|
|
100
|
+
|
|
101
|
+
## SHOULD: Organize by Domain
|
|
102
|
+
|
|
103
|
+
**For larger apps, SHOULD organize by domain/feature:**
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
src/
|
|
107
|
+
├── domains/
|
|
108
|
+
│ ├── events/
|
|
109
|
+
│ │ ├── components/
|
|
110
|
+
│ │ ├── hooks/
|
|
111
|
+
│ │ ├── services/
|
|
112
|
+
│ │ └── types.ts
|
|
113
|
+
│ └── users/
|
|
114
|
+
│ ├── components/
|
|
115
|
+
│ ├── hooks/
|
|
116
|
+
│ ├── services/
|
|
117
|
+
│ └── types.ts
|
|
118
|
+
├── shared/ # Shared across domains
|
|
119
|
+
│ ├── components/
|
|
120
|
+
│ ├── hooks/
|
|
121
|
+
│ └── utils/
|
|
122
|
+
└── App.tsx
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## MUST: Keep Root Directory Clean
|
|
126
|
+
|
|
127
|
+
**Root directory SHOULD only contain:**
|
|
128
|
+
- Configuration files (`package.json`, `tsconfig.json`, etc.)
|
|
129
|
+
- Documentation (`README.md`, `CHANGELOG.md`)
|
|
130
|
+
- Build outputs (`.gitignore` them)
|
|
131
|
+
- `.cursor/` directory
|
|
132
|
+
|
|
133
|
+
**MUST NOT:**
|
|
134
|
+
- Place source files in root
|
|
135
|
+
- Place test files in root
|
|
136
|
+
- Place component files in root
|
|
137
|
+
|
|
138
|
+
## MUST: Use Consistent Import Paths
|
|
139
|
+
|
|
140
|
+
**MUST use consistent import patterns:**
|
|
141
|
+
|
|
142
|
+
```tsx
|
|
143
|
+
// ✅ CORRECT - Absolute imports from src
|
|
144
|
+
import { EventCard } from '@/components/events/EventCard';
|
|
145
|
+
import { useEventData } from '@/hooks/useEventData';
|
|
146
|
+
import { formatEvent } from '@/utils/formatEvent';
|
|
147
|
+
|
|
148
|
+
// ✅ CORRECT - pace-core imports
|
|
149
|
+
import { Button, Card } from '@jmruthers/pace-core';
|
|
150
|
+
import { useUnifiedAuth } from '@jmruthers/pace-core';
|
|
151
|
+
|
|
152
|
+
// ❌ WRONG - Relative imports for distant files
|
|
153
|
+
import { EventCard } from '../../../components/events/EventCard';
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**Configure path aliases in `tsconfig.json`:**
|
|
157
|
+
```json
|
|
158
|
+
{
|
|
159
|
+
"compilerOptions": {
|
|
160
|
+
"paths": {
|
|
161
|
+
"@/*": ["./src/*"]
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## MUST: Separate App Code from pace-core
|
|
168
|
+
|
|
169
|
+
**MUST NOT:**
|
|
170
|
+
- Modify pace-core code directly
|
|
171
|
+
- Create components that duplicate pace-core functionality
|
|
172
|
+
- Import from pace-core source (use published package)
|
|
173
|
+
|
|
174
|
+
**MUST:**
|
|
175
|
+
- Use pace-core as a dependency
|
|
176
|
+
- Import from `@jmruthers/pace-core`
|
|
177
|
+
- Create app-specific components only when pace-core doesn't provide them
|
|
178
|
+
|
|
179
|
+
## SHOULD: Document Structure Decisions
|
|
180
|
+
|
|
181
|
+
**For non-standard structures, SHOULD document why:**
|
|
182
|
+
- Add `STRUCTURE.md` or section in `README.md`
|
|
183
|
+
- Explain deviations from standard
|
|
184
|
+
- Justify custom organization
|
|
185
|
+
|
|
186
|
+
## File Organization Checklist
|
|
187
|
+
|
|
188
|
+
Before committing, verify:
|
|
189
|
+
- [ ] Components organized by feature, not type
|
|
190
|
+
- [ ] Tests colocated with source files
|
|
191
|
+
- [ ] Naming conventions followed
|
|
192
|
+
- [ ] Migrations in `supabase/migrations/`
|
|
193
|
+
- [ ] Root directory clean
|
|
194
|
+
- [ ] Import paths consistent
|
|
195
|
+
- [ ] No pace-core code modifications
|
|
196
|
+
- [ ] Structure documented if non-standard
|
|
197
|
+
|
|
198
|
+
## Reference
|
|
199
|
+
|
|
200
|
+
See `project-structure.mdc` in pace-core for detailed structure patterns.
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Enforce SOLID architecture principles in consuming apps
|
|
3
|
+
globs: ["src/**/*.{ts,tsx}"]
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
paceCoreVersion: "0.6.x"
|
|
6
|
+
rulesVersion: "2025-01-28"
|
|
7
|
+
---
|
|
8
|
+
# SOLID Principles Guide
|
|
9
|
+
|
|
10
|
+
This guide enforces SOLID architecture principles to ensure maintainable, extensible, and testable code.
|
|
11
|
+
|
|
12
|
+
## Single Responsibility Principle (SRP)
|
|
13
|
+
|
|
14
|
+
**A class or function SHOULD have only one reason to change.**
|
|
15
|
+
|
|
16
|
+
### MUST: Single Responsibility
|
|
17
|
+
|
|
18
|
+
**Each function/component SHOULD do one thing:**
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
// ❌ WRONG: Multiple responsibilities (fetching, formatting, rendering in one component)
|
|
22
|
+
function UserProfile({ userId }) {
|
|
23
|
+
const [user, setUser] = useState(null);
|
|
24
|
+
const [events, setEvents] = useState([]);
|
|
25
|
+
useEffect(() => { fetchUser(userId).then(setUser); fetchEvents(userId).then(setEvents); }, [userId]);
|
|
26
|
+
return <div>...</div>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// ✅ CORRECT: Separated responsibilities (hooks for data, components for UI)
|
|
30
|
+
function UserProfile({ userId }) {
|
|
31
|
+
const user = useUser(userId);
|
|
32
|
+
const events = useUserEvents(userId);
|
|
33
|
+
return <div><UserInfo user={user} /><UserEvents events={events} /></div>;
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### MUST: Extract Complex Logic
|
|
38
|
+
|
|
39
|
+
**Complex logic SHOULD be extracted into hooks, services, or utilities:**
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
// ❌ WRONG: Business logic in component (filtering, sorting, formatting)
|
|
43
|
+
function EventList() {
|
|
44
|
+
const [events, setEvents] = useState([]);
|
|
45
|
+
const [filtered, setFiltered] = useState([]);
|
|
46
|
+
useEffect(() => { /* complex filtering/sorting logic */ }, [events]);
|
|
47
|
+
return <div>{filtered.map(...)}</div>;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ✅ CORRECT: Logic extracted to hook
|
|
51
|
+
function useFilteredEvents() {
|
|
52
|
+
const events = useEvents();
|
|
53
|
+
return useMemo(() => events.filter(...).sort(...).map(...), [events]);
|
|
54
|
+
}
|
|
55
|
+
function EventList() {
|
|
56
|
+
const filteredEvents = useFilteredEvents();
|
|
57
|
+
return <div>{filteredEvents.map(...)}</div>;
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Open/Closed Principle (OCP)
|
|
62
|
+
|
|
63
|
+
**Software entities SHOULD be open for extension but closed for modification.**
|
|
64
|
+
|
|
65
|
+
### SHOULD: Use Composition Over Modification
|
|
66
|
+
|
|
67
|
+
**Extend functionality through composition, not modification:**
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
// ❌ WRONG: Modifying base component behavior
|
|
71
|
+
function SpecialButton({ onClick, ...props }) {
|
|
72
|
+
const handleClick = (e) => { trackEvent('button-click'); onClick?.(e); };
|
|
73
|
+
return <BaseButton onClick={handleClick} {...props} />; // Modifies base
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ✅ CORRECT: Composition with pace-core (extend, don't modify)
|
|
77
|
+
import { Button } from '@jmruthers/pace-core';
|
|
78
|
+
function SpecialButton({ onClick, ...props }) {
|
|
79
|
+
const handleClick = (e) => { trackEvent('button-click'); onClick?.(e); };
|
|
80
|
+
return <Button onClick={handleClick} {...props} />;
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### SHOULD: Use Configuration Over Code Changes
|
|
85
|
+
|
|
86
|
+
**Extend behavior through configuration:**
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
// ✅ CORRECT: Extend through configuration, not code changes
|
|
90
|
+
interface DataTableConfig { columns: Column[]; features: FeatureConfig; rbac: RBACConfig; }
|
|
91
|
+
function MyDataTable({ config }: { config: DataTableConfig }) {
|
|
92
|
+
return <DataTable {...config} />;
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Liskov Substitution Principle (LSP)
|
|
97
|
+
|
|
98
|
+
**Subtypes MUST be substitutable for their base types.**
|
|
99
|
+
|
|
100
|
+
### MUST: Maintain Interface Contracts
|
|
101
|
+
|
|
102
|
+
**Derived components/hooks MUST maintain the same interface:**
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
// ✅ CORRECT: Derived hooks maintain same interface (substitutable)
|
|
106
|
+
interface BaseHook { data: Data | null; isLoading: boolean; error: Error | null; }
|
|
107
|
+
function useBaseData(): BaseHook { ... }
|
|
108
|
+
function useExtendedData(): BaseHook {
|
|
109
|
+
const base = useBaseData();
|
|
110
|
+
return { ...base }; // Maintains interface, extends behavior
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Interface Segregation Principle (ISP)
|
|
115
|
+
|
|
116
|
+
**Clients SHOULD NOT depend on interfaces they don't use.**
|
|
117
|
+
|
|
118
|
+
### MUST: Create Focused Interfaces
|
|
119
|
+
|
|
120
|
+
**Interfaces SHOULD be small and focused:**
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
// ❌ WRONG: Large interface with many responsibilities
|
|
124
|
+
interface UserService {
|
|
125
|
+
getUser(id: string): Promise<User>;
|
|
126
|
+
createUser(data: UserData): Promise<User>;
|
|
127
|
+
updateUser(id: string, data: Partial<User>): Promise<User>;
|
|
128
|
+
deleteUser(id: string): Promise<void>;
|
|
129
|
+
getUserEvents(id: string): Promise<Event[]>;
|
|
130
|
+
getUserOrganisations(id: string): Promise<Organisation[]>;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ✅ CORRECT: Segregated interfaces (focused, specific)
|
|
134
|
+
interface UserReader { getUser(id: string): Promise<User>; getUserEvents(id: string): Promise<Event[]>; }
|
|
135
|
+
interface UserWriter { createUser(data: UserData): Promise<User>; updateUser(id: string, data: Partial<User>): Promise<User>; deleteUser(id: string): Promise<void>; }
|
|
136
|
+
interface UserOrganisationService { getUserOrganisations(id: string): Promise<Organisation[]>; }
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### SHOULD: Use Specific Props
|
|
140
|
+
|
|
141
|
+
**Component props SHOULD be specific, not generic:**
|
|
142
|
+
|
|
143
|
+
```tsx
|
|
144
|
+
// ❌ WRONG: Generic props object (config: any)
|
|
145
|
+
function UserCard({ user, config }: { user: User; config: any }) { ... }
|
|
146
|
+
|
|
147
|
+
// ✅ CORRECT: Specific, focused props interface
|
|
148
|
+
interface UserCardProps { user: User; showEmail?: boolean; showAvatar?: boolean; onEdit?: (user: User) => void; }
|
|
149
|
+
function UserCard({ user, showEmail, showAvatar, onEdit }: UserCardProps) { ... }
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Dependency Inversion Principle (DIP)
|
|
153
|
+
|
|
154
|
+
**High-level modules SHOULD NOT depend on low-level modules. Both SHOULD depend on abstractions.**
|
|
155
|
+
|
|
156
|
+
### MUST: Depend on Abstractions
|
|
157
|
+
|
|
158
|
+
**Depend on interfaces/types, not concrete implementations:**
|
|
159
|
+
|
|
160
|
+
```tsx
|
|
161
|
+
// ❌ WRONG: Direct dependency on implementation (tight coupling)
|
|
162
|
+
function UserService() {
|
|
163
|
+
const supabase = useSecureSupabase();
|
|
164
|
+
async function getUser(id: string) {
|
|
165
|
+
const { data } = await supabase.from('users').select('*').eq('id', id).single();
|
|
166
|
+
return data;
|
|
167
|
+
}
|
|
168
|
+
return { getUser };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// ✅ CORRECT: Abstracted interface (depend on abstraction)
|
|
172
|
+
interface UserRepository { getUser(id: string): Promise<User | null>; }
|
|
173
|
+
function createUserRepository(supabase: SupabaseClient): UserRepository {
|
|
174
|
+
return { async getUser(id: string) { /* implementation */ } };
|
|
175
|
+
}
|
|
176
|
+
function useUserService() {
|
|
177
|
+
const supabase = useSecureSupabase();
|
|
178
|
+
const repository = useMemo(() => createUserRepository(supabase), [supabase]);
|
|
179
|
+
return { getUser: repository.getUser };
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### SHOULD: Use Dependency Injection
|
|
184
|
+
|
|
185
|
+
**Inject dependencies rather than creating them:**
|
|
186
|
+
|
|
187
|
+
```tsx
|
|
188
|
+
// ❌ WRONG: Hard-coded dependency
|
|
189
|
+
function EventService() { const api = new ApiClient('https://api.example.com'); }
|
|
190
|
+
|
|
191
|
+
// ✅ CORRECT: Injected dependency (dependency injection)
|
|
192
|
+
function EventService(api: ApiClient) { ... }
|
|
193
|
+
// Or with React context: const api = useApiClient(); useMemo(() => new EventService(api), [api]);
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## SOLID Checklist
|
|
197
|
+
|
|
198
|
+
Before committing code, verify:
|
|
199
|
+
- [ ] Each function/component has single responsibility
|
|
200
|
+
- [ ] Complex logic extracted to hooks/services
|
|
201
|
+
- [ ] Extension through composition, not modification
|
|
202
|
+
- [ ] Interfaces are focused and specific
|
|
203
|
+
- [ ] Dependencies are abstracted
|
|
204
|
+
- [ ] No god objects or bloated components
|
|
205
|
+
- [ ] Code is testable and maintainable
|
|
206
|
+
|
|
207
|
+
## Anti-Patterns to Avoid
|
|
208
|
+
|
|
209
|
+
1. **God Objects** - Classes/components that do too much
|
|
210
|
+
2. **Feature Envy** - Functions that use more of another object than their own
|
|
211
|
+
3. **Data Clumps** - Groups of data that should be objects
|
|
212
|
+
4. **Long Parameter Lists** - Use objects/interfaces instead
|
|
213
|
+
5. **Divergent Change** - One class changed for multiple reasons
|
|
214
|
+
6. **Shotgun Surgery** - One change requires many class modifications
|
|
215
|
+
|
|
216
|
+
## Reference
|
|
217
|
+
|
|
218
|
+
- Single Responsibility: Each module has one reason to change
|
|
219
|
+
- Open/Closed: Open for extension, closed for modification
|
|
220
|
+
- Liskov Substitution: Subtypes must be substitutable
|
|
221
|
+
- Interface Segregation: Many specific interfaces, not one general
|
|
222
|
+
- Dependency Inversion: Depend on abstractions, not concretions
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Enforce testing framework consistency and standards for consuming apps
|
|
3
|
+
globs: ["**/*.{test,spec}.{ts,tsx}"]
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
paceCoreVersion: "0.6.x"
|
|
6
|
+
rulesVersion: "2025-01-28"
|
|
7
|
+
---
|
|
8
|
+
# Testing Standards Guide
|
|
9
|
+
|
|
10
|
+
This guide ensures consistent, high-quality testing across consuming apps in the PACE suite.
|
|
11
|
+
|
|
12
|
+
## MUST: Meet Coverage Requirements
|
|
13
|
+
|
|
14
|
+
**MUST achieve minimum coverage:**
|
|
15
|
+
- ≥90% coverage for utils & hooks
|
|
16
|
+
- ≥70% coverage for components
|
|
17
|
+
|
|
18
|
+
**Verify coverage:**
|
|
19
|
+
```bash
|
|
20
|
+
npm run test:coverage
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## MUST: Use React Testing Library
|
|
24
|
+
|
|
25
|
+
**MUST use React Testing Library + userEvent for all component tests.**
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
// ✅ CORRECT: React Testing Library + userEvent
|
|
29
|
+
import { render, screen } from '@testing-library/react';
|
|
30
|
+
import userEvent from '@testing-library/user-event';
|
|
31
|
+
test('button clicks work', async () => {
|
|
32
|
+
const user = userEvent.setup();
|
|
33
|
+
const handleClick = vi.fn();
|
|
34
|
+
render(<Button onClick={handleClick}>Click me</Button>);
|
|
35
|
+
await user.click(screen.getByRole('button', { name: /click me/i }));
|
|
36
|
+
expect(handleClick).toHaveBeenCalledTimes(1);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// ❌ WRONG: Enzyme or other testing libraries
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## MUST: Colocate Tests
|
|
43
|
+
|
|
44
|
+
**Tests MUST be colocated with source files:**
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
src/
|
|
48
|
+
├── components/
|
|
49
|
+
│ └── EventCard/
|
|
50
|
+
│ ├── EventCard.tsx
|
|
51
|
+
│ └── EventCard.test.tsx
|
|
52
|
+
├── hooks/
|
|
53
|
+
│ ├── useEventData.ts
|
|
54
|
+
│ └── useEventData.test.ts
|
|
55
|
+
└── utils/
|
|
56
|
+
├── formatEvent.ts
|
|
57
|
+
└── formatEvent.test.ts
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## MUST: Use Vitest
|
|
61
|
+
|
|
62
|
+
**MUST use Vitest for all testing:**
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
// vitest.config.ts
|
|
66
|
+
import { defineConfig } from 'vitest/config';
|
|
67
|
+
|
|
68
|
+
export default defineConfig({
|
|
69
|
+
test: {
|
|
70
|
+
environment: 'jsdom',
|
|
71
|
+
setupFiles: ['./vitest.setup.ts'],
|
|
72
|
+
coverage: {
|
|
73
|
+
provider: 'v8',
|
|
74
|
+
reporter: ['text', 'json', 'html'],
|
|
75
|
+
thresholds: {
|
|
76
|
+
lines: 80,
|
|
77
|
+
functions: 80,
|
|
78
|
+
branches: 80,
|
|
79
|
+
statements: 80,
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## MUST: Test User Behavior, Not Implementation
|
|
87
|
+
|
|
88
|
+
**Tests MUST focus on what users see and do:**
|
|
89
|
+
|
|
90
|
+
```tsx
|
|
91
|
+
// ❌ WRONG: Testing implementation (component.state.count)
|
|
92
|
+
test('calls setState', () => { const component = render(<Counter />); expect(component.state.count).toBe(0); });
|
|
93
|
+
|
|
94
|
+
// ✅ CORRECT: Testing user behavior (what user sees and does)
|
|
95
|
+
test('displays count and increments on button click', async () => {
|
|
96
|
+
const user = userEvent.setup();
|
|
97
|
+
render(<Counter />);
|
|
98
|
+
expect(screen.getByText('Count: 0')).toBeInTheDocument();
|
|
99
|
+
await user.click(screen.getByRole('button', { name: /increment/i }));
|
|
100
|
+
expect(screen.getByText('Count: 1')).toBeInTheDocument();
|
|
101
|
+
});
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## MUST: Use Accessible Queries
|
|
105
|
+
|
|
106
|
+
**MUST prefer accessible queries (byRole, byLabelText, etc.):**
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
// ✅ CORRECT: Accessible queries (byRole, byLabelText, byText)
|
|
110
|
+
screen.getByRole('button', { name: /submit/i });
|
|
111
|
+
screen.getByLabelText(/email address/i);
|
|
112
|
+
|
|
113
|
+
// ❌ AVOID: Non-accessible queries (getByTestId, getByClassName - use as last resort)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## SHOULD: Test Critical Paths
|
|
117
|
+
|
|
118
|
+
**SHOULD test:**
|
|
119
|
+
- User workflows and interactions
|
|
120
|
+
- Error handling and edge cases
|
|
121
|
+
- Form validation
|
|
122
|
+
- Permission checks (RBAC)
|
|
123
|
+
- Data loading states
|
|
124
|
+
- Error states
|
|
125
|
+
|
|
126
|
+
## SHOULD: Use Descriptive Test Names
|
|
127
|
+
|
|
128
|
+
**Test names SHOULD describe behavior:**
|
|
129
|
+
|
|
130
|
+
```tsx
|
|
131
|
+
// ❌ WRONG - Vague
|
|
132
|
+
test('button works', () => { ... });
|
|
133
|
+
|
|
134
|
+
// ✅ CORRECT - Descriptive
|
|
135
|
+
test('increments counter when increment button is clicked', () => { ... });
|
|
136
|
+
test('displays error message when API call fails', () => { ... });
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## SHOULD: Group Related Tests
|
|
140
|
+
|
|
141
|
+
**SHOULD use `describe` blocks to group related tests:**
|
|
142
|
+
|
|
143
|
+
```tsx
|
|
144
|
+
describe('EventCard', () => {
|
|
145
|
+
describe('rendering', () => {
|
|
146
|
+
test('displays event title', () => { ... });
|
|
147
|
+
test('displays event date', () => { ... });
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
describe('interactions', () => {
|
|
151
|
+
test('calls onEdit when edit button clicked', () => { ... });
|
|
152
|
+
test('calls onDelete when delete button clicked', () => { ... });
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## MUST: Avoid Unnecessary Mocks
|
|
158
|
+
|
|
159
|
+
**MUST NOT mock unless necessary:**
|
|
160
|
+
|
|
161
|
+
```tsx
|
|
162
|
+
// ❌ WRONG: Unnecessary mock (global.fetch = mockFetch)
|
|
163
|
+
// ✅ CORRECT: Use real implementation or MSW
|
|
164
|
+
import { server } from './mocks/server';
|
|
165
|
+
server.use(rest.get('/api/events', (req, res, ctx) => res(ctx.json([{ id: '1', name: 'Event' }]))));
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## MUST: Test Async Code Properly
|
|
169
|
+
|
|
170
|
+
**MUST handle async operations correctly:**
|
|
171
|
+
|
|
172
|
+
```tsx
|
|
173
|
+
// ✅ CORRECT: Async testing with waitFor
|
|
174
|
+
test('loads and displays events', async () => {
|
|
175
|
+
render(<EventList />);
|
|
176
|
+
expect(screen.getByText(/loading/i)).toBeInTheDocument();
|
|
177
|
+
await waitFor(() => expect(screen.getByText('Event 1')).toBeInTheDocument());
|
|
178
|
+
expect(screen.queryByText(/loading/i)).not.toBeInTheDocument();
|
|
179
|
+
});
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## MUST: Clean Up After Tests
|
|
183
|
+
|
|
184
|
+
**MUST clean up resources:**
|
|
185
|
+
|
|
186
|
+
```tsx
|
|
187
|
+
// ✅ CORRECT: Cleanup after tests
|
|
188
|
+
afterEach(() => { cleanup(); vi.clearAllMocks(); });
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## SHOULD: Use Test Utilities
|
|
192
|
+
|
|
193
|
+
**SHOULD create reusable test utilities:**
|
|
194
|
+
|
|
195
|
+
```tsx
|
|
196
|
+
// ✅ CORRECT: Reusable test utilities
|
|
197
|
+
export function renderWithProviders(ui: React.ReactElement) {
|
|
198
|
+
return render(<UnifiedAuthProvider supabaseClient={mockSupabase} appName="Test App">{ui}</UnifiedAuthProvider>);
|
|
199
|
+
}
|
|
200
|
+
// Usage: renderWithProviders(<MyComponent />);
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## MUST: Include Timeout Parameters
|
|
204
|
+
|
|
205
|
+
**Tests MUST include timeout parameters to prevent hanging:**
|
|
206
|
+
|
|
207
|
+
```tsx
|
|
208
|
+
// ✅ CORRECT: Include timeout parameters to prevent hanging
|
|
209
|
+
test('async operation completes', async () => {
|
|
210
|
+
await waitFor(() => expect(screen.getByText('Loaded')).toBeInTheDocument(), { timeout: 5000 });
|
|
211
|
+
}, { timeout: 10000 });
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Testing Checklist
|
|
215
|
+
|
|
216
|
+
Before committing tests, verify:
|
|
217
|
+
- [ ] Coverage meets requirements (≥90% utils, ≥70% components)
|
|
218
|
+
- [ ] Using React Testing Library + userEvent
|
|
219
|
+
- [ ] Tests are colocated with source files
|
|
220
|
+
- [ ] Testing user behavior, not implementation
|
|
221
|
+
- [ ] Using accessible queries (byRole, byLabelText)
|
|
222
|
+
- [ ] Test names are descriptive
|
|
223
|
+
- [ ] Related tests grouped with describe
|
|
224
|
+
- [ ] No unnecessary mocks
|
|
225
|
+
- [ ] Async code tested properly
|
|
226
|
+
- [ ] Cleanup after tests
|
|
227
|
+
- [ ] Timeout parameters included
|
|
228
|
+
|
|
229
|
+
## Test Structure Template
|
|
230
|
+
|
|
231
|
+
```tsx
|
|
232
|
+
import { describe, test, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
233
|
+
import { render, screen, waitFor } from '@testing-library/react';
|
|
234
|
+
import userEvent from '@testing-library/user-event';
|
|
235
|
+
import { ComponentName } from './ComponentName';
|
|
236
|
+
|
|
237
|
+
describe('ComponentName', () => {
|
|
238
|
+
beforeEach(() => {
|
|
239
|
+
// Setup
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
afterEach(() => {
|
|
243
|
+
// Cleanup
|
|
244
|
+
vi.clearAllMocks();
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
describe('rendering', () => {
|
|
248
|
+
test('renders correctly', () => {
|
|
249
|
+
render(<ComponentName />);
|
|
250
|
+
// Assertions
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
describe('interactions', () => {
|
|
255
|
+
test('handles user interaction', async () => {
|
|
256
|
+
const user = userEvent.setup();
|
|
257
|
+
render(<ComponentName />);
|
|
258
|
+
// Test interaction
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## Reference
|
|
265
|
+
|
|
266
|
+
- React Testing Library: https://testing-library.com/react
|
|
267
|
+
- Vitest: https://vitest.dev
|
|
268
|
+
- Testing Standards: See `06-testing-and-docs-standard.md` in pace-core docs
|