@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.
Files changed (420) hide show
  1. package/core-usage-manifest.json +0 -4
  2. package/dist/{AuthService-B-cd2MA4.d.ts → AuthService-CbP_utw2.d.ts} +7 -3
  3. package/dist/{DataTable-GUFUNZ3N.js → DataTable-ON3IXISJ.js} +8 -8
  4. package/dist/{PublicPageProvider-B8HaLe69.d.ts → PublicPageProvider-C4uxosp6.d.ts} +83 -24
  5. package/dist/{UnifiedAuthProvider-BG0AL5eE.d.ts → UnifiedAuthProvider-BYA9qB-o.d.ts} +4 -3
  6. package/dist/{UnifiedAuthProvider-643PUAIM.js → UnifiedAuthProvider-X5NXANVI.js} +4 -2
  7. package/dist/{api-YP7XD5L6.js → api-I6UCQ5S6.js} +4 -2
  8. package/dist/{chunk-DDM4CCYT.js → chunk-4QYC5L4K.js} +60 -35
  9. package/dist/chunk-4QYC5L4K.js.map +1 -0
  10. package/dist/{chunk-IM4QE42D.js → chunk-73HSNNOQ.js} +141 -326
  11. package/dist/chunk-73HSNNOQ.js.map +1 -0
  12. package/dist/{chunk-YHCN776L.js → chunk-DZWK57KZ.js} +2 -75
  13. package/dist/chunk-DZWK57KZ.js.map +1 -0
  14. package/dist/{chunk-3GOZZZYH.js → chunk-HQVPB5MZ.js} +238 -301
  15. package/dist/chunk-HQVPB5MZ.js.map +1 -0
  16. package/dist/{chunk-THRPYOFK.js → chunk-HW3OVDUF.js} +5 -5
  17. package/dist/chunk-HW3OVDUF.js.map +1 -0
  18. package/dist/{chunk-F2IMUDXZ.js → chunk-I7PSE6JW.js} +75 -2
  19. package/dist/chunk-I7PSE6JW.js.map +1 -0
  20. package/dist/{chunk-VGZZXKBR.js → chunk-J2XXC7R5.js} +280 -52
  21. package/dist/chunk-J2XXC7R5.js.map +1 -0
  22. package/dist/{chunk-UCQSRW7Z.js → chunk-NIU6J6OX.js} +425 -378
  23. package/dist/chunk-NIU6J6OX.js.map +1 -0
  24. package/dist/{chunk-HESYZWZW.js → chunk-QWWZ5CAQ.js} +2 -2
  25. package/dist/{chunk-HEHYGYOX.js → chunk-RUYZKXOD.js} +401 -46
  26. package/dist/chunk-RUYZKXOD.js.map +1 -0
  27. package/dist/{chunk-2UUZZJFT.js → chunk-SDMHPX3X.js} +176 -160
  28. package/dist/{chunk-2UUZZJFT.js.map → chunk-SDMHPX3X.js.map} +1 -1
  29. package/dist/{chunk-MX64ZF6I.js → chunk-STYK4OH2.js} +11 -11
  30. package/dist/chunk-STYK4OH2.js.map +1 -0
  31. package/dist/{chunk-YGPFYGA6.js → chunk-VVBAW5A5.js} +822 -498
  32. package/dist/chunk-VVBAW5A5.js.map +1 -0
  33. package/dist/chunk-Y4BUBBHD.js +614 -0
  34. package/dist/chunk-Y4BUBBHD.js.map +1 -0
  35. package/dist/{chunk-SAUPYVLF.js → chunk-ZSAAAMVR.js} +1 -1
  36. package/dist/chunk-ZSAAAMVR.js.map +1 -0
  37. package/dist/components.d.ts +3 -4
  38. package/dist/components.js +19 -19
  39. package/dist/components.js.map +1 -1
  40. package/dist/eslint-rules/pace-core-compliance.cjs +0 -2
  41. package/dist/{file-reference-D037xOFK.d.ts → file-reference-BavO2eQj.d.ts} +13 -10
  42. package/dist/hooks.d.ts +10 -5
  43. package/dist/hooks.js +14 -8
  44. package/dist/hooks.js.map +1 -1
  45. package/dist/index.d.ts +13 -11
  46. package/dist/index.js +79 -69
  47. package/dist/index.js.map +1 -1
  48. package/dist/providers.d.ts +3 -3
  49. package/dist/providers.js +3 -1
  50. package/dist/rbac/index.d.ts +76 -12
  51. package/dist/rbac/index.js +12 -9
  52. package/dist/types.d.ts +1 -1
  53. package/dist/types.js +1 -1
  54. package/dist/{usePublicRouteParams-CTDELQ7H.d.ts → usePublicRouteParams-DxIDS4bC.d.ts} +16 -9
  55. package/dist/utils.js +16 -16
  56. package/docs/README.md +2 -2
  57. package/docs/api/classes/ColumnFactory.md +1 -1
  58. package/docs/api/classes/ErrorBoundary.md +1 -1
  59. package/docs/api/classes/InvalidScopeError.md +2 -2
  60. package/docs/api/classes/Logger.md +1 -1
  61. package/docs/api/classes/MissingUserContextError.md +2 -2
  62. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  63. package/docs/api/classes/PermissionDeniedError.md +1 -1
  64. package/docs/api/classes/RBACAuditManager.md +1 -1
  65. package/docs/api/classes/RBACCache.md +1 -1
  66. package/docs/api/classes/RBACEngine.md +4 -4
  67. package/docs/api/classes/RBACError.md +1 -1
  68. package/docs/api/classes/RBACNotInitializedError.md +2 -2
  69. package/docs/api/classes/SecureSupabaseClient.md +21 -16
  70. package/docs/api/classes/StorageUtils.md +7 -4
  71. package/docs/api/enums/FileCategory.md +1 -1
  72. package/docs/api/enums/LogLevel.md +1 -1
  73. package/docs/api/enums/RBACErrorCode.md +1 -1
  74. package/docs/api/enums/RPCFunction.md +1 -1
  75. package/docs/api/interfaces/AddressFieldProps.md +1 -1
  76. package/docs/api/interfaces/AddressFieldRef.md +1 -1
  77. package/docs/api/interfaces/AggregateConfig.md +1 -1
  78. package/docs/api/interfaces/AutocompleteOptions.md +1 -1
  79. package/docs/api/interfaces/AvatarProps.md +1 -1
  80. package/docs/api/interfaces/BadgeProps.md +1 -1
  81. package/docs/api/interfaces/ButtonProps.md +1 -1
  82. package/docs/api/interfaces/CalendarProps.md +20 -6
  83. package/docs/api/interfaces/CardProps.md +1 -1
  84. package/docs/api/interfaces/ColorPalette.md +1 -1
  85. package/docs/api/interfaces/ColorShade.md +1 -1
  86. package/docs/api/interfaces/ComplianceResult.md +1 -1
  87. package/docs/api/interfaces/DataAccessRecord.md +9 -9
  88. package/docs/api/interfaces/DataRecord.md +1 -1
  89. package/docs/api/interfaces/DataTableAction.md +1 -1
  90. package/docs/api/interfaces/DataTableColumn.md +1 -1
  91. package/docs/api/interfaces/DataTableProps.md +1 -1
  92. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  93. package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
  94. package/docs/api/interfaces/DatabaseIssue.md +1 -1
  95. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  96. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  97. package/docs/api/interfaces/EventAppRoleData.md +1 -1
  98. package/docs/api/interfaces/ExportColumn.md +1 -1
  99. package/docs/api/interfaces/ExportOptions.md +1 -1
  100. package/docs/api/interfaces/FileDisplayProps.md +62 -16
  101. package/docs/api/interfaces/FileMetadata.md +1 -1
  102. package/docs/api/interfaces/FileReference.md +2 -2
  103. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  104. package/docs/api/interfaces/FileUploadOptions.md +26 -12
  105. package/docs/api/interfaces/FileUploadProps.md +30 -19
  106. package/docs/api/interfaces/FooterProps.md +1 -1
  107. package/docs/api/interfaces/FormFieldProps.md +1 -1
  108. package/docs/api/interfaces/FormProps.md +1 -1
  109. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  110. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  111. package/docs/api/interfaces/InputProps.md +1 -1
  112. package/docs/api/interfaces/LabelProps.md +1 -1
  113. package/docs/api/interfaces/LoggerConfig.md +1 -1
  114. package/docs/api/interfaces/LoginFormProps.md +1 -1
  115. package/docs/api/interfaces/NavigationAccessRecord.md +10 -10
  116. package/docs/api/interfaces/NavigationContextType.md +9 -9
  117. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  118. package/docs/api/interfaces/NavigationItem.md +1 -1
  119. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  120. package/docs/api/interfaces/NavigationProviderProps.md +7 -7
  121. package/docs/api/interfaces/Organisation.md +1 -1
  122. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  123. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  124. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  125. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  126. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  127. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  128. package/docs/api/interfaces/PageAccessRecord.md +8 -8
  129. package/docs/api/interfaces/PagePermissionContextType.md +8 -8
  130. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  131. package/docs/api/interfaces/PagePermissionProviderProps.md +7 -7
  132. package/docs/api/interfaces/PaletteData.md +1 -1
  133. package/docs/api/interfaces/ParsedAddress.md +1 -1
  134. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  135. package/docs/api/interfaces/ProgressProps.md +3 -11
  136. package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
  137. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  138. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  139. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  140. package/docs/api/interfaces/QuickFix.md +1 -1
  141. package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
  142. package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
  143. package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
  144. package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
  145. package/docs/api/interfaces/RBACConfig.md +1 -1
  146. package/docs/api/interfaces/RBACContext.md +1 -1
  147. package/docs/api/interfaces/RBACLogger.md +1 -1
  148. package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
  149. package/docs/api/interfaces/RBACPerformanceMetrics.md +1 -1
  150. package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
  151. package/docs/api/interfaces/RBACPermissionCheckResult.md +1 -1
  152. package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
  153. package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
  154. package/docs/api/interfaces/RBACResult.md +1 -1
  155. package/docs/api/interfaces/RBACRoleGrantParams.md +1 -1
  156. package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
  157. package/docs/api/interfaces/RBACRoleRevokeParams.md +1 -1
  158. package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
  159. package/docs/api/interfaces/RBACRoleValidateParams.md +1 -1
  160. package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
  161. package/docs/api/interfaces/RBACRolesListParams.md +1 -1
  162. package/docs/api/interfaces/RBACRolesListResult.md +1 -1
  163. package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
  164. package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
  165. package/docs/api/interfaces/ResourcePermissions.md +1 -1
  166. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  167. package/docs/api/interfaces/RoleBasedRouterContextType.md +8 -8
  168. package/docs/api/interfaces/RoleBasedRouterProps.md +10 -10
  169. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  170. package/docs/api/interfaces/RouteAccessRecord.md +10 -10
  171. package/docs/api/interfaces/RouteConfig.md +10 -10
  172. package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
  173. package/docs/api/interfaces/SecureDataContextType.md +9 -9
  174. package/docs/api/interfaces/SecureDataProviderProps.md +8 -8
  175. package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
  176. package/docs/api/interfaces/SetupIssue.md +1 -1
  177. package/docs/api/interfaces/StorageConfig.md +4 -4
  178. package/docs/api/interfaces/StorageFileInfo.md +7 -7
  179. package/docs/api/interfaces/StorageFileMetadata.md +25 -14
  180. package/docs/api/interfaces/StorageListOptions.md +22 -9
  181. package/docs/api/interfaces/StorageListResult.md +4 -4
  182. package/docs/api/interfaces/StorageUploadOptions.md +21 -8
  183. package/docs/api/interfaces/StorageUploadResult.md +6 -6
  184. package/docs/api/interfaces/StorageUrlOptions.md +19 -6
  185. package/docs/api/interfaces/StyleImport.md +1 -1
  186. package/docs/api/interfaces/SwitchProps.md +1 -1
  187. package/docs/api/interfaces/TabsContentProps.md +1 -1
  188. package/docs/api/interfaces/TabsListProps.md +1 -1
  189. package/docs/api/interfaces/TabsProps.md +1 -1
  190. package/docs/api/interfaces/TabsTriggerProps.md +1 -1
  191. package/docs/api/interfaces/TextareaProps.md +1 -1
  192. package/docs/api/interfaces/ToastActionElement.md +1 -1
  193. package/docs/api/interfaces/ToastProps.md +1 -1
  194. package/docs/api/interfaces/UnifiedAuthContextType.md +53 -53
  195. package/docs/api/interfaces/UnifiedAuthProviderProps.md +13 -13
  196. package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
  197. package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
  198. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  199. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  200. package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
  201. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  202. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  203. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  204. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +1 -1
  205. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
  206. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  207. package/docs/api/interfaces/UseResolvedScopeOptions.md +4 -4
  208. package/docs/api/interfaces/UseResolvedScopeReturn.md +4 -4
  209. package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
  210. package/docs/api/interfaces/UserEventAccess.md +11 -11
  211. package/docs/api/interfaces/UserMenuProps.md +1 -1
  212. package/docs/api/interfaces/UserProfile.md +1 -1
  213. package/docs/api/modules.md +151 -92
  214. package/docs/api-reference/components.md +15 -7
  215. package/docs/api-reference/providers.md +2 -2
  216. package/docs/api-reference/rpc-functions.md +1 -0
  217. package/docs/best-practices/README.md +1 -1
  218. package/docs/best-practices/deployment.md +8 -8
  219. package/docs/getting-started/examples/README.md +2 -2
  220. package/docs/getting-started/installation-guide.md +4 -4
  221. package/docs/getting-started/quick-start.md +3 -3
  222. package/docs/migration/MIGRATION_GUIDE.md +3 -3
  223. package/docs/rbac/compliance/compliance-guide.md +2 -2
  224. package/docs/rbac/event-based-apps.md +2 -2
  225. package/docs/rbac/getting-started.md +2 -2
  226. package/docs/rbac/quick-start.md +2 -2
  227. package/docs/security/README.md +4 -4
  228. package/docs/standards/07-rbac-and-rls-standard.md +430 -7
  229. package/docs/troubleshooting/README.md +2 -2
  230. package/docs/troubleshooting/migration.md +3 -3
  231. package/package.json +1 -3
  232. package/scripts/check-pace-core-compliance.cjs +1 -1
  233. package/scripts/check-pace-core-compliance.js +1 -1
  234. package/src/__tests__/fixtures/supabase.ts +301 -0
  235. package/src/__tests__/public-recipe-view.test.ts +9 -9
  236. package/src/__tests__/rls-policies.test.ts +197 -61
  237. package/src/components/AddressField/AddressField.test.tsx +42 -0
  238. package/src/components/AddressField/AddressField.tsx +71 -60
  239. package/src/components/AddressField/README.md +1 -0
  240. package/src/components/Alert/Alert.test.tsx +50 -10
  241. package/src/components/Alert/Alert.tsx +5 -3
  242. package/src/components/Avatar/Avatar.test.tsx +95 -43
  243. package/src/components/Avatar/Avatar.tsx +16 -16
  244. package/src/components/Button/Button.test.tsx +2 -1
  245. package/src/components/Button/Button.tsx +3 -3
  246. package/src/components/Calendar/Calendar.test.tsx +53 -37
  247. package/src/components/Calendar/Calendar.tsx +409 -82
  248. package/src/components/Card/Card.test.tsx +7 -4
  249. package/src/components/Card/Card.tsx +3 -6
  250. package/src/components/Checkbox/Checkbox.tsx +2 -2
  251. package/src/components/DataTable/components/ActionButtons.tsx +5 -5
  252. package/src/components/DataTable/components/BulkOperationsDropdown.tsx +2 -2
  253. package/src/components/DataTable/components/ColumnFilter.tsx +1 -1
  254. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +3 -3
  255. package/src/components/DataTable/components/DataTableBody.tsx +12 -12
  256. package/src/components/DataTable/components/DataTableCore.tsx +3 -3
  257. package/src/components/DataTable/components/DataTableToolbar.tsx +5 -5
  258. package/src/components/DataTable/components/DraggableColumnHeader.tsx +3 -3
  259. package/src/components/DataTable/components/EditableRow.tsx +2 -2
  260. package/src/components/DataTable/components/EmptyState.tsx +3 -3
  261. package/src/components/DataTable/components/GroupHeader.tsx +2 -2
  262. package/src/components/DataTable/components/GroupingDropdown.tsx +1 -1
  263. package/src/components/DataTable/components/ImportModal.tsx +4 -4
  264. package/src/components/DataTable/components/LoadingState.tsx +1 -1
  265. package/src/components/DataTable/components/PaginationControls.tsx +11 -11
  266. package/src/components/DataTable/components/UnifiedTableBody.tsx +9 -9
  267. package/src/components/DataTable/components/ViewRowModal.tsx +2 -2
  268. package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +11 -37
  269. package/src/components/DataTable/components/__tests__/DataTableToolbar.test.tsx +157 -0
  270. package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +2 -1
  271. package/src/components/DataTable/components/__tests__/VirtualizedDataTable.test.tsx +128 -0
  272. package/src/components/DataTable/core/__tests__/ActionManager.test.ts +19 -0
  273. package/src/components/DataTable/core/__tests__/ColumnFactory.test.ts +51 -0
  274. package/src/components/DataTable/core/__tests__/ColumnManager.test.ts +84 -0
  275. package/src/components/DataTable/core/__tests__/DataManager.test.ts +14 -0
  276. package/src/components/DataTable/core/__tests__/DataTableContext.test.tsx +136 -0
  277. package/src/components/DataTable/core/__tests__/LocalDataAdapter.test.ts +16 -0
  278. package/src/components/DataTable/core/__tests__/PluginRegistry.test.ts +18 -0
  279. package/src/components/DataTable/hooks/useDataTablePermissions.ts +28 -7
  280. package/src/components/DataTable/utils/__tests__/hierarchicalUtils.test.ts +30 -1
  281. package/src/components/DataTable/utils/hierarchicalUtils.ts +38 -10
  282. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +8 -3
  283. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +4 -4
  284. package/src/components/Dialog/Dialog.tsx +2 -2
  285. package/src/components/EventSelector/EventSelector.tsx +7 -7
  286. package/src/components/FileDisplay/FileDisplay.tsx +291 -179
  287. package/src/components/FileUpload/FileUpload.tsx +7 -4
  288. package/src/components/Header/Header.test.tsx +28 -0
  289. package/src/components/Header/Header.tsx +22 -9
  290. package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +2 -2
  291. package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +19 -14
  292. package/src/components/LoadingSpinner/LoadingSpinner.tsx +5 -5
  293. package/src/components/NavigationMenu/NavigationMenu.test.tsx +127 -1
  294. package/src/components/OrganisationSelector/OrganisationSelector.tsx +8 -8
  295. package/src/components/PaceAppLayout/PaceAppLayout.integration.test.tsx +4 -0
  296. package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +3 -0
  297. package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +3 -0
  298. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +16 -6
  299. package/src/components/PaceAppLayout/PaceAppLayout.tsx +37 -3
  300. package/src/components/PaceAppLayout/test-setup.tsx +1 -0
  301. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +66 -45
  302. package/src/components/PaceLoginPage/PaceLoginPage.tsx +6 -4
  303. package/src/components/Progress/Progress.test.tsx +18 -19
  304. package/src/components/Progress/Progress.tsx +31 -32
  305. package/src/components/PublicLayout/PublicLayout.test.tsx +6 -6
  306. package/src/components/PublicLayout/PublicPageProvider.tsx +5 -3
  307. package/src/components/Select/Select.tsx +5 -5
  308. package/src/components/Switch/Switch.test.tsx +2 -1
  309. package/src/components/Switch/Switch.tsx +1 -1
  310. package/src/components/Toast/Toast.tsx +1 -1
  311. package/src/components/Tooltip/Tooltip.test.tsx +8 -2
  312. package/src/components/UserMenu/UserMenu.tsx +3 -3
  313. package/src/eslint-rules/pace-core-compliance.cjs +0 -2
  314. package/src/eslint-rules/pace-core-compliance.js +0 -2
  315. package/src/hooks/__tests__/hooks.integration.test.tsx +4 -1
  316. package/src/hooks/__tests__/useAppConfig.unit.test.ts +76 -5
  317. package/src/hooks/__tests__/useDataTableState.test.ts +76 -0
  318. package/src/hooks/__tests__/useFileUrl.unit.test.ts +25 -69
  319. package/src/hooks/__tests__/useFileUrlCache.test.ts +129 -0
  320. package/src/hooks/__tests__/usePreventTabReload.test.ts +88 -0
  321. package/src/hooks/__tests__/{usePublicEvent.unit.test.ts → usePublicEvent.test.ts} +28 -1
  322. package/src/hooks/__tests__/useQueryCache.test.ts +144 -0
  323. package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +58 -16
  324. package/src/hooks/index.ts +1 -1
  325. package/src/hooks/public/usePublicEvent.ts +2 -2
  326. package/src/hooks/public/usePublicFileDisplay.ts +173 -87
  327. package/src/hooks/useAppConfig.ts +24 -5
  328. package/src/hooks/useFileDisplay.ts +297 -34
  329. package/src/hooks/useFileReference.ts +56 -11
  330. package/src/hooks/useFileUrl.ts +1 -1
  331. package/src/hooks/useInactivityTracker.ts +16 -7
  332. package/src/hooks/usePermissionCache.test.ts +85 -8
  333. package/src/hooks/useQueryCache.ts +21 -0
  334. package/src/hooks/useSecureDataAccess.test.ts +80 -35
  335. package/src/hooks/useSecureDataAccess.ts +80 -37
  336. package/src/providers/services/EventServiceProvider.tsx +37 -17
  337. package/src/providers/services/InactivityServiceProvider.tsx +4 -4
  338. package/src/providers/services/OrganisationServiceProvider.tsx +8 -1
  339. package/src/providers/services/UnifiedAuthProvider.tsx +115 -29
  340. package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +451 -0
  341. package/src/rbac/__tests__/engine.comprehensive.test.ts +12 -0
  342. package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +8 -0
  343. package/src/rbac/__tests__/rbac-engine-simplified.test.ts +4 -0
  344. package/src/rbac/api.ts +240 -36
  345. package/src/rbac/cache-invalidation.ts +21 -7
  346. package/src/rbac/compliance/quick-fix-suggestions.ts +1 -1
  347. package/src/rbac/components/NavigationGuard.tsx +23 -63
  348. package/src/rbac/components/NavigationProvider.test.tsx +52 -23
  349. package/src/rbac/components/NavigationProvider.tsx +13 -11
  350. package/src/rbac/components/PagePermissionGuard.tsx +77 -203
  351. package/src/rbac/components/PagePermissionProvider.tsx +13 -11
  352. package/src/rbac/components/PermissionEnforcer.tsx +24 -62
  353. package/src/rbac/components/RoleBasedRouter.tsx +14 -12
  354. package/src/rbac/components/SecureDataProvider.tsx +13 -11
  355. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +104 -41
  356. package/src/rbac/components/__tests__/NavigationProvider.test.tsx +49 -12
  357. package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +22 -1
  358. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +161 -82
  359. package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +22 -1
  360. package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +77 -30
  361. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +39 -5
  362. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +47 -4
  363. package/src/rbac/engine.ts +4 -2
  364. package/src/rbac/hooks/__tests__/useSecureSupabase.test.ts +144 -52
  365. package/src/rbac/hooks/index.ts +3 -0
  366. package/src/rbac/hooks/useCan.test.ts +101 -53
  367. package/src/rbac/hooks/usePermissions.ts +108 -41
  368. package/src/rbac/hooks/useRBAC.test.ts +11 -3
  369. package/src/rbac/hooks/useRBAC.ts +83 -40
  370. package/src/rbac/hooks/useResolvedScope.test.ts +189 -63
  371. package/src/rbac/hooks/useResolvedScope.ts +128 -70
  372. package/src/rbac/hooks/useSecureSupabase.ts +36 -19
  373. package/src/rbac/hooks/useSuperAdminBypass.ts +126 -0
  374. package/src/rbac/request-deduplication.ts +1 -1
  375. package/src/rbac/secureClient.ts +72 -12
  376. package/src/rbac/security.ts +29 -23
  377. package/src/rbac/types.ts +10 -0
  378. package/src/rbac/utils/__tests__/contextValidator.test.ts +150 -0
  379. package/src/rbac/utils/__tests__/deep-equal.test.ts +53 -0
  380. package/src/rbac/utils/__tests__/eventContext.test.ts +6 -1
  381. package/src/rbac/utils/contextValidator.ts +288 -0
  382. package/src/rbac/utils/eventContext.ts +48 -2
  383. package/src/services/EventService.ts +165 -21
  384. package/src/services/OrganisationService.ts +37 -2
  385. package/src/services/__tests__/EventService.test.ts +26 -21
  386. package/src/types/file-reference.ts +13 -10
  387. package/src/utils/app/appNameResolver.test.ts +346 -73
  388. package/src/utils/context/superAdminOverride.ts +58 -0
  389. package/src/utils/file-reference/index.ts +61 -33
  390. package/src/utils/google-places/googlePlacesUtils.test.ts +98 -0
  391. package/src/utils/google-places/loadGoogleMapsScript.test.ts +83 -0
  392. package/src/utils/storage/helpers.test.ts +1 -1
  393. package/src/utils/storage/helpers.ts +38 -19
  394. package/src/utils/storage/types.ts +15 -8
  395. package/src/utils/validation/__tests__/csrf.test.ts +105 -0
  396. package/src/utils/validation/__tests__/sqlInjectionProtection.test.ts +92 -0
  397. package/src/vite-env.d.ts +2 -2
  398. package/dist/chunk-3GOZZZYH.js.map +0 -1
  399. package/dist/chunk-DDM4CCYT.js.map +0 -1
  400. package/dist/chunk-E7UAOUMY.js +0 -75
  401. package/dist/chunk-E7UAOUMY.js.map +0 -1
  402. package/dist/chunk-F2IMUDXZ.js.map +0 -1
  403. package/dist/chunk-HEHYGYOX.js.map +0 -1
  404. package/dist/chunk-IM4QE42D.js.map +0 -1
  405. package/dist/chunk-MX64ZF6I.js.map +0 -1
  406. package/dist/chunk-SAUPYVLF.js.map +0 -1
  407. package/dist/chunk-THRPYOFK.js.map +0 -1
  408. package/dist/chunk-UCQSRW7Z.js.map +0 -1
  409. package/dist/chunk-VGZZXKBR.js.map +0 -1
  410. package/dist/chunk-YGPFYGA6.js.map +0 -1
  411. package/dist/chunk-YHCN776L.js.map +0 -1
  412. package/src/hooks/__tests__/usePermissionCache.simple.test.ts +0 -192
  413. package/src/hooks/__tests__/usePermissionCache.unit.test.ts +0 -741
  414. package/src/hooks/__tests__/usePublicEvent.simple.test.ts +0 -703
  415. package/src/rbac/hooks/useRBAC.simple.test.ts +0 -95
  416. package/src/rbac/utils/__tests__/eventContext.unit.test.ts +0 -428
  417. /package/dist/{DataTable-GUFUNZ3N.js.map → DataTable-ON3IXISJ.js.map} +0 -0
  418. /package/dist/{UnifiedAuthProvider-643PUAIM.js.map → UnifiedAuthProvider-X5NXANVI.js.map} +0 -0
  419. /package/dist/{api-YP7XD5L6.js.map → api-I6UCQ5S6.js.map} +0 -0
  420. /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<HTMLDivElement>();
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(HTMLDivElement);
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
- expect(screen.getByRole('alert')).toBeInTheDocument();
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
- expect(screen.getByRole('alert')).toBeInTheDocument();
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
- expect(screen.getByRole('alert')).toBeInTheDocument();
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
- expect(screen.getByRole('alert')).toBeInTheDocument();
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
- expect(screen.getByRole('alert')).toBeInTheDocument();
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
- expect(screen.getByRole('alert')).toBeInTheDocument();
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
- expect(screen.getByRole('alert')).toBeInTheDocument();
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
- expect(screen.getByRole('alert')).toBeInTheDocument();
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
- HTMLDivElement,
69
- React.HTMLAttributes<HTMLDivElement> & { variant?: "default" | "destructive" | "inline" }
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
- <div
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: 'h-4 w-4 text-xs',
16
- sm: 'h-6 w-6 text-sm',
17
- md: 'h-10 w-10 text-base',
18
- lg: 'h-12 w-12 text-lg',
19
- xl: 'h-16 w-16 text-xl',
20
- '2xl': 'h-20 w-20 text-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
- return screen.getByText(fallbackText).closest('div');
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
- expect(avatar).toHaveClass('relative', 'flex', 'h-10', 'w-10', 'shrink-0', 'overflow-hidden', 'rounded-full');
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="h-16 w-16" />
103
+ <Avatar fallback="LG" className="size-16" />
57
104
  );
58
105
 
59
106
  const avatar = getAvatarContainer('LG');
60
- expect(avatar).toHaveClass('h-16', 'w-16');
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('h-6', 'w-6', 'text-sm');
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
- expect(ref.current).toBeInstanceOf(HTMLDivElement);
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
- expect(image).toHaveClass('object-cover', 'h-full', 'w-full', 'custom-img-class');
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
- 'flex',
199
- 'h-full',
200
- 'w-full',
201
- 'items-center',
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
- const avatar = document.querySelector('.relative.flex.h-10.w-10');
305
- expect(avatar).toBeInTheDocument();
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 direct URL over file props', () => {
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
- // Should show image, not FileDisplay
346
- const image = screen.getByAltText('User');
347
- expect(image).toBeInTheDocument();
348
- expect(image).toHaveAttribute('src', '/user.jpg');
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
- 'relative',
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
- 'flex',
427
- 'h-full',
428
- 'w-full',
429
- 'items-center',
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="h-20 w-20" fallback="CS" />
491
+ <Avatar className="size-20" fallback="CS" />
440
492
  );
441
493
 
442
494
  const avatar = getAvatarContainer('CS');
443
- expect(avatar).toHaveClass('h-20', 'w-20');
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('h-10', 'w-10');
508
+ expect(avatar).toHaveClass('size-10');
457
509
  } else {
458
- expect(avatar).toHaveClass(sizeClasses[size].split(' ')[0], sizeClasses[size].split(' ')[1]);
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: 'h-4 w-4 text-xs',
67
- sm: 'h-6 w-6 text-sm',
68
- md: 'h-10 w-10 text-base',
69
- lg: 'h-12 w-12 text-lg',
70
- xl: 'h-16 w-16 text-xl',
71
- '2xl': 'h-20 w-20 text-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
- ? "relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full"
136
- : `relative flex ${sizeClasses[size]} shrink-0 overflow-hidden rounded-full`
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 = "flex h-full w-full items-center justify-center rounded-full text-sec-50 bg-sec-500"
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 h-full w-full"
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
- <div
160
+ <figure
161
161
  ref={ref}
162
162
  className={containerClasses}
163
163
  {...props}
164
164
  >
165
165
  {showFallback ? (
166
- <div className={fallbackClasses} aria-label={alt || fallback}>
166
+ <figcaption className={fallbackClasses} aria-label={alt || fallback}>
167
167
  {fallback}
168
- </div>
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
- <div className={fallbackClasses} aria-label={alt || fallback}>
199
+ <figcaption className={fallbackClasses} aria-label={alt || fallback}>
200
200
  {fallback}
201
- </div>
201
+ </figcaption>
202
202
  )}
203
- </div>
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
- expect(button).toHaveClass('h-8', 'w-8');
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: 'h-8 w-8',
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
- <div
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
- </div>
210
+ </fieldset>
211
211
  );
212
212
  }
213
213