@jmruthers/pace-core 0.5.134 → 0.5.136

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 (522) hide show
  1. package/dist/{DataTable-C7GaRZye.d.ts → DataTable-CWAZZcXC.d.ts} +1 -1
  2. package/dist/{DataTable-A36PJG6N.js → DataTable-CYOHOX3O.js} +25 -13
  3. package/dist/{PublicLoadingSpinner-CUAnTvcg.d.ts → EventLogo-801uofbR.d.ts} +51 -135
  4. package/dist/UnifiedAuthProvider-5E5TUNMS.js +17 -0
  5. package/dist/{UnifiedAuthProvider-BVKmQd9u.d.ts → UnifiedAuthProvider-DJxGTftH.d.ts} +1 -1
  6. package/dist/{api-TNIBJWLM.js → api-45XYYO2A.js} +4 -3
  7. package/dist/{audit-T36HM7IM.js → audit-64X3VJXB.js} +3 -2
  8. package/dist/{chunk-CTJRBUX2.js → chunk-2TWNJ46Y.js} +2 -2
  9. package/dist/{chunk-UJI6WSMD.js → chunk-444EZN6N.js} +3 -3
  10. package/dist/chunk-444EZN6N.js.map +1 -0
  11. package/dist/{chunk-3CG5L6RN.js → chunk-4MT5BGGL.js} +90 -73
  12. package/dist/chunk-4MT5BGGL.js.map +1 -0
  13. package/dist/{chunk-PYUXFQJ3.js → chunk-56XJ3TU6.js} +2 -2
  14. package/dist/chunk-56XJ3TU6.js.map +1 -0
  15. package/dist/chunk-5DPZ5EAT.js +60 -0
  16. package/dist/chunk-5DPZ5EAT.js.map +1 -0
  17. package/dist/{chunk-66C4BSAY.js → chunk-ANBQRTPX.js} +9 -2
  18. package/dist/chunk-ANBQRTPX.js.map +1 -0
  19. package/dist/chunk-APIBCTL2.js +670 -0
  20. package/dist/chunk-APIBCTL2.js.map +1 -0
  21. package/dist/{chunk-GKHF54DI.js → chunk-BESYRHQM.js} +10 -4
  22. package/dist/chunk-BESYRHQM.js.map +1 -0
  23. package/dist/{chunk-WP5I5GLN.js → chunk-BVYWGZVV.js} +112 -97
  24. package/dist/chunk-BVYWGZVV.js.map +1 -0
  25. package/dist/{chunk-GEVIB2UB.js → chunk-ERISIBYU.js} +14 -5
  26. package/dist/chunk-ERISIBYU.js.map +1 -0
  27. package/dist/{chunk-CQZU6TFE.js → chunk-FHWWBIHA.js} +100 -62
  28. package/dist/chunk-FHWWBIHA.js.map +1 -0
  29. package/dist/{chunk-O3NWNXDY.js → chunk-FMUCXFII.js} +2 -2
  30. package/dist/chunk-FMUCXFII.js.map +1 -0
  31. package/dist/{chunk-GVDR7WNV.js → chunk-HJGGOMQ6.js} +194 -518
  32. package/dist/chunk-HJGGOMQ6.js.map +1 -0
  33. package/dist/{chunk-BDZUMRBD.js → chunk-K2WWTH7O.js} +13 -6
  34. package/dist/chunk-K2WWTH7O.js.map +1 -0
  35. package/dist/{chunk-BYXRHAIF.js → chunk-L6PGMCMD.js} +23 -14
  36. package/dist/chunk-L6PGMCMD.js.map +1 -0
  37. package/dist/chunk-LMC26NLJ.js +84 -0
  38. package/dist/chunk-LMC26NLJ.js.map +1 -0
  39. package/dist/{chunk-M6DDYFUD.js → chunk-LS353YLY.js} +19 -16
  40. package/dist/chunk-LS353YLY.js.map +1 -0
  41. package/dist/{chunk-ZYZCRSBD.js → chunk-LTV3XIJJ.js} +16 -11
  42. package/dist/chunk-LTV3XIJJ.js.map +1 -0
  43. package/dist/{chunk-HMNOSGVA.js → chunk-NOHEVYVX.js} +377 -666
  44. package/dist/chunk-NOHEVYVX.js.map +1 -0
  45. package/dist/{chunk-JCQZ6LA7.js → chunk-Q5QRDWKI.js} +9 -3
  46. package/dist/chunk-Q5QRDWKI.js.map +1 -0
  47. package/dist/chunk-S5OFRT4M.js +94 -0
  48. package/dist/chunk-S5OFRT4M.js.map +1 -0
  49. package/dist/{chunk-3DBFLLLU.js → chunk-SBVILCCA.js} +14 -9
  50. package/dist/chunk-SBVILCCA.js.map +1 -0
  51. package/dist/{chunk-TGIY2AR2.js → chunk-SL2YQDR6.js} +4 -3
  52. package/dist/{chunk-TGIY2AR2.js.map → chunk-SL2YQDR6.js.map} +1 -1
  53. package/dist/{chunk-VZ5OR6HD.js → chunk-TVYPTYOY.js} +55 -179
  54. package/dist/chunk-TVYPTYOY.js.map +1 -0
  55. package/dist/{chunk-ZV77RZMU.js → chunk-XARJS7CD.js} +2 -2
  56. package/dist/chunk-XDNLUEXI.js +138 -0
  57. package/dist/chunk-XDNLUEXI.js.map +1 -0
  58. package/dist/{chunk-F64FFPOZ.js → chunk-YLKIDTUK.js} +26 -20
  59. package/dist/chunk-YLKIDTUK.js.map +1 -0
  60. package/dist/{chunk-5F3NDPJV.js → chunk-ZZ2SS7NI.js} +10 -5
  61. package/dist/chunk-ZZ2SS7NI.js.map +1 -0
  62. package/dist/components.d.ts +7 -287
  63. package/dist/components.js +26 -157
  64. package/dist/components.js.map +1 -1
  65. package/dist/{file-reference-C9isKNPn.d.ts → file-reference-C6Gkn77H.d.ts} +1 -1
  66. package/dist/{formatting-DFcCxUEk.d.ts → formatting-CvUXy2mF.d.ts} +1 -1
  67. package/dist/hooks.d.ts +3 -3
  68. package/dist/hooks.js +22 -16
  69. package/dist/hooks.js.map +1 -1
  70. package/dist/index.d.ts +219 -9
  71. package/dist/index.js +49 -31
  72. package/dist/index.js.map +1 -1
  73. package/dist/providers.d.ts +1 -1
  74. package/dist/providers.js +5 -4
  75. package/dist/rbac/index.js +13 -12
  76. package/dist/styles/index.js +2 -1
  77. package/dist/theming/runtime.d.ts +2 -19
  78. package/dist/theming/runtime.js +2 -1
  79. package/dist/{types-D5rqZQXk.d.ts → types-Dfz9dmVH.d.ts} +12 -1
  80. package/dist/types.d.ts +2 -2
  81. package/dist/types.js +1 -1
  82. package/dist/{useInactivityTracker-MRUU55XI.js → useInactivityTracker-TO6ZOF35.js} +3 -2
  83. package/dist/{usePublicRouteParams-Dyt1tzI9.d.ts → usePublicRouteParams-B7PabvuH.d.ts} +1 -1
  84. package/dist/utils.d.ts +195 -232
  85. package/dist/utils.js +173 -331
  86. package/dist/utils.js.map +1 -1
  87. package/dist/{validation-DnhrNMju.d.ts → validation-8npbysjg.d.ts} +26 -8
  88. package/dist/validation.d.ts +261 -10
  89. package/dist/validation.js +82 -440
  90. package/dist/validation.js.map +1 -1
  91. package/docs/api/classes/ColumnFactory.md +1 -1
  92. package/docs/api/classes/ErrorBoundary.md +6 -6
  93. package/docs/api/classes/InvalidScopeError.md +1 -1
  94. package/docs/api/classes/MissingUserContextError.md +1 -1
  95. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  96. package/docs/api/classes/PermissionDeniedError.md +1 -1
  97. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  98. package/docs/api/classes/RBACAuditManager.md +6 -6
  99. package/docs/api/classes/RBACCache.md +1 -1
  100. package/docs/api/classes/RBACEngine.md +7 -7
  101. package/docs/api/classes/RBACError.md +1 -1
  102. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  103. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  104. package/docs/api/classes/StorageUtils.md +1 -1
  105. package/docs/api/enums/FileCategory.md +1 -1
  106. package/docs/api/interfaces/AggregateConfig.md +4 -4
  107. package/docs/api/interfaces/ButtonProps.md +1 -1
  108. package/docs/api/interfaces/CardProps.md +1 -1
  109. package/docs/api/interfaces/ColorPalette.md +1 -1
  110. package/docs/api/interfaces/ColorShade.md +29 -4
  111. package/docs/api/interfaces/DataAccessRecord.md +9 -9
  112. package/docs/api/interfaces/DataRecord.md +1 -1
  113. package/docs/api/interfaces/DataTableAction.md +18 -18
  114. package/docs/api/interfaces/DataTableColumn.md +61 -1
  115. package/docs/api/interfaces/DataTableProps.md +3 -3
  116. package/docs/api/interfaces/DataTableToolbarButton.md +7 -7
  117. package/docs/api/interfaces/EmptyStateConfig.md +5 -5
  118. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +14 -14
  119. package/docs/api/interfaces/EventAppRoleData.md +1 -1
  120. package/docs/api/interfaces/EventLogoProps.md +152 -0
  121. package/docs/api/interfaces/ExportColumn.md +90 -0
  122. package/docs/api/interfaces/ExportOptions.md +126 -0
  123. package/docs/api/interfaces/FileDisplayProps.md +15 -15
  124. package/docs/api/interfaces/FileMetadata.md +1 -1
  125. package/docs/api/interfaces/FileReference.md +1 -1
  126. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  127. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  128. package/docs/api/interfaces/FileUploadProps.md +1 -1
  129. package/docs/api/interfaces/FooterProps.md +1 -1
  130. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  131. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  132. package/docs/api/interfaces/InputProps.md +1 -1
  133. package/docs/api/interfaces/LabelProps.md +1 -1
  134. package/docs/api/interfaces/LoginFormProps.md +1 -1
  135. package/docs/api/interfaces/NavigationAccessRecord.md +10 -10
  136. package/docs/api/interfaces/NavigationContextType.md +9 -9
  137. package/docs/api/interfaces/NavigationGuardProps.md +10 -10
  138. package/docs/api/interfaces/NavigationItem.md +1 -1
  139. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  140. package/docs/api/interfaces/NavigationProviderProps.md +7 -7
  141. package/docs/api/interfaces/Organisation.md +1 -1
  142. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  143. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  144. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  145. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  146. package/docs/api/interfaces/PaceAppLayoutProps.md +27 -27
  147. package/docs/api/interfaces/PaceLoginPageProps.md +4 -4
  148. package/docs/api/interfaces/PageAccessRecord.md +8 -8
  149. package/docs/api/interfaces/PagePermissionContextType.md +8 -8
  150. package/docs/api/interfaces/PagePermissionGuardProps.md +11 -11
  151. package/docs/api/interfaces/PagePermissionProviderProps.md +7 -7
  152. package/docs/api/interfaces/PaletteData.md +4 -4
  153. package/docs/api/interfaces/PermissionEnforcerProps.md +11 -11
  154. package/docs/api/interfaces/ProtectedRouteProps.md +6 -6
  155. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  156. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  157. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  158. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  159. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  160. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  161. package/docs/api/interfaces/RBACConfig.md +1 -1
  162. package/docs/api/interfaces/RBACLogger.md +1 -1
  163. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  164. package/docs/api/interfaces/RoleBasedRouterContextType.md +8 -8
  165. package/docs/api/interfaces/RoleBasedRouterProps.md +10 -10
  166. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  167. package/docs/api/interfaces/RouteAccessRecord.md +10 -10
  168. package/docs/api/interfaces/RouteConfig.md +10 -10
  169. package/docs/api/interfaces/SecureDataContextType.md +9 -9
  170. package/docs/api/interfaces/SecureDataProviderProps.md +8 -8
  171. package/docs/api/interfaces/SessionRestorationLoaderProps.md +21 -0
  172. package/docs/api/interfaces/StorageConfig.md +1 -1
  173. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  174. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  175. package/docs/api/interfaces/StorageListOptions.md +1 -1
  176. package/docs/api/interfaces/StorageListResult.md +1 -1
  177. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  178. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  179. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  180. package/docs/api/interfaces/StyleImport.md +1 -1
  181. package/docs/api/interfaces/SwitchProps.md +1 -1
  182. package/docs/api/interfaces/ToastActionElement.md +1 -1
  183. package/docs/api/interfaces/ToastProps.md +1 -1
  184. package/docs/api/interfaces/UnifiedAuthContextType.md +53 -53
  185. package/docs/api/interfaces/UnifiedAuthProviderProps.md +13 -13
  186. package/docs/api/interfaces/UseInactivityTrackerOptions.md +9 -9
  187. package/docs/api/interfaces/UseInactivityTrackerReturn.md +8 -8
  188. package/docs/api/interfaces/UsePublicEventOptions.md +3 -3
  189. package/docs/api/interfaces/UsePublicEventReturn.md +5 -5
  190. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +4 -4
  191. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +9 -9
  192. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  193. package/docs/api/interfaces/UseResolvedScopeOptions.md +4 -4
  194. package/docs/api/interfaces/UseResolvedScopeReturn.md +4 -4
  195. package/docs/api/interfaces/UserEventAccess.md +11 -11
  196. package/docs/api/interfaces/UserMenuProps.md +1 -1
  197. package/docs/api/interfaces/UserProfile.md +1 -1
  198. package/docs/api/modules.md +648 -212
  199. package/docs/api-reference/components.md +106 -26
  200. package/docs/architecture/README.md +0 -2
  201. package/docs/implementation-guides/data-tables.md +277 -13
  202. package/docs/implementation-guides/forms.md +1 -16
  203. package/docs/implementation-guides/permission-enforcement.md +8 -2
  204. package/examples/README.md +30 -14
  205. package/examples/STRUCTURE.md +125 -0
  206. package/examples/components/DataTable/HierarchicalActionsExample.tsx +421 -0
  207. package/examples/components/DataTable/HierarchicalExample.tsx +475 -0
  208. package/examples/components/DataTable/InitialPageSizeExample.tsx +177 -0
  209. package/examples/components/DataTable/PerformanceExample.tsx +506 -0
  210. package/examples/components/DataTable/index.ts +13 -0
  211. package/examples/components/Dialog/BasicHtmlTest.tsx +55 -0
  212. package/examples/components/Dialog/DebugHtmlExample.tsx +68 -0
  213. package/examples/components/Dialog/HtmlDialogExample.tsx +202 -0
  214. package/examples/components/Dialog/ScrollableDialogExample.tsx +290 -0
  215. package/examples/components/Dialog/SimpleHtmlTest.tsx +61 -0
  216. package/examples/components/Dialog/SmartDialogExample.tsx +322 -0
  217. package/examples/components/Dialog/index.ts +15 -0
  218. package/examples/components/index.ts +11 -0
  219. package/examples/features/index.ts +12 -0
  220. package/examples/{public-pages → features/public-pages}/CorrectPublicPageImplementation.tsx +1 -1
  221. package/examples/{public-pages → features/public-pages}/PublicEventPage.tsx +1 -1
  222. package/examples/{public-pages → features/public-pages}/PublicPageApp.tsx +1 -1
  223. package/examples/{public-pages → features/public-pages}/PublicPageUsageExample.tsx +1 -1
  224. package/examples/index.ts +11 -3
  225. package/package.json +30 -10
  226. package/src/components/Alert/Alert.tsx +1 -1
  227. package/src/components/Avatar/Avatar.tsx +1 -1
  228. package/src/components/Button/Button.tsx +1 -1
  229. package/src/components/Card/Card.tsx +1 -1
  230. package/src/components/Checkbox/Checkbox.tsx +1 -1
  231. package/src/components/DataTable/DataTable.test.tsx +1 -1
  232. package/src/components/DataTable/DataTable.tsx +1 -30
  233. package/src/components/DataTable/__tests__/DataTable.grouping-aggregation.test.tsx +562 -0
  234. package/src/components/DataTable/__tests__/styles.test.ts +2 -2
  235. package/src/components/DataTable/components/ActionButtons.tsx +0 -15
  236. package/src/components/DataTable/components/DataTableCore.tsx +4 -185
  237. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +1 -1
  238. package/src/components/DataTable/components/DataTableModals.tsx +1 -27
  239. package/src/components/DataTable/components/EditableRow.tsx +1 -1
  240. package/src/components/DataTable/components/ImportModal.tsx +2 -14
  241. package/src/components/DataTable/components/PaginationControls.tsx +1 -1
  242. package/src/components/DataTable/components/UnifiedTableBody.tsx +109 -82
  243. package/src/components/DataTable/components/__tests__/ActionButtons.test.tsx +1 -1
  244. package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +1 -1
  245. package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +1 -1
  246. package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +1 -1
  247. package/src/components/DataTable/examples/GroupingAggregationExample.tsx +273 -0
  248. package/src/components/DataTable/examples/HierarchicalActionsExample.tsx +1 -1
  249. package/src/components/DataTable/examples/__tests__/HierarchicalActionsExample.test.tsx +1 -1
  250. package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +1 -1
  251. package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +1 -1
  252. package/src/components/DataTable/hooks/useDataTablePermissions.ts +2 -23
  253. package/src/components/DataTable/index.ts +4 -0
  254. package/src/components/DataTable/styles.ts +1 -1
  255. package/src/components/DataTable/types.ts +13 -0
  256. package/src/components/DataTable/utils/__tests__/exportUtils.test.ts +1 -1
  257. package/src/components/DataTable/utils/aggregationUtils.ts +161 -0
  258. package/src/components/DataTable/utils/exportUtils.ts +1 -1
  259. package/src/components/DataTable/utils/flexibleImport.ts +1 -11
  260. package/src/components/DataTable/utils/index.ts +1 -0
  261. package/src/components/DataTable/utils/paginationUtils.ts +1 -1
  262. package/src/components/Dialog/Dialog.tsx +2 -2
  263. package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +35 -7
  264. package/src/components/ErrorBoundary/ErrorBoundary.tsx +5 -4
  265. package/src/components/EventSelector/EventSelector.tsx +3 -2
  266. package/src/components/FileDisplay/FileDisplay.tsx +2 -36
  267. package/src/components/FileUpload/FileUpload.test.tsx +2 -2
  268. package/src/components/FileUpload/FileUpload.tsx +2 -2
  269. package/src/components/Footer/Footer.tsx +1 -1
  270. package/src/components/Form/Form.test.tsx +4 -509
  271. package/src/components/Form/Form.tsx +1 -1
  272. package/src/components/Form/FormField.tsx +1 -1
  273. package/src/components/Form/index.ts +0 -12
  274. package/src/components/Header/Header.tsx +1 -1
  275. package/src/components/Input/Input.tsx +1 -1
  276. package/src/components/Label/Label.tsx +1 -1
  277. package/src/components/LoginForm/LoginForm.tsx +1 -1
  278. package/src/components/NavigationMenu/NavigationMenu.test.tsx +19 -3
  279. package/src/components/NavigationMenu/NavigationMenu.tsx +9 -8
  280. package/src/components/OrganisationSelector/OrganisationSelector.tsx +4 -3
  281. package/src/components/PaceAppLayout/PaceAppLayout.tsx +14 -12
  282. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.integration.test.tsx +0 -16
  283. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.security.test.tsx +0 -1
  284. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.unit.test.tsx +0 -9
  285. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +35 -3
  286. package/src/components/PaceLoginPage/PaceLoginPage.tsx +13 -12
  287. package/src/components/PasswordReset/PasswordChangeForm.tsx +1 -1
  288. package/src/components/PasswordReset/index.ts +0 -2
  289. package/src/components/Progress/Progress.tsx +1 -1
  290. package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +35 -8
  291. package/src/components/ProtectedRoute/ProtectedRoute.tsx +3 -2
  292. package/src/components/PublicLayout/PublicErrorBoundary.tsx +1 -1
  293. package/src/components/PublicLayout/PublicLoadingSpinner.tsx +1 -1
  294. package/src/components/PublicLayout/PublicPageContextChecker.tsx +44 -43
  295. package/src/components/PublicLayout/PublicPageFooter.tsx +1 -1
  296. package/src/components/PublicLayout/PublicPageHeader.tsx +1 -15
  297. package/src/components/PublicLayout/PublicPageProvider.tsx +3 -2
  298. package/src/components/PublicLayout/__tests__/PublicPageContextChecker.test.tsx +2 -0
  299. package/src/components/PublicLayout/index.ts +4 -2
  300. package/src/components/Select/Select.tsx +1 -1
  301. package/src/components/{SessionRestorationLoader.tsx → SessionRestorationLoader/SessionRestorationLoader.tsx} +3 -2
  302. package/src/components/SessionRestorationLoader/index.ts +3 -0
  303. package/src/components/Switch/Switch.tsx +1 -1
  304. package/src/components/Table/Table.tsx +1 -1
  305. package/src/components/Toast/Toast.tsx +1 -1
  306. package/src/components/Tooltip/Tooltip.tsx +1 -1
  307. package/src/components/index.ts +4 -10
  308. package/src/hooks/__tests__/hooks.integration.test.tsx +37 -22
  309. package/src/hooks/__tests__/useComponentPerformance.unit.test.tsx +33 -17
  310. package/src/hooks/__tests__/useDataTablePerformance.unit.test.ts +28 -3
  311. package/src/hooks/__tests__/useFileDisplay.unit.test.ts +36 -9
  312. package/src/hooks/__tests__/useInactivityTracker.unit.test.ts +26 -2
  313. package/src/hooks/__tests__/usePerformanceMonitor.unit.test.ts +19 -6
  314. package/src/hooks/__tests__/usePermissionCache.simple.test.ts +17 -4
  315. package/src/hooks/__tests__/usePermissionCache.unit.test.ts +17 -4
  316. package/src/hooks/__tests__/usePublicEvent.simple.test.ts +26 -6
  317. package/src/hooks/__tests__/usePublicFileDisplay.test.ts +16 -6
  318. package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +3 -3
  319. package/src/hooks/__tests__/useSessionRestoration.unit.test.tsx +17 -3
  320. package/src/hooks/public/usePublicEvent.ts +7 -6
  321. package/src/hooks/public/usePublicEventLogo.ts +7 -4
  322. package/src/hooks/public/usePublicFileDisplay.ts +6 -150
  323. package/src/hooks/useComponentPerformance.ts +4 -1
  324. package/src/hooks/useDataTablePerformance.ts +4 -3
  325. package/src/hooks/useEventTheme.test.ts +18 -5
  326. package/src/hooks/useEventTheme.ts +4 -1
  327. package/src/hooks/useEvents.ts +2 -0
  328. package/src/hooks/useFileDisplay.ts +9 -8
  329. package/src/hooks/useFileReference.ts +4 -1
  330. package/src/hooks/useFileUrl.ts +4 -1
  331. package/src/hooks/useInactivityTracker.ts +5 -4
  332. package/src/hooks/useOrganisationSecurity.test.ts +33 -12
  333. package/src/hooks/useOrganisationSecurity.ts +8 -7
  334. package/src/hooks/usePerformanceMonitor.ts +6 -3
  335. package/src/hooks/usePermissionCache.ts +13 -6
  336. package/src/hooks/useSecureDataAccess.test.ts +2 -2
  337. package/src/hooks/useSecureDataAccess.ts +9 -8
  338. package/src/hooks/useSessionRestoration.ts +4 -1
  339. package/src/hooks/useStorage.ts +4 -1
  340. package/src/index.ts +25 -8
  341. package/src/providers/services/AuthServiceProvider.tsx +3 -2
  342. package/src/providers/services/EventServiceProvider.tsx +2 -1
  343. package/src/providers/services/InactivityServiceProvider.tsx +2 -1
  344. package/src/providers/services/OrganisationServiceProvider.tsx +2 -1
  345. package/src/providers/services/UnifiedAuthProvider.tsx +4 -3
  346. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +22 -2
  347. package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +24 -2
  348. package/src/rbac/__tests__/cache-invalidation.test.ts +20 -6
  349. package/src/rbac/api.ts +5 -2
  350. package/src/rbac/audit-enhanced.ts +6 -6
  351. package/src/rbac/audit.test.ts +60 -38
  352. package/src/rbac/audit.ts +8 -8
  353. package/src/rbac/cache-invalidation.ts +7 -4
  354. package/src/rbac/components/EnhancedNavigationMenu.tsx +11 -5
  355. package/src/rbac/components/NavigationGuard.tsx +7 -3
  356. package/src/rbac/components/NavigationProvider.tsx +6 -3
  357. package/src/rbac/components/PagePermissionGuard.tsx +28 -16
  358. package/src/rbac/components/PagePermissionProvider.tsx +4 -1
  359. package/src/rbac/components/PermissionEnforcer.tsx +9 -3
  360. package/src/rbac/components/RoleBasedRouter.tsx +3 -1
  361. package/src/rbac/components/SecureDataProvider.tsx +7 -3
  362. package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +87 -61
  363. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +83 -33
  364. package/src/rbac/components/__tests__/NavigationProvider.test.tsx +36 -13
  365. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +2 -2
  366. package/src/rbac/components/__tests__/PagePermissionProvider.test.tsx +22 -8
  367. package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +19 -6
  368. package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +43 -17
  369. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +42 -17
  370. package/src/rbac/engine.ts +15 -7
  371. package/src/rbac/hooks/usePermissions.ts +7 -3
  372. package/src/rbac/hooks/useResolvedScope.test.ts +2 -2
  373. package/src/rbac/hooks/useResolvedScope.ts +10 -7
  374. package/src/rbac/permissions.ts +5 -2
  375. package/src/rbac/security.test.ts +27 -16
  376. package/src/rbac/security.ts +5 -4
  377. package/src/services/AuthService.ts +22 -21
  378. package/src/services/EventService.ts +12 -12
  379. package/src/services/InactivityService.ts +5 -4
  380. package/src/services/OrganisationService.ts +26 -25
  381. package/src/services/__tests__/AuthService.test.ts +51 -19
  382. package/src/services/__tests__/EventService.test.ts +37 -5
  383. package/src/services/__tests__/InactivityService.test.ts +38 -4
  384. package/src/services/__tests__/OrganisationService.test.ts +3 -8
  385. package/src/services/base/BaseService.ts +3 -1
  386. package/src/theming/__tests__/runtime.test.ts +21 -12
  387. package/src/theming/parseEventColours.ts +5 -19
  388. package/src/theming/runtime.ts +8 -4
  389. package/src/types/validation.ts +2 -29
  390. package/src/utils/__tests__/appConfig.unit.test.ts +1 -1
  391. package/src/utils/__tests__/audit.unit.test.ts +1 -1
  392. package/src/utils/__tests__/auth-utils.unit.test.ts +1 -1
  393. package/src/utils/__tests__/bundleAnalysis.unit.test.ts +19 -19
  394. package/src/utils/__tests__/cn.unit.test.ts +1 -1
  395. package/src/utils/__tests__/debugLogger.test.ts +1 -1
  396. package/src/utils/__tests__/deviceFingerprint.unit.test.ts +1 -1
  397. package/src/utils/__tests__/dynamicUtils.unit.test.ts +1 -1
  398. package/src/utils/__tests__/formatting.unit.test.ts +1 -1
  399. package/src/utils/__tests__/lazyLoad.unit.test.tsx +1 -1
  400. package/src/utils/__tests__/logger.unit.test.ts +1 -1
  401. package/src/utils/__tests__/organisationContext.unit.test.ts +1 -1
  402. package/src/utils/__tests__/performanceBenchmark.test.ts +1 -1
  403. package/src/utils/__tests__/performanceBudgets.unit.test.ts +1 -1
  404. package/src/utils/__tests__/permissionTypes.unit.test.ts +1 -1
  405. package/src/utils/__tests__/permissionUtils.unit.test.ts +1 -1
  406. package/src/utils/__tests__/sanitization.unit.test.ts +1 -1
  407. package/src/utils/__tests__/schemaUtils.unit.test.ts +1 -1
  408. package/src/utils/__tests__/secureDataAccess.unit.test.ts +1 -1
  409. package/src/utils/__tests__/secureErrors.unit.test.ts +33 -15
  410. package/src/utils/__tests__/secureStorage.unit.test.ts +1 -1
  411. package/src/utils/__tests__/security.unit.test.ts +40 -18
  412. package/src/utils/__tests__/securityMonitor.unit.test.ts +1 -1
  413. package/src/utils/__tests__/sessionTracking.unit.test.ts +40 -29
  414. package/src/utils/__tests__/validationUtils.unit.test.ts +19 -6
  415. package/src/utils/{appIdResolver.test.ts → app/appIdResolver.test.ts} +28 -30
  416. package/src/utils/{appIdResolver.ts → app/appIdResolver.ts} +8 -5
  417. package/src/utils/{appNameResolver.test.ts → app/appNameResolver.test.ts} +1 -1
  418. package/src/utils/{appNameResolver.ts → app/appNameResolver.ts} +5 -1
  419. package/src/utils/{organisationContext.ts → context/organisationContext.ts} +6 -3
  420. package/src/utils/{sessionTracking.ts → context/sessionTracking.ts} +11 -12
  421. package/src/utils/{logger.ts → core/logger.ts} +4 -2
  422. package/src/utils/{deviceFingerprint.ts → device/deviceFingerprint.ts} +1 -1
  423. package/src/utils/{lazyLoad.tsx → dynamic/lazyLoad.tsx} +2 -2
  424. package/src/utils/{file-reference.test.ts → file-reference/__tests__/file-reference.test.ts} +5 -5
  425. package/src/utils/{file-reference.ts → file-reference/index.ts} +20 -38
  426. package/src/utils/index.ts +32 -54
  427. package/src/utils/{secureErrors.ts → security/secureErrors.ts} +6 -3
  428. package/src/utils/{security.ts → security/security.ts} +5 -2
  429. package/src/utils/storage/__tests__/helpers.unit.test.ts +1 -4
  430. package/src/utils/storage/helpers.ts +15 -8
  431. package/src/{components/Dialog/utils/__tests__/safeHtml.unit.test.ts → utils/validation/__tests__/htmlSanitization.unit.test.ts} +9 -15
  432. package/src/{validation → utils/validation}/csrf.ts +1 -1
  433. package/src/{components/Dialog/utils/safeHtml.ts → utils/validation/htmlSanitization.ts} +9 -10
  434. package/src/utils/validation/index.ts +79 -0
  435. package/src/utils/{sanitization.ts → validation/sanitization.ts} +71 -2
  436. package/src/{validation/schemaUtils.ts → utils/validation/schema.ts} +11 -6
  437. package/src/{validation → utils/validation}/sqlInjectionProtection.ts +2 -0
  438. package/src/utils/{validationUtils.ts → validation/validationUtils.ts} +4 -1
  439. package/src/validation/index.ts +3 -34
  440. package/dist/UnifiedAuthProvider-CQDZRJIS.js +0 -16
  441. package/dist/chunk-24MKLB7U.js +0 -81
  442. package/dist/chunk-24MKLB7U.js.map +0 -1
  443. package/dist/chunk-3CG5L6RN.js.map +0 -1
  444. package/dist/chunk-3DBFLLLU.js.map +0 -1
  445. package/dist/chunk-5F3NDPJV.js.map +0 -1
  446. package/dist/chunk-66C4BSAY.js.map +0 -1
  447. package/dist/chunk-BDZUMRBD.js.map +0 -1
  448. package/dist/chunk-BYXRHAIF.js.map +0 -1
  449. package/dist/chunk-CDQ3PX7L.js +0 -18
  450. package/dist/chunk-CDQ3PX7L.js.map +0 -1
  451. package/dist/chunk-CQZU6TFE.js.map +0 -1
  452. package/dist/chunk-F64FFPOZ.js.map +0 -1
  453. package/dist/chunk-GEVIB2UB.js.map +0 -1
  454. package/dist/chunk-GKHF54DI.js.map +0 -1
  455. package/dist/chunk-GVDR7WNV.js.map +0 -1
  456. package/dist/chunk-HMNOSGVA.js.map +0 -1
  457. package/dist/chunk-JCQZ6LA7.js.map +0 -1
  458. package/dist/chunk-M6DDYFUD.js.map +0 -1
  459. package/dist/chunk-O3NWNXDY.js.map +0 -1
  460. package/dist/chunk-PYUXFQJ3.js.map +0 -1
  461. package/dist/chunk-UJI6WSMD.js.map +0 -1
  462. package/dist/chunk-VZ5OR6HD.js.map +0 -1
  463. package/dist/chunk-WP5I5GLN.js.map +0 -1
  464. package/dist/chunk-ZYZCRSBD.js.map +0 -1
  465. package/src/components/Dialog/README.md +0 -804
  466. package/src/components/Form/FormErrorSummary.tsx +0 -113
  467. package/src/components/Form/FormFieldset.tsx +0 -127
  468. package/src/components/Form/FormLiveRegion.tsx +0 -198
  469. package/src/components/PasswordReset/PasswordResetForm.test.tsx +0 -597
  470. package/src/components/PasswordReset/PasswordResetForm.tsx +0 -201
  471. package/src/components/PublicLayout/PublicPageDebugger.tsx +0 -104
  472. package/src/components/PublicLayout/PublicPageDiagnostic.tsx +0 -162
  473. package/src/components/PublicLayout/__tests__/PublicPageDebugger.test.tsx +0 -185
  474. package/src/examples/CorrectPublicPageImplementation.tsx +0 -304
  475. package/src/examples/PublicEventPage.tsx +0 -287
  476. package/src/examples/PublicPageApp.tsx +0 -321
  477. package/src/examples/PublicPageUsageExample.tsx +0 -218
  478. package/src/utils/schemaUtils.ts +0 -37
  479. package/src/validation/__tests__/common.unit.test.ts +0 -101
  480. package/src/validation/__tests__/csrf.unit.test.ts +0 -365
  481. package/src/validation/__tests__/passwordSchema.unit.test.ts +0 -203
  482. package/src/validation/__tests__/sanitization.unit.test.ts +0 -250
  483. package/src/validation/__tests__/schemaUtils.unit.test.ts +0 -451
  484. package/src/validation/__tests__/sqlInjectionProtection.unit.test.ts +0 -462
  485. package/src/validation/__tests__/user.unit.test.ts +0 -440
  486. package/src/validation/sanitization.ts +0 -96
  487. /package/dist/{DataTable-A36PJG6N.js.map → DataTable-CYOHOX3O.js.map} +0 -0
  488. /package/dist/{UnifiedAuthProvider-CQDZRJIS.js.map → UnifiedAuthProvider-5E5TUNMS.js.map} +0 -0
  489. /package/dist/{api-TNIBJWLM.js.map → api-45XYYO2A.js.map} +0 -0
  490. /package/dist/{audit-T36HM7IM.js.map → audit-64X3VJXB.js.map} +0 -0
  491. /package/dist/{chunk-CTJRBUX2.js.map → chunk-2TWNJ46Y.js.map} +0 -0
  492. /package/dist/{chunk-ZV77RZMU.js.map → chunk-XARJS7CD.js.map} +0 -0
  493. /package/dist/{useInactivityTracker-MRUU55XI.js.map → useInactivityTracker-TO6ZOF35.js.map} +0 -0
  494. /package/examples/{public-pages → features/public-pages}/index.ts +0 -0
  495. /package/examples/{RBAC → features/rbac}/CompleteRBACExample.tsx +0 -0
  496. /package/examples/{RBAC → features/rbac}/EventBasedApp.tsx +0 -0
  497. /package/examples/{RBAC → features/rbac}/PermissionExample.tsx +0 -0
  498. /package/examples/{RBAC → features/rbac}/index.ts +0 -0
  499. /package/src/utils/{appConfig.ts → app/appConfig.ts} +0 -0
  500. /package/src/utils/{appNameResolver.simple.test.ts → app/appNameResolver.simple.test.ts} +0 -0
  501. /package/src/utils/{audit.ts → audit/audit.ts} +0 -0
  502. /package/src/utils/{organisationContext.test.ts → context/organisationContext.test.ts} +0 -0
  503. /package/src/utils/{cn.ts → core/cn.ts} +0 -0
  504. /package/src/utils/{debugLogger.ts → core/debugLogger.ts} +0 -0
  505. /package/src/utils/{dynamicUtils.ts → dynamic/dynamicUtils.ts} +0 -0
  506. /package/src/utils/{formatDate.test.ts → formatting/formatDate.test.ts} +0 -0
  507. /package/src/utils/{formatting.ts → formatting/formatting.ts} +0 -0
  508. /package/src/utils/{bundleAnalysis.ts → performance/bundleAnalysis.ts} +0 -0
  509. /package/src/utils/{performanceBenchmark.ts → performance/performanceBenchmark.ts} +0 -0
  510. /package/src/utils/{performanceBudgets.ts → performance/performanceBudgets.ts} +0 -0
  511. /package/src/utils/{permissionTypes.ts → permissions/permissionTypes.ts} +0 -0
  512. /package/src/utils/{permissionUtils.test.ts → permissions/permissionUtils.test.ts} +0 -0
  513. /package/src/utils/{permissionUtils.ts → permissions/permissionUtils.ts} +0 -0
  514. /package/src/utils/{auth-utils.ts → security/auth-utils.ts} +0 -0
  515. /package/src/utils/{secureDataAccess.test.ts → security/secureDataAccess.test.ts} +0 -0
  516. /package/src/utils/{secureDataAccess.ts → security/secureDataAccess.ts} +0 -0
  517. /package/src/utils/{secureStorage.ts → security/secureStorage.ts} +0 -0
  518. /package/src/utils/{securityMonitor.ts → security/securityMonitor.ts} +0 -0
  519. /package/src/{validation → utils/validation}/common.ts +0 -0
  520. /package/src/{validation → utils/validation}/passwordSchema.ts +0 -0
  521. /package/src/{validation → utils/validation}/user.ts +0 -0
  522. /package/src/utils/{validation.ts → validation/validation.ts} +0 -0
@@ -1,597 +0,0 @@
1
- /**
2
- * @file PasswordResetForm Component Tests
3
- * @package @jmruthers/pace-core
4
- * @module Components/PasswordReset
5
- * @since 0.1.0
6
- *
7
- * Comprehensive test suite for the PasswordResetForm component.
8
- * Tests cover rendering, form submission, loading states, error handling,
9
- * success states, and accessibility compliance.
10
- */
11
-
12
- import React from 'react';
13
- import { render, screen, fireEvent, waitFor } from '@testing-library/react';
14
- import userEvent from '@testing-library/user-event';
15
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
16
- import { PasswordResetForm, PasswordResetFormProps } from './PasswordResetForm';
17
-
18
- // Mock the UnifiedAuthProvider
19
- const mockResetPassword = vi.fn();
20
- vi.mock('../../providers/UnifiedAuthProvider', () => ({
21
- useUnifiedAuth: () => ({
22
- resetPassword: mockResetPassword
23
- })
24
- }));
25
-
26
- // Mock child components
27
- vi.mock('../Button/Button', () => ({
28
- Button: ({ children, onClick, disabled, className, type, variant, ...props }: any) => (
29
- <button
30
- onClick={onClick}
31
- disabled={disabled}
32
- className={className}
33
- type={type}
34
- data-variant={variant}
35
- {...props}
36
- >
37
- {children}
38
- </button>
39
- )
40
- }));
41
-
42
- vi.mock('../Input/Input', () => ({
43
- Input: ({ value, onChange, placeholder, disabled, required, type, id, ...props }: any) => (
44
- <input
45
- id={id}
46
- type={type}
47
- value={value}
48
- onChange={onChange}
49
- placeholder={placeholder}
50
- disabled={disabled}
51
- required={required}
52
- {...props}
53
- />
54
- )
55
- }));
56
-
57
- vi.mock('../Label', () => ({
58
- Label: ({ children, htmlFor, ...props }: any) => (
59
- <label htmlFor={htmlFor} {...props}>
60
- {children}
61
- </label>
62
- )
63
- }));
64
-
65
- describe('PasswordResetForm', () => {
66
- const defaultProps: PasswordResetFormProps = {};
67
-
68
- describe('Rendering', () => {
69
- it('renders password reset form with all elements', () => {
70
- render(<PasswordResetForm {...defaultProps} />);
71
-
72
- expect(screen.getByRole('form')).toBeInTheDocument();
73
- expect(screen.getByText('Reset Password')).toBeInTheDocument();
74
- expect(screen.getByText("Enter your email address and we'll send you a reset link.")).toBeInTheDocument();
75
- expect(screen.getByLabelText('Email Address')).toBeInTheDocument();
76
- expect(screen.getByPlaceholderText('Enter your email')).toBeInTheDocument();
77
- expect(screen.getByRole('button', { name: 'Send Reset Link' })).toBeInTheDocument();
78
- });
79
-
80
- it('renders with custom className', () => {
81
- const customClass = 'custom-password-reset-form';
82
- render(<PasswordResetForm {...defaultProps} className={customClass} />);
83
-
84
- const form = screen.getByRole('form');
85
- expect(form).toHaveClass(customClass);
86
- });
87
-
88
- it('has proper form structure and accessibility', () => {
89
- render(<PasswordResetForm {...defaultProps} />);
90
-
91
- const emailInput = screen.getByLabelText('Email Address');
92
- expect(emailInput).toHaveAttribute('type', 'email');
93
- expect(emailInput).toHaveAttribute('required');
94
- expect(emailInput).toHaveAttribute('id', 'email');
95
-
96
- const submitButton = screen.getByRole('button', { name: 'Send Reset Link' });
97
- expect(submitButton).toHaveAttribute('type', 'submit');
98
- });
99
- });
100
-
101
- describe('Form Interaction', () => {
102
- it('updates email input value when user types', async () => {
103
- const user = userEvent.setup();
104
- render(<PasswordResetForm {...defaultProps} />);
105
-
106
- const emailInput = screen.getByLabelText('Email Address');
107
- await user.type(emailInput, 'test@example.com');
108
-
109
- expect(emailInput).toHaveValue('test@example.com');
110
- });
111
-
112
- it('enables submit button when email is provided', async () => {
113
- const user = userEvent.setup();
114
- render(<PasswordResetForm {...defaultProps} />);
115
-
116
- const emailInput = screen.getByLabelText('Email Address');
117
- const submitButton = screen.getByRole('button', { name: 'Send Reset Link' });
118
-
119
- expect(submitButton).toBeDisabled();
120
-
121
- await user.type(emailInput, 'test@example.com');
122
-
123
- expect(submitButton).toBeEnabled();
124
- });
125
-
126
- it('disables submit button when email is empty', () => {
127
- render(<PasswordResetForm {...defaultProps} />);
128
-
129
- const submitButton = screen.getByRole('button', { name: 'Send Reset Link' });
130
- expect(submitButton).toBeDisabled();
131
- });
132
-
133
- it('disables submit button when email is only whitespace', async () => {
134
- const user = userEvent.setup();
135
- render(<PasswordResetForm {...defaultProps} />);
136
-
137
- const emailInput = screen.getByLabelText('Email Address');
138
- const submitButton = screen.getByRole('button', { name: 'Send Reset Link' });
139
-
140
- await user.type(emailInput, ' ');
141
-
142
- expect(submitButton).toBeDisabled();
143
- });
144
- });
145
-
146
- describe('Form Submission', () => {
147
- it('calls resetPassword with email when form is submitted', async () => {
148
- const user = userEvent.setup();
149
- mockResetPassword.mockResolvedValue({ error: null });
150
-
151
- render(<PasswordResetForm {...defaultProps} />);
152
-
153
- const emailInput = screen.getByLabelText('Email Address');
154
- const submitButton = screen.getByRole('button', { name: 'Send Reset Link' });
155
-
156
- await user.type(emailInput, 'test@example.com');
157
- await user.click(submitButton);
158
-
159
- expect(mockResetPassword).toHaveBeenCalledWith('test@example.com');
160
- });
161
-
162
- it('prevents form submission when email is empty', async () => {
163
- const user = userEvent.setup();
164
- render(<PasswordResetForm {...defaultProps} />);
165
-
166
- const form = screen.getByRole('form');
167
- const submitButton = screen.getByRole('button', { name: 'Send Reset Link' });
168
-
169
- expect(submitButton).toBeDisabled();
170
- await user.click(submitButton);
171
-
172
- expect(mockResetPassword).not.toHaveBeenCalled();
173
- });
174
-
175
- it('prevents form submission when email is only whitespace', async () => {
176
- const user = userEvent.setup();
177
- render(<PasswordResetForm {...defaultProps} />);
178
-
179
- const emailInput = screen.getByLabelText('Email Address');
180
- const submitButton = screen.getByRole('button', { name: 'Send Reset Link' });
181
-
182
- await user.type(emailInput, ' ');
183
- await user.click(submitButton);
184
-
185
- expect(mockResetPassword).not.toHaveBeenCalled();
186
- });
187
- });
188
-
189
- describe('Loading States', () => {
190
- it('shows loading state during form submission', async () => {
191
- const user = userEvent.setup();
192
- mockResetPassword.mockImplementation(() => new Promise(resolve => setTimeout(resolve, 100)));
193
-
194
- render(<PasswordResetForm {...defaultProps} />);
195
-
196
- const emailInput = screen.getByLabelText('Email Address');
197
- const submitButton = screen.getByRole('button', { name: 'Send Reset Link' });
198
-
199
- await user.type(emailInput, 'test@example.com');
200
- await user.click(submitButton);
201
-
202
- expect(screen.getByRole('button', { name: 'Sending...' })).toBeInTheDocument();
203
- expect(screen.getByRole('button', { name: 'Sending...' })).toBeDisabled();
204
- });
205
-
206
- it('disables email input during loading', async () => {
207
- const user = userEvent.setup();
208
- mockResetPassword.mockImplementation(() => new Promise(resolve => setTimeout(resolve, 100)));
209
-
210
- render(<PasswordResetForm {...defaultProps} />);
211
-
212
- const emailInput = screen.getByLabelText('Email Address');
213
- const submitButton = screen.getByRole('button', { name: 'Send Reset Link' });
214
-
215
- await user.type(emailInput, 'test@example.com');
216
- await user.click(submitButton);
217
-
218
- expect(emailInput).toBeDisabled();
219
- });
220
-
221
- it('resets loading state after successful submission', async () => {
222
- const user = userEvent.setup();
223
- mockResetPassword.mockResolvedValue({ error: null });
224
-
225
- render(<PasswordResetForm {...defaultProps} />);
226
-
227
- const emailInput = screen.getByLabelText('Email Address');
228
- const submitButton = screen.getByRole('button', { name: 'Send Reset Link' });
229
-
230
- await user.type(emailInput, 'test@example.com');
231
- await user.click(submitButton);
232
-
233
- await waitFor(() => {
234
- expect(screen.getByText('Check your email')).toBeInTheDocument();
235
- });
236
- });
237
-
238
- it('resets loading state after failed submission', async () => {
239
- const user = userEvent.setup();
240
- mockResetPassword.mockResolvedValue({ error: { message: 'Network error' } });
241
-
242
- render(<PasswordResetForm {...defaultProps} />);
243
-
244
- const emailInput = screen.getByLabelText('Email Address');
245
- const submitButton = screen.getByRole('button', { name: 'Send Reset Link' });
246
-
247
- await user.type(emailInput, 'test@example.com');
248
- await user.click(submitButton);
249
-
250
- await waitFor(() => {
251
- expect(screen.getByRole('button', { name: 'Send Reset Link' })).toBeInTheDocument();
252
- expect(screen.getByRole('button', { name: 'Send Reset Link' })).toBeEnabled();
253
- });
254
- });
255
- });
256
-
257
- describe('Success State', () => {
258
- it('shows success message after successful password reset', async () => {
259
- const user = userEvent.setup();
260
- mockResetPassword.mockResolvedValue({ error: null });
261
-
262
- render(<PasswordResetForm {...defaultProps} />);
263
-
264
- const emailInput = screen.getByLabelText('Email Address');
265
- const submitButton = screen.getByRole('button', { name: 'Send Reset Link' });
266
-
267
- await user.type(emailInput, 'test@example.com');
268
- await user.click(submitButton);
269
-
270
- await waitFor(() => {
271
- expect(screen.getByText('Check your email')).toBeInTheDocument();
272
- expect(screen.getByText('We have sent a password reset link to test@example.com')).toBeInTheDocument();
273
- expect(screen.getByRole('button', { name: 'Send another email' })).toBeInTheDocument();
274
- });
275
- });
276
-
277
- it('calls onSuccess callback when reset is successful', async () => {
278
- const user = userEvent.setup();
279
- const onSuccess = vi.fn();
280
- mockResetPassword.mockResolvedValue({ error: null });
281
-
282
- render(<PasswordResetForm {...defaultProps} onSuccess={onSuccess} />);
283
-
284
- const emailInput = screen.getByLabelText('Email Address');
285
- const submitButton = screen.getByRole('button', { name: 'Send Reset Link' });
286
-
287
- await user.type(emailInput, 'test@example.com');
288
- await user.click(submitButton);
289
-
290
- await waitFor(() => {
291
- expect(onSuccess).toHaveBeenCalledTimes(1);
292
- });
293
- });
294
-
295
- it('allows sending another email from success state', async () => {
296
- const user = userEvent.setup();
297
- mockResetPassword.mockResolvedValue({ error: null });
298
-
299
- render(<PasswordResetForm {...defaultProps} />);
300
-
301
- const emailInput = screen.getByLabelText('Email Address');
302
- const submitButton = screen.getByRole('button', { name: 'Send Reset Link' });
303
-
304
- await user.type(emailInput, 'test@example.com');
305
- await user.click(submitButton);
306
-
307
- await waitFor(() => {
308
- expect(screen.getByText('Check your email')).toBeInTheDocument();
309
- });
310
-
311
- const sendAnotherButton = screen.getByRole('button', { name: 'Send another email' });
312
- await user.click(sendAnotherButton);
313
-
314
- expect(screen.getByText('Reset Password')).toBeInTheDocument();
315
- expect(screen.getByLabelText('Email Address')).toBeInTheDocument();
316
- });
317
-
318
- it('resets form state when sending another email', async () => {
319
- const user = userEvent.setup();
320
- mockResetPassword.mockResolvedValue({ error: null });
321
-
322
- render(<PasswordResetForm {...defaultProps} />);
323
-
324
- const emailInput = screen.getByLabelText('Email Address');
325
- const submitButton = screen.getByRole('button', { name: 'Send Reset Link' });
326
-
327
- await user.type(emailInput, 'test@example.com');
328
- await user.click(submitButton);
329
-
330
- await waitFor(() => {
331
- expect(screen.getByText('Check your email')).toBeInTheDocument();
332
- });
333
-
334
- const sendAnotherButton = screen.getByRole('button', { name: 'Send another email' });
335
- await user.click(sendAnotherButton);
336
-
337
- // The form should be reset and show the original form
338
- expect(screen.getByText('Reset Password')).toBeInTheDocument();
339
- expect(screen.getByLabelText('Email Address')).toBeInTheDocument();
340
- });
341
- });
342
-
343
- describe('Error Handling', () => {
344
- it('displays error message when resetPassword returns error', async () => {
345
- const user = userEvent.setup();
346
- const errorMessage = 'Email not found';
347
- mockResetPassword.mockResolvedValue({ error: { message: errorMessage } });
348
-
349
- render(<PasswordResetForm {...defaultProps} />);
350
-
351
- const emailInput = screen.getByLabelText('Email Address');
352
- const submitButton = screen.getByRole('button', { name: 'Send Reset Link' });
353
-
354
- await user.type(emailInput, 'test@example.com');
355
- await user.click(submitButton);
356
-
357
- await waitFor(() => {
358
- expect(screen.getByText(errorMessage)).toBeInTheDocument();
359
- expect(screen.getByText(errorMessage)).toHaveAttribute('role', 'alert');
360
- });
361
- });
362
-
363
- it('calls onError callback when resetPassword returns error', async () => {
364
- const user = userEvent.setup();
365
- const onError = vi.fn();
366
- const errorMessage = 'Email not found';
367
- mockResetPassword.mockResolvedValue({ error: { message: errorMessage } });
368
-
369
- render(<PasswordResetForm {...defaultProps} onError={onError} />);
370
-
371
- const emailInput = screen.getByLabelText('Email Address');
372
- const submitButton = screen.getByRole('button', { name: 'Send Reset Link' });
373
-
374
- await user.type(emailInput, 'test@example.com');
375
- await user.click(submitButton);
376
-
377
- await waitFor(() => {
378
- expect(onError).toHaveBeenCalledWith(expect.objectContaining({
379
- message: errorMessage
380
- }));
381
- });
382
- });
383
-
384
- it('displays error message when resetPassword throws exception', async () => {
385
- const user = userEvent.setup();
386
- const errorMessage = 'Network error';
387
- mockResetPassword.mockRejectedValue(new Error(errorMessage));
388
-
389
- render(<PasswordResetForm {...defaultProps} />);
390
-
391
- const emailInput = screen.getByLabelText('Email Address');
392
- const submitButton = screen.getByRole('button', { name: 'Send Reset Link' });
393
-
394
- await user.type(emailInput, 'test@example.com');
395
- await user.click(submitButton);
396
-
397
- await waitFor(() => {
398
- expect(screen.getByText(errorMessage)).toBeInTheDocument();
399
- expect(screen.getByText(errorMessage)).toHaveAttribute('role', 'alert');
400
- });
401
- });
402
-
403
- it('calls onError callback when resetPassword throws exception', async () => {
404
- const user = userEvent.setup();
405
- const onError = vi.fn();
406
- const errorMessage = 'Network error';
407
- mockResetPassword.mockRejectedValue(new Error(errorMessage));
408
-
409
- render(<PasswordResetForm {...defaultProps} onError={onError} />);
410
-
411
- const emailInput = screen.getByLabelText('Email Address');
412
- const submitButton = screen.getByRole('button', { name: 'Send Reset Link' });
413
-
414
- await user.type(emailInput, 'test@example.com');
415
- await user.click(submitButton);
416
-
417
- await waitFor(() => {
418
- expect(onError).toHaveBeenCalledWith(expect.objectContaining({
419
- message: errorMessage
420
- }));
421
- });
422
- });
423
-
424
- it('handles non-Error exceptions gracefully', async () => {
425
- const user = userEvent.setup();
426
- mockResetPassword.mockRejectedValue('String error');
427
-
428
- render(<PasswordResetForm {...defaultProps} />);
429
-
430
- const emailInput = screen.getByLabelText('Email Address');
431
- const submitButton = screen.getByRole('button', { name: 'Send Reset Link' });
432
-
433
- await user.type(emailInput, 'test@example.com');
434
- await user.click(submitButton);
435
-
436
- await waitFor(() => {
437
- expect(screen.getByText('An unexpected error occurred')).toBeInTheDocument();
438
- });
439
- });
440
-
441
- it('clears error message when form is resubmitted', async () => {
442
- const user = userEvent.setup();
443
- mockResetPassword
444
- .mockResolvedValueOnce({ error: { message: 'First error' } })
445
- .mockResolvedValueOnce({ error: null });
446
-
447
- render(<PasswordResetForm {...defaultProps} />);
448
-
449
- const emailInput = screen.getByLabelText('Email Address');
450
- const submitButton = screen.getByRole('button', { name: 'Send Reset Link' });
451
-
452
- await user.type(emailInput, 'test@example.com');
453
- await user.click(submitButton);
454
-
455
- await waitFor(() => {
456
- expect(screen.getByText('First error')).toBeInTheDocument();
457
- });
458
-
459
- await user.click(submitButton);
460
-
461
- await waitFor(() => {
462
- expect(screen.queryByText('First error')).not.toBeInTheDocument();
463
- expect(screen.getByText('Check your email')).toBeInTheDocument();
464
- });
465
- });
466
- });
467
-
468
- describe('Accessibility', () => {
469
- it('has proper form labels and associations', () => {
470
- render(<PasswordResetForm {...defaultProps} />);
471
-
472
- const emailInput = screen.getByLabelText('Email Address');
473
- expect(emailInput).toHaveAttribute('id', 'email');
474
- expect(emailInput).toHaveAttribute('type', 'email');
475
- });
476
-
477
- it('announces errors to screen readers', async () => {
478
- const user = userEvent.setup();
479
- mockResetPassword.mockResolvedValue({ error: { message: 'Test error' } });
480
-
481
- render(<PasswordResetForm {...defaultProps} />);
482
-
483
- const emailInput = screen.getByLabelText('Email Address');
484
- const submitButton = screen.getByRole('button', { name: 'Send Reset Link' });
485
-
486
- await user.type(emailInput, 'test@example.com');
487
- await user.click(submitButton);
488
-
489
- await waitFor(() => {
490
- const errorElement = screen.getByText('Test error');
491
- expect(errorElement).toHaveAttribute('role', 'alert');
492
- });
493
- });
494
-
495
- it('maintains focus management during state changes', async () => {
496
- const user = userEvent.setup();
497
- mockResetPassword.mockResolvedValue({ error: null });
498
-
499
- render(<PasswordResetForm {...defaultProps} />);
500
-
501
- const emailInput = screen.getByLabelText('Email Address');
502
- const submitButton = screen.getByRole('button', { name: 'Send Reset Link' });
503
-
504
- await user.type(emailInput, 'test@example.com');
505
- await user.click(submitButton);
506
-
507
- await waitFor(() => {
508
- expect(screen.getByText('Check your email')).toBeInTheDocument();
509
- });
510
-
511
- const sendAnotherButton = screen.getByRole('button', { name: 'Send another email' });
512
- expect(sendAnotherButton).toBeInTheDocument();
513
- });
514
- });
515
-
516
- describe('Edge Cases', () => {
517
- it('handles empty onSuccess callback gracefully', async () => {
518
- const user = userEvent.setup();
519
- mockResetPassword.mockResolvedValue({ error: null });
520
-
521
- render(<PasswordResetForm {...defaultProps} onSuccess={undefined} />);
522
-
523
- const emailInput = screen.getByLabelText('Email Address');
524
- const submitButton = screen.getByRole('button', { name: 'Send Reset Link' });
525
-
526
- await user.type(emailInput, 'test@example.com');
527
- await user.click(submitButton);
528
-
529
- await waitFor(() => {
530
- expect(screen.getByText('Check your email')).toBeInTheDocument();
531
- });
532
- });
533
-
534
- it('handles empty onError callback gracefully', async () => {
535
- const user = userEvent.setup();
536
- mockResetPassword.mockResolvedValue({ error: { message: 'Test error' } });
537
-
538
- render(<PasswordResetForm {...defaultProps} onError={undefined} />);
539
-
540
- const emailInput = screen.getByLabelText('Email Address');
541
- const submitButton = screen.getByRole('button', { name: 'Send Reset Link' });
542
-
543
- await user.type(emailInput, 'test@example.com');
544
- await user.click(submitButton);
545
-
546
- await waitFor(() => {
547
- expect(screen.getByText('Test error')).toBeInTheDocument();
548
- });
549
- });
550
-
551
- it('handles very long email addresses', async () => {
552
- const user = userEvent.setup();
553
- const longEmail = 'a'.repeat(100) + '@example.com';
554
- mockResetPassword.mockResolvedValue({ error: null });
555
-
556
- render(<PasswordResetForm {...defaultProps} />);
557
-
558
- const emailInput = screen.getByLabelText('Email Address');
559
- const submitButton = screen.getByRole('button', { name: 'Send Reset Link' });
560
-
561
- await user.type(emailInput, longEmail);
562
- await user.click(submitButton);
563
-
564
- expect(mockResetPassword).toHaveBeenCalledWith(longEmail);
565
- });
566
-
567
- it('handles special characters in email', async () => {
568
- const user = userEvent.setup();
569
- const specialEmail = 'test+tag@example.co.uk';
570
- mockResetPassword.mockResolvedValue({ error: null });
571
-
572
- render(<PasswordResetForm {...defaultProps} />);
573
-
574
- const emailInput = screen.getByLabelText('Email Address');
575
- const submitButton = screen.getByRole('button', { name: 'Send Reset Link' });
576
-
577
- await user.type(emailInput, specialEmail);
578
- await user.click(submitButton);
579
-
580
- expect(mockResetPassword).toHaveBeenCalledWith(specialEmail);
581
- });
582
- });
583
-
584
- describe('Performance', () => {
585
- it('handles rapid input changes efficiently', async () => {
586
- const user = userEvent.setup();
587
- render(<PasswordResetForm {...defaultProps} />);
588
-
589
- const emailInput = screen.getByLabelText('Email Address');
590
-
591
- // Type rapidly to test performance
592
- await user.type(emailInput, 'test@example.com');
593
-
594
- expect(emailInput).toHaveValue('test@example.com');
595
- });
596
- });
597
- });