@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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { LoadingSpinner } from '../../LoadingSpinner/LoadingSpinner';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Loading state component for DataTable.
|
|
@@ -8,11 +9,9 @@ import React from 'react';
|
|
|
8
9
|
*/
|
|
9
10
|
export function LoadingState() {
|
|
10
11
|
return (
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
<
|
|
14
|
-
|
|
15
|
-
</div>
|
|
16
|
-
</div>
|
|
12
|
+
<p className="grid place-items-center text-center p-8">
|
|
13
|
+
<LoadingSpinner />
|
|
14
|
+
<strong>Loading...</strong>
|
|
15
|
+
</p>
|
|
17
16
|
);
|
|
18
17
|
}
|
|
@@ -381,6 +381,18 @@ const areRowPropsEqual = (prevProps: RowProps, nextProps: RowProps): boolean =>
|
|
|
381
381
|
return false;
|
|
382
382
|
}
|
|
383
383
|
|
|
384
|
+
// CRITICAL FIX: Check if editingData has changed
|
|
385
|
+
// This ensures EditableRow re-renders when editingData updates (e.g., when dropdown value changes)
|
|
386
|
+
// For React 19: Manual memoization is still beneficial for table rows to prevent unnecessary re-renders
|
|
387
|
+
// of hundreds/thousands of rows when only one row's editingData changes
|
|
388
|
+
if (prevProps.isEditing && nextProps.isEditing) {
|
|
389
|
+
// Simple reference equality check - if editingData object reference changed, it's different
|
|
390
|
+
// This works because setEditingRow creates a new object, so reference equality is sufficient
|
|
391
|
+
if (prevProps.editingData !== nextProps.editingData) {
|
|
392
|
+
return false;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
384
396
|
return true;
|
|
385
397
|
};
|
|
386
398
|
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Sort Indicator Component
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module Components/DataTable/Components
|
|
5
|
+
* @since 0.4.0
|
|
6
|
+
*
|
|
7
|
+
* Shared component for displaying column sort indicators.
|
|
8
|
+
* Provides consistent sorting chevron icons across all DataTable components.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import React from 'react';
|
|
12
|
+
import { ChevronUp, ChevronDown, ChevronsUpDown } from 'lucide-react';
|
|
13
|
+
import { cn } from '../../../utils/core/cn';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Props for the SortIndicator component
|
|
17
|
+
*/
|
|
18
|
+
export interface SortIndicatorProps {
|
|
19
|
+
/** Current sort state: 'asc' for ascending, 'desc' for descending, false for unsorted */
|
|
20
|
+
sortState: 'asc' | 'desc' | false;
|
|
21
|
+
/** Optional className for styling */
|
|
22
|
+
className?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Sort indicator component that displays the appropriate chevron icon
|
|
27
|
+
* based on the current sort state.
|
|
28
|
+
*
|
|
29
|
+
* @param props - Sort indicator configuration
|
|
30
|
+
* @returns The rendered sort indicator icon
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```tsx
|
|
34
|
+
* <SortIndicator sortState={column.getIsSorted()} />
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export function SortIndicator({ sortState, className }: SortIndicatorProps) {
|
|
38
|
+
return (
|
|
39
|
+
<>
|
|
40
|
+
{sortState === 'asc' ? (
|
|
41
|
+
<ChevronUp className={cn('size-4', className)} />
|
|
42
|
+
) : sortState === 'desc' ? (
|
|
43
|
+
<ChevronDown className={cn('size-4', className)} />
|
|
44
|
+
) : (
|
|
45
|
+
<ChevronsUpDown className={cn('size-4', className)} />
|
|
46
|
+
)}
|
|
47
|
+
</>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Summary
|
|
4
4
|
|
|
5
|
-
The DataTable subcomponents (FilterRow, EditableRow, ColumnFilter,
|
|
5
|
+
The DataTable subcomponents (FilterRow, EditableRow, ColumnFilter, etc.) are **intentionally not tested in isolation** because:
|
|
6
6
|
|
|
7
7
|
1. They are tightly integrated with TanStack React Table
|
|
8
8
|
2. They are extensively tested through DataTable integration tests
|
|
@@ -35,9 +35,9 @@ The following DataTable integration tests provide comprehensive coverage:
|
|
|
35
35
|
| **ColumnFilter** | ✅ | Integration tests validate filter input behavior |
|
|
36
36
|
| **FilterRow** | ✅ | Integration tests validate filtering across columns |
|
|
37
37
|
| **EditableRow** | ✅ | Integration tests validate editing workflows |
|
|
38
|
-
| **
|
|
39
|
-
| **
|
|
40
|
-
| **
|
|
38
|
+
| **Grouped rows** | ✅ | Integration tests validate grouping functionality (handled inline by RowComponent) |
|
|
39
|
+
| **Modal display** | ✅ | Integration tests validate modal functionality |
|
|
40
|
+
| **Column reordering** | ✅ | Integration tests validate column order persistence |
|
|
41
41
|
| **ActionButtons** | ✅ | Integration tests validate action buttons |
|
|
42
42
|
|
|
43
43
|
## Why This Approach Works
|
|
@@ -57,8 +57,8 @@ vi.mock('../../Select/Select', async () => {
|
|
|
57
57
|
{children}
|
|
58
58
|
</button>
|
|
59
59
|
),
|
|
60
|
-
SelectValue: ({
|
|
61
|
-
<span data-testid="select-value">{
|
|
60
|
+
SelectValue: ({ children }: any) => (
|
|
61
|
+
<span data-testid="select-value">{children}</span>
|
|
62
62
|
),
|
|
63
63
|
SelectContent: ({ children }: any) => (
|
|
64
64
|
<div data-testid="select-content">{children}</div>
|
|
@@ -449,79 +449,6 @@ describe('[component] ColumnFilter', () => {
|
|
|
449
449
|
});
|
|
450
450
|
});
|
|
451
451
|
|
|
452
|
-
describe('Clear Filter Button', () => {
|
|
453
|
-
it('shows clear button when filter has value', () => {
|
|
454
|
-
const column = createMockColumn({
|
|
455
|
-
getFilterValue: vi.fn(() => 'test'),
|
|
456
|
-
});
|
|
457
|
-
|
|
458
|
-
render(<ColumnFilter column={column} />);
|
|
459
|
-
|
|
460
|
-
const clearButton = screen.getByRole('button');
|
|
461
|
-
expect(clearButton).toBeInTheDocument();
|
|
462
|
-
expect(screen.getByTestId('x-icon')).toBeInTheDocument();
|
|
463
|
-
});
|
|
464
|
-
|
|
465
|
-
it('hides clear button when filter has no value', () => {
|
|
466
|
-
const column = createMockColumn({
|
|
467
|
-
getFilterValue: vi.fn(() => undefined),
|
|
468
|
-
});
|
|
469
|
-
|
|
470
|
-
render(<ColumnFilter column={column} />);
|
|
471
|
-
|
|
472
|
-
expect(screen.queryByTestId('x-icon')).not.toBeInTheDocument();
|
|
473
|
-
});
|
|
474
|
-
|
|
475
|
-
it('hides clear button when filter value is empty string', () => {
|
|
476
|
-
const column = createMockColumn({
|
|
477
|
-
getFilterValue: vi.fn(() => ''),
|
|
478
|
-
});
|
|
479
|
-
|
|
480
|
-
render(<ColumnFilter column={column} />);
|
|
481
|
-
|
|
482
|
-
expect(screen.queryByTestId('x-icon')).not.toBeInTheDocument();
|
|
483
|
-
});
|
|
484
|
-
|
|
485
|
-
it('clears filter when clear button is clicked', async () => {
|
|
486
|
-
const user = userEvent.setup();
|
|
487
|
-
const setFilterValue = vi.fn();
|
|
488
|
-
const column = createMockColumn({
|
|
489
|
-
getFilterValue: vi.fn(() => 'test'),
|
|
490
|
-
setFilterValue,
|
|
491
|
-
});
|
|
492
|
-
|
|
493
|
-
render(<ColumnFilter column={column} />);
|
|
494
|
-
|
|
495
|
-
const clearButton = screen.getByRole('button');
|
|
496
|
-
await user.click(clearButton);
|
|
497
|
-
|
|
498
|
-
expect(setFilterValue).toHaveBeenCalledWith(undefined);
|
|
499
|
-
});
|
|
500
|
-
});
|
|
501
|
-
|
|
502
|
-
describe('Filter Indicator', () => {
|
|
503
|
-
it('shows filter indicator dot when filter has value', () => {
|
|
504
|
-
const column = createMockColumn({
|
|
505
|
-
getFilterValue: vi.fn(() => 'test'),
|
|
506
|
-
});
|
|
507
|
-
|
|
508
|
-
render(<ColumnFilter column={column} />);
|
|
509
|
-
|
|
510
|
-
const indicator = document.querySelector('.bg-main-500.rounded-full');
|
|
511
|
-
expect(indicator).toBeInTheDocument();
|
|
512
|
-
});
|
|
513
|
-
|
|
514
|
-
it('hides filter indicator dot when filter has no value', () => {
|
|
515
|
-
const column = createMockColumn({
|
|
516
|
-
getFilterValue: vi.fn(() => undefined),
|
|
517
|
-
});
|
|
518
|
-
|
|
519
|
-
render(<ColumnFilter column={column} />);
|
|
520
|
-
|
|
521
|
-
const indicator = document.querySelector('.bg-main-500.rounded-full');
|
|
522
|
-
expect(indicator).not.toBeInTheDocument();
|
|
523
|
-
});
|
|
524
|
-
});
|
|
525
452
|
|
|
526
453
|
describe('Edge Cases', () => {
|
|
527
454
|
it('handles undefined filter value gracefully', () => {
|
|
@@ -618,15 +545,29 @@ describe('[component] ColumnFilter', () => {
|
|
|
618
545
|
expect(input).toBeInTheDocument();
|
|
619
546
|
});
|
|
620
547
|
|
|
621
|
-
it('
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
548
|
+
it('renders Filter icon in select filter', () => {
|
|
549
|
+
render(
|
|
550
|
+
<ColumnFilter
|
|
551
|
+
column={mockColumn}
|
|
552
|
+
filterType="select"
|
|
553
|
+
options={[{ value: 'option1', label: 'Option 1' }]}
|
|
554
|
+
/>
|
|
555
|
+
);
|
|
625
556
|
|
|
626
|
-
|
|
557
|
+
expect(screen.getByTestId('filter-icon')).toBeInTheDocument();
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
it('renders column name in select filter value', () => {
|
|
561
|
+
render(
|
|
562
|
+
<ColumnFilter
|
|
563
|
+
column={mockColumn}
|
|
564
|
+
filterType="select"
|
|
565
|
+
options={[{ value: 'option1', label: 'Option 1' }]}
|
|
566
|
+
/>
|
|
567
|
+
);
|
|
627
568
|
|
|
628
|
-
const
|
|
629
|
-
expect(
|
|
569
|
+
const selectValue = screen.getByTestId('select-value');
|
|
570
|
+
expect(selectValue).toHaveTextContent('test-column...');
|
|
630
571
|
});
|
|
631
572
|
});
|
|
632
573
|
|
|
@@ -89,8 +89,9 @@ describe('[component] DataTableErrorBoundary', () => {
|
|
|
89
89
|
</DataTableErrorBoundary>
|
|
90
90
|
);
|
|
91
91
|
|
|
92
|
-
//
|
|
93
|
-
|
|
92
|
+
// There may be multiple alerts (outer and inner), use getAllByRole
|
|
93
|
+
const alerts = screen.getAllByRole('alert');
|
|
94
|
+
expect(alerts.length).toBeGreaterThan(0);
|
|
94
95
|
expect(screen.getByText('DataTable Error')).toBeInTheDocument();
|
|
95
96
|
expect(screen.getByText('Something went wrong')).toBeInTheDocument();
|
|
96
97
|
});
|
|
@@ -127,8 +128,16 @@ describe('[component] DataTableErrorBoundary', () => {
|
|
|
127
128
|
</DataTableErrorBoundary>
|
|
128
129
|
);
|
|
129
130
|
|
|
131
|
+
// Check for error details summary
|
|
130
132
|
const details = screen.getByText('Error Details');
|
|
131
133
|
expect(details).toBeInTheDocument();
|
|
134
|
+
// Check that error message is displayed in the details/pre element
|
|
135
|
+
// The error message is inside a <pre> tag within <details>
|
|
136
|
+
const preElement = screen.getByText((content, element) => {
|
|
137
|
+
return element?.tagName.toLowerCase() === 'pre' && content.includes('Test error');
|
|
138
|
+
}, { selector: 'pre' });
|
|
139
|
+
expect(preElement).toBeInTheDocument();
|
|
140
|
+
expect(preElement).toHaveTextContent('Test error');
|
|
132
141
|
});
|
|
133
142
|
|
|
134
143
|
it('hides error details when showErrorDetails is false', () => {
|
|
@@ -140,10 +149,11 @@ describe('[component] DataTableErrorBoundary', () => {
|
|
|
140
149
|
|
|
141
150
|
// Error details section is shown when error.message exists
|
|
142
151
|
// showErrorDetails only controls stack trace visibility, not the details section
|
|
143
|
-
// So we check that the details section exists but stack trace is not shown
|
|
144
152
|
expect(screen.getByText('Error Details')).toBeInTheDocument();
|
|
145
153
|
// Stack trace should not be visible when showErrorDetails is false
|
|
146
154
|
expect(screen.queryByText(/Stack Trace/i)).not.toBeInTheDocument();
|
|
155
|
+
// Error message should still be visible
|
|
156
|
+
expect(screen.getByText('Test error')).toBeInTheDocument();
|
|
147
157
|
});
|
|
148
158
|
|
|
149
159
|
it('displays stack trace when showErrorDetails is true', () => {
|
|
@@ -155,6 +165,8 @@ describe('[component] DataTableErrorBoundary', () => {
|
|
|
155
165
|
|
|
156
166
|
// Stack trace should be in the details section
|
|
157
167
|
expect(screen.getByText('Error Details')).toBeInTheDocument();
|
|
168
|
+
// Stack trace text should be present when showErrorDetails is true
|
|
169
|
+
expect(screen.getByText(/Stack Trace/i)).toBeInTheDocument();
|
|
158
170
|
});
|
|
159
171
|
});
|
|
160
172
|
|
|
@@ -237,7 +249,8 @@ describe('[component] DataTableErrorBoundary', () => {
|
|
|
237
249
|
</DataTableErrorBoundary>
|
|
238
250
|
);
|
|
239
251
|
|
|
240
|
-
|
|
252
|
+
const alerts = screen.getAllByRole('alert');
|
|
253
|
+
expect(alerts.length).toBeGreaterThan(0);
|
|
241
254
|
|
|
242
255
|
const retryButton = screen.getByRole('button', { name: /retry/i });
|
|
243
256
|
await user.click(retryButton);
|
|
@@ -317,7 +330,8 @@ describe('[component] DataTableErrorBoundary', () => {
|
|
|
317
330
|
</DataTableErrorBoundary>
|
|
318
331
|
);
|
|
319
332
|
|
|
320
|
-
|
|
333
|
+
const alerts = screen.getAllByRole('alert');
|
|
334
|
+
expect(alerts.length).toBeGreaterThan(0);
|
|
321
335
|
|
|
322
336
|
const resetButton = screen.getByRole('button', { name: /reset/i });
|
|
323
337
|
|
|
@@ -352,7 +366,9 @@ describe('[component] DataTableErrorBoundary', () => {
|
|
|
352
366
|
</DataTableErrorBoundary>
|
|
353
367
|
);
|
|
354
368
|
|
|
355
|
-
|
|
369
|
+
// There may be multiple alerts when error has no message
|
|
370
|
+
const alerts = screen.getAllByRole('alert');
|
|
371
|
+
expect(alerts.length).toBeGreaterThan(0);
|
|
356
372
|
expect(screen.getByText('An unexpected error occurred')).toBeInTheDocument();
|
|
357
373
|
});
|
|
358
374
|
|
|
@@ -365,19 +381,31 @@ describe('[component] DataTableErrorBoundary', () => {
|
|
|
365
381
|
</DataTableErrorBoundary>
|
|
366
382
|
);
|
|
367
383
|
|
|
368
|
-
|
|
384
|
+
const alerts = screen.getAllByRole('alert');
|
|
385
|
+
expect(alerts.length).toBeGreaterThan(0);
|
|
369
386
|
|
|
370
387
|
const retryButton = screen.getByRole('button', { name: /retry/i });
|
|
371
388
|
await user.click(retryButton);
|
|
372
389
|
|
|
373
|
-
//
|
|
390
|
+
// Wait for retry to complete and error state to reset
|
|
391
|
+
await waitFor(() => {
|
|
392
|
+
// Wait for the timeout to complete
|
|
393
|
+
}, { timeout: 200 });
|
|
394
|
+
|
|
395
|
+
// Trigger another error - rerender with new error
|
|
374
396
|
rerender(
|
|
375
397
|
<DataTableErrorBoundary showRetryButton={true}>
|
|
376
398
|
<ThrowError shouldThrow={true} message="Second error" />
|
|
377
399
|
</DataTableErrorBoundary>
|
|
378
400
|
);
|
|
379
401
|
|
|
380
|
-
|
|
402
|
+
// Wait for the new error to be caught and displayed
|
|
403
|
+
await waitFor(() => {
|
|
404
|
+
const newAlerts = screen.getAllByRole('alert');
|
|
405
|
+
expect(newAlerts.length).toBeGreaterThan(0);
|
|
406
|
+
const preElement = document.querySelector('pre');
|
|
407
|
+
expect(preElement).toHaveTextContent('Second error');
|
|
408
|
+
}, { timeout: 300 });
|
|
381
409
|
});
|
|
382
410
|
|
|
383
411
|
it('handles cleanup on unmount', () => {
|
|
@@ -306,7 +306,8 @@ describe('[component] EmptyState', () => {
|
|
|
306
306
|
it('uses semantic heading for title', () => {
|
|
307
307
|
render(<EmptyState title="Custom Title" />);
|
|
308
308
|
|
|
309
|
-
|
|
309
|
+
// AlertTitle renders as h5, not h3
|
|
310
|
+
const heading = screen.getByRole('heading', { level: 5 });
|
|
310
311
|
expect(heading).toHaveTextContent('Custom Title');
|
|
311
312
|
});
|
|
312
313
|
|
|
@@ -410,11 +411,12 @@ describe('[component] EmptyState', () => {
|
|
|
410
411
|
});
|
|
411
412
|
|
|
412
413
|
describe('Layout and Styling', () => {
|
|
413
|
-
it('renders with centered
|
|
414
|
+
it('renders with centered grid layout', () => {
|
|
414
415
|
render(<EmptyState />);
|
|
415
416
|
|
|
416
417
|
const container = screen.getByRole('status');
|
|
417
|
-
|
|
418
|
+
// EmptyState uses grid place-items-center, not flex
|
|
419
|
+
expect(container).toHaveClass('grid', 'place-items-center');
|
|
418
420
|
});
|
|
419
421
|
|
|
420
422
|
it('applies text-center class', () => {
|
|
@@ -428,7 +430,8 @@ describe('[component] EmptyState', () => {
|
|
|
428
430
|
render(<EmptyState />);
|
|
429
431
|
|
|
430
432
|
const container = screen.getByRole('status');
|
|
431
|
-
|
|
433
|
+
// EmptyState uses p-4, not p-8
|
|
434
|
+
expect(container).toHaveClass('p-4');
|
|
432
435
|
});
|
|
433
436
|
});
|
|
434
437
|
});
|
|
@@ -123,7 +123,7 @@ describe('[component] FilterRow', () => {
|
|
|
123
123
|
expect(filters.length).toBeGreaterThan(0);
|
|
124
124
|
});
|
|
125
125
|
|
|
126
|
-
it('displays
|
|
126
|
+
it('displays non-breaking space for non-filterable columns', () => {
|
|
127
127
|
const columns = [
|
|
128
128
|
columnHelper.accessor('name', {
|
|
129
129
|
header: 'Name',
|
|
@@ -133,9 +133,17 @@ describe('[component] FilterRow', () => {
|
|
|
133
133
|
const table = createTable(columns);
|
|
134
134
|
const visibleColumns = table.getHeaderGroups()[0]?.headers || [];
|
|
135
135
|
|
|
136
|
-
render(<FilterRow table={table} visibleColumns={visibleColumns} />);
|
|
137
|
-
|
|
138
|
-
|
|
136
|
+
const { container } = render(<FilterRow table={table} visibleColumns={visibleColumns} />);
|
|
137
|
+
|
|
138
|
+
// Check that the cell exists and does not contain a filter component
|
|
139
|
+
const cell = container.querySelector('td');
|
|
140
|
+
expect(cell).toBeInTheDocument();
|
|
141
|
+
// The cell should not contain a ColumnFilter component
|
|
142
|
+
expect(cell?.querySelector('[data-testid="column-filter"]')).not.toBeInTheDocument();
|
|
143
|
+
// The cell should be empty or contain only whitespace (non-breaking space from React Fragment)
|
|
144
|
+
// React Fragment with renders as a non-breaking space, but textContent may normalize it
|
|
145
|
+
// The important thing is that no filter component is rendered
|
|
146
|
+
expect(cell?.children.length).toBe(0);
|
|
139
147
|
});
|
|
140
148
|
|
|
141
149
|
it('renders filter with correct placeholder', () => {
|
|
@@ -18,37 +18,45 @@ describe('[component] LoadingState', () => {
|
|
|
18
18
|
it('renders loading spinner and text', () => {
|
|
19
19
|
render(<LoadingState />);
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
// There are two "Loading..." texts (sr-only and visible), use getAllByText
|
|
22
|
+
const loadingTexts = screen.getAllByText('Loading...');
|
|
23
|
+
expect(loadingTexts.length).toBeGreaterThan(0);
|
|
24
|
+
// Check that the visible text is present
|
|
25
|
+
expect(screen.getByText('Loading...', { selector: 'strong' })).toBeInTheDocument();
|
|
22
26
|
});
|
|
23
27
|
|
|
24
28
|
it('renders with centered layout', () => {
|
|
25
29
|
render(<LoadingState />);
|
|
26
30
|
|
|
27
|
-
// Find the
|
|
28
|
-
const
|
|
31
|
+
// Find the container using getByRole for the spinner
|
|
32
|
+
const spinner = screen.getByRole('status');
|
|
33
|
+
const container = spinner.closest('p');
|
|
29
34
|
expect(container).toHaveClass('text-center', 'p-8');
|
|
30
35
|
});
|
|
31
36
|
|
|
32
37
|
it('renders with padding', () => {
|
|
33
38
|
render(<LoadingState />);
|
|
34
39
|
|
|
35
|
-
// Find the
|
|
36
|
-
const
|
|
40
|
+
// Find the container using getByRole for the spinner
|
|
41
|
+
const spinner = screen.getByRole('status');
|
|
42
|
+
const container = spinner.closest('p');
|
|
37
43
|
expect(container).toHaveClass('p-8');
|
|
38
44
|
});
|
|
39
45
|
|
|
40
46
|
it('renders spinner with animation class', () => {
|
|
41
47
|
render(<LoadingState />);
|
|
42
48
|
|
|
43
|
-
const spinner = screen.
|
|
49
|
+
const spinner = screen.getByRole('status');
|
|
44
50
|
expect(spinner).toHaveClass('animate-spin');
|
|
45
51
|
});
|
|
46
52
|
|
|
47
|
-
it('renders
|
|
53
|
+
it('renders grid container with items centered', () => {
|
|
48
54
|
render(<LoadingState />);
|
|
49
55
|
|
|
50
|
-
|
|
51
|
-
|
|
56
|
+
// Container uses grid, not flex
|
|
57
|
+
const spinner = screen.getByRole('status');
|
|
58
|
+
const container = spinner.closest('p');
|
|
59
|
+
expect(container).toHaveClass('grid', 'place-items-center');
|
|
52
60
|
});
|
|
53
61
|
});
|
|
54
62
|
|
|
@@ -56,15 +64,17 @@ describe('[component] LoadingState', () => {
|
|
|
56
64
|
it('provides aria-live region for loading state', () => {
|
|
57
65
|
render(<LoadingState />);
|
|
58
66
|
|
|
59
|
-
|
|
60
|
-
|
|
67
|
+
// The spinner has role="status" which provides the aria-live region
|
|
68
|
+
const spinner = screen.getByRole('status');
|
|
69
|
+
expect(spinner).toBeInTheDocument();
|
|
61
70
|
});
|
|
62
71
|
|
|
63
72
|
it('announces loading state to screen readers', () => {
|
|
64
73
|
render(<LoadingState />);
|
|
65
74
|
|
|
66
|
-
|
|
67
|
-
|
|
75
|
+
// Check that the sr-only text is present for screen readers
|
|
76
|
+
const srOnlyText = screen.getByText('Loading...', { selector: 'span.sr-only' });
|
|
77
|
+
expect(srOnlyText).toBeInTheDocument();
|
|
68
78
|
});
|
|
69
79
|
});
|
|
70
80
|
|
|
@@ -72,49 +82,53 @@ describe('[component] LoadingState', () => {
|
|
|
72
82
|
it('renders spinner before loading text', () => {
|
|
73
83
|
render(<LoadingState />);
|
|
74
84
|
|
|
75
|
-
const
|
|
76
|
-
const
|
|
77
|
-
const text = container?.
|
|
85
|
+
const spinner = screen.getByRole('status');
|
|
86
|
+
const container = spinner.closest('p');
|
|
87
|
+
const text = container?.querySelector('strong');
|
|
78
88
|
|
|
79
89
|
expect(spinner).toBeInTheDocument();
|
|
80
90
|
expect(text).toHaveTextContent('Loading...');
|
|
81
91
|
});
|
|
82
92
|
|
|
83
|
-
it('applies
|
|
93
|
+
it('applies grid layout for centering', () => {
|
|
84
94
|
render(<LoadingState />);
|
|
85
95
|
|
|
86
|
-
|
|
87
|
-
|
|
96
|
+
// Container uses grid place-items-center, not space-x-2
|
|
97
|
+
const spinner = screen.getByRole('status');
|
|
98
|
+
const container = spinner.closest('p');
|
|
99
|
+
expect(container).toHaveClass('grid', 'place-items-center');
|
|
88
100
|
});
|
|
89
101
|
});
|
|
90
102
|
|
|
91
103
|
describe('Styling', () => {
|
|
92
|
-
it('
|
|
104
|
+
it('renders visible loading text', () => {
|
|
93
105
|
render(<LoadingState />);
|
|
94
106
|
|
|
95
|
-
|
|
96
|
-
|
|
107
|
+
// Check that the visible text (in strong) is present
|
|
108
|
+
const text = screen.getByText('Loading...', { selector: 'strong' });
|
|
109
|
+
expect(text).toBeInTheDocument();
|
|
97
110
|
});
|
|
98
111
|
|
|
99
112
|
it('spinner has rounded-full class', () => {
|
|
100
113
|
render(<LoadingState />);
|
|
101
114
|
|
|
102
|
-
const spinner = screen.
|
|
115
|
+
const spinner = screen.getByRole('status');
|
|
103
116
|
expect(spinner).toHaveClass('rounded-full');
|
|
104
117
|
});
|
|
105
118
|
|
|
106
119
|
it('spinner has border styling', () => {
|
|
107
120
|
render(<LoadingState />);
|
|
108
121
|
|
|
109
|
-
const spinner = screen.
|
|
110
|
-
|
|
122
|
+
const spinner = screen.getByRole('status');
|
|
123
|
+
// Spinner uses border-2 border-solid border-current border-r-transparent
|
|
124
|
+
expect(spinner).toHaveClass('border-2', 'border-solid', 'border-current', 'border-r-transparent');
|
|
111
125
|
});
|
|
112
126
|
|
|
113
127
|
it('spinner has appropriate size', () => {
|
|
114
128
|
render(<LoadingState />);
|
|
115
129
|
|
|
116
|
-
const spinner = screen.
|
|
117
|
-
// LoadingState spinner uses Tailwind v4 size-* utility
|
|
130
|
+
const spinner = screen.getByRole('status');
|
|
131
|
+
// LoadingState spinner uses Tailwind v4 size-* utility
|
|
118
132
|
expect(spinner).toHaveClass('size-6');
|
|
119
133
|
});
|
|
120
134
|
});
|
|
@@ -40,10 +40,6 @@ export function usePermissionTracking({
|
|
|
40
40
|
if (permissions.canRead.isLoading && !hasStartedTracking.current) {
|
|
41
41
|
hasStartedTracking.current = true;
|
|
42
42
|
permissionCheckStartTime.current = Date.now();
|
|
43
|
-
logger.debug('DataTable: Permission check started', {
|
|
44
|
-
pageId: effectivePageId,
|
|
45
|
-
timestamp: permissionCheckStartTime.current,
|
|
46
|
-
});
|
|
47
43
|
|
|
48
44
|
if (permissionWarningTimeoutId.current) {
|
|
49
45
|
clearTimeout(permissionWarningTimeoutId.current);
|
|
@@ -7,9 +7,10 @@ export { DataTableToolbar } from './DataTableToolbar';
|
|
|
7
7
|
export { DataTableModals } from './DataTableModals';
|
|
8
8
|
export { ImportModal } from './ImportModal';
|
|
9
9
|
export type { ImportModalConfig } from './ImportModal';
|
|
10
|
-
export { GroupHeader } from './GroupHeader';
|
|
11
10
|
export { GroupingDropdown } from './GroupingDropdown';
|
|
12
11
|
export { DataTableErrorBoundary } from './DataTableErrorBoundary';
|
|
13
12
|
export { PaginationControls } from './PaginationControls';
|
|
14
13
|
export { LoadingState } from './LoadingState';
|
|
15
14
|
export { EmptyState } from './EmptyState';
|
|
15
|
+
export { SortIndicator } from './SortIndicator';
|
|
16
|
+
export type { SortIndicatorProps } from './SortIndicator';
|