@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
|
@@ -40,20 +40,21 @@ import { getTextContent } from "./utils/text";
|
|
|
40
40
|
* Provides select dropdown functionality with search, keyboard navigation, and accessibility.
|
|
41
41
|
*
|
|
42
42
|
* @param props - Select configuration
|
|
43
|
-
* @param ref - Forwarded ref to the
|
|
43
|
+
* @param ref - Forwarded ref to the fieldset element
|
|
44
44
|
* @returns The rendered select component
|
|
45
45
|
*/
|
|
46
|
-
export const Select = React.forwardRef<
|
|
47
|
-
({
|
|
46
|
+
export const Select = React.forwardRef<HTMLFieldSetElement, SelectProps & UseSelectStateProps>(
|
|
47
|
+
({
|
|
48
48
|
children,
|
|
49
49
|
className,
|
|
50
50
|
direction = 'down',
|
|
51
|
+
showCheckmark = true,
|
|
51
52
|
...selectProps
|
|
52
53
|
}, ref) => {
|
|
53
|
-
const internalRef = React.useRef<
|
|
54
|
+
const internalRef = React.useRef<HTMLFieldSetElement>(null);
|
|
54
55
|
const selectRef = React.useMemo(() => {
|
|
55
56
|
if (ref && typeof ref === 'object' && 'current' in ref) {
|
|
56
|
-
return ref as React.RefObject<
|
|
57
|
+
return ref as React.RefObject<HTMLFieldSetElement | null>;
|
|
57
58
|
}
|
|
58
59
|
return internalRef;
|
|
59
60
|
}, [ref]);
|
|
@@ -108,7 +109,7 @@ export const Select = React.forwardRef<HTMLFormElement, SelectProps & UseSelectS
|
|
|
108
109
|
// Query the DOM for the SelectItem (li element) with matching value
|
|
109
110
|
// Must be a select-item, not the trigger which also has data-value
|
|
110
111
|
let selectItem = selectElement.querySelector(`[data-testid="select-item"][data-value="${state.value}"]`) as HTMLElement;
|
|
111
|
-
|
|
112
|
+
|
|
112
113
|
// If not found, try querying within content containers
|
|
113
114
|
if (!selectItem) {
|
|
114
115
|
const hiddenContent = selectElement.querySelector('[data-testid="select-content-hidden"]');
|
|
@@ -116,7 +117,7 @@ export const Select = React.forwardRef<HTMLFormElement, SelectProps & UseSelectS
|
|
|
116
117
|
selectItem = hiddenContent.querySelector(`[data-testid="select-item"][data-value="${state.value}"]`) as HTMLElement;
|
|
117
118
|
}
|
|
118
119
|
}
|
|
119
|
-
|
|
120
|
+
|
|
120
121
|
// Try visible content as fallback
|
|
121
122
|
if (!selectItem) {
|
|
122
123
|
const visibleContent = selectElement.querySelector('[data-testid="select-content"]');
|
|
@@ -130,7 +131,7 @@ export const Select = React.forwardRef<HTMLFormElement, SelectProps & UseSelectS
|
|
|
130
131
|
let textContent = selectItem.textContent?.trim() || '';
|
|
131
132
|
// Remove any check mark icons or other decorators (basic cleanup)
|
|
132
133
|
textContent = textContent.split('\n').map(line => line.trim()).filter(line => line).join(' ');
|
|
133
|
-
|
|
134
|
+
|
|
134
135
|
if (textContent) {
|
|
135
136
|
actions.setSelectedText(textContent);
|
|
136
137
|
// Also register it for future use
|
|
@@ -138,11 +139,11 @@ export const Select = React.forwardRef<HTMLFormElement, SelectProps & UseSelectS
|
|
|
138
139
|
return;
|
|
139
140
|
}
|
|
140
141
|
}
|
|
141
|
-
|
|
142
|
+
|
|
142
143
|
// If not found and we have a content container, items might still be rendering
|
|
143
144
|
// Don't clear selectedText in this case - let the MutationObserver or retry effect handle it
|
|
144
|
-
const hasContentContainer = selectElement.querySelector('[data-testid="select-content-hidden"]') ||
|
|
145
|
-
|
|
145
|
+
const hasContentContainer = selectElement.querySelector('[data-testid="select-content-hidden"]') ||
|
|
146
|
+
selectElement.querySelector('[data-testid="select-content"]');
|
|
146
147
|
if (!hasContentContainer) {
|
|
147
148
|
// No content container means no items exist - clear selectedText
|
|
148
149
|
actions.setSelectedText('');
|
|
@@ -198,23 +199,20 @@ export const Select = React.forwardRef<HTMLFormElement, SelectProps & UseSelectS
|
|
|
198
199
|
registerItem,
|
|
199
200
|
unregisterItem,
|
|
200
201
|
direction,
|
|
201
|
-
|
|
202
|
+
showCheckmark,
|
|
203
|
+
}), [state, actions, registerItem, unregisterItem, direction, showCheckmark]);
|
|
202
204
|
|
|
203
205
|
return (
|
|
204
|
-
<
|
|
206
|
+
<fieldset
|
|
205
207
|
ref={selectRef}
|
|
206
|
-
className={cn("relative", className)}
|
|
208
|
+
className={cn("relative border-0 p-0 m-0", className)}
|
|
207
209
|
data-value={state.value}
|
|
208
210
|
data-testid="select-root"
|
|
209
|
-
onSubmit={(e) => {
|
|
210
|
-
e.preventDefault();
|
|
211
|
-
e.stopPropagation();
|
|
212
|
-
}}
|
|
213
211
|
>
|
|
214
212
|
<SelectContext.Provider value={contextValue}>
|
|
215
213
|
{children}
|
|
216
214
|
</SelectContext.Provider>
|
|
217
|
-
</
|
|
215
|
+
</fieldset>
|
|
218
216
|
);
|
|
219
217
|
}
|
|
220
218
|
);
|
|
@@ -239,14 +237,14 @@ export const SelectTrigger = React.forwardRef<HTMLButtonElement, SelectTriggerPr
|
|
|
239
237
|
|
|
240
238
|
// Use ref to store the latest handleClick to avoid re-creating the effect
|
|
241
239
|
const handleClickRef = React.useRef<(e: React.MouseEvent) => void>(undefined);
|
|
242
|
-
|
|
240
|
+
|
|
243
241
|
const handleClick = React.useCallback((e: React.MouseEvent) => {
|
|
244
242
|
if (disabled) {
|
|
245
243
|
e.preventDefault();
|
|
246
244
|
e.stopPropagation();
|
|
247
245
|
return;
|
|
248
246
|
}
|
|
249
|
-
|
|
247
|
+
|
|
250
248
|
e.preventDefault();
|
|
251
249
|
e.stopPropagation();
|
|
252
250
|
actions.setOpen(!open);
|
|
@@ -304,15 +302,27 @@ export const SelectTrigger = React.forwardRef<HTMLButtonElement, SelectTriggerPr
|
|
|
304
302
|
...props
|
|
305
303
|
};
|
|
306
304
|
|
|
305
|
+
// Call all hooks unconditionally at the top level
|
|
306
|
+
// Hooks must be called in the same order on every render
|
|
307
|
+
// Simple ref forwarding - must be called before any early returns
|
|
308
|
+
const handleRef = React.useCallback((node: HTMLButtonElement | null) => {
|
|
309
|
+
if (typeof ref === 'function') {
|
|
310
|
+
ref(node);
|
|
311
|
+
} else if (ref) {
|
|
312
|
+
(ref as React.MutableRefObject<HTMLButtonElement | null>).current = node;
|
|
313
|
+
}
|
|
314
|
+
}, [ref]);
|
|
315
|
+
|
|
316
|
+
// Early return after all hooks have been called
|
|
307
317
|
if (asChild) {
|
|
308
|
-
const childElement = children as React.ReactElement<{ children?: React.ReactNode;
|
|
318
|
+
const childElement = children as React.ReactElement<{ children?: React.ReactNode;[key: string]: unknown }>;
|
|
309
319
|
const childChildren = React.Children.toArray(childElement.props.children);
|
|
310
320
|
const hasChevron = childChildren.some(child => {
|
|
311
321
|
if (React.isValidElement(child)) {
|
|
312
|
-
const childProps = child.props as { 'data-testid'?: string;
|
|
313
|
-
return child.type === ChevronDown ||
|
|
314
|
-
|
|
315
|
-
|
|
322
|
+
const childProps = child.props as { 'data-testid'?: string;[key: string]: unknown };
|
|
323
|
+
return child.type === ChevronDown ||
|
|
324
|
+
(child.type === 'svg' && childProps['data-testid'] === 'chevron-down') ||
|
|
325
|
+
(typeof child === 'object' && 'type' in child && typeof child.type === 'function' && child.type.name === 'ChevronDown');
|
|
316
326
|
}
|
|
317
327
|
return false;
|
|
318
328
|
});
|
|
@@ -329,27 +339,17 @@ export const SelectTrigger = React.forwardRef<HTMLButtonElement, SelectTriggerPr
|
|
|
329
339
|
className: mergedClassName,
|
|
330
340
|
children: hasChevron ? childChildren : [
|
|
331
341
|
...childChildren,
|
|
332
|
-
<ChevronDown
|
|
342
|
+
<ChevronDown
|
|
333
343
|
key="chevron-down"
|
|
334
344
|
className={cn(
|
|
335
345
|
"size-4 opacity-50 transition-transform pointer-events-none float-right",
|
|
336
346
|
open && "rotate-180"
|
|
337
|
-
)}
|
|
347
|
+
)}
|
|
338
348
|
/>
|
|
339
349
|
]
|
|
340
350
|
});
|
|
341
351
|
}
|
|
342
352
|
|
|
343
|
-
|
|
344
|
-
// Simple ref forwarding
|
|
345
|
-
const handleRef = React.useCallback((node: HTMLButtonElement | null) => {
|
|
346
|
-
if (typeof ref === 'function') {
|
|
347
|
-
ref(node);
|
|
348
|
-
} else if (ref) {
|
|
349
|
-
(ref as React.MutableRefObject<HTMLButtonElement | null>).current = node;
|
|
350
|
-
}
|
|
351
|
-
}, [ref]);
|
|
352
|
-
|
|
353
353
|
return (
|
|
354
354
|
<Button
|
|
355
355
|
ref={handleRef}
|
|
@@ -382,11 +382,11 @@ export const SelectTrigger = React.forwardRef<HTMLButtonElement, SelectTriggerPr
|
|
|
382
382
|
{...props}
|
|
383
383
|
>
|
|
384
384
|
{children}
|
|
385
|
-
<ChevronDown
|
|
385
|
+
<ChevronDown
|
|
386
386
|
className={cn(
|
|
387
387
|
"size-4 opacity-50 transition-transform pointer-events-none float-right",
|
|
388
388
|
open && "rotate-180"
|
|
389
|
-
)}
|
|
389
|
+
)}
|
|
390
390
|
/>
|
|
391
391
|
</Button>
|
|
392
392
|
);
|
|
@@ -411,8 +411,8 @@ export const SelectValue = React.forwardRef<HTMLSpanElement, SelectValueProps>(
|
|
|
411
411
|
const { selectedText } = useSelectContext();
|
|
412
412
|
|
|
413
413
|
return (
|
|
414
|
-
<span
|
|
415
|
-
ref={ref}
|
|
414
|
+
<span
|
|
415
|
+
ref={ref}
|
|
416
416
|
data-testid="select-value"
|
|
417
417
|
style={{ pointerEvents: 'none' }}
|
|
418
418
|
className="pointer-events-none"
|
|
@@ -437,9 +437,9 @@ SelectValue.displayName = "SelectValue";
|
|
|
437
437
|
* @returns The rendered select content
|
|
438
438
|
*/
|
|
439
439
|
export const SelectContent = React.forwardRef<HTMLUListElement, SelectContentProps>(
|
|
440
|
-
({
|
|
441
|
-
children,
|
|
442
|
-
className,
|
|
440
|
+
({
|
|
441
|
+
children,
|
|
442
|
+
className,
|
|
443
443
|
searchable = false,
|
|
444
444
|
searchPlaceholder = "Search...",
|
|
445
445
|
maxHeight = "max(20rem, 50vh)",
|
|
@@ -475,19 +475,19 @@ export const SelectContent = React.forwardRef<HTMLUListElement, SelectContentPro
|
|
|
475
475
|
}
|
|
476
476
|
|
|
477
477
|
const opensUpward = direction === 'up';
|
|
478
|
-
|
|
478
|
+
|
|
479
479
|
return (
|
|
480
480
|
<ul
|
|
481
481
|
ref={ref}
|
|
482
482
|
className={cn(
|
|
483
483
|
"absolute z-[99999] w-full overflow-y-auto border border-main-300 bg-main-50 shadow-lg",
|
|
484
484
|
"list-none p-0 m-0",
|
|
485
|
-
opensUpward
|
|
486
|
-
? "rounded-t-md border-b-0"
|
|
485
|
+
opensUpward
|
|
486
|
+
? "rounded-t-md border-b-0"
|
|
487
487
|
: "rounded-b-md border-t-0",
|
|
488
488
|
className
|
|
489
489
|
)}
|
|
490
|
-
style={{
|
|
490
|
+
style={{
|
|
491
491
|
[opensUpward ? 'bottom' : 'top']: '100%',
|
|
492
492
|
left: 0,
|
|
493
493
|
right: 0,
|
|
@@ -500,38 +500,36 @@ export const SelectContent = React.forwardRef<HTMLUListElement, SelectContentPro
|
|
|
500
500
|
role="listbox"
|
|
501
501
|
>
|
|
502
502
|
{searchable && (
|
|
503
|
-
<
|
|
504
|
-
<
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
</div>
|
|
534
|
-
</div>
|
|
503
|
+
<li className="relative p-2 border-b border-main-200 sticky top-0 bg-main-50 z-10" data-testid="select-search-item">
|
|
504
|
+
<Search className="absolute left-2 top-1/2 transform -translate-y-1/2 size-4 text-main-400" />
|
|
505
|
+
<input
|
|
506
|
+
ref={searchInputRef}
|
|
507
|
+
type="text"
|
|
508
|
+
placeholder={searchPlaceholder}
|
|
509
|
+
value={searchTerm}
|
|
510
|
+
onChange={(e) => setSearchTerm(e.target.value)}
|
|
511
|
+
onKeyDown={(e) => {
|
|
512
|
+
if (e.key === 'Escape') {
|
|
513
|
+
e.preventDefault();
|
|
514
|
+
setSearchTerm('');
|
|
515
|
+
}
|
|
516
|
+
}}
|
|
517
|
+
className="w-full pl-8 pr-8 py-1 text-sm border border-main-200 rounded focus:outline-none focus:ring-2 focus:ring-main-500"
|
|
518
|
+
data-testid="select-search-input"
|
|
519
|
+
aria-label="Search options"
|
|
520
|
+
/>
|
|
521
|
+
{searchTerm && (
|
|
522
|
+
<button
|
|
523
|
+
type="button"
|
|
524
|
+
onClick={() => setSearchTerm('')}
|
|
525
|
+
className="absolute right-2 top-1/2 transform -translate-y-1/2 text-main-400 hover:text-main-600"
|
|
526
|
+
data-testid="select-clear-search"
|
|
527
|
+
aria-label="Clear search"
|
|
528
|
+
>
|
|
529
|
+
<X className="size-4" />
|
|
530
|
+
</button>
|
|
531
|
+
)}
|
|
532
|
+
</li>
|
|
535
533
|
)}
|
|
536
534
|
{filteredChildren}
|
|
537
535
|
</ul>
|
|
@@ -553,9 +551,12 @@ SelectContent.displayName = "SelectContent";
|
|
|
553
551
|
* @returns The rendered select item
|
|
554
552
|
*/
|
|
555
553
|
export const SelectItem = React.forwardRef<HTMLLIElement, SelectItemProps>(
|
|
556
|
-
({ value, children, disabled = false, className, onClick }, ref) => {
|
|
557
|
-
const { value: selectedValue, actions, registerItem, unregisterItem } = useSelectContext();
|
|
554
|
+
({ value, children, disabled = false, className, onClick, showCheckmark: propShowCheckmark }, ref) => {
|
|
555
|
+
const { value: selectedValue, actions, registerItem, unregisterItem, showCheckmark: contextShowCheckmark = true } = useSelectContext();
|
|
558
556
|
const isSelected = selectedValue === value;
|
|
557
|
+
|
|
558
|
+
// Use prop if provided, otherwise use context value, default to true
|
|
559
|
+
const showCheckmark = propShowCheckmark ?? contextShowCheckmark;
|
|
559
560
|
|
|
560
561
|
const itemText = getTextContent(children);
|
|
561
562
|
|
|
@@ -571,9 +572,11 @@ export const SelectItem = React.forwardRef<HTMLLIElement, SelectItemProps>(
|
|
|
571
572
|
}
|
|
572
573
|
};
|
|
573
574
|
}, [value, itemText, registerItem, unregisterItem]);
|
|
574
|
-
|
|
575
|
+
|
|
575
576
|
const handleMouseDown = (e: React.MouseEvent) => {
|
|
576
577
|
if (!disabled) {
|
|
578
|
+
// CRITICAL FIX: Prevent event from bubbling to avoid interference with table row interactions
|
|
579
|
+
e.stopPropagation();
|
|
577
580
|
const event = new CustomEvent('selectItemMouseDown', { detail: { value } });
|
|
578
581
|
document.dispatchEvent(event);
|
|
579
582
|
}
|
|
@@ -581,6 +584,8 @@ export const SelectItem = React.forwardRef<HTMLLIElement, SelectItemProps>(
|
|
|
581
584
|
|
|
582
585
|
const handleClick = (e: React.MouseEvent) => {
|
|
583
586
|
if (!disabled) {
|
|
587
|
+
// CRITICAL FIX: Prevent event from bubbling to avoid interference with table row clicks
|
|
588
|
+
e.stopPropagation();
|
|
584
589
|
if (onClick) {
|
|
585
590
|
onClick(e);
|
|
586
591
|
}
|
|
@@ -634,7 +639,7 @@ export const SelectItem = React.forwardRef<HTMLLIElement, SelectItemProps>(
|
|
|
634
639
|
tabIndex={disabled ? -1 : 0}
|
|
635
640
|
>
|
|
636
641
|
{children}
|
|
637
|
-
{isSelected && (
|
|
642
|
+
{isSelected && showCheckmark && (
|
|
638
643
|
<Check className="absolute right-2 size-4 flex-shrink-0 mt-0.5" />
|
|
639
644
|
)}
|
|
640
645
|
</li>
|
|
@@ -649,18 +654,24 @@ SelectItem.displayName = "SelectItem";
|
|
|
649
654
|
|
|
650
655
|
/**
|
|
651
656
|
* Select group component.
|
|
652
|
-
* Groups related select items together.
|
|
657
|
+
* Groups related select items together using a nested list structure.
|
|
653
658
|
*
|
|
654
659
|
* @param props - Select group configuration
|
|
655
|
-
* @param ref - Forwarded ref to the
|
|
660
|
+
* @param ref - Forwarded ref to the ul element
|
|
656
661
|
* @returns The rendered select group
|
|
657
662
|
*/
|
|
658
|
-
export const SelectGroup = React.forwardRef<
|
|
663
|
+
export const SelectGroup = React.forwardRef<HTMLUListElement, { children: React.ReactNode; className?: string }>(
|
|
659
664
|
({ children, className }, ref) => {
|
|
660
665
|
return (
|
|
661
|
-
<
|
|
662
|
-
|
|
663
|
-
|
|
666
|
+
<li className="list-none" data-testid="select-group-wrapper">
|
|
667
|
+
<ul
|
|
668
|
+
ref={ref}
|
|
669
|
+
className={cn("p-1 list-none m-0", className)}
|
|
670
|
+
data-testid="select-group"
|
|
671
|
+
>
|
|
672
|
+
{children}
|
|
673
|
+
</ul>
|
|
674
|
+
</li>
|
|
664
675
|
);
|
|
665
676
|
}
|
|
666
677
|
);
|
|
@@ -671,15 +682,15 @@ SelectGroup.displayName = "SelectGroup";
|
|
|
671
682
|
* Provides a label for a group of select items.
|
|
672
683
|
*
|
|
673
684
|
* @param props - Select label configuration
|
|
674
|
-
* @param ref - Forwarded ref to the
|
|
685
|
+
* @param ref - Forwarded ref to the li element
|
|
675
686
|
* @returns The rendered select label
|
|
676
687
|
*/
|
|
677
|
-
export const SelectLabel = React.forwardRef<
|
|
688
|
+
export const SelectLabel = React.forwardRef<HTMLLIElement, { children: React.ReactNode; className?: string }>(
|
|
678
689
|
({ children, className }, ref) => {
|
|
679
690
|
return (
|
|
680
|
-
<
|
|
691
|
+
<li ref={ref} className={cn("px-2 py-1.5 text-sm font-semibold", className)} data-testid="select-label">
|
|
681
692
|
{children}
|
|
682
|
-
</
|
|
693
|
+
</li>
|
|
683
694
|
);
|
|
684
695
|
}
|
|
685
696
|
);
|
|
@@ -690,13 +701,13 @@ SelectLabel.displayName = "SelectLabel";
|
|
|
690
701
|
* Provides visual separation between groups of select items.
|
|
691
702
|
*
|
|
692
703
|
* @param props - Select separator configuration
|
|
693
|
-
* @param ref - Forwarded ref to the
|
|
704
|
+
* @param ref - Forwarded ref to the hr element
|
|
694
705
|
* @returns The rendered select separator
|
|
695
706
|
*/
|
|
696
|
-
export const SelectSeparator = React.forwardRef<
|
|
707
|
+
export const SelectSeparator = React.forwardRef<HTMLHRElement, { className?: string }>(
|
|
697
708
|
({ className }, ref) => {
|
|
698
709
|
return (
|
|
699
|
-
<
|
|
710
|
+
<hr
|
|
700
711
|
ref={ref}
|
|
701
712
|
className={cn("my-1 h-px bg-sec-200", className)}
|
|
702
713
|
data-testid="select-separator"
|
|
@@ -67,6 +67,7 @@ export interface SelectContextValue extends SelectState {
|
|
|
67
67
|
registerItem?: (value: string, text: string) => void;
|
|
68
68
|
unregisterItem?: (value: string) => void;
|
|
69
69
|
direction?: SelectDirection;
|
|
70
|
+
showCheckmark?: boolean;
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
/**
|
|
@@ -74,12 +75,13 @@ export interface SelectContextValue extends SelectState {
|
|
|
74
75
|
*/
|
|
75
76
|
export interface SelectProps
|
|
76
77
|
extends Omit<
|
|
77
|
-
React.HTMLAttributes<
|
|
78
|
+
React.HTMLAttributes<HTMLFieldSetElement>,
|
|
78
79
|
"onChange" | "onKeyDown" | "onFocus" | "onBlur"
|
|
79
80
|
> {
|
|
80
81
|
children: React.ReactNode;
|
|
81
82
|
className?: string;
|
|
82
83
|
direction?: SelectDirection;
|
|
84
|
+
showCheckmark?: boolean;
|
|
83
85
|
}
|
|
84
86
|
|
|
85
87
|
/**
|
|
@@ -120,4 +122,5 @@ export interface SelectItemProps {
|
|
|
120
122
|
disabled?: boolean;
|
|
121
123
|
className?: string;
|
|
122
124
|
onClick?: (e: React.MouseEvent) => void;
|
|
125
|
+
showCheckmark?: boolean;
|
|
123
126
|
}
|
|
@@ -142,6 +142,8 @@ export const UserMenu = React.memo<UserMenuProps>(function UserMenu({
|
|
|
142
142
|
className,
|
|
143
143
|
showAvatar = true,
|
|
144
144
|
}) {
|
|
145
|
+
// Call all hooks unconditionally at the top level
|
|
146
|
+
// Hooks must be called in the same order on every render
|
|
145
147
|
const userInfo = useMemo(() => {
|
|
146
148
|
if (!user) return null;
|
|
147
149
|
return {
|
|
@@ -156,11 +158,7 @@ export const UserMenu = React.memo<UserMenuProps>(function UserMenu({
|
|
|
156
158
|
if (onSignOut) await onSignOut();
|
|
157
159
|
}, [onSignOut]);
|
|
158
160
|
|
|
159
|
-
|
|
160
|
-
return null; // Or a loading/login button
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// Password change dialog state and handlers (moved closer to Dialog usage)
|
|
161
|
+
// Password change dialog state and handlers - must be called before any early returns
|
|
164
162
|
const [isPasswordDialogOpen, setPasswordDialogOpen] = useState(false);
|
|
165
163
|
|
|
166
164
|
const handlePasswordChange = useCallback(async (newPassword: string, confirmPassword: string) => {
|
|
@@ -170,6 +168,11 @@ export const UserMenu = React.memo<UserMenuProps>(function UserMenu({
|
|
|
170
168
|
return await onChangePassword(newPassword, confirmPassword);
|
|
171
169
|
}, [onChangePassword]);
|
|
172
170
|
|
|
171
|
+
// Early return after all hooks have been called
|
|
172
|
+
if (!user || !userInfo) {
|
|
173
|
+
return null; // Or a loading/login button
|
|
174
|
+
}
|
|
175
|
+
|
|
173
176
|
return (
|
|
174
177
|
<>
|
|
175
178
|
<Select className={className}>
|
|
@@ -210,7 +213,7 @@ export const UserMenu = React.memo<UserMenuProps>(function UserMenu({
|
|
|
210
213
|
</Select>
|
|
211
214
|
|
|
212
215
|
<Dialog open={isPasswordDialogOpen} onOpenChange={setPasswordDialogOpen}>
|
|
213
|
-
<DialogContent>
|
|
216
|
+
<DialogContent persistOpenState={false}>
|
|
214
217
|
<DialogHeader>
|
|
215
218
|
<DialogTitle>Change Password</DialogTitle>
|
|
216
219
|
</DialogHeader>
|
|
@@ -137,7 +137,7 @@ describe('Service Hooks', () => {
|
|
|
137
137
|
it('should return AuthService from context', () => {
|
|
138
138
|
const mockService = createMockService('auth');
|
|
139
139
|
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
140
|
-
<
|
|
140
|
+
<section>{children}</section>
|
|
141
141
|
);
|
|
142
142
|
|
|
143
143
|
// Mock the context with proper structure
|
|
@@ -150,7 +150,7 @@ describe('Service Hooks', () => {
|
|
|
150
150
|
|
|
151
151
|
it('should throw error when used outside provider', () => {
|
|
152
152
|
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
153
|
-
<
|
|
153
|
+
<section>{children}</section>
|
|
154
154
|
);
|
|
155
155
|
|
|
156
156
|
// Mock the context to return null
|
|
@@ -164,7 +164,7 @@ describe('Service Hooks', () => {
|
|
|
164
164
|
it('should subscribe to service changes', () => {
|
|
165
165
|
const mockService = createMockService('auth');
|
|
166
166
|
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
167
|
-
<
|
|
167
|
+
<section>{children}</section>
|
|
168
168
|
);
|
|
169
169
|
|
|
170
170
|
mockUseContext.mockReturnValue({ authService: mockService });
|
|
@@ -179,7 +179,7 @@ describe('Service Hooks', () => {
|
|
|
179
179
|
it('should return OrganisationService from context', () => {
|
|
180
180
|
const mockService = createMockService('organisation');
|
|
181
181
|
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
182
|
-
<
|
|
182
|
+
<section>{children}</section>
|
|
183
183
|
);
|
|
184
184
|
|
|
185
185
|
mockUseContext.mockReturnValue({ organisationService: mockService });
|
|
@@ -191,7 +191,7 @@ describe('Service Hooks', () => {
|
|
|
191
191
|
|
|
192
192
|
it('should throw error when used outside provider', () => {
|
|
193
193
|
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
194
|
-
<
|
|
194
|
+
<section>{children}</section>
|
|
195
195
|
);
|
|
196
196
|
|
|
197
197
|
mockUseContext.mockReturnValue(null);
|
|
@@ -206,7 +206,7 @@ describe('Service Hooks', () => {
|
|
|
206
206
|
it('should return EventService from context', () => {
|
|
207
207
|
const mockService = createMockService('event');
|
|
208
208
|
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
209
|
-
<
|
|
209
|
+
<section>{children}</section>
|
|
210
210
|
);
|
|
211
211
|
|
|
212
212
|
mockUseContext.mockReturnValue({ eventService: mockService });
|
|
@@ -218,7 +218,7 @@ describe('Service Hooks', () => {
|
|
|
218
218
|
|
|
219
219
|
it('should throw error when used outside provider', () => {
|
|
220
220
|
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
221
|
-
<
|
|
221
|
+
<section>{children}</section>
|
|
222
222
|
);
|
|
223
223
|
|
|
224
224
|
mockUseContext.mockReturnValue(null);
|
|
@@ -233,7 +233,7 @@ describe('Service Hooks', () => {
|
|
|
233
233
|
it('should return InactivityService from context', () => {
|
|
234
234
|
const mockService = createMockService('inactivity');
|
|
235
235
|
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
236
|
-
<
|
|
236
|
+
<section>{children}</section>
|
|
237
237
|
);
|
|
238
238
|
|
|
239
239
|
vi.spyOn(React, 'useContext').mockReturnValue({ inactivityService: mockService });
|
|
@@ -245,7 +245,7 @@ describe('Service Hooks', () => {
|
|
|
245
245
|
|
|
246
246
|
it('should throw error when used outside provider', () => {
|
|
247
247
|
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
248
|
-
<
|
|
248
|
+
<section>{children}</section>
|
|
249
249
|
);
|
|
250
250
|
|
|
251
251
|
mockUseContext.mockReturnValue(null);
|
|
@@ -276,7 +276,7 @@ describe('Service Hooks', () => {
|
|
|
276
276
|
};
|
|
277
277
|
|
|
278
278
|
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
279
|
-
<
|
|
279
|
+
<section>{children}</section>
|
|
280
280
|
);
|
|
281
281
|
|
|
282
282
|
mockUseContext.mockReturnValue({ authService: mockService });
|
|
@@ -327,7 +327,7 @@ describe('Service Hooks', () => {
|
|
|
327
327
|
};
|
|
328
328
|
|
|
329
329
|
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
330
|
-
<
|
|
330
|
+
<section>{children}</section>
|
|
331
331
|
);
|
|
332
332
|
|
|
333
333
|
mockUseContext.mockReturnValue({ rbacService: mockService });
|
|
@@ -382,7 +382,7 @@ describe('Service Hooks', () => {
|
|
|
382
382
|
};
|
|
383
383
|
|
|
384
384
|
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
385
|
-
<
|
|
385
|
+
<section>{children}</section>
|
|
386
386
|
);
|
|
387
387
|
|
|
388
388
|
mockUseContext.mockReturnValue({ organisationService: mockService });
|
|
@@ -434,7 +434,7 @@ describe('Service Hooks', () => {
|
|
|
434
434
|
};
|
|
435
435
|
|
|
436
436
|
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
437
|
-
<
|
|
437
|
+
<section>{children}</section>
|
|
438
438
|
);
|
|
439
439
|
|
|
440
440
|
mockUseContext.mockReturnValue({ eventService: mockService });
|
|
@@ -527,7 +527,7 @@ describe('Service Hooks', () => {
|
|
|
527
527
|
};
|
|
528
528
|
|
|
529
529
|
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
530
|
-
<
|
|
530
|
+
<section>{children}</section>
|
|
531
531
|
);
|
|
532
532
|
|
|
533
533
|
// Test individual hooks
|
|
@@ -570,7 +570,7 @@ describe('Service Hooks', () => {
|
|
|
570
570
|
};
|
|
571
571
|
|
|
572
572
|
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
573
|
-
<
|
|
573
|
+
<section>{children}</section>
|
|
574
574
|
);
|
|
575
575
|
|
|
576
576
|
mockUseContext.mockReturnValue({ authService: mockService });
|
|
@@ -600,7 +600,7 @@ describe('Service Hooks', () => {
|
|
|
600
600
|
};
|
|
601
601
|
|
|
602
602
|
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
603
|
-
<
|
|
603
|
+
<section>{children}</section>
|
|
604
604
|
);
|
|
605
605
|
|
|
606
606
|
mockUseContext.mockReturnValue({ authService: mockService });
|