@jmruthers/pace-core 0.6.5 → 0.6.7
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 +104 -0
- package/README.md +5 -403
- package/audit-tool/00-dependencies.cjs +394 -0
- package/audit-tool/audits/01-pace-core-compliance.cjs +556 -0
- package/audit-tool/audits/02-project-structure.cjs +255 -0
- package/audit-tool/audits/03-architecture.cjs +196 -0
- package/audit-tool/audits/04-code-quality.cjs +149 -0
- package/audit-tool/audits/05-styling.cjs +224 -0
- package/audit-tool/audits/06-security-rbac.cjs +544 -0
- package/audit-tool/audits/07-api-tech-stack.cjs +301 -0
- package/audit-tool/audits/08-testing-documentation.cjs +202 -0
- package/audit-tool/audits/09-operations.cjs +208 -0
- package/audit-tool/index.cjs +291 -0
- package/audit-tool/utils/code-utils.cjs +218 -0
- package/audit-tool/utils/file-utils.cjs +230 -0
- package/audit-tool/utils/report-utils.cjs +241 -0
- package/core-usage-manifest.json +93 -0
- package/cursor-rules/00-standards-overview.mdc +156 -0
- package/cursor-rules/01-pace-core-compliance.mdc +586 -0
- package/cursor-rules/02-project-structure.mdc +42 -4
- package/cursor-rules/{03-solid-principles.mdc → 03-architecture.mdc} +126 -10
- package/cursor-rules/04-code-quality.mdc +419 -0
- package/cursor-rules/{08-markup-quality.mdc → 05-styling.mdc} +104 -34
- package/cursor-rules/06-security-rbac.mdc +518 -0
- package/cursor-rules/07-api-tech-stack.mdc +377 -0
- package/cursor-rules/08-testing-documentation.mdc +324 -0
- package/cursor-rules/09-operations.mdc +365 -0
- package/dist/{AuthService-Cb34EQs3.d.ts → AuthService-DmfO5rGS.d.ts} +10 -0
- package/dist/DataTable-7PMH7XN7.js +15 -0
- package/dist/{DataTable-BMRU8a1j.d.ts → DataTable-DRUIgtUH.d.ts} +1 -1
- package/dist/{PublicPageProvider-QTFVrL-Z.d.ts → PublicPageProvider-DlsCaR5v.d.ts} +33 -72
- package/dist/UnifiedAuthProvider-ZT6TIGM7.js +7 -0
- package/dist/api-Y4MQWOFW.js +4 -0
- package/dist/audit-MYQXYZFU.js +3 -0
- package/dist/{chunk-DGUM43GV.js → chunk-3RG5ZIWI.js} +1 -4
- package/dist/{chunk-QXHPKYJV.js → chunk-4SXLQIZO.js} +1 -26
- package/dist/{chunk-UPPMRMYG.js → chunk-5X4QLXRG.js} +73 -151
- package/dist/chunk-6F3IILHI.js +62 -0
- package/dist/{chunk-E66EQZE6.js → chunk-6GLLNA6U.js} +3 -9
- package/dist/{chunk-ZSAAAMVR.js → chunk-6QYDGKQY.js} +1 -4
- package/dist/{chunk-FMUCXFII.js → chunk-7ILTDCL2.js} +9 -5
- package/dist/{chunk-M43Y4SSO.js → chunk-A3W6LW53.js} +15 -13
- package/dist/{chunk-63FOKYGO.js → chunk-AHU7G2R5.js} +2 -11
- package/dist/{chunk-HU2C6SSC.js → chunk-BM4CQ5P3.js} +606 -559
- package/dist/chunk-C7NSAPTL.js +1 -0
- package/dist/{chunk-J36DSWQK.js → chunk-FEJLJNWA.js} +7 -41
- package/dist/{chunk-IHB5DR3H.js → chunk-FTCRZOG2.js} +188 -387
- package/dist/{chunk-G37KK66H.js → chunk-FYHN4DD5.js} +60 -19
- package/dist/chunk-GHYHJTYV.js +994 -0
- package/dist/{chunk-VBXEHIUJ.js → chunk-HF6O3O37.js} +6 -88
- package/dist/{chunk-FFQEQTNW.js → chunk-IUBRCBSY.js} +134 -45
- package/dist/{chunk-6COVEUS7.js → chunk-JGWDVX64.js} +983 -1034
- package/dist/{chunk-RGAWHO7N.js → chunk-L4XMVJKY.js} +77 -222
- package/dist/chunk-MBADTM7L.js +64 -0
- package/dist/{chunk-M7MPQISP.js → chunk-OJ4SKRSV.js} +3 -16
- package/dist/{chunk-IVOFDYWT.js → chunk-Q7Q7V5NV.js} +2109 -1604
- package/dist/{chunk-JGRYX5UX.js → chunk-S7DKJPLT.js} +29 -58
- package/dist/{chunk-PWLANIRT.js → chunk-TTRFSOKR.js} +1 -7
- package/dist/{chunk-5DRSZLL2.js → chunk-UH3NTO3F.js} +1 -6
- package/dist/{chunk-NTM7ZSB6.js → chunk-VBCS3DUA.js} +261 -168
- package/dist/{chunk-EFN2EIMK.js → chunk-ZFYPMX46.js} +271 -87
- package/dist/{chunk-L4OXEN46.js → chunk-ZKAWKYT4.js} +10 -24
- package/dist/components.d.ts +7 -5
- package/dist/components.js +46 -257
- package/dist/{database.generated-CzIvgcPu.d.ts → database.generated-CcnC_DRc.d.ts} +4795 -3691
- package/dist/eslint-rules/index.cjs +35 -0
- package/{src/eslint-rules/pace-core-compliance.cjs → dist/eslint-rules/rules/01-pace-core-compliance.cjs} +234 -235
- package/dist/eslint-rules/rules/04-code-quality.cjs +290 -0
- package/dist/eslint-rules/rules/05-styling.cjs +61 -0
- package/dist/eslint-rules/rules/06-security-rbac.cjs +806 -0
- package/dist/eslint-rules/rules/07-api-tech-stack.cjs +263 -0
- package/dist/eslint-rules/rules/08-testing.cjs +94 -0
- package/dist/eslint-rules/utils/helpers.cjs +42 -0
- package/dist/eslint-rules/utils/manifest-loader.cjs +75 -0
- package/dist/hooks.d.ts +6 -6
- package/dist/hooks.js +62 -172
- package/dist/icons/index.d.ts +1 -0
- package/dist/icons/index.js +1 -0
- package/dist/index.d.ts +12 -11
- package/dist/index.js +67 -660
- package/dist/providers.d.ts +2 -2
- package/dist/providers.js +8 -35
- package/dist/rbac/eslint-rules.d.ts +46 -44
- package/dist/rbac/eslint-rules.js +7 -4
- package/dist/rbac/index.d.ts +109 -586
- package/dist/rbac/index.js +14 -207
- package/dist/styles/index.js +2 -12
- package/dist/theming/runtime.d.ts +14 -1
- package/dist/theming/runtime.js +3 -19
- package/dist/{timezone-CHhWg6b4.d.ts → timezone-BZe_eUxx.d.ts} +175 -1
- package/dist/{types-CkbwOr4Y.d.ts → types-DXstZpNI.d.ts} +4 -17
- package/dist/types-t9H8qKRw.d.ts +55 -0
- package/dist/types.d.ts +1 -1
- package/dist/types.js +7 -94
- package/dist/{usePublicRouteParams-ClnV4tnv.d.ts → usePublicRouteParams-MamNgwqe.d.ts} +20 -20
- package/dist/utils.d.ts +24 -117
- package/dist/utils.js +54 -392
- package/docs/README.md +17 -7
- package/docs/api/README.md +4 -402
- package/docs/api/modules.md +301 -871
- package/docs/api-reference/components.md +21 -21
- package/docs/api-reference/deprecated.md +31 -6
- package/docs/api-reference/hooks.md +80 -80
- package/docs/api-reference/rpc-functions.md +78 -3
- package/docs/api-reference/types.md +1 -1
- package/docs/api-reference/utilities.md +1 -1
- package/docs/architecture/README.md +1 -1
- package/docs/core-concepts/events.md +3 -3
- package/docs/core-concepts/organisations.md +6 -6
- package/docs/core-concepts/permissions.md +6 -6
- package/docs/documentation-index.md +12 -18
- package/docs/getting-started/cursor-rules.md +3 -23
- package/docs/getting-started/dependencies.md +650 -0
- package/docs/getting-started/documentation-index.md +1 -1
- package/docs/getting-started/examples/README.md +4 -4
- package/docs/getting-started/examples/full-featured-app.md +1 -1
- package/docs/getting-started/faq.md +2 -2
- package/docs/getting-started/installation-guide.md +20 -7
- package/docs/getting-started/quick-reference.md +4 -4
- package/docs/getting-started/quick-start.md +23 -12
- package/docs/implementation-guides/authentication.md +15 -15
- package/docs/implementation-guides/component-styling.md +1 -1
- package/docs/implementation-guides/data-tables.md +126 -33
- package/docs/implementation-guides/datatable-rbac-usage.md +1 -1
- package/docs/implementation-guides/dynamic-colors.md +3 -3
- package/docs/implementation-guides/file-upload-storage.md +2 -2
- package/docs/implementation-guides/hierarchical-datatable.md +40 -60
- package/docs/implementation-guides/inactivity-tracking.md +3 -3
- package/docs/implementation-guides/large-datasets.md +3 -2
- package/docs/implementation-guides/organisation-security.md +2 -2
- package/docs/implementation-guides/performance.md +2 -2
- package/docs/implementation-guides/permission-enforcement.md +5 -1
- package/docs/migration/V0.3.44_organisation-context-timing-fix.md +1 -1
- package/docs/migration/V0.4.0_rbac-migration.md +6 -6
- package/docs/rbac/MIGRATION_GUIDE.md +819 -0
- package/docs/rbac/RBAC_CONTRACT.md +724 -0
- package/docs/rbac/README.md +17 -8
- package/docs/rbac/advanced-patterns.md +6 -6
- package/docs/rbac/api-reference.md +20 -20
- package/docs/rbac/edge-functions-guide.md +376 -0
- package/docs/rbac/event-based-apps.md +3 -3
- package/docs/rbac/examples.md +41 -41
- package/docs/rbac/getting-started.md +37 -37
- package/docs/rbac/performance.md +1 -1
- package/docs/rbac/quick-start.md +52 -52
- package/docs/rbac/secure-client-protection.md +1 -35
- package/docs/rbac/troubleshooting.md +1 -1
- package/docs/security/README.md +5 -5
- package/docs/standards/0-standards-overview.md +220 -0
- package/docs/standards/1-pace-core-compliance-standards.md +986 -0
- package/docs/standards/2-project-structure-standards.md +949 -0
- package/docs/standards/3-architecture-standards.md +606 -0
- package/docs/standards/4-code-quality-standards.md +728 -0
- package/docs/standards/5-styling-standards.md +348 -0
- package/docs/standards/{07-rbac-and-rls-standard.md → 6-security-rbac-standards.md} +269 -66
- package/docs/standards/7-api-tech-stack-standards.md +662 -0
- package/docs/standards/8-testing-documentation-standards.md +401 -0
- package/docs/standards/9-operations-standards.md +1102 -0
- package/docs/standards/README.md +185 -57
- package/docs/troubleshooting/README.md +4 -4
- package/docs/troubleshooting/common-issues.md +2 -2
- package/docs/troubleshooting/debugging.md +9 -9
- package/docs/troubleshooting/migration.md +4 -4
- package/docs/troubleshooting/organisation-context-setup.md +42 -19
- package/eslint-config-pace-core.cjs +33 -6
- package/package.json +35 -23
- package/scripts/install-cursor-rules.cjs +25 -6
- package/scripts/install-eslint-config.cjs +284 -0
- package/src/__tests__/fixtures/supabase.ts +1 -1
- package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +3 -3
- package/src/__tests__/helpers/__tests__/optimized-test-setup.test.ts +1 -1
- package/src/__tests__/helpers/__tests__/supabaseMock.test.ts +1 -1
- package/src/__tests__/helpers/__tests__/test-providers.test.tsx +2 -2
- package/src/__tests__/helpers/__tests__/test-utils.test.tsx +13 -13
- package/src/__tests__/helpers/component-test-utils.tsx +1 -1
- package/src/__tests__/helpers/supabaseMock.ts +2 -2
- package/src/__tests__/integration/UserProfile.test.tsx +14 -14
- package/src/__tests__/public-recipe-view.test.ts +38 -9
- package/src/__tests__/rbac/PagePermissionGuard.test.tsx +6 -6
- package/src/__tests__/templates/accessibility.test.template.tsx +9 -9
- package/src/__tests__/templates/component.test.template.tsx +18 -15
- package/src/components/Button/Button.tsx +5 -1
- package/src/components/Calendar/Calendar.tsx +201 -47
- package/src/components/ContextSelector/ContextSelector.tsx +106 -119
- package/src/components/DataTable/AUDIT_REPORT.md +293 -0
- package/src/components/DataTable/__tests__/DataTableCore.test.tsx +10 -2
- package/src/components/DataTable/__tests__/a11y.basic.test.tsx +10 -4
- package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +9 -9
- package/src/components/DataTable/components/ColumnFilter.tsx +63 -74
- package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +43 -41
- package/src/components/DataTable/components/DataTableCore.tsx +186 -13
- package/src/components/DataTable/components/DataTableErrorBoundary.tsx +9 -11
- package/src/components/DataTable/components/DataTableLayout.tsx +35 -21
- package/src/components/DataTable/components/EditFields.tsx +23 -3
- package/src/components/DataTable/components/EditableRow.tsx +12 -9
- package/src/components/DataTable/components/EmptyState.tsx +10 -9
- package/src/components/DataTable/components/FilterRow.tsx +2 -4
- package/src/components/DataTable/components/ImportModal.tsx +124 -126
- package/src/components/DataTable/components/LoadingState.tsx +5 -6
- package/src/components/DataTable/components/RowComponent.tsx +12 -0
- package/src/components/DataTable/components/SortIndicator.tsx +50 -0
- package/src/components/DataTable/components/__tests__/COVERAGE_NOTE.md +4 -4
- package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +23 -82
- package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +37 -9
- package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +7 -4
- package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +12 -4
- package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +41 -27
- package/src/components/DataTable/components/hooks/usePermissionTracking.ts +0 -4
- package/src/components/DataTable/components/index.ts +2 -1
- package/src/components/DataTable/hooks/__tests__/useDataTableState.test.ts +51 -47
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +24 -21
- package/src/components/DataTable/hooks/useDataTableState.ts +125 -9
- package/src/components/DataTable/hooks/useTableColumns.ts +40 -2
- package/src/components/DataTable/hooks/useTableHandlers.ts +11 -0
- package/src/components/DataTable/types.ts +5 -18
- package/src/components/DataTable/utils/a11yUtils.ts +17 -0
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +2 -1
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +11 -15
- package/src/components/DateTimeField/DateTimeField.tsx +10 -9
- package/src/components/Dialog/Dialog.test.tsx +128 -104
- package/src/components/Dialog/Dialog.tsx +742 -24
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +77 -79
- package/src/components/FileDisplay/FileDisplay.test.tsx +4 -2
- package/src/components/FileDisplay/FileDisplay.tsx +23 -17
- package/src/components/FileUpload/FileUpload.test.tsx +52 -14
- package/src/components/FileUpload/FileUpload.tsx +112 -130
- package/src/components/Form/Form.test.tsx +6 -8
- package/src/components/Form/Form.tsx +365 -4
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +14 -13
- package/src/components/NavigationMenu/useNavigationFiltering.ts +11 -21
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +6 -4
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +11 -15
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +108 -61
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +27 -3
- package/src/components/Progress/Progress.tsx +2 -4
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +8 -8
- package/src/components/Select/Select.tsx +109 -98
- package/src/components/Select/types.ts +4 -1
- package/src/components/UserMenu/UserMenu.tsx +9 -6
- package/src/hooks/__tests__/ServiceHooks.test.tsx +16 -16
- package/src/hooks/__tests__/hooks.integration.test.tsx +55 -57
- package/src/hooks/__tests__/useAppConfig.unit.test.ts +129 -67
- package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +97 -97
- package/src/hooks/__tests__/usePublicEvent.simple.test.ts +149 -67
- package/src/hooks/__tests__/usePublicEvent.test.ts +149 -79
- package/src/hooks/__tests__/usePublicEvent.unit.test.ts +158 -109
- package/src/hooks/__tests__/useSessionDraft.test.ts +163 -0
- package/src/hooks/__tests__/useSessionRestoration.unit.test.tsx +10 -5
- package/src/hooks/public/usePublicEvent.ts +67 -195
- package/src/hooks/public/usePublicEventLogo.test.ts +70 -17
- package/src/hooks/public/usePublicEventLogo.ts +24 -14
- package/src/hooks/public/usePublicFileDisplay.ts +2 -2
- package/src/hooks/public/usePublicRouteParams.ts +5 -5
- package/src/hooks/useAppConfig.ts +28 -26
- package/src/hooks/useEventTheme.test.ts +217 -239
- package/src/hooks/useEventTheme.ts +16 -28
- package/src/hooks/useFileDisplay.ts +2 -2
- package/src/hooks/useOrganisationPermissions.ts +5 -7
- package/src/hooks/useQueryCache.ts +0 -1
- package/src/hooks/useSessionDraft.ts +380 -0
- package/src/hooks/useSessionRestoration.ts +3 -1
- package/src/icons/index.ts +27 -0
- package/src/index.ts +5 -0
- package/src/providers/OrganisationProvider.tsx +23 -14
- package/src/providers/UnifiedAuthProvider.smoke.test.tsx +21 -21
- package/src/providers/__tests__/AuthProvider.test.tsx +21 -21
- package/src/providers/__tests__/EventProvider.test.tsx +61 -61
- package/src/providers/__tests__/InactivityProvider.test.tsx +56 -56
- package/src/providers/__tests__/OrganisationProvider.test.tsx +75 -75
- package/src/providers/__tests__/ProviderLifecycle.test.tsx +37 -37
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +103 -103
- package/src/providers/services/EventServiceProvider.tsx +1 -24
- package/src/providers/services/UnifiedAuthProvider.tsx +5 -48
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +7 -7
- package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +13 -10
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +7 -457
- package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +33 -7
- package/src/rbac/adapters.tsx +7 -295
- package/src/rbac/api.test.ts +44 -56
- package/src/rbac/api.ts +10 -17
- package/src/rbac/cache-invalidation.ts +0 -1
- package/src/rbac/compliance/index.ts +10 -0
- package/src/rbac/compliance/pattern-detector.ts +553 -0
- package/src/rbac/compliance/runtime-compliance.ts +22 -0
- package/src/rbac/components/AccessDenied.tsx +150 -0
- package/src/rbac/components/NavigationGuard.tsx +12 -20
- package/src/rbac/components/PagePermissionGuard.tsx +4 -24
- package/src/rbac/components/__tests__/NavigationGuard.test.tsx +21 -8
- package/src/rbac/components/index.ts +3 -41
- package/src/rbac/eslint-rules.js +1 -1
- package/src/rbac/hooks/index.ts +0 -3
- package/src/rbac/hooks/permissions/index.ts +0 -3
- package/src/rbac/hooks/permissions/useAccessLevel.ts +4 -8
- package/src/rbac/hooks/usePermissions.ts +0 -3
- package/src/rbac/hooks/useResolvedScope.test.ts +57 -47
- package/src/rbac/hooks/useResolvedScope.ts +58 -140
- package/src/rbac/hooks/useResourcePermissions.test.ts +124 -38
- package/src/rbac/hooks/useResourcePermissions.ts +139 -48
- package/src/rbac/hooks/useRoleManagement.test.ts +65 -22
- package/src/rbac/hooks/useRoleManagement.ts +147 -19
- package/src/rbac/hooks/useSecureSupabase.ts +4 -8
- package/src/rbac/index.ts +7 -9
- package/src/rbac/utils/contextValidator.ts +9 -7
- package/src/services/AuthService.ts +130 -18
- package/src/services/EventService.ts +4 -97
- package/src/services/InactivityService.ts +16 -0
- package/src/services/OrganisationService.ts +7 -44
- package/src/services/__tests__/OrganisationService.test.ts +26 -8
- package/src/services/base/BaseService.ts +0 -3
- package/src/styles/core.css +7 -0
- package/src/theming/__tests__/parseEventColours.test.ts +9 -3
- package/src/theming/parseEventColours.ts +22 -10
- package/src/types/database.generated.ts +4733 -3809
- package/src/utils/__tests__/lazyLoad.unit.test.tsx +42 -39
- package/src/utils/__tests__/organisationContext.unit.test.ts +9 -10
- package/src/utils/context/organisationContext.test.ts +13 -28
- package/src/utils/context/organisationContext.ts +21 -52
- package/src/utils/dynamic/dynamicUtils.ts +1 -1
- package/src/utils/file-reference/index.ts +39 -15
- package/src/utils/formatting/formatDateTime.test.ts +3 -2
- package/src/utils/google-places/loadGoogleMapsScript.ts +29 -4
- package/src/utils/index.ts +4 -1
- package/src/utils/persistence/__tests__/keyDerivation.test.ts +135 -0
- package/src/utils/persistence/__tests__/sensitiveFieldDetection.test.ts +123 -0
- package/src/utils/persistence/keyDerivation.ts +304 -0
- package/src/utils/persistence/sensitiveFieldDetection.ts +212 -0
- package/src/utils/security/secureStorage.ts +5 -5
- package/src/utils/storage/README.md +1 -1
- package/src/utils/storage/helpers.ts +3 -3
- package/src/utils/supabase/createBaseClient.ts +147 -0
- package/src/utils/timezone/timezone.test.ts +1 -2
- package/src/utils/timezone/timezone.ts +1 -1
- package/src/utils/validation/csrf.ts +4 -4
- package/cursor-rules/00-pace-core-compliance.mdc +0 -331
- package/cursor-rules/01-standards-compliance.mdc +0 -244
- package/cursor-rules/04-testing-standards.mdc +0 -268
- package/cursor-rules/05-bug-reports-and-features.mdc +0 -246
- package/cursor-rules/06-code-quality.mdc +0 -309
- package/cursor-rules/07-tech-stack-compliance.mdc +0 -214
- package/cursor-rules/CHANGELOG.md +0 -119
- package/cursor-rules/README.md +0 -192
- package/dist/DataTable-AOVNCPTX.js +0 -175
- package/dist/DataTable-AOVNCPTX.js.map +0 -1
- package/dist/UnifiedAuthProvider-4SBX4LU5.js +0 -18
- package/dist/UnifiedAuthProvider-4SBX4LU5.js.map +0 -1
- package/dist/api-O6HTBX5Y.js +0 -52
- package/dist/api-O6HTBX5Y.js.map +0 -1
- package/dist/audit-V53FV5AG.js +0 -17
- package/dist/audit-V53FV5AG.js.map +0 -1
- package/dist/chunk-5DRSZLL2.js.map +0 -1
- package/dist/chunk-63FOKYGO.js.map +0 -1
- package/dist/chunk-6COVEUS7.js.map +0 -1
- package/dist/chunk-AFVQODI2.js +0 -263
- package/dist/chunk-AFVQODI2.js.map +0 -1
- package/dist/chunk-DGUM43GV.js.map +0 -1
- package/dist/chunk-E66EQZE6.js.map +0 -1
- package/dist/chunk-EFN2EIMK.js.map +0 -1
- package/dist/chunk-FFQEQTNW.js.map +0 -1
- package/dist/chunk-FMUCXFII.js.map +0 -1
- package/dist/chunk-G37KK66H.js.map +0 -1
- package/dist/chunk-G7QEZTYQ.js +0 -2053
- package/dist/chunk-G7QEZTYQ.js.map +0 -1
- package/dist/chunk-HU2C6SSC.js.map +0 -1
- package/dist/chunk-IHB5DR3H.js.map +0 -1
- package/dist/chunk-IVOFDYWT.js.map +0 -1
- package/dist/chunk-J36DSWQK.js.map +0 -1
- package/dist/chunk-JGRYX5UX.js.map +0 -1
- package/dist/chunk-KQCRWDSA.js +0 -1
- package/dist/chunk-KQCRWDSA.js.map +0 -1
- package/dist/chunk-L4OXEN46.js.map +0 -1
- package/dist/chunk-LMC26NLJ.js +0 -84
- package/dist/chunk-LMC26NLJ.js.map +0 -1
- package/dist/chunk-M43Y4SSO.js.map +0 -1
- package/dist/chunk-M7MPQISP.js.map +0 -1
- package/dist/chunk-NTM7ZSB6.js.map +0 -1
- package/dist/chunk-PWLANIRT.js.map +0 -1
- package/dist/chunk-QXHPKYJV.js.map +0 -1
- package/dist/chunk-RGAWHO7N.js.map +0 -1
- package/dist/chunk-UPPMRMYG.js.map +0 -1
- package/dist/chunk-VBXEHIUJ.js.map +0 -1
- package/dist/chunk-ZSAAAMVR.js.map +0 -1
- package/dist/components.js.map +0 -1
- package/dist/contextValidator-5OGXSPKS.js +0 -9
- package/dist/contextValidator-5OGXSPKS.js.map +0 -1
- package/dist/eslint-rules/pace-core-compliance.cjs +0 -510
- package/dist/hooks.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/providers.js.map +0 -1
- package/dist/rbac/eslint-rules.js.map +0 -1
- package/dist/rbac/index.js.map +0 -1
- package/dist/styles/index.js.map +0 -1
- package/dist/theming/runtime.js.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/utils.js.map +0 -1
- package/docs/best-practices/README.md +0 -472
- package/docs/best-practices/accessibility.md +0 -601
- package/docs/best-practices/common-patterns.md +0 -516
- package/docs/best-practices/deployment.md +0 -1103
- package/docs/best-practices/performance.md +0 -1328
- package/docs/best-practices/security.md +0 -940
- package/docs/best-practices/testing.md +0 -1034
- package/docs/rbac/compliance/compliance-guide.md +0 -544
- package/docs/standards/01-architecture-standard.md +0 -44
- package/docs/standards/02-api-and-rpc-standard.md +0 -39
- package/docs/standards/03-component-standard.md +0 -32
- package/docs/standards/04-code-style-standard.md +0 -32
- package/docs/standards/05-security-standard.md +0 -44
- package/docs/standards/06-testing-and-docs-standard.md +0 -29
- package/docs/standards/pace-core-compliance.md +0 -432
- package/scripts/audit/core/checks/accessibility.cjs +0 -197
- package/scripts/audit/core/checks/api-usage.cjs +0 -191
- package/scripts/audit/core/checks/bundle.cjs +0 -142
- package/scripts/audit/core/checks/compliance.cjs +0 -2706
- package/scripts/audit/core/checks/config.cjs +0 -54
- package/scripts/audit/core/checks/coverage.cjs +0 -84
- package/scripts/audit/core/checks/dependencies.cjs +0 -994
- package/scripts/audit/core/checks/documentation.cjs +0 -268
- package/scripts/audit/core/checks/environment.cjs +0 -116
- package/scripts/audit/core/checks/error-handling.cjs +0 -340
- package/scripts/audit/core/checks/forms.cjs +0 -172
- package/scripts/audit/core/checks/heuristics.cjs +0 -68
- package/scripts/audit/core/checks/hooks.cjs +0 -334
- package/scripts/audit/core/checks/imports.cjs +0 -244
- package/scripts/audit/core/checks/performance.cjs +0 -325
- package/scripts/audit/core/checks/routes.cjs +0 -117
- package/scripts/audit/core/checks/state.cjs +0 -130
- package/scripts/audit/core/checks/structure.cjs +0 -65
- package/scripts/audit/core/checks/style.cjs +0 -584
- package/scripts/audit/core/checks/testing.cjs +0 -122
- package/scripts/audit/core/checks/typescript.cjs +0 -61
- package/scripts/audit/core/scanner.cjs +0 -199
- package/scripts/audit/core/utils.cjs +0 -137
- package/scripts/audit/index.cjs +0 -223
- package/scripts/audit/reporters/console.cjs +0 -151
- package/scripts/audit/reporters/json.cjs +0 -54
- package/scripts/audit/reporters/markdown.cjs +0 -124
- package/scripts/audit-consuming-app.cjs +0 -86
- package/src/components/DataTable/components/DataTableBody.tsx +0 -454
- package/src/components/DataTable/components/DraggableColumnHeader.tsx +0 -156
- package/src/components/DataTable/components/ExpandButton.tsx +0 -113
- package/src/components/DataTable/components/GroupHeader.tsx +0 -54
- package/src/components/DataTable/components/ViewRowModal.tsx +0 -68
- package/src/components/DataTable/components/VirtualizedDataTable.tsx +0 -525
- package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +0 -462
- package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +0 -393
- package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +0 -476
- package/src/components/DataTable/components/__tests__/VirtualizedDataTable.test.tsx +0 -128
- package/src/components/DataTable/core/DataTableContext.tsx +0 -216
- package/src/components/DataTable/core/__tests__/DataTableContext.test.tsx +0 -136
- package/src/components/DataTable/hooks/__tests__/useColumnReordering.test.ts +0 -570
- package/src/components/DataTable/hooks/useColumnReordering.ts +0 -123
- package/src/components/DataTable/utils/debugTools.ts +0 -514
- package/src/eslint-rules/pace-core-compliance.js +0 -638
- package/src/rbac/components/EnhancedNavigationMenu.test.tsx +0 -555
- package/src/rbac/components/EnhancedNavigationMenu.tsx +0 -293
- package/src/rbac/components/NavigationProvider.test.tsx +0 -481
- package/src/rbac/components/NavigationProvider.tsx +0 -345
- package/src/rbac/components/PagePermissionProvider.test.tsx +0 -476
- package/src/rbac/components/PagePermissionProvider.tsx +0 -279
- package/src/rbac/components/PermissionEnforcer.tsx +0 -312
- package/src/rbac/components/RoleBasedRouter.tsx +0 -440
- package/src/rbac/components/SecureDataProvider.test.tsx +0 -543
- package/src/rbac/components/SecureDataProvider.tsx +0 -339
- package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +0 -620
- package/src/rbac/components/__tests__/NavigationProvider.test.tsx +0 -726
- package/src/rbac/components/__tests__/PagePermissionProvider.test.tsx +0 -661
- package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +0 -881
- package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +0 -783
- package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +0 -645
- package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +0 -659
- package/src/rbac/hooks/permissions/useCachedPermissions.ts +0 -79
- package/src/rbac/hooks/permissions/useHasAllPermissions.ts +0 -90
- package/src/rbac/hooks/permissions/useHasAnyPermission.ts +0 -90
|
@@ -118,6 +118,7 @@
|
|
|
118
118
|
import React, { Component, ReactNode } from 'react';
|
|
119
119
|
import { performanceBudgetMonitor } from '../../utils/performance/performanceBudgets';
|
|
120
120
|
import { logger } from '../../utils/core/logger';
|
|
121
|
+
import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from '../Card/Card';
|
|
121
122
|
|
|
122
123
|
/**
|
|
123
124
|
* State interface for the ErrorBoundary component
|
|
@@ -181,18 +182,18 @@ export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundarySt
|
|
|
181
182
|
|
|
182
183
|
constructor(props: ErrorBoundaryProps) {
|
|
183
184
|
super(props);
|
|
184
|
-
this.state = {
|
|
185
|
-
hasError: false,
|
|
186
|
-
retryCount: 0
|
|
185
|
+
this.state = {
|
|
186
|
+
hasError: false,
|
|
187
|
+
retryCount: 0
|
|
187
188
|
};
|
|
188
189
|
}
|
|
189
190
|
|
|
190
191
|
static getDerivedStateFromError(error: Error): Partial<ErrorBoundaryState> {
|
|
191
192
|
const errorId = `error_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
192
|
-
return {
|
|
193
|
-
hasError: true,
|
|
193
|
+
return {
|
|
194
|
+
hasError: true,
|
|
194
195
|
error,
|
|
195
|
-
errorId
|
|
196
|
+
errorId
|
|
196
197
|
};
|
|
197
198
|
}
|
|
198
199
|
|
|
@@ -200,12 +201,12 @@ export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundarySt
|
|
|
200
201
|
const { componentName = 'Unknown Component', onError, enableReporting = true } = this.props;
|
|
201
202
|
const errorId = this.state.errorId!;
|
|
202
203
|
const componentNameForHandler = componentName || 'Unknown Component';
|
|
203
|
-
|
|
204
|
+
|
|
204
205
|
this.setState({ errorInfo });
|
|
205
|
-
|
|
206
|
+
|
|
206
207
|
// Enhanced logging with component name and error ID
|
|
207
208
|
logger.error('ErrorBoundary', `[${componentNameForHandler}] Caught error ${errorId}:`, error, errorInfo);
|
|
208
|
-
|
|
209
|
+
|
|
209
210
|
// Performance monitoring - track error occurrence
|
|
210
211
|
performanceBudgetMonitor.measure('ERROR_BOUNDARY_TRIGGER', 1, {
|
|
211
212
|
componentName: componentNameForHandler,
|
|
@@ -218,7 +219,7 @@ export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundarySt
|
|
|
218
219
|
if (enableReporting) {
|
|
219
220
|
this.reportError(errorId, componentNameForHandler);
|
|
220
221
|
}
|
|
221
|
-
|
|
222
|
+
|
|
222
223
|
// Call error handler: prefer prop onError, fall back to global handler from props
|
|
223
224
|
if (onError) {
|
|
224
225
|
onError(error, errorInfo, errorId);
|
|
@@ -242,7 +243,7 @@ export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundarySt
|
|
|
242
243
|
|
|
243
244
|
if (retryCount < maxRetries) {
|
|
244
245
|
logger.debug('ErrorBoundary', `Retrying component render (attempt ${retryCount + 1}/${maxRetries})`);
|
|
245
|
-
|
|
246
|
+
|
|
246
247
|
this.setState(prevState => ({
|
|
247
248
|
hasError: false,
|
|
248
249
|
error: undefined,
|
|
@@ -261,11 +262,11 @@ export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundarySt
|
|
|
261
262
|
|
|
262
263
|
render() {
|
|
263
264
|
if (this.state.hasError) {
|
|
264
|
-
const {
|
|
265
|
-
componentName = 'Component',
|
|
266
|
-
fallback,
|
|
267
|
-
enableRetry = true,
|
|
268
|
-
maxRetries = 3
|
|
265
|
+
const {
|
|
266
|
+
componentName = 'Component',
|
|
267
|
+
fallback,
|
|
268
|
+
enableRetry = true,
|
|
269
|
+
maxRetries = 3
|
|
269
270
|
} = this.props;
|
|
270
271
|
const { retryCount, errorId } = this.state;
|
|
271
272
|
|
|
@@ -276,73 +277,70 @@ export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundarySt
|
|
|
276
277
|
|
|
277
278
|
// Enhanced error UI with retry functionality
|
|
278
279
|
return (
|
|
279
|
-
<
|
|
280
|
-
role="alert"
|
|
281
|
-
className="
|
|
280
|
+
<Card
|
|
281
|
+
role="alert"
|
|
282
|
+
className="bg-destructive/10 border-destructive/20"
|
|
282
283
|
data-error-boundary={errorId}
|
|
283
284
|
>
|
|
284
|
-
<
|
|
285
|
-
<
|
|
286
|
-
<
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
<
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
>
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
</button>
|
|
312
|
-
</div>
|
|
313
|
-
)}
|
|
285
|
+
<CardHeader className="flex items-start gap-3">
|
|
286
|
+
<svg className="w-5 h-5 text-destructive flex-shrink-0" viewBox="0 0 20 20" fill="currentColor">
|
|
287
|
+
<path fillRule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clipRule="evenodd" />
|
|
288
|
+
</svg>
|
|
289
|
+
<CardTitle className="text-destructive">
|
|
290
|
+
Error in {componentName}
|
|
291
|
+
</CardTitle>
|
|
292
|
+
<CardDescription className="text-destructive/80">
|
|
293
|
+
{this.state.error?.message || 'An unexpected error occurred.'}
|
|
294
|
+
</CardDescription>
|
|
295
|
+
</CardHeader>
|
|
296
|
+
|
|
297
|
+
{import.meta.env.MODE === 'development' && this.state.error && (
|
|
298
|
+
<CardContent>
|
|
299
|
+
<details className="text-sm text-destructive/70">
|
|
300
|
+
<summary className="cursor-pointer font-medium mb-2">
|
|
301
|
+
Error Details (Development)
|
|
302
|
+
</summary>
|
|
303
|
+
<pre>Error ID: {errorId}
|
|
304
|
+
<code className="overflow-auto max-h-32">
|
|
305
|
+
{this.state.error.toString()}
|
|
306
|
+
{this.state.errorInfo?.componentStack}
|
|
307
|
+
</code>
|
|
308
|
+
</pre>
|
|
309
|
+
</details>
|
|
310
|
+
</CardContent>
|
|
311
|
+
)}
|
|
314
312
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
313
|
+
{enableRetry && retryCount < maxRetries && (
|
|
314
|
+
<CardFooter className="flex gap-3">
|
|
315
|
+
<button
|
|
316
|
+
onClick={this.handleRetry}
|
|
317
|
+
className="px-4 py-2 bg-destructive text-destructive-foreground rounded-md hover:bg-destructive/90 transition-colors text-sm font-medium"
|
|
318
|
+
>
|
|
319
|
+
Retry ({retryCount + 1}/{maxRetries})
|
|
320
|
+
</button>
|
|
321
|
+
<button
|
|
322
|
+
onClick={() => window.location.reload()}
|
|
323
|
+
className="px-4 py-2 bg-sec-600 text-main-50 rounded-md hover:bg-sec-700 transition-colors text-sm font-medium"
|
|
324
|
+
>
|
|
325
|
+
Reload Page
|
|
326
|
+
</button>
|
|
327
|
+
</CardFooter>
|
|
328
|
+
)}
|
|
328
329
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
</div>
|
|
344
|
-
</div>
|
|
345
|
-
</div>
|
|
330
|
+
{retryCount >= maxRetries && (
|
|
331
|
+
<CardFooter className="flex flex-col gap-3">
|
|
332
|
+
<p className="text-acc-800">
|
|
333
|
+
Maximum retry attempts reached. Please reload the page or contact support.
|
|
334
|
+
</p>
|
|
335
|
+
<button
|
|
336
|
+
onClick={() => window.location.reload()}
|
|
337
|
+
className="px-3 py-1 bg-acc-600 text-main-50 rounded text-sm hover:bg-acc-700"
|
|
338
|
+
>
|
|
339
|
+
Reload Page
|
|
340
|
+
</button>
|
|
341
|
+
</CardFooter>
|
|
342
|
+
)}
|
|
343
|
+
</Card>
|
|
346
344
|
);
|
|
347
345
|
}
|
|
348
346
|
|
|
@@ -223,7 +223,8 @@ describe('[component] FileDisplay', () => {
|
|
|
223
223
|
|
|
224
224
|
// Dialog should open
|
|
225
225
|
expect(await screen.findByText('Confirm Delete')).toBeInTheDocument();
|
|
226
|
-
|
|
226
|
+
// Button has aria-label="Delete file", so accessible name is "Delete file"
|
|
227
|
+
const confirmDeleteBtn = await screen.findByRole('button', { name: 'Delete file' }, { timeout: 2000 });
|
|
227
228
|
await userEvent.click(confirmDeleteBtn);
|
|
228
229
|
|
|
229
230
|
confirmSpy.mockRestore();
|
|
@@ -419,7 +420,8 @@ describe('[component] FileDisplay', () => {
|
|
|
419
420
|
|
|
420
421
|
// Dialog should open
|
|
421
422
|
expect(await screen.findByText('Confirm Delete')).toBeInTheDocument();
|
|
422
|
-
|
|
423
|
+
// Button has aria-label="Delete file", so accessible name is "Delete file"
|
|
424
|
+
const confirmDeleteBtn = await screen.findByRole('button', { name: 'Delete file' }, { timeout: 2000 });
|
|
423
425
|
await userEvent.click(confirmDeleteBtn);
|
|
424
426
|
});
|
|
425
427
|
|
|
@@ -654,9 +654,32 @@ function FileDisplayPublic({
|
|
|
654
654
|
enableChildren,
|
|
655
655
|
showMetadata
|
|
656
656
|
}: FileDisplayProps) {
|
|
657
|
+
// Call all hooks unconditionally at the top level
|
|
658
|
+
// Hooks must be called in the same order on every render
|
|
657
659
|
const publicPageContext = useContext(PublicPageContext);
|
|
658
660
|
const supabase = publicPageContext?.supabase ?? null;
|
|
659
661
|
|
|
662
|
+
// Call hook unconditionally - if supabase is null, the hook will handle it
|
|
663
|
+
// Use a dummy supabase client if null to satisfy type requirements
|
|
664
|
+
// The hook should handle null gracefully, but TypeScript requires a valid type
|
|
665
|
+
const {
|
|
666
|
+
fileUrl,
|
|
667
|
+
fileReference,
|
|
668
|
+
fileReferences,
|
|
669
|
+
fileUrls,
|
|
670
|
+
fileCount,
|
|
671
|
+
isLoading,
|
|
672
|
+
error,
|
|
673
|
+
refetch
|
|
674
|
+
} = usePublicFileDisplay(
|
|
675
|
+
table_name,
|
|
676
|
+
record_id,
|
|
677
|
+
organisation_id,
|
|
678
|
+
category,
|
|
679
|
+
{ supabase: supabase as any } // Type assertion needed due to type mismatch between contexts
|
|
680
|
+
);
|
|
681
|
+
|
|
682
|
+
// Early return after all hooks have been called
|
|
660
683
|
if (!supabase) {
|
|
661
684
|
// If fallback is enabled, show fallback UI instead of error
|
|
662
685
|
if (showFallback) {
|
|
@@ -700,23 +723,6 @@ function FileDisplayPublic({
|
|
|
700
723
|
);
|
|
701
724
|
}
|
|
702
725
|
|
|
703
|
-
const {
|
|
704
|
-
fileUrl,
|
|
705
|
-
fileReference,
|
|
706
|
-
fileReferences,
|
|
707
|
-
fileUrls,
|
|
708
|
-
fileCount,
|
|
709
|
-
isLoading,
|
|
710
|
-
error,
|
|
711
|
-
refetch
|
|
712
|
-
} = usePublicFileDisplay(
|
|
713
|
-
table_name,
|
|
714
|
-
record_id,
|
|
715
|
-
organisation_id,
|
|
716
|
-
category,
|
|
717
|
-
{ supabase }
|
|
718
|
-
);
|
|
719
|
-
|
|
720
726
|
// Log errors for debugging public file display issues
|
|
721
727
|
if (error) {
|
|
722
728
|
logger.error('FileDisplayPublic', 'Error fetching file', {
|
|
@@ -116,17 +116,19 @@ describe('[component] FileUpload', () => {
|
|
|
116
116
|
describe('Drag and Drop', () => {
|
|
117
117
|
it('shows drag state message on dragover', () => {
|
|
118
118
|
renderWithProviders(<FileUpload {...baseProps} />);
|
|
119
|
-
const dropArea = screen.
|
|
119
|
+
const dropArea = screen.getByRole('button', { name: /File upload area/i });
|
|
120
120
|
|
|
121
121
|
fireEvent.dragOver(dropArea);
|
|
122
|
+
// Component shows "Drop files here..." (with ellipsis)
|
|
122
123
|
expect(screen.getByText(/Drop files here/i)).toBeInTheDocument();
|
|
123
124
|
});
|
|
124
125
|
|
|
125
126
|
it('resets drag state on dragleave', () => {
|
|
126
127
|
renderWithProviders(<FileUpload {...baseProps} />);
|
|
127
|
-
const dropArea = screen.
|
|
128
|
+
const dropArea = screen.getByRole('button', { name: /File upload area/i });
|
|
128
129
|
|
|
129
130
|
fireEvent.dragOver(dropArea);
|
|
131
|
+
// Component shows "Drop files here..." (with ellipsis)
|
|
130
132
|
expect(screen.getByText(/Drop files here/i)).toBeInTheDocument();
|
|
131
133
|
|
|
132
134
|
fireEvent.dragLeave(dropArea);
|
|
@@ -135,24 +137,41 @@ describe('[component] FileUpload', () => {
|
|
|
135
137
|
|
|
136
138
|
it('handles file drop', async () => {
|
|
137
139
|
const onUploadSuccess = vi.fn();
|
|
140
|
+
// Ensure uploadFile mock resolves immediately
|
|
141
|
+
const mockUpload = vi.fn(async () => createMockUploadResult());
|
|
142
|
+
mockUseFileReference.mockReturnValue({
|
|
143
|
+
uploadFile: mockUpload,
|
|
144
|
+
isLoading: false,
|
|
145
|
+
error: null,
|
|
146
|
+
});
|
|
147
|
+
|
|
138
148
|
renderWithProviders(
|
|
139
149
|
<FileUpload {...baseProps} onUploadSuccess={onUploadSuccess} showProgress />
|
|
140
150
|
);
|
|
141
151
|
|
|
142
|
-
const dropArea = screen.
|
|
152
|
+
const dropArea = screen.getByRole('button', { name: /File upload area/i });
|
|
143
153
|
const file = createTestFile('test.png', 'image/png');
|
|
144
154
|
|
|
155
|
+
// Use fireEvent.drop with proper dataTransfer mock
|
|
145
156
|
await act(async () => {
|
|
146
157
|
fireEvent.drop(dropArea, {
|
|
147
158
|
dataTransfer: {
|
|
148
159
|
files: [file],
|
|
149
|
-
|
|
160
|
+
items: [{
|
|
161
|
+
kind: 'file',
|
|
162
|
+
type: file.type,
|
|
163
|
+
getAsFile: () => file
|
|
164
|
+
}],
|
|
165
|
+
types: ['Files']
|
|
166
|
+
}
|
|
150
167
|
});
|
|
151
168
|
});
|
|
152
169
|
|
|
170
|
+
// Wait for upload to complete - the uploadFile is called asynchronously
|
|
153
171
|
await waitFor(() => {
|
|
172
|
+
expect(mockUpload).toHaveBeenCalled();
|
|
154
173
|
expect(onUploadSuccess).toHaveBeenCalled();
|
|
155
|
-
});
|
|
174
|
+
}, { timeout: 3000 });
|
|
156
175
|
});
|
|
157
176
|
|
|
158
177
|
it('does not handle drop when disabled', () => {
|
|
@@ -204,7 +223,7 @@ describe('[component] FileUpload', () => {
|
|
|
204
223
|
|
|
205
224
|
it('triggers file input click when drop area is clicked', () => {
|
|
206
225
|
renderWithProviders(<FileUpload {...baseProps} />);
|
|
207
|
-
const dropArea = screen.
|
|
226
|
+
const dropArea = screen.getByRole('button', { name: /File upload area/i });
|
|
208
227
|
const input = screen.getByTestId('file-input') as HTMLInputElement;
|
|
209
228
|
const clickSpy = vi.spyOn(input, 'click');
|
|
210
229
|
|
|
@@ -398,12 +417,18 @@ describe('[component] FileUpload', () => {
|
|
|
398
417
|
fireEvent.change(input, { target: { files: [file] } });
|
|
399
418
|
});
|
|
400
419
|
|
|
420
|
+
// Wait for upload to complete and file card to be rendered with File icon
|
|
421
|
+
// The File icon is rendered as an SVG inside CardHeader (header) inside Card (article)
|
|
422
|
+
// Since showPreview is false and it's a PDF, the File icon should be rendered
|
|
423
|
+
// In tests, lucide-react icons are mocked with data-testid="lucide-{name}"
|
|
401
424
|
await waitFor(() => {
|
|
425
|
+
// First verify the file name is present
|
|
402
426
|
expect(screen.getByText('test.pdf')).toBeInTheDocument();
|
|
403
|
-
|
|
404
|
-
|
|
427
|
+
|
|
428
|
+
// The File icon is mocked with data-testid="lucide-file" in tests
|
|
429
|
+
const fileIcon = screen.getByTestId('lucide-file');
|
|
405
430
|
expect(fileIcon).toBeInTheDocument();
|
|
406
|
-
});
|
|
431
|
+
}, { timeout: 3000 });
|
|
407
432
|
});
|
|
408
433
|
|
|
409
434
|
it('shows loading spinner when showProgress is false', async () => {
|
|
@@ -429,14 +454,27 @@ describe('[component] FileUpload', () => {
|
|
|
429
454
|
});
|
|
430
455
|
|
|
431
456
|
// Wait for spinner to appear
|
|
457
|
+
// The LoadingSpinner has role="status" but no accessible name
|
|
458
|
+
// When showProgress is false and isUploading is true, spinner appears in CardHeader
|
|
459
|
+
// The upload starts immediately when file is selected, and status changes to 'uploading'
|
|
460
|
+
// Wait for the upload state to be set to 'uploading' which triggers isUploading
|
|
461
|
+
// The spinner appears in the CardHeader (the drop area) when isUploading && !showProgress
|
|
432
462
|
await waitFor(() => {
|
|
433
|
-
|
|
434
|
-
|
|
463
|
+
// The spinner should appear when isUploading is true and showProgress is false
|
|
464
|
+
// isUploading is true when uploadStates has entries with status 'uploading' or 'processing'
|
|
465
|
+
// The spinner is in the CardHeader (the drop area)
|
|
466
|
+
const dropArea = screen.getByRole('button', { name: /File upload area/i });
|
|
467
|
+
const spinner = dropArea.querySelector('[role="status"]');
|
|
435
468
|
expect(spinner).toBeInTheDocument();
|
|
469
|
+
// LoadingSpinner renders a canvas element with animate-spin class directly on it
|
|
470
|
+
// So we check if the spinner element itself has the class
|
|
471
|
+
expect(spinner).toHaveClass('animate-spin');
|
|
472
|
+
}, { timeout: 2000 });
|
|
473
|
+
|
|
474
|
+
// Now resolve the upload to complete it
|
|
475
|
+
await act(async () => {
|
|
476
|
+
resolveUpload!(createMockUploadResult());
|
|
436
477
|
});
|
|
437
|
-
|
|
438
|
-
// Resolve upload to clean up
|
|
439
|
-
resolveUpload!(createMockUploadResult());
|
|
440
478
|
});
|
|
441
479
|
});
|
|
442
480
|
|