@jmruthers/pace-core 0.5.136 → 0.5.139

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 (292) hide show
  1. package/dist/{DataTable-CYOHOX3O.js → DataTable-JXFCA2BJ.js} +10 -9
  2. package/dist/{EventLogo-801uofbR.d.ts → EventLogo-rFL_kRjk.d.ts} +73 -1
  3. package/dist/{UnifiedAuthProvider-5E5TUNMS.js → UnifiedAuthProvider-XIQQ7LVU.js} +4 -5
  4. package/dist/{chunk-YLKIDTUK.js → chunk-22WKWKRX.js} +4 -4
  5. package/dist/{chunk-TVYPTYOY.js → chunk-4C7EXCAR.js} +60 -24
  6. package/dist/chunk-4C7EXCAR.js.map +1 -0
  7. package/dist/{chunk-NOHEVYVX.js → chunk-5JMOHWDI.js} +417 -319
  8. package/dist/chunk-5JMOHWDI.js.map +1 -0
  9. package/dist/{chunk-FHWWBIHA.js → chunk-6DXZ6V5Q.js} +5 -5
  10. package/dist/{chunk-2TWNJ46Y.js → chunk-6LAAY47Q.js} +2 -2
  11. package/dist/{chunk-444EZN6N.js → chunk-7QCC6MCP.js} +88 -1
  12. package/dist/chunk-7QCC6MCP.js.map +1 -0
  13. package/dist/chunk-BJPBT3CU.js +21 -0
  14. package/dist/chunk-BJPBT3CU.js.map +1 -0
  15. package/dist/{chunk-L6PGMCMD.js → chunk-BOOI7GK2.js} +38 -12
  16. package/dist/chunk-BOOI7GK2.js.map +1 -0
  17. package/dist/{chunk-XARJS7CD.js → chunk-INQLMHPF.js} +2 -2
  18. package/dist/chunk-JISYG63F.js +70 -0
  19. package/dist/chunk-JISYG63F.js.map +1 -0
  20. package/dist/{chunk-SL2YQDR6.js → chunk-MA6EPSGZ.js} +2 -2
  21. package/dist/{chunk-5DPZ5EAT.js → chunk-OWAG3GSU.js} +1 -3
  22. package/dist/{chunk-LTV3XIJJ.js → chunk-T6JN6LH6.js} +4 -4
  23. package/dist/{chunk-HJGGOMQ6.js → chunk-TLT2ZR3L.js} +147 -103
  24. package/dist/chunk-TLT2ZR3L.js.map +1 -0
  25. package/dist/{chunk-4MT5BGGL.js → chunk-YCWDTTUK.js} +4 -6
  26. package/dist/{chunk-4MT5BGGL.js.map → chunk-YCWDTTUK.js.map} +1 -1
  27. package/dist/components.d.ts +1 -1
  28. package/dist/components.js +12 -11
  29. package/dist/components.js.map +1 -1
  30. package/dist/hooks.js +8 -9
  31. package/dist/hooks.js.map +1 -1
  32. package/dist/index.d.ts +2 -2
  33. package/dist/index.js +15 -14
  34. package/dist/index.js.map +1 -1
  35. package/dist/providers.js +3 -4
  36. package/dist/rbac/index.js +8 -9
  37. package/dist/schema-DTDZQe2u.d.ts +28 -0
  38. package/dist/types.d.ts +152 -3
  39. package/dist/types.js +51 -16
  40. package/dist/types.js.map +1 -1
  41. package/dist/utils.d.ts +89 -4
  42. package/dist/utils.js +214 -96
  43. package/dist/utils.js.map +1 -1
  44. package/dist/validation.d.ts +1 -343
  45. package/dist/validation.js +3 -100
  46. package/docs/api/classes/ColumnFactory.md +1 -1
  47. package/docs/api/classes/ErrorBoundary.md +1 -1
  48. package/docs/api/classes/InvalidScopeError.md +1 -1
  49. package/docs/api/classes/MissingUserContextError.md +1 -1
  50. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  51. package/docs/api/classes/PermissionDeniedError.md +1 -1
  52. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  53. package/docs/api/classes/RBACAuditManager.md +1 -1
  54. package/docs/api/classes/RBACCache.md +1 -1
  55. package/docs/api/classes/RBACEngine.md +1 -1
  56. package/docs/api/classes/RBACError.md +1 -1
  57. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  58. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  59. package/docs/api/classes/StorageUtils.md +1 -1
  60. package/docs/api/enums/FileCategory.md +1 -1
  61. package/docs/api/interfaces/AggregateConfig.md +1 -1
  62. package/docs/api/interfaces/BadgeProps.md +27 -0
  63. package/docs/api/interfaces/ButtonProps.md +1 -1
  64. package/docs/api/interfaces/CardProps.md +1 -1
  65. package/docs/api/interfaces/ColorPalette.md +1 -1
  66. package/docs/api/interfaces/ColorShade.md +1 -1
  67. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  68. package/docs/api/interfaces/DataRecord.md +1 -1
  69. package/docs/api/interfaces/DataTableAction.md +1 -1
  70. package/docs/api/interfaces/DataTableColumn.md +1 -1
  71. package/docs/api/interfaces/DataTableProps.md +1 -1
  72. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  73. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  74. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  75. package/docs/api/interfaces/EventAppRoleData.md +1 -1
  76. package/docs/api/interfaces/EventLogoProps.md +1 -1
  77. package/docs/api/interfaces/ExportColumn.md +1 -1
  78. package/docs/api/interfaces/ExportOptions.md +1 -1
  79. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  80. package/docs/api/interfaces/FileMetadata.md +1 -1
  81. package/docs/api/interfaces/FileReference.md +1 -1
  82. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  83. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  84. package/docs/api/interfaces/FileUploadProps.md +1 -1
  85. package/docs/api/interfaces/FooterProps.md +1 -1
  86. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  87. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  88. package/docs/api/interfaces/InputProps.md +1 -1
  89. package/docs/api/interfaces/LabelProps.md +1 -1
  90. package/docs/api/interfaces/LoginFormProps.md +1 -1
  91. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  92. package/docs/api/interfaces/NavigationContextType.md +1 -1
  93. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  94. package/docs/api/interfaces/NavigationItem.md +1 -1
  95. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  96. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  97. package/docs/api/interfaces/Organisation.md +1 -1
  98. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  99. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  100. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  101. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  102. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  103. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  104. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  105. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  106. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  107. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  108. package/docs/api/interfaces/PaletteData.md +1 -1
  109. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  110. package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
  111. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  112. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  113. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  114. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  115. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  116. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  117. package/docs/api/interfaces/RBACConfig.md +1 -1
  118. package/docs/api/interfaces/RBACLogger.md +1 -1
  119. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  120. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  121. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  122. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  123. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  124. package/docs/api/interfaces/RouteConfig.md +1 -1
  125. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  126. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  127. package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
  128. package/docs/api/interfaces/StorageConfig.md +1 -1
  129. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  130. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  131. package/docs/api/interfaces/StorageListOptions.md +1 -1
  132. package/docs/api/interfaces/StorageListResult.md +1 -1
  133. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  134. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  135. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  136. package/docs/api/interfaces/StyleImport.md +1 -1
  137. package/docs/api/interfaces/SwitchProps.md +1 -1
  138. package/docs/api/interfaces/ToastActionElement.md +1 -1
  139. package/docs/api/interfaces/ToastProps.md +1 -1
  140. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  141. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  142. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  143. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  144. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  145. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  146. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +1 -1
  147. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
  148. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  149. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  150. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  151. package/docs/api/interfaces/UserEventAccess.md +1 -1
  152. package/docs/api/interfaces/UserMenuProps.md +1 -1
  153. package/docs/api/interfaces/UserProfile.md +1 -1
  154. package/docs/api/modules.md +84 -15
  155. package/docs/architecture/README.md +0 -1
  156. package/docs/styles/README.md +0 -2
  157. package/examples/RBAC/CompleteRBACExample.tsx +324 -0
  158. package/examples/RBAC/EventBasedApp.tsx +239 -0
  159. package/examples/RBAC/PermissionExample.tsx +151 -0
  160. package/examples/RBAC/index.ts +13 -0
  161. package/examples/public-pages/CorrectPublicPageImplementation.tsx +301 -0
  162. package/examples/public-pages/PublicEventPage.tsx +274 -0
  163. package/examples/public-pages/PublicPageApp.tsx +308 -0
  164. package/examples/public-pages/PublicPageUsageExample.tsx +216 -0
  165. package/examples/public-pages/index.ts +14 -0
  166. package/package.json +1 -10
  167. package/src/__tests__/TEST_STANDARD.md +92 -0
  168. package/src/components/Badge/Badge.test.tsx +314 -0
  169. package/src/components/Badge/Badge.tsx +304 -0
  170. package/src/components/Badge/index.ts +3 -0
  171. package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +217 -0
  172. package/src/components/DataTable/__tests__/styles.test.ts +1 -1
  173. package/src/components/DataTable/components/ColumnFilter.tsx +8 -4
  174. package/src/components/DataTable/components/DataTableBody.tsx +461 -0
  175. package/src/components/DataTable/components/DraggableColumnHeader.tsx +144 -0
  176. package/src/components/DataTable/components/FilterRow.tsx +9 -3
  177. package/src/components/DataTable/components/PaginationControls.tsx +1 -0
  178. package/src/components/DataTable/components/VirtualizedDataTable.tsx +513 -0
  179. package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +14 -68
  180. package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +62 -0
  181. package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +43 -0
  182. package/src/components/DataTable/core/ActionManager.ts +235 -0
  183. package/src/components/DataTable/core/ColumnManager.ts +205 -0
  184. package/src/components/DataTable/core/DataManager.ts +188 -0
  185. package/src/components/DataTable/core/DataTableContext.tsx +181 -0
  186. package/src/components/DataTable/core/LocalDataAdapter.ts +273 -0
  187. package/src/components/DataTable/core/PluginRegistry.ts +229 -0
  188. package/src/components/DataTable/core/StateManager.ts +311 -0
  189. package/src/components/DataTable/core/interfaces.ts +338 -0
  190. package/src/components/DataTable/styles.ts +27 -6
  191. package/src/components/DataTable/utils/__tests__/columnUtils.test.ts +94 -0
  192. package/src/components/DataTable/utils/columnUtils.ts +40 -0
  193. package/src/components/DataTable/utils/debugTools.ts +609 -0
  194. package/src/components/DataTable/utils/index.ts +1 -0
  195. package/src/components/Dialog/README.md +804 -0
  196. package/src/components/Dialog/utils/__tests__/safeHtml.unit.test.ts +611 -0
  197. package/src/components/Dialog/utils/safeHtml.ts +185 -0
  198. package/src/components/Footer/Footer.test.tsx +1 -1
  199. package/src/components/Form/Form.test.tsx +1 -1
  200. package/src/components/Form/FormErrorSummary.tsx +113 -0
  201. package/src/components/Form/FormFieldset.tsx +127 -0
  202. package/src/components/Form/FormLiveRegion.tsx +198 -0
  203. package/src/components/LoginForm/LoginForm.test.tsx +1 -1
  204. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.performance.test.tsx +76 -10
  205. package/src/components/PaceLoginPage/PaceLoginPage.tsx +1 -1
  206. package/src/components/PasswordReset/PasswordResetForm.test.tsx +597 -0
  207. package/src/components/PasswordReset/PasswordResetForm.tsx +201 -0
  208. package/src/components/PublicLayout/PublicPageDebugger.tsx +104 -0
  209. package/src/components/PublicLayout/PublicPageDiagnostic.tsx +162 -0
  210. package/src/components/PublicLayout/__tests__/PublicPageFooter.test.tsx +1 -1
  211. package/src/components/Select/Select.test.tsx +1 -1
  212. package/src/components/Select/Select.tsx +20 -8
  213. package/src/components/Table/__tests__/Table.test.tsx +1 -1
  214. package/src/components/index.ts +3 -0
  215. package/src/hooks/__tests__/useFileUrl.unit.test.ts +83 -85
  216. package/src/index.ts +4 -0
  217. package/src/rbac/hooks/useCan.test.ts +24 -0
  218. package/src/rbac/hooks/usePermissions.ts +49 -12
  219. package/src/styles/core.css +3 -0
  220. package/src/utils/appConfig.ts +47 -0
  221. package/src/utils/appIdResolver.test.ts +499 -0
  222. package/src/utils/appIdResolver.ts +130 -0
  223. package/src/utils/appNameResolver.simple.test.ts +212 -0
  224. package/src/utils/appNameResolver.test.ts +121 -0
  225. package/src/utils/appNameResolver.ts +191 -0
  226. package/src/utils/audit.ts +127 -0
  227. package/src/utils/auth-utils.ts +96 -0
  228. package/src/utils/bundleAnalysis.ts +129 -0
  229. package/src/utils/cn.ts +7 -0
  230. package/src/utils/debugLogger.ts +67 -0
  231. package/src/utils/deviceFingerprint.ts +215 -0
  232. package/src/utils/dynamicUtils.ts +105 -0
  233. package/src/utils/file-reference.test.ts +788 -0
  234. package/src/utils/file-reference.ts +519 -0
  235. package/src/utils/formatDate.test.ts +237 -0
  236. package/src/utils/formatting.ts +133 -0
  237. package/src/utils/index.ts +7 -0
  238. package/src/utils/lazyLoad.tsx +44 -0
  239. package/src/utils/logger.ts +179 -0
  240. package/src/utils/organisationContext.test.ts +322 -0
  241. package/src/utils/organisationContext.ts +153 -0
  242. package/src/utils/performanceBenchmark.ts +64 -0
  243. package/src/utils/performanceBudgets.ts +110 -0
  244. package/src/utils/permissionTypes.ts +37 -0
  245. package/src/utils/permissionUtils.test.ts +393 -0
  246. package/src/utils/permissionUtils.ts +34 -0
  247. package/src/utils/sanitization.ts +264 -0
  248. package/src/utils/schemaUtils.ts +37 -0
  249. package/src/utils/secureDataAccess.test.ts +711 -0
  250. package/src/utils/secureDataAccess.ts +377 -0
  251. package/src/utils/secureErrors.ts +79 -0
  252. package/src/utils/secureStorage.ts +244 -0
  253. package/src/utils/security.ts +156 -0
  254. package/src/utils/securityMonitor.ts +45 -0
  255. package/src/utils/sessionTracking.ts +126 -0
  256. package/src/utils/validation.ts +111 -0
  257. package/src/utils/validationUtils.ts +120 -0
  258. package/src/validation/index.ts +2 -2
  259. package/dist/chunk-444EZN6N.js.map +0 -1
  260. package/dist/chunk-APIBCTL2.js +0 -670
  261. package/dist/chunk-APIBCTL2.js.map +0 -1
  262. package/dist/chunk-HJGGOMQ6.js.map +0 -1
  263. package/dist/chunk-K2WWTH7O.js +0 -94
  264. package/dist/chunk-K2WWTH7O.js.map +0 -1
  265. package/dist/chunk-L6PGMCMD.js.map +0 -1
  266. package/dist/chunk-LMC26NLJ.js +0 -84
  267. package/dist/chunk-LMC26NLJ.js.map +0 -1
  268. package/dist/chunk-NOHEVYVX.js.map +0 -1
  269. package/dist/chunk-TVYPTYOY.js.map +0 -1
  270. package/dist/validation-8npbysjg.d.ts +0 -177
  271. /package/dist/{DataTable-CYOHOX3O.js.map → DataTable-JXFCA2BJ.js.map} +0 -0
  272. /package/dist/{UnifiedAuthProvider-5E5TUNMS.js.map → UnifiedAuthProvider-XIQQ7LVU.js.map} +0 -0
  273. /package/dist/{chunk-YLKIDTUK.js.map → chunk-22WKWKRX.js.map} +0 -0
  274. /package/dist/{chunk-FHWWBIHA.js.map → chunk-6DXZ6V5Q.js.map} +0 -0
  275. /package/dist/{chunk-2TWNJ46Y.js.map → chunk-6LAAY47Q.js.map} +0 -0
  276. /package/dist/{chunk-XARJS7CD.js.map → chunk-INQLMHPF.js.map} +0 -0
  277. /package/dist/{chunk-SL2YQDR6.js.map → chunk-MA6EPSGZ.js.map} +0 -0
  278. /package/dist/{chunk-5DPZ5EAT.js.map → chunk-OWAG3GSU.js.map} +0 -0
  279. /package/dist/{chunk-LTV3XIJJ.js.map → chunk-T6JN6LH6.js.map} +0 -0
  280. /package/examples/{components → components 2}/DataTable/HierarchicalActionsExample.tsx +0 -0
  281. /package/examples/{components → components 2}/DataTable/HierarchicalExample.tsx +0 -0
  282. /package/examples/{components → components 2}/DataTable/InitialPageSizeExample.tsx +0 -0
  283. /package/examples/{components → components 2}/DataTable/PerformanceExample.tsx +0 -0
  284. /package/examples/{components → components 2}/DataTable/index.ts +0 -0
  285. /package/examples/{components → components 2}/Dialog/BasicHtmlTest.tsx +0 -0
  286. /package/examples/{components → components 2}/Dialog/DebugHtmlExample.tsx +0 -0
  287. /package/examples/{components → components 2}/Dialog/HtmlDialogExample.tsx +0 -0
  288. /package/examples/{components → components 2}/Dialog/ScrollableDialogExample.tsx +0 -0
  289. /package/examples/{components → components 2}/Dialog/SimpleHtmlTest.tsx +0 -0
  290. /package/examples/{components → components 2}/Dialog/SmartDialogExample.tsx +0 -0
  291. /package/examples/{components → components 2}/Dialog/index.ts +0 -0
  292. /package/examples/{components → components 2}/index.ts +0 -0
@@ -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
+
@@ -0,0 +1,217 @@
1
+ /**
2
+ * @file DataTableCore Test Setup
3
+ * @package @jmruthers/pace-core
4
+ * @module Components/DataTable/__tests__
5
+ * @since 0.4.0
6
+ *
7
+ * Shared test setup and mocks for DataTableCore component tests.
8
+ * This file is imported by all DataTableCore test files to ensure consistent mocking.
9
+ */
10
+
11
+ import { vi } from 'vitest';
12
+ import React from 'react';
13
+
14
+ // Mock the RBAC hooks
15
+ vi.mock('../../../rbac/hooks', () => ({
16
+ useCan: vi.fn(),
17
+ useResolvedScope: vi.fn(() => ({
18
+ resolvedScope: {
19
+ organisationId: 'test-org-id',
20
+ eventId: 'test-event-id',
21
+ appId: 'test-app-id'
22
+ }
23
+ }))
24
+ }));
25
+
26
+ // Mock the auth provider
27
+ vi.mock('../../../providers/UnifiedAuthProvider', () => ({
28
+ useUnifiedAuth: vi.fn(() => ({
29
+ user: { id: 'test-user', email: 'test@example.com' },
30
+ isAuthenticated: true,
31
+ isLoading: false,
32
+ error: null,
33
+ selectedOrganisationId: 'test-org-id',
34
+ selectedEventId: 'test-event-id',
35
+ supabase: null,
36
+ })),
37
+ }));
38
+
39
+ // Mock the performance hook
40
+ vi.mock('../../../hooks/useDataTablePerformance', () => ({
41
+ useDataTablePerformance: vi.fn(() => ({
42
+ paginationMode: 'client',
43
+ isVirtualized: false,
44
+ pageSizeOptions: [10, 25, 50],
45
+ processedData: [],
46
+ totalCount: 0,
47
+ isLoading: false,
48
+ searchQuery: '',
49
+ setSearchQuery: vi.fn(),
50
+ searchResults: [],
51
+ fetchServerData: vi.fn(),
52
+ serverData: null,
53
+ memoryUsage: 0,
54
+ errorState: {
55
+ hasErrors: false,
56
+ errorCount: 0,
57
+ lastError: null,
58
+ fallbacksActive: [],
59
+ },
60
+ retryLastOperation: vi.fn(),
61
+ clearErrors: vi.fn(),
62
+ cleanup: vi.fn(),
63
+ shouldUseVirtualization: false,
64
+ performanceMetrics: {},
65
+ })),
66
+ }));
67
+
68
+ // Mock the data table state hook
69
+ vi.mock('../hooks/useDataTableState', () => ({
70
+ useDataTableState: vi.fn(() => ({
71
+ state: {
72
+ sorting: [],
73
+ columnFilters: [],
74
+ columnVisibility: {},
75
+ grouping: [],
76
+ expanded: {},
77
+ pagination: { pageIndex: 0, pageSize: 10 },
78
+ columnOrder: [],
79
+ rowSelection: {},
80
+ isCreating: false,
81
+ creationData: {},
82
+ editingRowId: null,
83
+ editingData: {},
84
+ showImportModal: false,
85
+ showFilterRow: false,
86
+ searchQuery: '',
87
+ },
88
+ actions: {
89
+ setSorting: vi.fn(),
90
+ setColumnFilters: vi.fn(),
91
+ setColumnVisibility: vi.fn(),
92
+ setGrouping: vi.fn(),
93
+ setExpanded: vi.fn(),
94
+ setPagination: vi.fn(),
95
+ setColumnOrder: vi.fn(),
96
+ setRowSelection: vi.fn(),
97
+ setCreating: vi.fn(),
98
+ setCreationData: vi.fn(),
99
+ clearCreationData: vi.fn(),
100
+ setEditingRow: vi.fn(),
101
+ clearEditing: vi.fn(),
102
+ setImportModal: vi.fn(),
103
+ setFilterRow: vi.fn(),
104
+ setSearchQuery: vi.fn(),
105
+ resetState: vi.fn(),
106
+ },
107
+ })),
108
+ }));
109
+
110
+ // Mock the hierarchical state hook
111
+ vi.mock('../hooks/useHierarchicalState', () => ({
112
+ useHierarchicalState: vi.fn(() => ({
113
+ hierarchicalData: [],
114
+ setHierarchicalData: vi.fn(),
115
+ expandedRows: {},
116
+ setExpandedRows: vi.fn(),
117
+ })),
118
+ }));
119
+
120
+ // Mock the column order persistence hook
121
+ vi.mock('../hooks/useColumnOrderPersistence', () => ({
122
+ useColumnOrderPersistence: vi.fn(() => ({
123
+ columnOrder: [],
124
+ setColumnOrder: vi.fn(),
125
+ })),
126
+ }));
127
+
128
+ // Mock the column factory
129
+ vi.mock('../core/ColumnFactory', () => ({
130
+ ColumnFactory: {
131
+ createColumns: vi.fn((columns) => columns),
132
+ createActionColumn: vi.fn(() => ({
133
+ id: 'actions',
134
+ header: 'Actions',
135
+ cell: () => React.createElement('div', { 'data-testid': 'action-cell' }, 'Actions'),
136
+ })),
137
+ },
138
+ }));
139
+
140
+ // Mock the hierarchical utilities
141
+ vi.mock('../utils/hierarchicalUtils', () => ({
142
+ validateHierarchicalData: vi.fn(() => true),
143
+ sortHierarchicalData: vi.fn((data) => data),
144
+ }));
145
+
146
+ vi.mock('../utils/hierarchicalSorting', () => ({
147
+ sortHierarchicalData: vi.fn((data) => data),
148
+ }));
149
+
150
+ // Create a mock implementation that can be overridden
151
+ const mockUseDataTablePermissions = vi.hoisted(() => vi.fn(() => ({
152
+ permissions: {
153
+ canRead: { can: true },
154
+ canCreate: { can: true },
155
+ canUpdate: { can: true },
156
+ canDelete: { can: true },
157
+ canExport: { can: true },
158
+ canImport: { can: true },
159
+ },
160
+ secureFeatures: {
161
+ search: true,
162
+ sorting: true,
163
+ pagination: true,
164
+ selection: true,
165
+ creation: true,
166
+ editing: true,
167
+ deletion: true,
168
+ deleteSelected: true,
169
+ export: true,
170
+ import: true,
171
+ columnVisibility: true,
172
+ filtering: true,
173
+ grouping: true,
174
+ },
175
+ effectivePageId: 'test-page',
176
+ })));
177
+
178
+ vi.mock('../hooks/useDataTablePermissions', () => ({
179
+ useDataTablePermissions: mockUseDataTablePermissions
180
+ }));
181
+
182
+ vi.mock('../hooks/useTableColumns', () => ({
183
+ useTableColumns: vi.fn(({ columns }) => ({
184
+ enhancedColumns: columns || []
185
+ }))
186
+ }));
187
+
188
+ /**
189
+ * Shared test data and utilities
190
+ */
191
+ export const createTestSetup = () => {
192
+ const mockData = require('./test-utils/dataFactories').createTestData(5);
193
+ const mockColumns = require('./test-utils/dataFactories').createTestColumns();
194
+ const mockRBAC = { pageId: 'test-page' };
195
+ const mockPerformance = { serverSideThreshold: 10000 };
196
+
197
+ return {
198
+ mockData,
199
+ mockColumns,
200
+ mockRBAC,
201
+ mockPerformance,
202
+ };
203
+ };
204
+
205
+ /**
206
+ * Setup function to be called in beforeEach
207
+ */
208
+ export const setupTestMocks = async () => {
209
+ vi.clearAllMocks();
210
+ const { useCan } = await import('../../../rbac/hooks');
211
+ (useCan as any).mockReturnValue({
212
+ can: true,
213
+ isLoading: false,
214
+ error: null,
215
+ });
216
+ };
217
+
@@ -18,7 +18,7 @@ import {
18
18
  } from '../styles';
19
19
 
20
20
  // Mock the cn utility
21
- vi.mock('../../../utils/cn', () => ({
21
+ vi.mock('../../../utils/core/cn', () => ({
22
22
  cn: (...classes: (string | undefined | null | false)[]) =>
23
23
  classes.filter(Boolean).join(' '),
24
24
  }));
@@ -4,6 +4,7 @@ import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '.
4
4
  import { Button } from '../../Button/Button';
5
5
  import { X, Filter } from 'lucide-react';
6
6
  import type { Column } from '@tanstack/react-table';
7
+ import { getColumnHeaderText } from '../utils/columnUtils';
7
8
 
8
9
  interface ColumnFilterProps {
9
10
  column: Column<any, unknown>;
@@ -38,6 +39,9 @@ export function ColumnFilter({
38
39
  };
39
40
 
40
41
  const hasFilter = columnFilterValue !== undefined && columnFilterValue !== '';
42
+
43
+ // Get the default placeholder using column header text
44
+ const defaultPlaceholder = `Filter ${getColumnHeaderText(column)}...`;
41
45
 
42
46
  const renderFilterInput = () => {
43
47
  switch (filterType) {
@@ -48,7 +52,7 @@ export function ColumnFilter({
48
52
  onValueChange={handleFilterChange}
49
53
  >
50
54
  <SelectTrigger className="h-8">
51
- <SelectValue placeholder={placeholder || `Filter ${column.id}...`} />
55
+ <SelectValue placeholder={placeholder || defaultPlaceholder} />
52
56
  </SelectTrigger>
53
57
  <SelectContent>
54
58
  <SelectItem value="">All</SelectItem>
@@ -68,7 +72,7 @@ export function ColumnFilter({
68
72
  type="number"
69
73
  value={columnFilterValue as string || ''}
70
74
  onChange={(e) => handleFilterChange(e.target.value ? Number(e.target.value) : undefined)}
71
- placeholder={placeholder || `Filter ${column.id}...`}
75
+ placeholder={placeholder || defaultPlaceholder}
72
76
  className="h-8 datatable-number-no-spinners"
73
77
  />
74
78
  );
@@ -79,7 +83,7 @@ export function ColumnFilter({
79
83
  type="date"
80
84
  value={columnFilterValue as string || ''}
81
85
  onChange={(e) => handleFilterChange(e.target.value || undefined)}
82
- placeholder={placeholder || `Filter ${column.id}...`}
86
+ placeholder={placeholder || defaultPlaceholder}
83
87
  className="h-8"
84
88
  />
85
89
  );
@@ -89,7 +93,7 @@ export function ColumnFilter({
89
93
  <Input
90
94
  value={columnFilterValue as string || ''}
91
95
  onChange={(e) => handleFilterChange(e.target.value || undefined)}
92
- placeholder={placeholder || `Filter ${column.id}...`}
96
+ placeholder={placeholder || defaultPlaceholder}
93
97
  className="h-8"
94
98
  />
95
99
  );