@jmruthers/pace-core 0.5.135 → 0.5.137

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 (544) hide show
  1. package/dist/{DataTable-A36PJG6N.js → DataTable-6M4L6BI2.js} +26 -13
  2. package/dist/{DataTable-C7GaRZye.d.ts → DataTable-CWAZZcXC.d.ts} +1 -1
  3. package/dist/{PublicLoadingSpinner-CUAnTvcg.d.ts → EventLogo-rFL_kRjk.d.ts} +123 -135
  4. package/dist/{UnifiedAuthProvider-BVKmQd9u.d.ts → UnifiedAuthProvider-DJxGTftH.d.ts} +1 -1
  5. package/dist/{UnifiedAuthProvider-CQDZRJIS.js → UnifiedAuthProvider-XIQQ7LVU.js} +5 -5
  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-F64FFPOZ.js → chunk-22WKWKRX.js} +26 -20
  9. package/dist/chunk-22WKWKRX.js.map +1 -0
  10. package/dist/{chunk-VZ5OR6HD.js → chunk-4C7EXCAR.js} +62 -150
  11. package/dist/chunk-4C7EXCAR.js.map +1 -0
  12. package/dist/{chunk-PYUXFQJ3.js → chunk-56XJ3TU6.js} +2 -2
  13. package/dist/chunk-56XJ3TU6.js.map +1 -0
  14. package/dist/{chunk-CTJRBUX2.js → chunk-6LAAY47Q.js} +2 -2
  15. package/dist/{chunk-UJI6WSMD.js → chunk-7QCC6MCP.js} +90 -3
  16. package/dist/chunk-7QCC6MCP.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-CQZU6TFE.js → chunk-BCIBECNB.js} +100 -62
  20. package/dist/chunk-BCIBECNB.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-BJPBT3CU.js +21 -0
  24. package/dist/chunk-BJPBT3CU.js.map +1 -0
  25. package/dist/{chunk-BYXRHAIF.js → chunk-BLCXZEYF.js} +23 -14
  26. package/dist/chunk-BLCXZEYF.js.map +1 -0
  27. package/dist/{chunk-WP5I5GLN.js → chunk-BVYWGZVV.js} +112 -97
  28. package/dist/chunk-BVYWGZVV.js.map +1 -0
  29. package/dist/{chunk-GEVIB2UB.js → chunk-ERISIBYU.js} +14 -5
  30. package/dist/chunk-ERISIBYU.js.map +1 -0
  31. package/dist/{chunk-O3NWNXDY.js → chunk-FMUCXFII.js} +2 -2
  32. package/dist/chunk-FMUCXFII.js.map +1 -0
  33. package/dist/{chunk-GVDR7WNV.js → chunk-HAWZXGR2.js} +334 -614
  34. package/dist/chunk-HAWZXGR2.js.map +1 -0
  35. package/dist/{chunk-ZV77RZMU.js → chunk-INQLMHPF.js} +2 -2
  36. package/dist/chunk-JISYG63F.js +70 -0
  37. package/dist/chunk-JISYG63F.js.map +1 -0
  38. package/dist/{chunk-HMNOSGVA.js → chunk-KYRHUBIU.js} +576 -767
  39. package/dist/chunk-KYRHUBIU.js.map +1 -0
  40. package/dist/{chunk-M6DDYFUD.js → chunk-LS353YLY.js} +19 -16
  41. package/dist/chunk-LS353YLY.js.map +1 -0
  42. package/dist/{chunk-TGIY2AR2.js → chunk-MA6EPSGZ.js} +4 -3
  43. package/dist/{chunk-TGIY2AR2.js.map → chunk-MA6EPSGZ.js.map} +1 -1
  44. package/dist/chunk-OWAG3GSU.js +58 -0
  45. package/dist/chunk-OWAG3GSU.js.map +1 -0
  46. package/dist/{chunk-JCQZ6LA7.js → chunk-Q5QRDWKI.js} +9 -3
  47. package/dist/chunk-Q5QRDWKI.js.map +1 -0
  48. package/dist/chunk-S5OFRT4M.js +94 -0
  49. package/dist/chunk-S5OFRT4M.js.map +1 -0
  50. package/dist/{chunk-3DBFLLLU.js → chunk-SBVILCCA.js} +14 -9
  51. package/dist/chunk-SBVILCCA.js.map +1 -0
  52. package/dist/{chunk-ZYZCRSBD.js → chunk-T6JN6LH6.js} +16 -11
  53. package/dist/chunk-T6JN6LH6.js.map +1 -0
  54. package/dist/chunk-XDNLUEXI.js +138 -0
  55. package/dist/chunk-XDNLUEXI.js.map +1 -0
  56. package/dist/{chunk-3CG5L6RN.js → chunk-YCWDTTUK.js} +90 -75
  57. package/dist/chunk-YCWDTTUK.js.map +1 -0
  58. package/dist/{chunk-5F3NDPJV.js → chunk-ZZ2SS7NI.js} +10 -5
  59. package/dist/chunk-ZZ2SS7NI.js.map +1 -0
  60. package/dist/components.d.ts +7 -287
  61. package/dist/components.js +27 -157
  62. package/dist/components.js.map +1 -1
  63. package/dist/{file-reference-C9isKNPn.d.ts → file-reference-C6Gkn77H.d.ts} +1 -1
  64. package/dist/{formatting-DFcCxUEk.d.ts → formatting-CvUXy2mF.d.ts} +1 -1
  65. package/dist/hooks.d.ts +3 -3
  66. package/dist/hooks.js +21 -16
  67. package/dist/hooks.js.map +1 -1
  68. package/dist/index.d.ts +101 -9
  69. package/dist/index.js +44 -31
  70. package/dist/index.js.map +1 -1
  71. package/dist/providers.d.ts +1 -1
  72. package/dist/providers.js +4 -4
  73. package/dist/rbac/index.js +12 -12
  74. package/dist/schema-DTDZQe2u.d.ts +28 -0
  75. package/dist/styles/index.js +2 -1
  76. package/dist/theming/runtime.d.ts +2 -19
  77. package/dist/theming/runtime.js +2 -1
  78. package/dist/{types-D5rqZQXk.d.ts → types-Dfz9dmVH.d.ts} +12 -1
  79. package/dist/types.d.ts +153 -4
  80. package/dist/types.js +51 -16
  81. package/dist/types.js.map +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 +221 -173
  85. package/dist/utils.js +185 -225
  86. package/dist/utils.js.map +1 -1
  87. package/dist/validation.d.ts +24 -115
  88. package/dist/validation.js +19 -474
  89. package/dist/validation.js.map +1 -1
  90. package/docs/api/classes/ColumnFactory.md +1 -1
  91. package/docs/api/classes/ErrorBoundary.md +6 -6
  92. package/docs/api/classes/InvalidScopeError.md +1 -1
  93. package/docs/api/classes/MissingUserContextError.md +1 -1
  94. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  95. package/docs/api/classes/PermissionDeniedError.md +1 -1
  96. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  97. package/docs/api/classes/RBACAuditManager.md +6 -6
  98. package/docs/api/classes/RBACCache.md +1 -1
  99. package/docs/api/classes/RBACEngine.md +7 -7
  100. package/docs/api/classes/RBACError.md +1 -1
  101. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  102. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  103. package/docs/api/classes/StorageUtils.md +1 -1
  104. package/docs/api/enums/FileCategory.md +1 -1
  105. package/docs/api/interfaces/AggregateConfig.md +4 -4
  106. package/docs/api/interfaces/BadgeProps.md +27 -0
  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 +1 -1
  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 +1 -1
  122. package/docs/api/interfaces/ExportOptions.md +8 -8
  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 +591 -220
  199. package/docs/api-reference/components.md +106 -26
  200. package/docs/architecture/README.md +0 -3
  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/docs/styles/README.md +0 -2
  205. package/examples/README.md +30 -14
  206. package/examples/STRUCTURE.md +125 -0
  207. package/examples/components 2/DataTable/HierarchicalActionsExample.tsx +421 -0
  208. package/examples/components 2/DataTable/HierarchicalExample.tsx +475 -0
  209. package/examples/components 2/DataTable/InitialPageSizeExample.tsx +177 -0
  210. package/examples/components 2/DataTable/PerformanceExample.tsx +506 -0
  211. package/examples/components 2/DataTable/index.ts +13 -0
  212. package/examples/components 2/Dialog/BasicHtmlTest.tsx +55 -0
  213. package/examples/components 2/Dialog/DebugHtmlExample.tsx +68 -0
  214. package/examples/components 2/Dialog/HtmlDialogExample.tsx +202 -0
  215. package/examples/components 2/Dialog/ScrollableDialogExample.tsx +290 -0
  216. package/examples/components 2/Dialog/SimpleHtmlTest.tsx +61 -0
  217. package/examples/components 2/Dialog/SmartDialogExample.tsx +322 -0
  218. package/examples/components 2/Dialog/index.ts +15 -0
  219. package/examples/components 2/index.ts +11 -0
  220. package/examples/features/index.ts +12 -0
  221. package/{src/examples → examples/features/public-pages}/CorrectPublicPageImplementation.tsx +14 -17
  222. package/{src/examples → examples/features/public-pages}/PublicEventPage.tsx +14 -27
  223. package/{src/examples → examples/features/public-pages}/PublicPageApp.tsx +15 -28
  224. package/{src/examples → examples/features/public-pages}/PublicPageUsageExample.tsx +8 -10
  225. package/examples/features/public-pages/index.ts +14 -0
  226. package/examples/features/rbac/CompleteRBACExample.tsx +324 -0
  227. package/examples/features/rbac/EventBasedApp.tsx +239 -0
  228. package/examples/features/rbac/PermissionExample.tsx +151 -0
  229. package/examples/features/rbac/index.ts +13 -0
  230. package/examples/index.ts +11 -3
  231. package/package.json +30 -19
  232. package/src/__tests__/TEST_STANDARD.md +92 -0
  233. package/src/components/Alert/Alert.tsx +1 -1
  234. package/src/components/Avatar/Avatar.tsx +1 -1
  235. package/src/components/Badge/Badge.test.tsx +314 -0
  236. package/src/components/Badge/Badge.tsx +304 -0
  237. package/src/components/Badge/index.ts +3 -0
  238. package/src/components/Button/Button.tsx +1 -1
  239. package/src/components/Card/Card.tsx +1 -1
  240. package/src/components/Checkbox/Checkbox.tsx +1 -1
  241. package/src/components/DataTable/DataTable.test.tsx +1 -1
  242. package/src/components/DataTable/DataTable.tsx +1 -30
  243. package/src/components/DataTable/__tests__/DataTable.grouping-aggregation.test.tsx +562 -0
  244. package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +217 -0
  245. package/src/components/DataTable/__tests__/styles.test.ts +3 -3
  246. package/src/components/DataTable/components/ActionButtons.tsx +0 -15
  247. package/src/components/DataTable/components/ColumnFilter.tsx +8 -4
  248. package/src/components/DataTable/components/DataTableBody.tsx +461 -0
  249. package/src/components/DataTable/components/DataTableCore.tsx +4 -185
  250. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +1 -1
  251. package/src/components/DataTable/components/DataTableModals.tsx +1 -27
  252. package/src/components/DataTable/components/DraggableColumnHeader.tsx +144 -0
  253. package/src/components/DataTable/components/EditableRow.tsx +1 -1
  254. package/src/components/DataTable/components/FilterRow.tsx +9 -3
  255. package/src/components/DataTable/components/ImportModal.tsx +2 -14
  256. package/src/components/DataTable/components/PaginationControls.tsx +2 -1
  257. package/src/components/DataTable/components/UnifiedTableBody.tsx +109 -82
  258. package/src/components/DataTable/components/VirtualizedDataTable.tsx +513 -0
  259. package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +14 -68
  260. package/src/components/DataTable/components/__tests__/ActionButtons.test.tsx +1 -1
  261. package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +62 -0
  262. package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +1 -1
  263. package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +1 -1
  264. package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +43 -0
  265. package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +1 -1
  266. package/src/components/DataTable/core/ActionManager.ts +235 -0
  267. package/src/components/DataTable/core/ColumnManager.ts +205 -0
  268. package/src/components/DataTable/core/DataManager.ts +188 -0
  269. package/src/components/DataTable/core/DataTableContext.tsx +181 -0
  270. package/src/components/DataTable/core/LocalDataAdapter.ts +273 -0
  271. package/src/components/DataTable/core/PluginRegistry.ts +229 -0
  272. package/src/components/DataTable/core/StateManager.ts +311 -0
  273. package/src/components/DataTable/core/interfaces.ts +338 -0
  274. package/src/components/DataTable/examples/GroupingAggregationExample.tsx +273 -0
  275. package/src/components/DataTable/examples/HierarchicalActionsExample.tsx +1 -1
  276. package/src/components/DataTable/examples/__tests__/HierarchicalActionsExample.test.tsx +1 -1
  277. package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +1 -1
  278. package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +1 -1
  279. package/src/components/DataTable/hooks/useDataTablePermissions.ts +2 -23
  280. package/src/components/DataTable/index.ts +4 -0
  281. package/src/components/DataTable/styles.ts +28 -7
  282. package/src/components/DataTable/types.ts +13 -0
  283. package/src/components/DataTable/utils/__tests__/columnUtils.test.ts +94 -0
  284. package/src/components/DataTable/utils/__tests__/exportUtils.test.ts +1 -1
  285. package/src/components/DataTable/utils/aggregationUtils.ts +161 -0
  286. package/src/components/DataTable/utils/columnUtils.ts +40 -0
  287. package/src/components/DataTable/utils/debugTools.ts +609 -0
  288. package/src/components/DataTable/utils/exportUtils.ts +1 -1
  289. package/src/components/DataTable/utils/flexibleImport.ts +1 -11
  290. package/src/components/DataTable/utils/index.ts +2 -0
  291. package/src/components/DataTable/utils/paginationUtils.ts +1 -1
  292. package/src/components/Dialog/Dialog.tsx +2 -2
  293. package/src/components/Dialog/utils/__tests__/safeHtml.unit.test.ts +8 -1
  294. package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +35 -7
  295. package/src/components/ErrorBoundary/ErrorBoundary.tsx +5 -4
  296. package/src/components/EventSelector/EventSelector.tsx +3 -2
  297. package/src/components/FileDisplay/FileDisplay.tsx +2 -36
  298. package/src/components/FileUpload/FileUpload.test.tsx +2 -2
  299. package/src/components/FileUpload/FileUpload.tsx +2 -2
  300. package/src/components/Footer/Footer.test.tsx +1 -1
  301. package/src/components/Footer/Footer.tsx +1 -1
  302. package/src/components/Form/Form.test.tsx +5 -510
  303. package/src/components/Form/Form.tsx +1 -1
  304. package/src/components/Form/FormField.tsx +1 -1
  305. package/src/components/Form/index.ts +0 -12
  306. package/src/components/Header/Header.tsx +1 -1
  307. package/src/components/Input/Input.tsx +1 -1
  308. package/src/components/Label/Label.tsx +1 -1
  309. package/src/components/LoginForm/LoginForm.test.tsx +1 -1
  310. package/src/components/LoginForm/LoginForm.tsx +1 -1
  311. package/src/components/NavigationMenu/NavigationMenu.test.tsx +19 -3
  312. package/src/components/NavigationMenu/NavigationMenu.tsx +9 -8
  313. package/src/components/OrganisationSelector/OrganisationSelector.tsx +4 -3
  314. package/src/components/PaceAppLayout/PaceAppLayout.tsx +14 -12
  315. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.integration.test.tsx +0 -16
  316. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.performance.test.tsx +76 -10
  317. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.security.test.tsx +0 -1
  318. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.unit.test.tsx +0 -9
  319. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +35 -3
  320. package/src/components/PaceLoginPage/PaceLoginPage.tsx +14 -13
  321. package/src/components/PasswordReset/PasswordChangeForm.tsx +1 -1
  322. package/src/components/PasswordReset/index.ts +0 -2
  323. package/src/components/Progress/Progress.tsx +1 -1
  324. package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +35 -8
  325. package/src/components/ProtectedRoute/ProtectedRoute.tsx +3 -2
  326. package/src/components/PublicLayout/PublicErrorBoundary.tsx +1 -1
  327. package/src/components/PublicLayout/PublicLoadingSpinner.tsx +1 -1
  328. package/src/components/PublicLayout/PublicPageContextChecker.tsx +44 -43
  329. package/src/components/PublicLayout/PublicPageFooter.tsx +1 -1
  330. package/src/components/PublicLayout/PublicPageHeader.tsx +1 -15
  331. package/src/components/PublicLayout/PublicPageProvider.tsx +3 -2
  332. package/src/components/PublicLayout/__tests__/PublicPageContextChecker.test.tsx +2 -0
  333. package/src/components/PublicLayout/__tests__/PublicPageFooter.test.tsx +1 -1
  334. package/src/components/PublicLayout/index.ts +4 -2
  335. package/src/components/Select/Select.test.tsx +1 -1
  336. package/src/components/Select/Select.tsx +21 -9
  337. package/src/components/{SessionRestorationLoader.tsx → SessionRestorationLoader/SessionRestorationLoader.tsx} +3 -2
  338. package/src/components/SessionRestorationLoader/index.ts +3 -0
  339. package/src/components/Switch/Switch.tsx +1 -1
  340. package/src/components/Table/Table.tsx +1 -1
  341. package/src/components/Table/__tests__/Table.test.tsx +1 -1
  342. package/src/components/Toast/Toast.tsx +1 -1
  343. package/src/components/Tooltip/Tooltip.tsx +1 -1
  344. package/src/components/index.ts +7 -10
  345. package/src/hooks/__tests__/hooks.integration.test.tsx +37 -22
  346. package/src/hooks/__tests__/useComponentPerformance.unit.test.tsx +33 -17
  347. package/src/hooks/__tests__/useDataTablePerformance.unit.test.ts +28 -3
  348. package/src/hooks/__tests__/useFileDisplay.unit.test.ts +36 -9
  349. package/src/hooks/__tests__/useFileUrl.unit.test.ts +83 -85
  350. package/src/hooks/__tests__/useInactivityTracker.unit.test.ts +26 -2
  351. package/src/hooks/__tests__/usePerformanceMonitor.unit.test.ts +19 -6
  352. package/src/hooks/__tests__/usePermissionCache.simple.test.ts +17 -4
  353. package/src/hooks/__tests__/usePermissionCache.unit.test.ts +17 -4
  354. package/src/hooks/__tests__/usePublicEvent.simple.test.ts +26 -6
  355. package/src/hooks/__tests__/usePublicFileDisplay.test.ts +16 -6
  356. package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +3 -3
  357. package/src/hooks/__tests__/useSessionRestoration.unit.test.tsx +17 -3
  358. package/src/hooks/public/usePublicEvent.ts +7 -6
  359. package/src/hooks/public/usePublicEventLogo.ts +7 -4
  360. package/src/hooks/public/usePublicFileDisplay.ts +6 -150
  361. package/src/hooks/useComponentPerformance.ts +4 -1
  362. package/src/hooks/useDataTablePerformance.ts +4 -3
  363. package/src/hooks/useEventTheme.test.ts +18 -5
  364. package/src/hooks/useEventTheme.ts +4 -1
  365. package/src/hooks/useEvents.ts +2 -0
  366. package/src/hooks/useFileDisplay.ts +9 -8
  367. package/src/hooks/useFileReference.ts +4 -1
  368. package/src/hooks/useFileUrl.ts +4 -1
  369. package/src/hooks/useInactivityTracker.ts +5 -4
  370. package/src/hooks/useOrganisationSecurity.test.ts +33 -12
  371. package/src/hooks/useOrganisationSecurity.ts +8 -7
  372. package/src/hooks/usePerformanceMonitor.ts +6 -3
  373. package/src/hooks/usePermissionCache.ts +13 -6
  374. package/src/hooks/useSecureDataAccess.test.ts +2 -2
  375. package/src/hooks/useSecureDataAccess.ts +9 -8
  376. package/src/hooks/useSessionRestoration.ts +4 -1
  377. package/src/hooks/useStorage.ts +4 -1
  378. package/src/index.ts +20 -7
  379. package/src/providers/services/AuthServiceProvider.tsx +3 -2
  380. package/src/providers/services/EventServiceProvider.tsx +2 -1
  381. package/src/providers/services/InactivityServiceProvider.tsx +2 -1
  382. package/src/providers/services/OrganisationServiceProvider.tsx +2 -1
  383. package/src/providers/services/UnifiedAuthProvider.tsx +4 -3
  384. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +22 -2
  385. package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +24 -2
  386. package/src/rbac/__tests__/cache-invalidation.test.ts +20 -6
  387. package/src/rbac/api.ts +5 -2
  388. package/src/rbac/audit-enhanced.ts +6 -6
  389. package/src/rbac/audit.test.ts +60 -38
  390. package/src/rbac/audit.ts +8 -8
  391. package/src/rbac/cache-invalidation.ts +7 -4
  392. package/src/rbac/components/EnhancedNavigationMenu.tsx +11 -5
  393. package/src/rbac/components/NavigationGuard.tsx +7 -3
  394. package/src/rbac/components/NavigationProvider.tsx +6 -3
  395. package/src/rbac/components/PagePermissionGuard.tsx +28 -16
  396. package/src/rbac/components/PagePermissionProvider.tsx +4 -1
  397. package/src/rbac/components/PermissionEnforcer.tsx +9 -3
  398. package/src/rbac/components/RoleBasedRouter.tsx +3 -1
  399. package/src/rbac/components/SecureDataProvider.tsx +7 -3
  400. package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +87 -61
  401. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +83 -33
  402. package/src/rbac/components/__tests__/NavigationProvider.test.tsx +36 -13
  403. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +2 -2
  404. package/src/rbac/components/__tests__/PagePermissionProvider.test.tsx +22 -8
  405. package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +19 -6
  406. package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +43 -17
  407. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +42 -17
  408. package/src/rbac/engine.ts +15 -7
  409. package/src/rbac/hooks/usePermissions.ts +7 -3
  410. package/src/rbac/hooks/useResolvedScope.test.ts +2 -2
  411. package/src/rbac/hooks/useResolvedScope.ts +10 -7
  412. package/src/rbac/permissions.ts +5 -2
  413. package/src/rbac/security.test.ts +27 -16
  414. package/src/rbac/security.ts +5 -4
  415. package/src/services/AuthService.ts +22 -21
  416. package/src/services/EventService.ts +12 -12
  417. package/src/services/InactivityService.ts +5 -4
  418. package/src/services/OrganisationService.ts +26 -25
  419. package/src/services/__tests__/AuthService.test.ts +51 -19
  420. package/src/services/__tests__/EventService.test.ts +37 -5
  421. package/src/services/__tests__/InactivityService.test.ts +38 -4
  422. package/src/services/__tests__/OrganisationService.test.ts +3 -8
  423. package/src/services/base/BaseService.ts +3 -1
  424. package/src/styles/core.css +3 -0
  425. package/src/theming/__tests__/runtime.test.ts +21 -12
  426. package/src/theming/parseEventColours.ts +5 -19
  427. package/src/theming/runtime.ts +8 -4
  428. package/src/types/validation.ts +2 -29
  429. package/src/utils/__tests__/appConfig.unit.test.ts +1 -1
  430. package/src/utils/__tests__/audit.unit.test.ts +1 -1
  431. package/src/utils/__tests__/auth-utils.unit.test.ts +1 -1
  432. package/src/utils/__tests__/bundleAnalysis.unit.test.ts +19 -19
  433. package/src/utils/__tests__/cn.unit.test.ts +1 -1
  434. package/src/utils/__tests__/debugLogger.test.ts +1 -1
  435. package/src/utils/__tests__/deviceFingerprint.unit.test.ts +1 -1
  436. package/src/utils/__tests__/dynamicUtils.unit.test.ts +1 -1
  437. package/src/utils/__tests__/formatting.unit.test.ts +1 -1
  438. package/src/utils/__tests__/lazyLoad.unit.test.tsx +1 -1
  439. package/src/utils/__tests__/logger.unit.test.ts +1 -1
  440. package/src/utils/__tests__/organisationContext.unit.test.ts +1 -1
  441. package/src/utils/__tests__/performanceBenchmark.test.ts +1 -1
  442. package/src/utils/__tests__/performanceBudgets.unit.test.ts +1 -1
  443. package/src/utils/__tests__/permissionTypes.unit.test.ts +1 -1
  444. package/src/utils/__tests__/permissionUtils.unit.test.ts +1 -1
  445. package/src/utils/__tests__/sanitization.unit.test.ts +1 -1
  446. package/src/utils/__tests__/schemaUtils.unit.test.ts +1 -1
  447. package/src/utils/__tests__/secureDataAccess.unit.test.ts +1 -1
  448. package/src/utils/__tests__/secureErrors.unit.test.ts +33 -15
  449. package/src/utils/__tests__/secureStorage.unit.test.ts +1 -1
  450. package/src/utils/__tests__/security.unit.test.ts +40 -18
  451. package/src/utils/__tests__/securityMonitor.unit.test.ts +1 -1
  452. package/src/utils/__tests__/sessionTracking.unit.test.ts +40 -29
  453. package/src/utils/__tests__/validationUtils.unit.test.ts +19 -6
  454. package/src/utils/app/appConfig.ts +47 -0
  455. package/src/utils/app/appIdResolver.test.ts +497 -0
  456. package/src/utils/app/appIdResolver.ts +133 -0
  457. package/src/utils/app/appNameResolver.simple.test.ts +212 -0
  458. package/src/utils/app/appNameResolver.test.ts +121 -0
  459. package/src/utils/app/appNameResolver.ts +195 -0
  460. package/src/utils/audit/audit.ts +127 -0
  461. package/src/utils/context/organisationContext.test.ts +322 -0
  462. package/src/utils/context/organisationContext.ts +156 -0
  463. package/src/utils/context/sessionTracking.ts +125 -0
  464. package/src/utils/core/cn.ts +7 -0
  465. package/src/utils/core/debugLogger.ts +67 -0
  466. package/src/utils/core/logger.ts +181 -0
  467. package/src/utils/device/deviceFingerprint.ts +215 -0
  468. package/src/utils/dynamic/dynamicUtils.ts +105 -0
  469. package/src/utils/dynamic/lazyLoad.tsx +44 -0
  470. package/src/utils/file-reference/__tests__/file-reference.test.ts +788 -0
  471. package/src/utils/file-reference/index.ts +501 -0
  472. package/src/utils/formatting/formatDate.test.ts +237 -0
  473. package/src/utils/formatting/formatting.ts +133 -0
  474. package/src/utils/index.ts +39 -54
  475. package/src/utils/performance/bundleAnalysis.ts +129 -0
  476. package/src/utils/performance/performanceBenchmark.ts +64 -0
  477. package/src/utils/performance/performanceBudgets.ts +110 -0
  478. package/src/utils/permissions/permissionTypes.ts +37 -0
  479. package/src/utils/permissions/permissionUtils.test.ts +393 -0
  480. package/src/utils/permissions/permissionUtils.ts +34 -0
  481. package/src/utils/security/auth-utils.ts +96 -0
  482. package/src/utils/security/secureDataAccess.test.ts +711 -0
  483. package/src/utils/security/secureDataAccess.ts +377 -0
  484. package/src/utils/security/secureErrors.ts +82 -0
  485. package/src/utils/security/secureStorage.ts +244 -0
  486. package/src/utils/security/security.ts +159 -0
  487. package/src/utils/security/securityMonitor.ts +45 -0
  488. package/src/utils/storage/__tests__/helpers.unit.test.ts +1 -4
  489. package/src/utils/storage/helpers.ts +15 -8
  490. package/src/utils/validation/__tests__/htmlSanitization.unit.test.ts +598 -0
  491. package/src/{validation → utils/validation}/csrf.ts +1 -1
  492. package/src/utils/validation/htmlSanitization.ts +184 -0
  493. package/src/utils/validation/index.ts +79 -0
  494. package/src/utils/validation/sanitization.ts +333 -0
  495. package/src/{validation/schemaUtils.ts → utils/validation/schema.ts} +11 -6
  496. package/src/{validation → utils/validation}/sqlInjectionProtection.ts +2 -0
  497. package/src/utils/validation/validation.ts +111 -0
  498. package/src/utils/validation/validationUtils.ts +123 -0
  499. package/src/validation/index.ts +3 -34
  500. package/dist/chunk-24MKLB7U.js +0 -81
  501. package/dist/chunk-24MKLB7U.js.map +0 -1
  502. package/dist/chunk-3CG5L6RN.js.map +0 -1
  503. package/dist/chunk-3DBFLLLU.js.map +0 -1
  504. package/dist/chunk-5F3NDPJV.js.map +0 -1
  505. package/dist/chunk-66C4BSAY.js.map +0 -1
  506. package/dist/chunk-BDZUMRBD.js +0 -87
  507. package/dist/chunk-BDZUMRBD.js.map +0 -1
  508. package/dist/chunk-BYXRHAIF.js.map +0 -1
  509. package/dist/chunk-CDQ3PX7L.js +0 -18
  510. package/dist/chunk-CDQ3PX7L.js.map +0 -1
  511. package/dist/chunk-CQZU6TFE.js.map +0 -1
  512. package/dist/chunk-F64FFPOZ.js.map +0 -1
  513. package/dist/chunk-GEVIB2UB.js.map +0 -1
  514. package/dist/chunk-GKHF54DI.js.map +0 -1
  515. package/dist/chunk-GVDR7WNV.js.map +0 -1
  516. package/dist/chunk-HMNOSGVA.js.map +0 -1
  517. package/dist/chunk-JCQZ6LA7.js.map +0 -1
  518. package/dist/chunk-M6DDYFUD.js.map +0 -1
  519. package/dist/chunk-O3NWNXDY.js.map +0 -1
  520. package/dist/chunk-PYUXFQJ3.js.map +0 -1
  521. package/dist/chunk-UJI6WSMD.js.map +0 -1
  522. package/dist/chunk-VZ5OR6HD.js.map +0 -1
  523. package/dist/chunk-WP5I5GLN.js.map +0 -1
  524. package/dist/chunk-ZYZCRSBD.js.map +0 -1
  525. package/dist/validation-DnhrNMju.d.ts +0 -159
  526. package/src/components/PublicLayout/__tests__/PublicPageDebugger.test.tsx +0 -185
  527. package/src/validation/__tests__/common.unit.test.ts +0 -101
  528. package/src/validation/__tests__/csrf.unit.test.ts +0 -365
  529. package/src/validation/__tests__/passwordSchema.unit.test.ts +0 -203
  530. package/src/validation/__tests__/sanitization.unit.test.ts +0 -250
  531. package/src/validation/__tests__/schemaUtils.unit.test.ts +0 -451
  532. package/src/validation/__tests__/sqlInjectionProtection.unit.test.ts +0 -462
  533. package/src/validation/__tests__/user.unit.test.ts +0 -440
  534. package/src/validation/sanitization.ts +0 -96
  535. /package/dist/{DataTable-A36PJG6N.js.map → DataTable-6M4L6BI2.js.map} +0 -0
  536. /package/dist/{UnifiedAuthProvider-CQDZRJIS.js.map → UnifiedAuthProvider-XIQQ7LVU.js.map} +0 -0
  537. /package/dist/{api-TNIBJWLM.js.map → api-45XYYO2A.js.map} +0 -0
  538. /package/dist/{audit-T36HM7IM.js.map → audit-64X3VJXB.js.map} +0 -0
  539. /package/dist/{chunk-CTJRBUX2.js.map → chunk-6LAAY47Q.js.map} +0 -0
  540. /package/dist/{chunk-ZV77RZMU.js.map → chunk-INQLMHPF.js.map} +0 -0
  541. /package/dist/{useInactivityTracker-MRUU55XI.js.map → useInactivityTracker-TO6ZOF35.js.map} +0 -0
  542. /package/src/{validation → utils/validation}/common.ts +0 -0
  543. /package/src/{validation → utils/validation}/passwordSchema.ts +0 -0
  544. /package/src/{validation → utils/validation}/user.ts +0 -0
@@ -0,0 +1,314 @@
1
+ /**
2
+ * @file Badge Component Tests
3
+ * @description Comprehensive test suite for Badge component
4
+ */
5
+
6
+ import React from 'react';
7
+ import { screen } from '@testing-library/react';
8
+ import { describe, it, expect, vi } from 'vitest';
9
+ import { Badge } from './Badge';
10
+ import { renderWithProviders } from '../../__tests__/helpers/test-utils';
11
+ import type { BadgeVariant } from './Badge';
12
+
13
+ describe('Badge Component', () => {
14
+ describe('Rendering', () => {
15
+ it('renders with text content', () => {
16
+ renderWithProviders(<Badge>New</Badge>);
17
+ expect(screen.getByText('New')).toBeInTheDocument();
18
+ });
19
+
20
+ it('renders as span element', () => {
21
+ const { container } = renderWithProviders(<Badge>Test</Badge>);
22
+ const badge = container.querySelector('span');
23
+ expect(badge).toBeInTheDocument();
24
+ });
25
+
26
+ it('renders with custom className', () => {
27
+ renderWithProviders(<Badge className="custom-class">Styled badge</Badge>);
28
+ const badge = screen.getByText('Styled badge');
29
+ expect(badge).toHaveClass('custom-class');
30
+ });
31
+
32
+ it('renders with data-testid', () => {
33
+ renderWithProviders(<Badge data-testid="test-badge">Test</Badge>);
34
+ expect(screen.getByTestId('test-badge')).toBeInTheDocument();
35
+ });
36
+
37
+ it('renders with children elements', () => {
38
+ renderWithProviders(
39
+ <Badge>
40
+ <span>Icon</span> Text
41
+ </Badge>
42
+ );
43
+ expect(screen.getByText('Icon')).toBeInTheDocument();
44
+ expect(screen.getByText('Text')).toBeInTheDocument();
45
+ });
46
+ });
47
+
48
+ describe('Variants', () => {
49
+ const allVariants: BadgeVariant[] = [
50
+ // Solid variants
51
+ 'solid-main-muted',
52
+ 'solid-main-normal',
53
+ 'solid-main-strong',
54
+ 'solid-sec-muted',
55
+ 'solid-sec-normal',
56
+ 'solid-sec-strong',
57
+ 'solid-acc-muted',
58
+ 'solid-acc-normal',
59
+ 'solid-acc-strong',
60
+ // Outline variants
61
+ 'outline-main-muted',
62
+ 'outline-main-normal',
63
+ 'outline-main-strong',
64
+ 'outline-sec-muted',
65
+ 'outline-sec-normal',
66
+ 'outline-sec-strong',
67
+ 'outline-acc-muted',
68
+ 'outline-acc-normal',
69
+ 'outline-acc-strong',
70
+ // Soft variants
71
+ 'soft-main-muted',
72
+ 'soft-main-normal',
73
+ 'soft-main-strong',
74
+ 'soft-sec-muted',
75
+ 'soft-sec-normal',
76
+ 'soft-sec-strong',
77
+ 'soft-acc-muted',
78
+ 'soft-acc-normal',
79
+ 'soft-acc-strong',
80
+ ];
81
+
82
+ allVariants.forEach((variant) => {
83
+ it(`renders ${variant} variant correctly`, () => {
84
+ renderWithProviders(<Badge variant={variant}>Test {variant}</Badge>);
85
+ const badge = screen.getByText(`Test ${variant}`);
86
+ expect(badge).toBeInTheDocument();
87
+ expect(badge).toHaveClass('inline-flex', 'items-center', 'rounded-2xl');
88
+ });
89
+ });
90
+
91
+ it('uses default variant when not specified', () => {
92
+ renderWithProviders(<Badge>Default</Badge>);
93
+ const badge = screen.getByText('Default');
94
+ expect(badge).toHaveClass('bg-main-500', 'text-main-50');
95
+ });
96
+
97
+ it('applies solid variant classes correctly', () => {
98
+ renderWithProviders(<Badge variant="solid-main-normal">Solid</Badge>);
99
+ const badge = screen.getByText('Solid');
100
+ expect(badge).toHaveClass('bg-main-500', 'text-main-50');
101
+ });
102
+
103
+ it('applies outline variant classes correctly', () => {
104
+ renderWithProviders(<Badge variant="outline-sec-muted">Outline</Badge>);
105
+ const badge = screen.getByText('Outline');
106
+ expect(badge).toHaveClass('outline', 'outline-1');
107
+ });
108
+
109
+ it('applies soft variant classes correctly', () => {
110
+ renderWithProviders(<Badge variant="soft-acc-strong">Soft</Badge>);
111
+ const badge = screen.getByText('Soft');
112
+ expect(badge).toHaveClass('shadow-badge-soft');
113
+ });
114
+ });
115
+
116
+ describe('Color System', () => {
117
+ it('uses main color palette for main variants', () => {
118
+ renderWithProviders(<Badge variant="solid-main-normal">Main</Badge>);
119
+ const badge = screen.getByText('Main');
120
+ expect(badge).toHaveClass('bg-main-500', 'text-main-50');
121
+ });
122
+
123
+ it('uses sec color palette for sec variants', () => {
124
+ renderWithProviders(<Badge variant="solid-sec-normal">Sec</Badge>);
125
+ const badge = screen.getByText('Sec');
126
+ expect(badge).toHaveClass('bg-sec-500', 'text-sec-50');
127
+ });
128
+
129
+ it('uses acc color palette for acc variants', () => {
130
+ renderWithProviders(<Badge variant="solid-acc-normal">Acc</Badge>);
131
+ const badge = screen.getByText('Acc');
132
+ expect(badge).toHaveClass('bg-acc-500', 'text-acc-50');
133
+ });
134
+
135
+ it('does not use standard Tailwind colors', () => {
136
+ renderWithProviders(<Badge variant="solid-main-normal">Test</Badge>);
137
+ const badge = screen.getByText('Test');
138
+ // Should not have standard Tailwind color classes
139
+ expect(badge).not.toHaveClass('bg-blue-', 'text-red-', 'bg-gray-', 'bg-white', 'bg-black');
140
+ });
141
+ });
142
+
143
+ describe('Shade Variants', () => {
144
+ it('applies muted shade correctly (dark text on light)', () => {
145
+ renderWithProviders(<Badge variant="solid-main-muted">Muted</Badge>);
146
+ const badge = screen.getByText('Muted');
147
+ expect(badge).toHaveClass('bg-main-200', 'text-main-600');
148
+ });
149
+
150
+ it('applies normal shade correctly (light text on medium)', () => {
151
+ renderWithProviders(<Badge variant="solid-main-normal">Normal</Badge>);
152
+ const badge = screen.getByText('Normal');
153
+ expect(badge).toHaveClass('bg-main-500', 'text-main-50');
154
+ });
155
+
156
+ it('applies strong shade correctly (light text on dark)', () => {
157
+ renderWithProviders(<Badge variant="solid-main-strong">Strong</Badge>);
158
+ const badge = screen.getByText('Strong');
159
+ expect(badge).toHaveClass('bg-main-700', 'text-main-50');
160
+ });
161
+ });
162
+
163
+ describe('Props Forwarding', () => {
164
+ it('forwards HTML attributes', () => {
165
+ renderWithProviders(
166
+ <Badge id="test-id" data-custom="value" aria-label="Test badge">
167
+ Test
168
+ </Badge>
169
+ );
170
+ const badge = screen.getByText('Test');
171
+ expect(badge).toHaveAttribute('id', 'test-id');
172
+ expect(badge).toHaveAttribute('data-custom', 'value');
173
+ expect(badge).toHaveAttribute('aria-label', 'Test badge');
174
+ });
175
+
176
+ it('forwards ref correctly', () => {
177
+ const ref = React.createRef<HTMLSpanElement>();
178
+ renderWithProviders(<Badge ref={ref}>Ref badge</Badge>);
179
+ expect(ref.current).toBeInstanceOf(HTMLSpanElement);
180
+ expect(ref.current).toHaveTextContent('Ref badge');
181
+ });
182
+
183
+ it('merges custom className with variant classes', () => {
184
+ renderWithProviders(
185
+ <Badge variant="solid-main-normal" className="custom-class px-4">
186
+ Custom
187
+ </Badge>
188
+ );
189
+ const badge = screen.getByText('Custom');
190
+ expect(badge).toHaveClass('custom-class', 'px-4', 'bg-main-500');
191
+ });
192
+ });
193
+
194
+ describe('Accessibility', () => {
195
+ it('renders as non-interactive span element', () => {
196
+ const { container } = renderWithProviders(<Badge>Test</Badge>);
197
+ const badge = container.querySelector('span');
198
+ expect(badge).toBeInTheDocument();
199
+ // Should not be a button or have button role
200
+ expect(badge).not.toHaveAttribute('role', 'button');
201
+ });
202
+
203
+ it('does not have focus styles by default', () => {
204
+ renderWithProviders(<Badge>Test</Badge>);
205
+ const badge = screen.getByText('Test');
206
+ // Badge should not have focus-visible classes since it's non-interactive
207
+ expect(badge).not.toHaveClass('focus-visible:outline-none');
208
+ });
209
+
210
+ it('supports aria-label for screen readers', () => {
211
+ renderWithProviders(<Badge aria-label="Status: Active">Active</Badge>);
212
+ const badge = screen.getByText('Active');
213
+ expect(badge).toHaveAttribute('aria-label', 'Status: Active');
214
+ });
215
+
216
+ it('supports aria-describedby', () => {
217
+ renderWithProviders(
218
+ <Badge aria-describedby="badge-description">Test</Badge>
219
+ );
220
+ const badge = screen.getByText('Test');
221
+ expect(badge).toHaveAttribute('aria-describedby', 'badge-description');
222
+ });
223
+ });
224
+
225
+ describe('Integration', () => {
226
+ it('works with React.forwardRef', () => {
227
+ const ref = React.createRef<HTMLSpanElement>();
228
+ renderWithProviders(<Badge ref={ref}>Ref badge</Badge>);
229
+ expect(ref.current).toBeInstanceOf(HTMLSpanElement);
230
+ });
231
+
232
+ it('can be wrapped in interactive elements', () => {
233
+ const handleClick = vi.fn();
234
+ renderWithProviders(
235
+ <button onClick={handleClick}>
236
+ <Badge>Clickable badge</Badge>
237
+ </button>
238
+ );
239
+ const button = screen.getByRole('button');
240
+ expect(button).toBeInTheDocument();
241
+ expect(button).toHaveTextContent('Clickable badge');
242
+ });
243
+
244
+ it('works in complex layouts', () => {
245
+ renderWithProviders(
246
+ <div>
247
+ <h2>Status</h2>
248
+ <Badge variant="solid-main-normal">Active</Badge>
249
+ <Badge variant="outline-sec-muted">Pending</Badge>
250
+ <Badge variant="soft-acc-strong">Featured</Badge>
251
+ </div>
252
+ );
253
+ expect(screen.getByText('Active')).toBeInTheDocument();
254
+ expect(screen.getByText('Pending')).toBeInTheDocument();
255
+ expect(screen.getByText('Featured')).toBeInTheDocument();
256
+ });
257
+ });
258
+
259
+ describe('Edge Cases', () => {
260
+ it('handles empty children gracefully', () => {
261
+ const { container } = renderWithProviders(<Badge></Badge>);
262
+ const badge = container.querySelector('span');
263
+ expect(badge).toBeInTheDocument();
264
+ });
265
+
266
+ it('handles very long text content', () => {
267
+ const longText = 'This is a very long badge text that might cause layout issues but should be handled gracefully';
268
+ renderWithProviders(<Badge>{longText}</Badge>);
269
+ expect(screen.getByText(longText)).toBeInTheDocument();
270
+ });
271
+
272
+ it('handles special characters in text', () => {
273
+ const specialText = 'Badge with special chars: !@#$%^&*()_+-=[]{}|;:,.<>?';
274
+ renderWithProviders(<Badge>{specialText}</Badge>);
275
+ expect(screen.getByText(specialText)).toBeInTheDocument();
276
+ });
277
+
278
+ it('handles numeric content', () => {
279
+ renderWithProviders(<Badge>123</Badge>);
280
+ expect(screen.getByText('123')).toBeInTheDocument();
281
+ });
282
+
283
+ it('handles multiple className overrides', () => {
284
+ renderWithProviders(
285
+ <Badge variant="solid-main-normal" className="px-8 py-2 text-sm">
286
+ Override
287
+ </Badge>
288
+ );
289
+ const badge = screen.getByText('Override');
290
+ expect(badge).toHaveClass('px-8', 'py-2', 'text-sm');
291
+ });
292
+ });
293
+
294
+ describe('Style Variants', () => {
295
+ it('solid variant has no border or blur', () => {
296
+ renderWithProviders(<Badge variant="solid-main-normal">Solid</Badge>);
297
+ const badge = screen.getByText('Solid');
298
+ expect(badge).not.toHaveClass('border', 'backdrop-blur');
299
+ });
300
+
301
+ it('outline variant has outline', () => {
302
+ renderWithProviders(<Badge variant="outline-main-muted">Outline</Badge>);
303
+ const badge = screen.getByText('Outline');
304
+ expect(badge).toHaveClass('outline', 'outline-1');
305
+ });
306
+
307
+ it('soft variant has shadow', () => {
308
+ renderWithProviders(<Badge variant="soft-main-normal">Soft</Badge>);
309
+ const badge = screen.getByText('Soft');
310
+ expect(badge).toHaveClass('shadow-badge-soft');
311
+ });
312
+ });
313
+ });
314
+
@@ -0,0 +1,304 @@
1
+ /**
2
+ * @file Badge Component
3
+ * @package @jmruthers/pace-core
4
+ * @module Components
5
+ * @since 0.1.0
6
+ *
7
+ * A small, non-interactive visual label component for displaying concise information
8
+ * such as status indicators, tags, or labels. Acts as a visual alternative to buttons
9
+ * for non-interactive content.
10
+ *
11
+ * Features:
12
+ * - 27 combined variants (3 styles × 3 colors × 3 shades)
13
+ * - Non-interactive by default (renders as <span>)
14
+ * - Supports all pace-core color palettes (main, sec, acc)
15
+ * - Customizable styling via className prop
16
+ * - Accessible and screen reader friendly
17
+ *
18
+ * @example
19
+ * ```tsx
20
+ * // Basic badge with default variant
21
+ * <Badge>New</Badge>
22
+ *
23
+ * // Badge with specific variant
24
+ * <Badge variant="solid-main-normal">Active</Badge>
25
+ *
26
+ * // Outline variant
27
+ * <Badge variant="outline-sec-muted">Pending</Badge>
28
+ *
29
+ * // Soft variant with accent color
30
+ * <Badge variant="soft-acc-strong">Featured</Badge>
31
+ *
32
+ * // Custom styling
33
+ * <Badge variant="solid-main-normal" className="px-4">
34
+ * Custom Badge
35
+ * </Badge>
36
+ * ```
37
+ *
38
+ * @accessibility
39
+ * - Renders as semantic <span> element
40
+ * - No focus styles (non-interactive by default)
41
+ * - Screen reader friendly through semantic HTML
42
+ * - Can be wrapped in interactive elements if needed
43
+ */
44
+
45
+ import * as React from 'react';
46
+ import { cn } from '../../utils/core/cn';
47
+
48
+ // ============================================================================
49
+ // TYPE DEFINITIONS
50
+ // ============================================================================
51
+
52
+ export type BadgeVariant =
53
+ | 'solid-main-muted' | 'solid-main-normal' | 'solid-main-strong'
54
+ | 'solid-sec-muted' | 'solid-sec-normal' | 'solid-sec-strong'
55
+ | 'solid-acc-muted' | 'solid-acc-normal' | 'solid-acc-strong'
56
+ | 'outline-main-muted' | 'outline-main-normal' | 'outline-main-strong'
57
+ | 'outline-sec-muted' | 'outline-sec-normal' | 'outline-sec-strong'
58
+ | 'outline-acc-muted' | 'outline-acc-normal' | 'outline-acc-strong'
59
+ | 'soft-main-muted' | 'soft-main-normal' | 'soft-main-strong'
60
+ | 'soft-sec-muted' | 'soft-sec-normal' | 'soft-sec-strong'
61
+ | 'soft-acc-muted' | 'soft-acc-normal' | 'soft-acc-strong';
62
+
63
+ export interface BadgeProps extends React.HTMLAttributes<HTMLSpanElement> {
64
+ /** Visual variant of the badge (style-color-shade) */
65
+ variant?: BadgeVariant;
66
+ }
67
+
68
+ // ============================================================================
69
+ // VARIANT CONFIGURATION
70
+ // ============================================================================
71
+
72
+ /**
73
+ * Shade configuration mapping
74
+ * Maps shade names to background, text, border, and outline text color shades
75
+ */
76
+ const shadeConfig = {
77
+ muted: {
78
+ bg: 200,
79
+ text: 600,
80
+ border: 400,
81
+ outlineText: 600,
82
+ outlineBg: 100,
83
+ },
84
+ normal: {
85
+ bg: 500,
86
+ text: 50,
87
+ border: 700,
88
+ outlineText: 700,
89
+ outlineBg: 200,
90
+ },
91
+ strong: {
92
+ bg: 700,
93
+ text: 50,
94
+ border: 800,
95
+ outlineText: 900,
96
+ outlineBg: 400,
97
+ },
98
+ } as const;
99
+
100
+ type Shade = keyof typeof shadeConfig;
101
+
102
+ /**
103
+ * Color palette options
104
+ */
105
+ const colors = ['main', 'sec', 'acc'] as const;
106
+ type Color = typeof colors[number];
107
+
108
+ /**
109
+ * Style options
110
+ */
111
+ const styles = ['solid', 'outline', 'soft'] as const;
112
+ type Style = typeof styles[number];
113
+
114
+ /**
115
+ * Build variant class string for a given style, color, and shade
116
+ * Generates classes programmatically from configuration
117
+ */
118
+ function buildVariantClasses(style: Style, color: Color, shade: Shade): string {
119
+ const cfg = shadeConfig[shade];
120
+ const parts: string[] = [];
121
+
122
+ if (style === 'solid') {
123
+ // Solid: flat background colors
124
+ parts.push(`bg-${color}-${cfg.bg}`, `text-${color}-${cfg.text}`);
125
+ } else if (style === 'outline') {
126
+ // Outline: outlined with light background
127
+ parts.push('outline outline-1 -outline-offset-1', `outline-${color}-${cfg.border}`, `bg-${color}-${cfg.outlineBg}`, `text-${color}-${cfg.outlineText}`);
128
+ } else if (style === 'soft') {
129
+ // Soft: uses shadow-badge-soft utility with colored shadows
130
+ parts.push(
131
+ `bg-${color}-${cfg.bg}`,
132
+ 'shadow-badge-soft',
133
+ `shadow-${color}-${cfg.bg}`,
134
+ `text-${color}-${cfg.text}`,
135
+ 'my-1', 'mx-1', 'py-0', 'px-2'
136
+ );
137
+ }
138
+
139
+ return parts.join(' ');
140
+ }
141
+
142
+ /**
143
+ * Generate all class name patterns for Tailwind scanning
144
+ * This ensures Tailwind can detect all dynamically generated classes
145
+ *
146
+ * Tailwind v4 content scanning: These classes are listed here so Tailwind
147
+ * can detect them during content scanning, even when built dynamically.
148
+ *
149
+ * Classes used: shadow-badge-soft shadow-main-200 shadow-main-500 shadow-main-700
150
+ * shadow-sec-200 shadow-sec-500 shadow-sec-700 shadow-acc-200 shadow-acc-500 shadow-acc-700
151
+ */
152
+ const tailwindClassScan = [
153
+ // Solid background classes
154
+ 'bg-main-100', 'bg-main-600', 'bg-main-900',
155
+ 'bg-sec-100', 'bg-sec-600', 'bg-sec-900',
156
+ 'bg-acc-100', 'bg-acc-600', 'bg-acc-900',
157
+ // Solid text classes
158
+ 'text-main-50', 'text-main-900',
159
+ 'text-sec-50', 'text-sec-900',
160
+ 'text-acc-50', 'text-acc-900',
161
+ // Outline classes
162
+ 'outline outline-1',
163
+ 'outline-main-400', 'outline-main-700', 'outline-main-800',
164
+ 'outline-sec-400', 'outline-sec-700', 'outline-sec-800',
165
+ 'outline-acc-400', 'outline-acc-700', 'outline-acc-800',
166
+ // Outline background classes
167
+ 'bg-main-100', 'bg-main-200', 'bg-main-300',
168
+ 'bg-sec-100', 'bg-sec-200', 'bg-sec-300',
169
+ 'bg-acc-100', 'bg-acc-200', 'bg-acc-300',
170
+ // Outline text classes
171
+ 'text-main-500', 'text-main-600', 'text-main-800',
172
+ 'text-sec-500', 'text-sec-600', 'text-sec-800',
173
+ 'text-acc-500', 'text-acc-600', 'text-acc-800',
174
+ // Soft background classes (solid, no opacity)
175
+ 'bg-main-200', 'bg-main-500', 'bg-main-700',
176
+ 'bg-sec-200', 'bg-sec-500', 'bg-sec-700',
177
+ 'bg-acc-200', 'bg-acc-500', 'bg-acc-700',
178
+ // Soft shadow utility classes - CRITICAL: These must be detected by Tailwind
179
+ 'shadow-badge-soft',
180
+ 'shadow-main-200', 'shadow-main-500', 'shadow-main-700',
181
+ 'shadow-sec-200', 'shadow-sec-500', 'shadow-sec-700',
182
+ 'shadow-acc-200', 'shadow-acc-500', 'shadow-acc-700',
183
+ // Soft margin/padding classes
184
+ 'my-3', 'mx-3', 'py-0', 'px-2',
185
+ ] as const;
186
+
187
+ /**
188
+ * Generate all variant class combinations programmatically
189
+ * Uses the configuration to build the lookup map dynamically
190
+ */
191
+ function generateVariantClasses(): Record<BadgeVariant, string> {
192
+ const variantClasses = {} as Record<BadgeVariant, string>;
193
+
194
+ for (const style of styles) {
195
+ for (const color of colors) {
196
+ for (const shade of Object.keys(shadeConfig) as Shade[]) {
197
+ const variant = `${style}-${color}-${shade}` as BadgeVariant;
198
+ variantClasses[variant] = buildVariantClasses(style, color, shade);
199
+ }
200
+ }
201
+ }
202
+
203
+ return variantClasses;
204
+ }
205
+
206
+ // Pre-generate all variant classes
207
+ const variantClassesMap = generateVariantClasses();
208
+
209
+ // Ensure Tailwind detects shadow-badge-soft class
210
+ // This string literal ensures Tailwind v4 content scanning includes the class
211
+ const _tailwindShadowDetection = 'shadow-badge-soft';
212
+
213
+ // ============================================================================
214
+ // CLASS GENERATION FUNCTION
215
+ // ============================================================================
216
+
217
+ /**
218
+ * Get badge classes based on variant
219
+ *
220
+ * Variant format: {style}-{color}-{shade}
221
+ * - style: solid (flat), outline (bordered), soft (blurred)
222
+ * - color: main, sec, acc
223
+ * - shade: muted (dark on light), normal (light on medium), strong (light on dark)
224
+ */
225
+ function getBadgeClasses(variant: BadgeVariant = 'solid-main-normal'): string {
226
+ const baseClasses = 'inline-flex text-balance items-center rounded-2xl px-3 py-1 text-xs font-medium transition-colors box-border';
227
+ return `${baseClasses} ${variantClassesMap[variant]}`;
228
+ }
229
+
230
+ // ============================================================================
231
+ // BADGE COMPONENT
232
+ // ============================================================================
233
+
234
+ /**
235
+ * Badge Component
236
+ * A small, non-interactive visual label for displaying concise information.
237
+ *
238
+ * @component
239
+ * @example
240
+ * ```tsx
241
+ * // Default variant
242
+ * <Badge>New</Badge>
243
+ *
244
+ * // Specific variant
245
+ * <Badge variant="solid-main-normal">Active</Badge>
246
+ *
247
+ * // Outline variant
248
+ * <Badge variant="outline-sec-muted">Pending</Badge>
249
+ *
250
+ * // Soft variant
251
+ * <Badge variant="soft-acc-strong">Featured</Badge>
252
+ * ```
253
+ */
254
+ const Badge = React.forwardRef<HTMLSpanElement, BadgeProps>(
255
+ ({ className, variant = 'solid-main-normal', ...props }, ref) => {
256
+ const isSoftVariant = variant.startsWith('soft-');
257
+
258
+ if (isSoftVariant) {
259
+ // For soft variants, we need both shadow-badge-soft and shadow color class
260
+ // twMerge treats all shadow-* classes as conflicting, so we need to add them after merging
261
+ const variantClasses = getBadgeClasses(variant);
262
+ // Extract the shadow color class from variantClasses (e.g., shadow-main-200)
263
+ const shadowColorMatch = variantClasses.match(/\bshadow-(\w+)-(\d+)\b/);
264
+ const shadowColorClass = shadowColorMatch ? shadowColorMatch[0] : '';
265
+
266
+ // Remove shadow classes from variantClasses to avoid twMerge conflicts
267
+ const classesWithoutShadows = variantClasses
268
+ .replace(/\bshadow-badge-soft\b/g, '')
269
+ .replace(/\bshadow-\w+-\d+\b/g, '')
270
+ .replace(/\s+/g, ' ')
271
+ .trim();
272
+
273
+ // Merge non-shadow classes first, then append shadow classes to ensure both are present
274
+ // shadow-badge-soft first (defines shape), then shadow color (defines color)
275
+ const mergedClasses = cn(classesWithoutShadows, className);
276
+ const finalClasses = `${mergedClasses} shadow-badge-soft ${shadowColorClass}`.trim();
277
+
278
+ return (
279
+ <span
280
+ ref={ref}
281
+ className={finalClasses}
282
+ {...props}
283
+ />
284
+ );
285
+ }
286
+
287
+ return (
288
+ <span
289
+ ref={ref}
290
+ className={cn(getBadgeClasses(variant), className)}
291
+ {...props}
292
+ />
293
+ );
294
+ }
295
+ );
296
+
297
+ Badge.displayName = 'Badge';
298
+
299
+ // ============================================================================
300
+ // EXPORTS
301
+ // ============================================================================
302
+
303
+ export { Badge };
304
+
@@ -0,0 +1,3 @@
1
+ export { Badge } from './Badge';
2
+ export type { BadgeProps, BadgeVariant } from './Badge';
3
+
@@ -46,7 +46,7 @@
46
46
 
47
47
  import * as React from 'react';
48
48
  import { Slot } from '@radix-ui/react-slot';
49
- import { cn } from '../../utils/cn';
49
+ import { cn } from '../../utils/core/cn';
50
50
  import { Tooltip } from '../Tooltip';
51
51
 
52
52
  // ============================================================================
@@ -62,7 +62,7 @@
62
62
  */
63
63
 
64
64
  import * as React from "react"
65
- import { cn } from "../../utils/cn"
65
+ import { cn } from "../../utils/core/cn"
66
66
 
67
67
  export interface CardProps extends React.HTMLAttributes<HTMLElement> {
68
68
  /** Visual variant of the card */
@@ -49,7 +49,7 @@ import * as React from "react"
49
49
  import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
50
50
  import { Check } from "lucide-react"
51
51
 
52
- import { cn } from "../../utils/cn"
52
+ import { cn } from "../../utils/core/cn"
53
53
 
54
54
  const Checkbox = React.forwardRef<
55
55
  React.ElementRef<typeof CheckboxPrimitive.Root>,
@@ -49,7 +49,7 @@ const mockLogger = {
49
49
  };
50
50
 
51
51
  // Note: Must define mock inside factory due to hoisting
52
- vi.mock('../../utils/logger', () => {
52
+ vi.mock('../../utils/core/logger', () => {
53
53
  return {
54
54
  createLogger: () => mockLogger,
55
55
  };