@jmruthers/pace-core 0.5.189 → 0.5.190
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/core-usage-manifest.json +0 -4
- package/dist/{AuthService-B-cd2MA4.d.ts → AuthService-CbP_utw2.d.ts} +7 -3
- package/dist/{DataTable-GUFUNZ3N.js → DataTable-ON3IXISJ.js} +8 -8
- package/dist/{PublicPageProvider-B8HaLe69.d.ts → PublicPageProvider-C4uxosp6.d.ts} +83 -24
- package/dist/{UnifiedAuthProvider-BG0AL5eE.d.ts → UnifiedAuthProvider-BYA9qB-o.d.ts} +4 -3
- package/dist/{UnifiedAuthProvider-643PUAIM.js → UnifiedAuthProvider-X5NXANVI.js} +4 -2
- package/dist/{api-YP7XD5L6.js → api-I6UCQ5S6.js} +4 -2
- package/dist/{chunk-DDM4CCYT.js → chunk-4QYC5L4K.js} +60 -35
- package/dist/chunk-4QYC5L4K.js.map +1 -0
- package/dist/{chunk-IM4QE42D.js → chunk-73HSNNOQ.js} +141 -326
- package/dist/chunk-73HSNNOQ.js.map +1 -0
- package/dist/{chunk-YHCN776L.js → chunk-DZWK57KZ.js} +2 -75
- package/dist/chunk-DZWK57KZ.js.map +1 -0
- package/dist/{chunk-3GOZZZYH.js → chunk-HQVPB5MZ.js} +238 -301
- package/dist/chunk-HQVPB5MZ.js.map +1 -0
- package/dist/{chunk-THRPYOFK.js → chunk-HW3OVDUF.js} +5 -5
- package/dist/chunk-HW3OVDUF.js.map +1 -0
- package/dist/{chunk-F2IMUDXZ.js → chunk-I7PSE6JW.js} +75 -2
- package/dist/chunk-I7PSE6JW.js.map +1 -0
- package/dist/{chunk-VGZZXKBR.js → chunk-J2XXC7R5.js} +280 -52
- package/dist/chunk-J2XXC7R5.js.map +1 -0
- package/dist/{chunk-UCQSRW7Z.js → chunk-NIU6J6OX.js} +425 -378
- package/dist/chunk-NIU6J6OX.js.map +1 -0
- package/dist/{chunk-HESYZWZW.js → chunk-QWWZ5CAQ.js} +2 -2
- package/dist/{chunk-HEHYGYOX.js → chunk-RUYZKXOD.js} +401 -46
- package/dist/chunk-RUYZKXOD.js.map +1 -0
- package/dist/{chunk-2UUZZJFT.js → chunk-SDMHPX3X.js} +176 -160
- package/dist/{chunk-2UUZZJFT.js.map → chunk-SDMHPX3X.js.map} +1 -1
- package/dist/{chunk-MX64ZF6I.js → chunk-STYK4OH2.js} +11 -11
- package/dist/chunk-STYK4OH2.js.map +1 -0
- package/dist/{chunk-YGPFYGA6.js → chunk-VVBAW5A5.js} +822 -498
- package/dist/chunk-VVBAW5A5.js.map +1 -0
- package/dist/chunk-Y4BUBBHD.js +614 -0
- package/dist/chunk-Y4BUBBHD.js.map +1 -0
- package/dist/{chunk-SAUPYVLF.js → chunk-ZSAAAMVR.js} +1 -1
- package/dist/chunk-ZSAAAMVR.js.map +1 -0
- package/dist/components.d.ts +3 -4
- package/dist/components.js +19 -19
- package/dist/components.js.map +1 -1
- package/dist/eslint-rules/pace-core-compliance.cjs +0 -2
- package/dist/{file-reference-D037xOFK.d.ts → file-reference-BavO2eQj.d.ts} +13 -10
- package/dist/hooks.d.ts +10 -5
- package/dist/hooks.js +14 -8
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +13 -11
- package/dist/index.js +79 -69
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +3 -3
- package/dist/providers.js +3 -1
- package/dist/rbac/index.d.ts +76 -12
- package/dist/rbac/index.js +12 -9
- package/dist/types.d.ts +1 -1
- package/dist/types.js +1 -1
- package/dist/{usePublicRouteParams-CTDELQ7H.d.ts → usePublicRouteParams-DxIDS4bC.d.ts} +16 -9
- package/dist/utils.js +16 -16
- package/docs/README.md +2 -2
- package/docs/api/classes/ColumnFactory.md +1 -1
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +2 -2
- package/docs/api/classes/Logger.md +1 -1
- package/docs/api/classes/MissingUserContextError.md +2 -2
- package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
- package/docs/api/classes/PermissionDeniedError.md +1 -1
- package/docs/api/classes/RBACAuditManager.md +1 -1
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +4 -4
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +2 -2
- package/docs/api/classes/SecureSupabaseClient.md +21 -16
- package/docs/api/classes/StorageUtils.md +7 -4
- package/docs/api/enums/FileCategory.md +1 -1
- package/docs/api/enums/LogLevel.md +1 -1
- package/docs/api/enums/RBACErrorCode.md +1 -1
- package/docs/api/enums/RPCFunction.md +1 -1
- package/docs/api/interfaces/AddressFieldProps.md +1 -1
- package/docs/api/interfaces/AddressFieldRef.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/AutocompleteOptions.md +1 -1
- package/docs/api/interfaces/AvatarProps.md +1 -1
- package/docs/api/interfaces/BadgeProps.md +1 -1
- package/docs/api/interfaces/ButtonProps.md +1 -1
- package/docs/api/interfaces/CalendarProps.md +20 -6
- package/docs/api/interfaces/CardProps.md +1 -1
- package/docs/api/interfaces/ColorPalette.md +1 -1
- package/docs/api/interfaces/ColorShade.md +1 -1
- package/docs/api/interfaces/ComplianceResult.md +1 -1
- package/docs/api/interfaces/DataAccessRecord.md +9 -9
- package/docs/api/interfaces/DataRecord.md +1 -1
- package/docs/api/interfaces/DataTableAction.md +1 -1
- package/docs/api/interfaces/DataTableColumn.md +1 -1
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
- package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
- package/docs/api/interfaces/DatabaseIssue.md +1 -1
- package/docs/api/interfaces/EmptyStateConfig.md +1 -1
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventAppRoleData.md +1 -1
- package/docs/api/interfaces/ExportColumn.md +1 -1
- package/docs/api/interfaces/ExportOptions.md +1 -1
- package/docs/api/interfaces/FileDisplayProps.md +62 -16
- package/docs/api/interfaces/FileMetadata.md +1 -1
- package/docs/api/interfaces/FileReference.md +2 -2
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadOptions.md +26 -12
- package/docs/api/interfaces/FileUploadProps.md +30 -19
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/FormFieldProps.md +1 -1
- package/docs/api/interfaces/FormProps.md +1 -1
- package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
- package/docs/api/interfaces/InputProps.md +1 -1
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoggerConfig.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +1 -1
- package/docs/api/interfaces/NavigationAccessRecord.md +10 -10
- package/docs/api/interfaces/NavigationContextType.md +9 -9
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +1 -1
- package/docs/api/interfaces/NavigationMenuProps.md +1 -1
- package/docs/api/interfaces/NavigationProviderProps.md +7 -7
- package/docs/api/interfaces/Organisation.md +1 -1
- package/docs/api/interfaces/OrganisationContextType.md +1 -1
- package/docs/api/interfaces/OrganisationMembership.md +1 -1
- package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
- package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
- package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
- package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
- package/docs/api/interfaces/PageAccessRecord.md +8 -8
- package/docs/api/interfaces/PagePermissionContextType.md +8 -8
- package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
- package/docs/api/interfaces/PagePermissionProviderProps.md +7 -7
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/ParsedAddress.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
- package/docs/api/interfaces/ProgressProps.md +3 -11
- package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
- package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
- package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
- package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
- package/docs/api/interfaces/QuickFix.md +1 -1
- package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
- package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
- package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
- package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
- package/docs/api/interfaces/RBACConfig.md +1 -1
- package/docs/api/interfaces/RBACContext.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
- package/docs/api/interfaces/RBACPerformanceMetrics.md +1 -1
- package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
- package/docs/api/interfaces/RBACPermissionCheckResult.md +1 -1
- package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
- package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
- package/docs/api/interfaces/RBACResult.md +1 -1
- package/docs/api/interfaces/RBACRoleGrantParams.md +1 -1
- package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
- package/docs/api/interfaces/RBACRoleRevokeParams.md +1 -1
- package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
- package/docs/api/interfaces/RBACRoleValidateParams.md +1 -1
- package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
- package/docs/api/interfaces/RBACRolesListParams.md +1 -1
- package/docs/api/interfaces/RBACRolesListResult.md +1 -1
- package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
- package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
- package/docs/api/interfaces/ResourcePermissions.md +1 -1
- package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +8 -8
- package/docs/api/interfaces/RoleBasedRouterProps.md +10 -10
- package/docs/api/interfaces/RoleManagementResult.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +10 -10
- package/docs/api/interfaces/RouteConfig.md +10 -10
- package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
- package/docs/api/interfaces/SecureDataContextType.md +9 -9
- package/docs/api/interfaces/SecureDataProviderProps.md +8 -8
- package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
- package/docs/api/interfaces/SetupIssue.md +1 -1
- package/docs/api/interfaces/StorageConfig.md +4 -4
- package/docs/api/interfaces/StorageFileInfo.md +7 -7
- package/docs/api/interfaces/StorageFileMetadata.md +25 -14
- package/docs/api/interfaces/StorageListOptions.md +22 -9
- package/docs/api/interfaces/StorageListResult.md +4 -4
- package/docs/api/interfaces/StorageUploadOptions.md +21 -8
- package/docs/api/interfaces/StorageUploadResult.md +6 -6
- package/docs/api/interfaces/StorageUrlOptions.md +19 -6
- package/docs/api/interfaces/StyleImport.md +1 -1
- package/docs/api/interfaces/SwitchProps.md +1 -1
- package/docs/api/interfaces/TabsContentProps.md +1 -1
- package/docs/api/interfaces/TabsListProps.md +1 -1
- package/docs/api/interfaces/TabsProps.md +1 -1
- package/docs/api/interfaces/TabsTriggerProps.md +1 -1
- package/docs/api/interfaces/TextareaProps.md +1 -1
- package/docs/api/interfaces/ToastActionElement.md +1 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +53 -53
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +13 -13
- package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
- package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
- package/docs/api/interfaces/UsePublicFileDisplayOptions.md +1 -1
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeOptions.md +4 -4
- package/docs/api/interfaces/UseResolvedScopeReturn.md +4 -4
- package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +11 -11
- package/docs/api/interfaces/UserMenuProps.md +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +151 -92
- package/docs/api-reference/components.md +15 -7
- package/docs/api-reference/providers.md +2 -2
- package/docs/api-reference/rpc-functions.md +1 -0
- package/docs/best-practices/README.md +1 -1
- package/docs/best-practices/deployment.md +8 -8
- package/docs/getting-started/examples/README.md +2 -2
- package/docs/getting-started/installation-guide.md +4 -4
- package/docs/getting-started/quick-start.md +3 -3
- package/docs/migration/MIGRATION_GUIDE.md +3 -3
- package/docs/rbac/compliance/compliance-guide.md +2 -2
- package/docs/rbac/event-based-apps.md +2 -2
- package/docs/rbac/getting-started.md +2 -2
- package/docs/rbac/quick-start.md +2 -2
- package/docs/security/README.md +4 -4
- package/docs/standards/07-rbac-and-rls-standard.md +430 -7
- package/docs/troubleshooting/README.md +2 -2
- package/docs/troubleshooting/migration.md +3 -3
- package/package.json +1 -3
- package/scripts/check-pace-core-compliance.cjs +1 -1
- package/scripts/check-pace-core-compliance.js +1 -1
- package/src/__tests__/fixtures/supabase.ts +301 -0
- package/src/__tests__/public-recipe-view.test.ts +9 -9
- package/src/__tests__/rls-policies.test.ts +197 -61
- package/src/components/AddressField/AddressField.test.tsx +42 -0
- package/src/components/AddressField/AddressField.tsx +71 -60
- package/src/components/AddressField/README.md +1 -0
- package/src/components/Alert/Alert.test.tsx +50 -10
- package/src/components/Alert/Alert.tsx +5 -3
- package/src/components/Avatar/Avatar.test.tsx +95 -43
- package/src/components/Avatar/Avatar.tsx +16 -16
- package/src/components/Button/Button.test.tsx +2 -1
- package/src/components/Button/Button.tsx +3 -3
- package/src/components/Calendar/Calendar.test.tsx +53 -37
- package/src/components/Calendar/Calendar.tsx +409 -82
- package/src/components/Card/Card.test.tsx +7 -4
- package/src/components/Card/Card.tsx +3 -6
- package/src/components/Checkbox/Checkbox.tsx +2 -2
- package/src/components/DataTable/components/ActionButtons.tsx +5 -5
- package/src/components/DataTable/components/BulkOperationsDropdown.tsx +2 -2
- package/src/components/DataTable/components/ColumnFilter.tsx +1 -1
- package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +3 -3
- package/src/components/DataTable/components/DataTableBody.tsx +12 -12
- package/src/components/DataTable/components/DataTableCore.tsx +3 -3
- package/src/components/DataTable/components/DataTableToolbar.tsx +5 -5
- package/src/components/DataTable/components/DraggableColumnHeader.tsx +3 -3
- package/src/components/DataTable/components/EditableRow.tsx +2 -2
- package/src/components/DataTable/components/EmptyState.tsx +3 -3
- package/src/components/DataTable/components/GroupHeader.tsx +2 -2
- package/src/components/DataTable/components/GroupingDropdown.tsx +1 -1
- package/src/components/DataTable/components/ImportModal.tsx +4 -4
- package/src/components/DataTable/components/LoadingState.tsx +1 -1
- package/src/components/DataTable/components/PaginationControls.tsx +11 -11
- package/src/components/DataTable/components/UnifiedTableBody.tsx +9 -9
- package/src/components/DataTable/components/ViewRowModal.tsx +2 -2
- package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +11 -37
- package/src/components/DataTable/components/__tests__/DataTableToolbar.test.tsx +157 -0
- package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +2 -1
- package/src/components/DataTable/components/__tests__/VirtualizedDataTable.test.tsx +128 -0
- package/src/components/DataTable/core/__tests__/ActionManager.test.ts +19 -0
- package/src/components/DataTable/core/__tests__/ColumnFactory.test.ts +51 -0
- package/src/components/DataTable/core/__tests__/ColumnManager.test.ts +84 -0
- package/src/components/DataTable/core/__tests__/DataManager.test.ts +14 -0
- package/src/components/DataTable/core/__tests__/DataTableContext.test.tsx +136 -0
- package/src/components/DataTable/core/__tests__/LocalDataAdapter.test.ts +16 -0
- package/src/components/DataTable/core/__tests__/PluginRegistry.test.ts +18 -0
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +28 -7
- package/src/components/DataTable/utils/__tests__/hierarchicalUtils.test.ts +30 -1
- package/src/components/DataTable/utils/hierarchicalUtils.ts +38 -10
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +8 -3
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +4 -4
- package/src/components/Dialog/Dialog.tsx +2 -2
- package/src/components/EventSelector/EventSelector.tsx +7 -7
- package/src/components/FileDisplay/FileDisplay.tsx +291 -179
- package/src/components/FileUpload/FileUpload.tsx +7 -4
- package/src/components/Header/Header.test.tsx +28 -0
- package/src/components/Header/Header.tsx +22 -9
- package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +2 -2
- package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +19 -14
- package/src/components/LoadingSpinner/LoadingSpinner.tsx +5 -5
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +127 -1
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +8 -8
- package/src/components/PaceAppLayout/PaceAppLayout.integration.test.tsx +4 -0
- package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +3 -0
- package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +3 -0
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +16 -6
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +37 -3
- package/src/components/PaceAppLayout/test-setup.tsx +1 -0
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +66 -45
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +6 -4
- package/src/components/Progress/Progress.test.tsx +18 -19
- package/src/components/Progress/Progress.tsx +31 -32
- package/src/components/PublicLayout/PublicLayout.test.tsx +6 -6
- package/src/components/PublicLayout/PublicPageProvider.tsx +5 -3
- package/src/components/Select/Select.tsx +5 -5
- package/src/components/Switch/Switch.test.tsx +2 -1
- package/src/components/Switch/Switch.tsx +1 -1
- package/src/components/Toast/Toast.tsx +1 -1
- package/src/components/Tooltip/Tooltip.test.tsx +8 -2
- package/src/components/UserMenu/UserMenu.tsx +3 -3
- package/src/eslint-rules/pace-core-compliance.cjs +0 -2
- package/src/eslint-rules/pace-core-compliance.js +0 -2
- package/src/hooks/__tests__/hooks.integration.test.tsx +4 -1
- package/src/hooks/__tests__/useAppConfig.unit.test.ts +76 -5
- package/src/hooks/__tests__/useDataTableState.test.ts +76 -0
- package/src/hooks/__tests__/useFileUrl.unit.test.ts +25 -69
- package/src/hooks/__tests__/useFileUrlCache.test.ts +129 -0
- package/src/hooks/__tests__/usePreventTabReload.test.ts +88 -0
- package/src/hooks/__tests__/{usePublicEvent.unit.test.ts → usePublicEvent.test.ts} +28 -1
- package/src/hooks/__tests__/useQueryCache.test.ts +144 -0
- package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +58 -16
- package/src/hooks/index.ts +1 -1
- package/src/hooks/public/usePublicEvent.ts +2 -2
- package/src/hooks/public/usePublicFileDisplay.ts +173 -87
- package/src/hooks/useAppConfig.ts +24 -5
- package/src/hooks/useFileDisplay.ts +297 -34
- package/src/hooks/useFileReference.ts +56 -11
- package/src/hooks/useFileUrl.ts +1 -1
- package/src/hooks/useInactivityTracker.ts +16 -7
- package/src/hooks/usePermissionCache.test.ts +85 -8
- package/src/hooks/useQueryCache.ts +21 -0
- package/src/hooks/useSecureDataAccess.test.ts +80 -35
- package/src/hooks/useSecureDataAccess.ts +80 -37
- package/src/providers/services/EventServiceProvider.tsx +37 -17
- package/src/providers/services/InactivityServiceProvider.tsx +4 -4
- package/src/providers/services/OrganisationServiceProvider.tsx +8 -1
- package/src/providers/services/UnifiedAuthProvider.tsx +115 -29
- package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +451 -0
- package/src/rbac/__tests__/engine.comprehensive.test.ts +12 -0
- package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +8 -0
- package/src/rbac/__tests__/rbac-engine-simplified.test.ts +4 -0
- package/src/rbac/api.ts +240 -36
- package/src/rbac/cache-invalidation.ts +21 -7
- package/src/rbac/compliance/quick-fix-suggestions.ts +1 -1
- package/src/rbac/components/NavigationGuard.tsx +23 -63
- package/src/rbac/components/NavigationProvider.test.tsx +52 -23
- package/src/rbac/components/NavigationProvider.tsx +13 -11
- package/src/rbac/components/PagePermissionGuard.tsx +77 -203
- package/src/rbac/components/PagePermissionProvider.tsx +13 -11
- package/src/rbac/components/PermissionEnforcer.tsx +24 -62
- package/src/rbac/components/RoleBasedRouter.tsx +14 -12
- package/src/rbac/components/SecureDataProvider.tsx +13 -11
- package/src/rbac/components/__tests__/NavigationGuard.test.tsx +104 -41
- package/src/rbac/components/__tests__/NavigationProvider.test.tsx +49 -12
- package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +22 -1
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +161 -82
- package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +22 -1
- package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +77 -30
- package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +39 -5
- package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +47 -4
- package/src/rbac/engine.ts +4 -2
- package/src/rbac/hooks/__tests__/useSecureSupabase.test.ts +144 -52
- package/src/rbac/hooks/index.ts +3 -0
- package/src/rbac/hooks/useCan.test.ts +101 -53
- package/src/rbac/hooks/usePermissions.ts +108 -41
- package/src/rbac/hooks/useRBAC.test.ts +11 -3
- package/src/rbac/hooks/useRBAC.ts +83 -40
- package/src/rbac/hooks/useResolvedScope.test.ts +189 -63
- package/src/rbac/hooks/useResolvedScope.ts +128 -70
- package/src/rbac/hooks/useSecureSupabase.ts +36 -19
- package/src/rbac/hooks/useSuperAdminBypass.ts +126 -0
- package/src/rbac/request-deduplication.ts +1 -1
- package/src/rbac/secureClient.ts +72 -12
- package/src/rbac/security.ts +29 -23
- package/src/rbac/types.ts +10 -0
- package/src/rbac/utils/__tests__/contextValidator.test.ts +150 -0
- package/src/rbac/utils/__tests__/deep-equal.test.ts +53 -0
- package/src/rbac/utils/__tests__/eventContext.test.ts +6 -1
- package/src/rbac/utils/contextValidator.ts +288 -0
- package/src/rbac/utils/eventContext.ts +48 -2
- package/src/services/EventService.ts +165 -21
- package/src/services/OrganisationService.ts +37 -2
- package/src/services/__tests__/EventService.test.ts +26 -21
- package/src/types/file-reference.ts +13 -10
- package/src/utils/app/appNameResolver.test.ts +346 -73
- package/src/utils/context/superAdminOverride.ts +58 -0
- package/src/utils/file-reference/index.ts +61 -33
- package/src/utils/google-places/googlePlacesUtils.test.ts +98 -0
- package/src/utils/google-places/loadGoogleMapsScript.test.ts +83 -0
- package/src/utils/storage/helpers.test.ts +1 -1
- package/src/utils/storage/helpers.ts +38 -19
- package/src/utils/storage/types.ts +15 -8
- package/src/utils/validation/__tests__/csrf.test.ts +105 -0
- package/src/utils/validation/__tests__/sqlInjectionProtection.test.ts +92 -0
- package/src/vite-env.d.ts +2 -2
- package/dist/chunk-3GOZZZYH.js.map +0 -1
- package/dist/chunk-DDM4CCYT.js.map +0 -1
- package/dist/chunk-E7UAOUMY.js +0 -75
- package/dist/chunk-E7UAOUMY.js.map +0 -1
- package/dist/chunk-F2IMUDXZ.js.map +0 -1
- package/dist/chunk-HEHYGYOX.js.map +0 -1
- package/dist/chunk-IM4QE42D.js.map +0 -1
- package/dist/chunk-MX64ZF6I.js.map +0 -1
- package/dist/chunk-SAUPYVLF.js.map +0 -1
- package/dist/chunk-THRPYOFK.js.map +0 -1
- package/dist/chunk-UCQSRW7Z.js.map +0 -1
- package/dist/chunk-VGZZXKBR.js.map +0 -1
- package/dist/chunk-YGPFYGA6.js.map +0 -1
- package/dist/chunk-YHCN776L.js.map +0 -1
- package/src/hooks/__tests__/usePermissionCache.simple.test.ts +0 -192
- package/src/hooks/__tests__/usePermissionCache.unit.test.ts +0 -741
- package/src/hooks/__tests__/usePublicEvent.simple.test.ts +0 -703
- package/src/rbac/hooks/useRBAC.simple.test.ts +0 -95
- package/src/rbac/utils/__tests__/eventContext.unit.test.ts +0 -428
- /package/dist/{DataTable-GUFUNZ3N.js.map → DataTable-ON3IXISJ.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-643PUAIM.js.map → UnifiedAuthProvider-X5NXANVI.js.map} +0 -0
- /package/dist/{api-YP7XD5L6.js.map → api-I6UCQ5S6.js.map} +0 -0
- /package/dist/{chunk-HESYZWZW.js.map → chunk-QWWZ5CAQ.js.map} +0 -0
|
@@ -11,6 +11,19 @@ import { renderWithProviders } from '../../__tests__/helpers/test-utils';
|
|
|
11
11
|
|
|
12
12
|
describe('Alert Component', () => {
|
|
13
13
|
describe('Rendering', () => {
|
|
14
|
+
it('renders as semantic aside element', () => {
|
|
15
|
+
renderWithProviders(
|
|
16
|
+
<Alert>
|
|
17
|
+
<AlertTitle>Test Title</AlertTitle>
|
|
18
|
+
<AlertDescription>Test description</AlertDescription>
|
|
19
|
+
</Alert>
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
const alert = screen.getByRole('alert');
|
|
23
|
+
expect(alert.tagName).toBe('ASIDE');
|
|
24
|
+
expect(alert).toBeInTheDocument();
|
|
25
|
+
});
|
|
26
|
+
|
|
14
27
|
it('renders with default variant', () => {
|
|
15
28
|
renderWithProviders(
|
|
16
29
|
<Alert>
|
|
@@ -21,6 +34,7 @@ describe('Alert Component', () => {
|
|
|
21
34
|
|
|
22
35
|
const alert = screen.getByRole('alert');
|
|
23
36
|
expect(alert).toBeInTheDocument();
|
|
37
|
+
expect(alert.tagName).toBe('ASIDE');
|
|
24
38
|
expect(alert).toHaveClass('relative', 'w-full', 'rounded-lg', 'border', 'p-4');
|
|
25
39
|
});
|
|
26
40
|
|
|
@@ -34,6 +48,7 @@ describe('Alert Component', () => {
|
|
|
34
48
|
|
|
35
49
|
const alert = screen.getByRole('alert');
|
|
36
50
|
expect(alert).toBeInTheDocument();
|
|
51
|
+
expect(alert.tagName).toBe('ASIDE');
|
|
37
52
|
expect(alert).toHaveClass('border-destructive', 'text-destructive');
|
|
38
53
|
});
|
|
39
54
|
|
|
@@ -58,6 +73,7 @@ describe('Alert Component', () => {
|
|
|
58
73
|
);
|
|
59
74
|
|
|
60
75
|
const alert = screen.getByRole('alert');
|
|
76
|
+
expect(alert.tagName).toBe('ASIDE');
|
|
61
77
|
expect(alert).toHaveClass('custom-alert-class');
|
|
62
78
|
});
|
|
63
79
|
|
|
@@ -72,7 +88,7 @@ describe('Alert Component', () => {
|
|
|
72
88
|
});
|
|
73
89
|
|
|
74
90
|
it('forwards ref correctly', () => {
|
|
75
|
-
const ref = React.createRef<
|
|
91
|
+
const ref = React.createRef<HTMLElement>();
|
|
76
92
|
|
|
77
93
|
renderWithProviders(
|
|
78
94
|
<Alert ref={ref}>
|
|
@@ -80,7 +96,8 @@ describe('Alert Component', () => {
|
|
|
80
96
|
</Alert>
|
|
81
97
|
);
|
|
82
98
|
|
|
83
|
-
expect(ref.current).toBeInstanceOf(
|
|
99
|
+
expect(ref.current).toBeInstanceOf(HTMLElement);
|
|
100
|
+
expect(ref.current?.tagName).toBe('ASIDE');
|
|
84
101
|
expect(ref.current).toHaveAttribute('role', 'alert');
|
|
85
102
|
});
|
|
86
103
|
});
|
|
@@ -258,6 +275,7 @@ describe('Alert Component', () => {
|
|
|
258
275
|
|
|
259
276
|
const alert = screen.getByRole('alert');
|
|
260
277
|
expect(alert).toBeInTheDocument();
|
|
278
|
+
expect(alert.tagName).toBe('ASIDE');
|
|
261
279
|
});
|
|
262
280
|
|
|
263
281
|
it('does not have role="alert" for inline variant', () => {
|
|
@@ -283,6 +301,7 @@ describe('Alert Component', () => {
|
|
|
283
301
|
|
|
284
302
|
const alert = screen.getByRole('alert');
|
|
285
303
|
expect(alert).toBeInTheDocument();
|
|
304
|
+
expect(alert.tagName).toBe('ASIDE');
|
|
286
305
|
|
|
287
306
|
// Screen readers will announce the content within the alert
|
|
288
307
|
expect(screen.getByText('Important Notice')).toBeInTheDocument();
|
|
@@ -297,9 +316,11 @@ describe('Alert Component', () => {
|
|
|
297
316
|
</Alert>
|
|
298
317
|
);
|
|
299
318
|
|
|
319
|
+
const alert = screen.getByRole('alert');
|
|
300
320
|
const title = screen.getByRole('heading', { level: 5 });
|
|
301
321
|
const description = screen.getByText('Semantic description with proper heading structure');
|
|
302
322
|
|
|
323
|
+
expect(alert.tagName).toBe('ASIDE');
|
|
303
324
|
expect(title).toBeInTheDocument();
|
|
304
325
|
expect(description.tagName).toBe('P');
|
|
305
326
|
});
|
|
@@ -320,7 +341,9 @@ describe('Alert Component', () => {
|
|
|
320
341
|
</Alert>
|
|
321
342
|
);
|
|
322
343
|
|
|
323
|
-
|
|
344
|
+
const alert = screen.getByRole('alert');
|
|
345
|
+
expect(alert).toBeInTheDocument();
|
|
346
|
+
expect(alert.tagName).toBe('ASIDE');
|
|
324
347
|
expect(screen.getByText('⚠️')).toBeInTheDocument();
|
|
325
348
|
expect(screen.getByRole('button', { name: 'Dismiss' })).toBeInTheDocument();
|
|
326
349
|
});
|
|
@@ -334,7 +357,9 @@ describe('Alert Component', () => {
|
|
|
334
357
|
</Alert>
|
|
335
358
|
);
|
|
336
359
|
|
|
337
|
-
|
|
360
|
+
const alert = screen.getByRole('alert');
|
|
361
|
+
expect(alert).toBeInTheDocument();
|
|
362
|
+
expect(alert.tagName).toBe('ASIDE');
|
|
338
363
|
expect(screen.getByText('First description')).toBeInTheDocument();
|
|
339
364
|
expect(screen.getByText('Second description')).toBeInTheDocument();
|
|
340
365
|
});
|
|
@@ -346,7 +371,9 @@ describe('Alert Component', () => {
|
|
|
346
371
|
</Alert>
|
|
347
372
|
);
|
|
348
373
|
|
|
349
|
-
|
|
374
|
+
const alert = screen.getByRole('alert');
|
|
375
|
+
expect(alert).toBeInTheDocument();
|
|
376
|
+
expect(alert.tagName).toBe('ASIDE');
|
|
350
377
|
expect(screen.getByText('Description without title')).toBeInTheDocument();
|
|
351
378
|
});
|
|
352
379
|
|
|
@@ -357,7 +384,9 @@ describe('Alert Component', () => {
|
|
|
357
384
|
</Alert>
|
|
358
385
|
);
|
|
359
386
|
|
|
360
|
-
|
|
387
|
+
const alert = screen.getByRole('alert');
|
|
388
|
+
expect(alert).toBeInTheDocument();
|
|
389
|
+
expect(alert.tagName).toBe('ASIDE');
|
|
361
390
|
expect(screen.getByRole('heading', { level: 5 })).toHaveTextContent('Title without description');
|
|
362
391
|
});
|
|
363
392
|
});
|
|
@@ -368,6 +397,7 @@ describe('Alert Component', () => {
|
|
|
368
397
|
|
|
369
398
|
const alert = screen.getByRole('alert');
|
|
370
399
|
expect(alert).toBeInTheDocument();
|
|
400
|
+
expect(alert.tagName).toBe('ASIDE');
|
|
371
401
|
expect(alert).toBeEmptyDOMElement();
|
|
372
402
|
});
|
|
373
403
|
|
|
@@ -395,7 +425,9 @@ describe('Alert Component', () => {
|
|
|
395
425
|
);
|
|
396
426
|
|
|
397
427
|
// Should fallback to default behavior
|
|
398
|
-
|
|
428
|
+
const alert = screen.getByRole('alert');
|
|
429
|
+
expect(alert).toBeInTheDocument();
|
|
430
|
+
expect(alert.tagName).toBe('ASIDE');
|
|
399
431
|
});
|
|
400
432
|
|
|
401
433
|
it('handles rapid variant changes', () => {
|
|
@@ -424,7 +456,9 @@ describe('Alert Component', () => {
|
|
|
424
456
|
</Alert>
|
|
425
457
|
);
|
|
426
458
|
|
|
427
|
-
|
|
459
|
+
const alert = screen.getByRole('alert');
|
|
460
|
+
expect(alert).toBeInTheDocument();
|
|
461
|
+
expect(alert.tagName).toBe('ASIDE');
|
|
428
462
|
});
|
|
429
463
|
});
|
|
430
464
|
|
|
@@ -441,7 +475,9 @@ describe('Alert Component', () => {
|
|
|
441
475
|
</form>
|
|
442
476
|
);
|
|
443
477
|
|
|
444
|
-
|
|
478
|
+
const alert = screen.getByRole('alert');
|
|
479
|
+
expect(alert).toBeInTheDocument();
|
|
480
|
+
expect(alert.tagName).toBe('ASIDE');
|
|
445
481
|
expect(screen.getByRole('textbox')).toBeInTheDocument();
|
|
446
482
|
expect(screen.getByRole('button', { name: 'Submit' })).toBeInTheDocument();
|
|
447
483
|
});
|
|
@@ -462,6 +498,8 @@ describe('Alert Component', () => {
|
|
|
462
498
|
|
|
463
499
|
const alerts = screen.getAllByRole('alert');
|
|
464
500
|
expect(alerts).toHaveLength(2);
|
|
501
|
+
expect(alerts[0].tagName).toBe('ASIDE');
|
|
502
|
+
expect(alerts[1].tagName).toBe('ASIDE');
|
|
465
503
|
expect(alerts[0]).toHaveClass('bg-background', 'text-foreground');
|
|
466
504
|
expect(alerts[1]).toHaveClass('border-destructive', 'text-destructive');
|
|
467
505
|
});
|
|
@@ -482,7 +520,9 @@ describe('Alert Component', () => {
|
|
|
482
520
|
</Alert>
|
|
483
521
|
);
|
|
484
522
|
|
|
485
|
-
|
|
523
|
+
const alert = screen.getByRole('alert');
|
|
524
|
+
expect(alert).toBeInTheDocument();
|
|
525
|
+
expect(alert.tagName).toBe('ASIDE');
|
|
486
526
|
expect(screen.getByText('Complex Alert')).toBeInTheDocument();
|
|
487
527
|
expect(screen.getByText('This is a complex description with')).toBeInTheDocument();
|
|
488
528
|
expect(screen.getByText('Multiple elements')).toBeInTheDocument();
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* Features:
|
|
11
11
|
* - Multiple visual variants (default, destructive, inline)
|
|
12
12
|
* - Title and description support
|
|
13
|
+
* - Semantic HTML: renders as `<aside>` element
|
|
13
14
|
* - ARIA role="alert" for accessibility
|
|
14
15
|
* - Keyboard and screen reader accessible
|
|
15
16
|
* - Composable with icons and actions
|
|
@@ -40,6 +41,7 @@
|
|
|
40
41
|
* ```
|
|
41
42
|
*
|
|
42
43
|
* @accessibility
|
|
44
|
+
* - Uses semantic HTML: `<aside>` element for better semantic meaning
|
|
43
45
|
* - Uses role="alert" for screen reader announcement
|
|
44
46
|
* - Title and description are semantically structured
|
|
45
47
|
* - Supports keyboard navigation and focus
|
|
@@ -65,8 +67,8 @@ const getAlertClasses = (variant: "default" | "destructive" | "inline" = "defaul
|
|
|
65
67
|
};
|
|
66
68
|
|
|
67
69
|
const Alert = React.forwardRef<
|
|
68
|
-
|
|
69
|
-
React.HTMLAttributes<
|
|
70
|
+
HTMLElement,
|
|
71
|
+
React.HTMLAttributes<HTMLElement> & { variant?: "default" | "destructive" | "inline" }
|
|
70
72
|
>(({ className, variant = "default", ...props }, ref) => {
|
|
71
73
|
const contextValue = React.useMemo(() => ({ variant }), [variant])
|
|
72
74
|
|
|
@@ -80,7 +82,7 @@ const Alert = React.forwardRef<
|
|
|
80
82
|
|
|
81
83
|
return (
|
|
82
84
|
<AlertContext.Provider value={contextValue}>
|
|
83
|
-
<
|
|
85
|
+
<aside
|
|
84
86
|
ref={ref}
|
|
85
87
|
className={cn(getAlertClasses(variant), className)}
|
|
86
88
|
role="alert"
|
|
@@ -10,24 +10,70 @@ import { Avatar } from './Avatar';
|
|
|
10
10
|
import { FileCategory } from '../../types/file-reference';
|
|
11
11
|
import { renderWithProviders, userEvent } from '../../__tests__/helpers/test-utils';
|
|
12
12
|
|
|
13
|
+
const mockUseUnifiedAuthValue = {
|
|
14
|
+
supabase: null as Record<string, unknown> | null,
|
|
15
|
+
user: { id: 'test-user' },
|
|
16
|
+
signOut: vi.fn(),
|
|
17
|
+
updatePassword: vi.fn(),
|
|
18
|
+
isAuthenticated: true,
|
|
19
|
+
isLoading: false,
|
|
20
|
+
error: null
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const mockUseFileReferenceById = vi.fn(() => ({
|
|
24
|
+
fileReference: null,
|
|
25
|
+
fileUrl: null,
|
|
26
|
+
isLoading: false
|
|
27
|
+
}));
|
|
28
|
+
|
|
29
|
+
vi.mock('../../providers/services/UnifiedAuthProvider', () => ({
|
|
30
|
+
useUnifiedAuth: () => mockUseUnifiedAuthValue,
|
|
31
|
+
UnifiedAuthProvider: ({ children }: { children: React.ReactNode }) => <>{children}</>
|
|
32
|
+
}));
|
|
33
|
+
|
|
34
|
+
vi.mock('../../hooks/useFileReference', () => ({
|
|
35
|
+
useFileReferenceById: (...args: unknown[]) => mockUseFileReferenceById(...args)
|
|
36
|
+
}));
|
|
37
|
+
|
|
38
|
+
// Mock FileDisplay component
|
|
39
|
+
vi.mock('../FileDisplay/FileDisplay', () => ({
|
|
40
|
+
FileDisplay: ({ fallbackText, className }: any) => (
|
|
41
|
+
<div data-testid="file-display" className={className}>
|
|
42
|
+
<p>{fallbackText}</p>
|
|
43
|
+
</div>
|
|
44
|
+
)
|
|
45
|
+
}));
|
|
46
|
+
|
|
13
47
|
// Size classes for testing
|
|
14
48
|
const sizeClasses = {
|
|
15
|
-
xs: '
|
|
16
|
-
sm: '
|
|
17
|
-
md: '
|
|
18
|
-
lg: '
|
|
19
|
-
xl: '
|
|
20
|
-
'2xl': '
|
|
49
|
+
xs: 'size-4 text-xs',
|
|
50
|
+
sm: 'size-6 text-sm',
|
|
51
|
+
md: 'size-10 text-base',
|
|
52
|
+
lg: 'size-12 text-lg',
|
|
53
|
+
xl: 'size-16 text-xl',
|
|
54
|
+
'2xl': 'size-20 text-2xl'
|
|
21
55
|
};
|
|
22
56
|
|
|
23
|
-
// Helper function to get the avatar container element
|
|
57
|
+
// Helper function to get the avatar container element (outer container, not inner fallback)
|
|
24
58
|
const getAvatarContainer = (fallbackText: string) => {
|
|
25
|
-
|
|
59
|
+
const fallbackNode = screen.getByText(fallbackText);
|
|
60
|
+
const container = fallbackNode.parentElement as HTMLElement | null;
|
|
61
|
+
if (!container) {
|
|
62
|
+
throw new Error('Avatar container not found');
|
|
63
|
+
}
|
|
64
|
+
return container;
|
|
26
65
|
};
|
|
27
66
|
|
|
28
67
|
describe('Avatar Component', () => {
|
|
29
68
|
beforeEach(() => {
|
|
30
69
|
vi.clearAllMocks();
|
|
70
|
+
mockUseUnifiedAuthValue.supabase = null;
|
|
71
|
+
mockUseFileReferenceById.mockReset();
|
|
72
|
+
mockUseFileReferenceById.mockReturnValue({
|
|
73
|
+
fileReference: null,
|
|
74
|
+
fileUrl: null,
|
|
75
|
+
isLoading: false
|
|
76
|
+
});
|
|
31
77
|
});
|
|
32
78
|
|
|
33
79
|
describe('Rendering', () => {
|
|
@@ -38,7 +84,8 @@ describe('Avatar Component', () => {
|
|
|
38
84
|
|
|
39
85
|
const avatar = getAvatarContainer('AB');
|
|
40
86
|
expect(avatar).toBeInTheDocument();
|
|
41
|
-
|
|
87
|
+
// Avatar uses Tailwind v4 size-* utility instead of h-* w-*
|
|
88
|
+
expect(avatar).toHaveClass('size-10', 'overflow-hidden', 'rounded-full');
|
|
42
89
|
expect(screen.getByText('AB')).toBeInTheDocument();
|
|
43
90
|
});
|
|
44
91
|
|
|
@@ -53,11 +100,11 @@ describe('Avatar Component', () => {
|
|
|
53
100
|
|
|
54
101
|
it('renders with custom size via className', () => {
|
|
55
102
|
renderWithProviders(
|
|
56
|
-
<Avatar fallback="LG" className="
|
|
103
|
+
<Avatar fallback="LG" className="size-16" />
|
|
57
104
|
);
|
|
58
105
|
|
|
59
106
|
const avatar = getAvatarContainer('LG');
|
|
60
|
-
expect(avatar).toHaveClass('
|
|
107
|
+
expect(avatar).toHaveClass('size-16');
|
|
61
108
|
});
|
|
62
109
|
|
|
63
110
|
it('renders with size prop', () => {
|
|
@@ -66,7 +113,7 @@ describe('Avatar Component', () => {
|
|
|
66
113
|
);
|
|
67
114
|
|
|
68
115
|
const avatar = getAvatarContainer('SM');
|
|
69
|
-
expect(avatar).toHaveClass('
|
|
116
|
+
expect(avatar).toHaveClass('size-6', 'text-sm');
|
|
70
117
|
});
|
|
71
118
|
|
|
72
119
|
it('forwards ref correctly', () => {
|
|
@@ -76,7 +123,8 @@ describe('Avatar Component', () => {
|
|
|
76
123
|
<Avatar ref={ref} fallback="RF" />
|
|
77
124
|
);
|
|
78
125
|
|
|
79
|
-
|
|
126
|
+
// Avatar uses figure element, not div
|
|
127
|
+
expect(ref.current).toBeInstanceOf(HTMLElement);
|
|
80
128
|
});
|
|
81
129
|
|
|
82
130
|
it('accepts HTML attributes', () => {
|
|
@@ -128,13 +176,17 @@ describe('Avatar Component', () => {
|
|
|
128
176
|
expect(image).toBeInTheDocument();
|
|
129
177
|
});
|
|
130
178
|
|
|
131
|
-
it('applies image classes correctly', () => {
|
|
179
|
+
it('applies container and image classes correctly', () => {
|
|
132
180
|
renderWithProviders(
|
|
133
181
|
<Avatar src="/user.jpg" alt="User" fallback="IC" className="custom-img-class" />
|
|
134
182
|
);
|
|
135
183
|
|
|
136
184
|
const image = screen.getByAltText('User');
|
|
137
|
-
|
|
185
|
+
// className is applied to container, not image. Image only gets base image classes
|
|
186
|
+
expect(image).toHaveClass('object-cover', 'size-full');
|
|
187
|
+
// Container should have the custom className
|
|
188
|
+
const container = image.parentElement;
|
|
189
|
+
expect(container).toHaveClass('custom-img-class');
|
|
138
190
|
});
|
|
139
191
|
});
|
|
140
192
|
|
|
@@ -195,12 +247,10 @@ describe('Avatar Component', () => {
|
|
|
195
247
|
const fallback = screen.getByText('AB');
|
|
196
248
|
expect(fallback).toBeInTheDocument();
|
|
197
249
|
expect(fallback).toHaveClass(
|
|
198
|
-
'
|
|
199
|
-
'
|
|
200
|
-
'
|
|
201
|
-
'
|
|
202
|
-
'justify-center',
|
|
203
|
-
'rounded-full',
|
|
250
|
+
'size-full',
|
|
251
|
+
'grid',
|
|
252
|
+
'place-items-center',
|
|
253
|
+
'text-center',
|
|
204
254
|
'text-sec-50',
|
|
205
255
|
'bg-sec-500'
|
|
206
256
|
);
|
|
@@ -301,8 +351,10 @@ describe('Avatar Component', () => {
|
|
|
301
351
|
);
|
|
302
352
|
|
|
303
353
|
// Avatar container should exist even with empty fallback
|
|
304
|
-
|
|
305
|
-
|
|
354
|
+
// Avatar uses figure element, not div
|
|
355
|
+
const fallback = document.querySelector('[aria-label=""]') as HTMLElement | null;
|
|
356
|
+
expect(fallback).not.toBeNull();
|
|
357
|
+
expect(fallback?.parentElement).toBeInstanceOf(HTMLElement);
|
|
306
358
|
});
|
|
307
359
|
|
|
308
360
|
it('handles very long fallback text', () => {
|
|
@@ -329,7 +381,10 @@ describe('Avatar Component', () => {
|
|
|
329
381
|
expect(screen.getByText('123')).toBeInTheDocument();
|
|
330
382
|
});
|
|
331
383
|
|
|
332
|
-
it('prioritizes
|
|
384
|
+
it('prioritizes file props over direct URL', () => {
|
|
385
|
+
// Provide mock supabase so file props can be used
|
|
386
|
+
mockUseUnifiedAuthValue.supabase = {} as any;
|
|
387
|
+
|
|
333
388
|
renderWithProviders(
|
|
334
389
|
<Avatar
|
|
335
390
|
src="/user.jpg"
|
|
@@ -342,10 +397,12 @@ describe('Avatar Component', () => {
|
|
|
342
397
|
/>
|
|
343
398
|
);
|
|
344
399
|
|
|
345
|
-
//
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
expect(
|
|
400
|
+
// File props take priority over direct URL, so should show FileDisplay
|
|
401
|
+
// FileDisplay will render fallback when file doesn't exist in test
|
|
402
|
+
// The fallback text "PR" should be visible
|
|
403
|
+
expect(screen.getByText('PR')).toBeInTheDocument();
|
|
404
|
+
// Since file props take priority, direct URL image should not be rendered
|
|
405
|
+
expect(screen.queryByAltText('User')).not.toBeInTheDocument();
|
|
349
406
|
});
|
|
350
407
|
});
|
|
351
408
|
|
|
@@ -405,12 +462,9 @@ describe('Avatar Component', () => {
|
|
|
405
462
|
);
|
|
406
463
|
|
|
407
464
|
const avatar = getAvatarContainer('ST');
|
|
465
|
+
// Avatar uses Tailwind v4 size-* utility instead of h-* w-*
|
|
408
466
|
expect(avatar).toHaveClass(
|
|
409
|
-
'
|
|
410
|
-
'flex',
|
|
411
|
-
'h-10',
|
|
412
|
-
'w-10',
|
|
413
|
-
'shrink-0',
|
|
467
|
+
'size-10',
|
|
414
468
|
'overflow-hidden',
|
|
415
469
|
'rounded-full'
|
|
416
470
|
);
|
|
@@ -423,12 +477,10 @@ describe('Avatar Component', () => {
|
|
|
423
477
|
|
|
424
478
|
const fallback = screen.getByText('FS');
|
|
425
479
|
expect(fallback).toHaveClass(
|
|
426
|
-
'
|
|
427
|
-
'
|
|
428
|
-
'
|
|
429
|
-
'
|
|
430
|
-
'justify-center',
|
|
431
|
-
'rounded-full',
|
|
480
|
+
'size-full',
|
|
481
|
+
'grid',
|
|
482
|
+
'place-items-center',
|
|
483
|
+
'text-center',
|
|
432
484
|
'text-sec-50',
|
|
433
485
|
'bg-sec-500'
|
|
434
486
|
);
|
|
@@ -436,11 +488,11 @@ describe('Avatar Component', () => {
|
|
|
436
488
|
|
|
437
489
|
it('handles custom size overrides', () => {
|
|
438
490
|
renderWithProviders(
|
|
439
|
-
<Avatar className="
|
|
491
|
+
<Avatar className="size-20" fallback="CS" />
|
|
440
492
|
);
|
|
441
493
|
|
|
442
494
|
const avatar = getAvatarContainer('CS');
|
|
443
|
-
expect(avatar).toHaveClass('
|
|
495
|
+
expect(avatar).toHaveClass('size-20');
|
|
444
496
|
});
|
|
445
497
|
|
|
446
498
|
it('applies size variants correctly', () => {
|
|
@@ -453,9 +505,9 @@ describe('Avatar Component', () => {
|
|
|
453
505
|
|
|
454
506
|
const avatar = getAvatarContainer(size.toUpperCase());
|
|
455
507
|
if (size === 'md') {
|
|
456
|
-
expect(avatar).toHaveClass('
|
|
508
|
+
expect(avatar).toHaveClass('size-10');
|
|
457
509
|
} else {
|
|
458
|
-
expect(avatar).toHaveClass(sizeClasses[size].split(' ')[0]
|
|
510
|
+
expect(avatar).toHaveClass(sizeClasses[size].split(' ')[0]);
|
|
459
511
|
}
|
|
460
512
|
|
|
461
513
|
unmount();
|
|
@@ -63,12 +63,12 @@ import { cn } from "../../utils/core/cn"
|
|
|
63
63
|
* Size variants for avatar
|
|
64
64
|
*/
|
|
65
65
|
const sizeClasses = {
|
|
66
|
-
xs: '
|
|
67
|
-
sm: '
|
|
68
|
-
md: '
|
|
69
|
-
lg: '
|
|
70
|
-
xl: '
|
|
71
|
-
'2xl': '
|
|
66
|
+
xs: 'size-4 text-xs',
|
|
67
|
+
sm: 'size-6 text-sm',
|
|
68
|
+
md: 'size-10 text-base',
|
|
69
|
+
lg: 'size-12 text-lg',
|
|
70
|
+
xl: 'size-16 text-xl',
|
|
71
|
+
'2xl': 'size-20 text-2xl'
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
interface AvatarProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
@@ -132,14 +132,14 @@ const Avatar = React.forwardRef<HTMLDivElement, AvatarProps>(
|
|
|
132
132
|
|
|
133
133
|
// Base classes for avatar container
|
|
134
134
|
const baseClasses = size === 'md'
|
|
135
|
-
? "
|
|
136
|
-
: `
|
|
135
|
+
? " size-10 overflow-hidden rounded-full"
|
|
136
|
+
: ` ${sizeClasses[size]} overflow-hidden rounded-full`
|
|
137
137
|
|
|
138
138
|
// Fallback classes (don't include className to avoid conflicts)
|
|
139
|
-
const fallbackClasses = "
|
|
139
|
+
const fallbackClasses = "size-full grid place-items-center text-center text-sec-50 bg-sec-500"
|
|
140
140
|
|
|
141
141
|
// Image classes for direct URL and fileId approaches
|
|
142
|
-
const imageClasses = "object-cover
|
|
142
|
+
const imageClasses = "object-cover size-full"
|
|
143
143
|
|
|
144
144
|
// Container classes (include className here for container styling)
|
|
145
145
|
const containerClasses = cn(baseClasses, className)
|
|
@@ -157,15 +157,15 @@ const Avatar = React.forwardRef<HTMLDivElement, AvatarProps>(
|
|
|
157
157
|
}, [src])
|
|
158
158
|
|
|
159
159
|
return (
|
|
160
|
-
<
|
|
160
|
+
<figure
|
|
161
161
|
ref={ref}
|
|
162
162
|
className={containerClasses}
|
|
163
163
|
{...props}
|
|
164
164
|
>
|
|
165
165
|
{showFallback ? (
|
|
166
|
-
<
|
|
166
|
+
<figcaption className={fallbackClasses} aria-label={alt || fallback}>
|
|
167
167
|
{fallback}
|
|
168
|
-
</
|
|
168
|
+
</figcaption>
|
|
169
169
|
) : hasFileProps ? (
|
|
170
170
|
// File reference props approach - use FileDisplay
|
|
171
171
|
<FileDisplay
|
|
@@ -196,11 +196,11 @@ const Avatar = React.forwardRef<HTMLDivElement, AvatarProps>(
|
|
|
196
196
|
/>
|
|
197
197
|
) : (
|
|
198
198
|
// Fallback if nothing else works
|
|
199
|
-
<
|
|
199
|
+
<figcaption className={fallbackClasses} aria-label={alt || fallback}>
|
|
200
200
|
{fallback}
|
|
201
|
-
</
|
|
201
|
+
</figcaption>
|
|
202
202
|
)}
|
|
203
|
-
</
|
|
203
|
+
</figure>
|
|
204
204
|
)
|
|
205
205
|
}
|
|
206
206
|
)
|
|
@@ -410,7 +410,8 @@ describe('IconButton Component', () => {
|
|
|
410
410
|
);
|
|
411
411
|
|
|
412
412
|
const button = screen.getByRole('button');
|
|
413
|
-
|
|
413
|
+
// IconButton uses Tailwind v4 size-* utility instead of h-* w-*
|
|
414
|
+
expect(button).toHaveClass('size-8');
|
|
414
415
|
});
|
|
415
416
|
});
|
|
416
417
|
|
|
@@ -81,7 +81,7 @@ function getButtonClasses(variant: ButtonProps['variant'] = 'default', size: But
|
|
|
81
81
|
default: 'h-9 px-4 py-2',
|
|
82
82
|
sm: 'h-8 rounded-md px-3 text-xs',
|
|
83
83
|
lg: 'h-10 rounded-md px-8',
|
|
84
|
-
icon: '
|
|
84
|
+
icon: 'size-8',
|
|
85
85
|
};
|
|
86
86
|
|
|
87
87
|
return `${baseClasses} ${variantClasses[variant]} ${sizeClasses[size]}`;
|
|
@@ -183,7 +183,7 @@ export function ButtonGroup({
|
|
|
183
183
|
};
|
|
184
184
|
|
|
185
185
|
return (
|
|
186
|
-
<
|
|
186
|
+
<fieldset
|
|
187
187
|
className={cn(
|
|
188
188
|
'flex',
|
|
189
189
|
orientation === 'horizontal' ? 'flex-row items-center' : 'flex-col',
|
|
@@ -207,7 +207,7 @@ export function ButtonGroup({
|
|
|
207
207
|
}
|
|
208
208
|
return child;
|
|
209
209
|
})}
|
|
210
|
-
</
|
|
210
|
+
</fieldset>
|
|
211
211
|
);
|
|
212
212
|
}
|
|
213
213
|
|