@jmruthers/pace-core 0.5.76 → 0.5.78

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 (447) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/{RBACService-C4udt_Zp.d.ts → AuthService-Df3IozMG.d.ts} +10 -118
  3. package/dist/{DataTable-ntgmhO2W.d.ts → DataTable-BE0OXZKQ.d.ts} +9 -2
  4. package/dist/{DataTable-4GAVPIEG.js → DataTable-ETGVF4Y5.js} +50 -13
  5. package/dist/{PublicLoadingSpinner-BiNER8F5.d.ts → PublicLoadingSpinner-CnUaz0vG.d.ts} +5 -2
  6. package/dist/{UnifiedAuthProvider-Bj6YCf7c.d.ts → UnifiedAuthProvider-B391Aqum.d.ts} +42 -45
  7. package/dist/{UnifiedAuthProvider-3NKDOSOK.js → UnifiedAuthProvider-P5SOJAQ6.js} +4 -5
  8. package/dist/{api-DDMUKIUD.js → api-KG4A2X7P.js} +9 -3
  9. package/dist/{audit-6TOCAMKO.js → audit-65VNHEV2.js} +2 -2
  10. package/dist/{chunk-K34IM5CT.js → chunk-2OGV6IRV.js} +196 -626
  11. package/dist/chunk-2OGV6IRV.js.map +1 -0
  12. package/dist/{chunk-NTNILOBC.js → chunk-5BO3MI5Y.js} +4 -4
  13. package/dist/{chunk-XLZ7U46Z.js → chunk-CVMVPYAL.js} +9 -60
  14. package/dist/chunk-CVMVPYAL.js.map +1 -0
  15. package/dist/{chunk-URUTVZ7N.js → chunk-FL4ZCQLD.js} +2 -2
  16. package/dist/{chunk-LW7MMEAQ.js → chunk-FT2M4R4F.js} +2 -2
  17. package/dist/{chunk-5BSLGBYI.js → chunk-JCQZ6LA7.js} +2 -8
  18. package/dist/{chunk-5BSLGBYI.js.map → chunk-JCQZ6LA7.js.map} +1 -1
  19. package/dist/{chunk-KHJS6VIA.js → chunk-LRQ6RBJC.js} +157 -112
  20. package/dist/chunk-LRQ6RBJC.js.map +1 -0
  21. package/dist/{chunk-WN6XJWOS.js → chunk-MNJXXD6C.js} +274 -743
  22. package/dist/chunk-MNJXXD6C.js.map +1 -0
  23. package/dist/{chunk-KK73ZB4E.js → chunk-PTR5PMPE.js} +153 -132
  24. package/dist/chunk-PTR5PMPE.js.map +1 -0
  25. package/dist/{chunk-B2WTCLCV.js → chunk-Q7APDV6H.js} +18 -8
  26. package/dist/chunk-Q7APDV6H.js.map +1 -0
  27. package/dist/{chunk-A4FUBC7B.js → chunk-QGVSOUJ2.js} +2 -4
  28. package/dist/{chunk-A4FUBC7B.js.map → chunk-QGVSOUJ2.js.map} +1 -1
  29. package/dist/{chunk-FGMFQSHX.js → chunk-S63MFSY6.js} +500 -551
  30. package/dist/chunk-S63MFSY6.js.map +1 -0
  31. package/dist/{chunk-AFGTSUAD.js → chunk-VSOKOFRF.js} +4 -4
  32. package/dist/chunk-WUXCWRL6.js +20 -0
  33. package/dist/chunk-WUXCWRL6.js.map +1 -0
  34. package/dist/{chunk-Y6TXWPJO.js → chunk-YVVGHRGI.js} +105 -31
  35. package/dist/chunk-YVVGHRGI.js.map +1 -0
  36. package/dist/{chunk-M5IWZRBT.js → chunk-ZMNXIJP4.js} +2187 -981
  37. package/dist/chunk-ZMNXIJP4.js.map +1 -0
  38. package/dist/components.d.ts +6 -6
  39. package/dist/components.js +14 -18
  40. package/dist/components.js.map +1 -1
  41. package/dist/{database-C3Szpi5J.d.ts → database-BXAfr2Y_.d.ts} +18 -0
  42. package/dist/hooks.d.ts +5 -5
  43. package/dist/hooks.js +8 -9
  44. package/dist/hooks.js.map +1 -1
  45. package/dist/index.d.ts +19 -27
  46. package/dist/index.js +21 -29
  47. package/dist/index.js.map +1 -1
  48. package/dist/{organisation-BtshODVF.d.ts → organisation-D6qRDtbF.d.ts} +1 -1
  49. package/dist/providers.d.ts +7 -21
  50. package/dist/providers.js +3 -10
  51. package/dist/rbac/index.d.ts +71 -221
  52. package/dist/rbac/index.js +15 -16
  53. package/dist/{types-CGX9Vyf5.d.ts → types-BDg1mAGG.d.ts} +36 -6
  54. package/dist/types.d.ts +3 -3
  55. package/dist/types.js +61 -18
  56. package/dist/types.js.map +1 -1
  57. package/dist/{unified-CM7T0aTK.d.ts → unified-DQ4VcT7H.d.ts} +1 -1
  58. package/dist/{usePublicRouteParams-B-CumWRc.d.ts → usePublicRouteParams-BlgwXweB.d.ts} +3 -3
  59. package/dist/utils.d.ts +2 -2
  60. package/dist/utils.js +52 -9
  61. package/dist/utils.js.map +1 -1
  62. package/docs/CONTENT_AUDIT_REPORT.md +253 -0
  63. package/docs/DOCUMENTATION_AUDIT.md +172 -0
  64. package/docs/README.md +142 -147
  65. package/docs/STYLE_GUIDE.md +37 -0
  66. package/docs/api/classes/ColumnFactory.md +17 -17
  67. package/docs/api/classes/ErrorBoundary.md +1 -1
  68. package/docs/api/classes/InvalidScopeError.md +4 -4
  69. package/docs/api/classes/MissingUserContextError.md +4 -4
  70. package/docs/api/classes/OrganisationContextRequiredError.md +4 -4
  71. package/docs/api/classes/PermissionDeniedError.md +5 -5
  72. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  73. package/docs/api/classes/RBACAuditManager.md +8 -8
  74. package/docs/api/classes/RBACCache.md +35 -5
  75. package/docs/api/classes/RBACEngine.md +49 -20
  76. package/docs/api/classes/RBACError.md +4 -4
  77. package/docs/api/classes/RBACNotInitializedError.md +4 -4
  78. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  79. package/docs/api/classes/StorageUtils.md +1 -1
  80. package/docs/api/enums/FileCategory.md +1 -1
  81. package/docs/api/interfaces/AggregateConfig.md +4 -4
  82. package/docs/api/interfaces/ButtonProps.md +1 -1
  83. package/docs/api/interfaces/CardProps.md +1 -1
  84. package/docs/api/interfaces/ColorPalette.md +1 -1
  85. package/docs/api/interfaces/ColorShade.md +1 -1
  86. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  87. package/docs/api/interfaces/DataRecord.md +11 -0
  88. package/docs/api/interfaces/DataTableAction.md +65 -29
  89. package/docs/api/interfaces/DataTableColumn.md +36 -23
  90. package/docs/api/interfaces/DataTableProps.md +80 -38
  91. package/docs/api/interfaces/DataTableToolbarButton.md +7 -7
  92. package/docs/api/interfaces/EmptyStateConfig.md +5 -5
  93. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  94. package/docs/api/interfaces/EventLogoProps.md +1 -1
  95. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  96. package/docs/api/interfaces/FileMetadata.md +1 -1
  97. package/docs/api/interfaces/FileReference.md +1 -1
  98. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  99. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  100. package/docs/api/interfaces/FileUploadProps.md +1 -1
  101. package/docs/api/interfaces/FooterProps.md +1 -1
  102. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  103. package/docs/api/interfaces/InputProps.md +1 -1
  104. package/docs/api/interfaces/LabelProps.md +1 -1
  105. package/docs/api/interfaces/LoginFormProps.md +1 -1
  106. package/docs/api/interfaces/NavigationAccessRecord.md +11 -11
  107. package/docs/api/interfaces/NavigationContextType.md +9 -9
  108. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  109. package/docs/api/interfaces/NavigationItem.md +1 -1
  110. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  111. package/docs/api/interfaces/NavigationProviderProps.md +7 -7
  112. package/docs/api/interfaces/Organisation.md +1 -1
  113. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  114. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  115. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  116. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  117. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  118. package/docs/api/interfaces/PaceLoginPageProps.md +16 -3
  119. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  120. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  121. package/docs/api/interfaces/PagePermissionGuardProps.md +2 -2
  122. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  123. package/docs/api/interfaces/PaletteData.md +1 -1
  124. package/docs/api/interfaces/PermissionEnforcerProps.md +4 -4
  125. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  126. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  127. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  128. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  129. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  130. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  131. package/docs/api/interfaces/RBACConfig.md +1 -1
  132. package/docs/api/interfaces/RBACLogger.md +1 -1
  133. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  134. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  135. package/docs/api/interfaces/RouteAccessRecord.md +2 -2
  136. package/docs/api/interfaces/RouteConfig.md +2 -2
  137. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  138. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  139. package/docs/api/interfaces/StorageConfig.md +1 -1
  140. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  141. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  142. package/docs/api/interfaces/StorageListOptions.md +1 -1
  143. package/docs/api/interfaces/StorageListResult.md +1 -1
  144. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  145. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  146. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  147. package/docs/api/interfaces/StyleImport.md +1 -1
  148. package/docs/api/interfaces/SwitchProps.md +1 -1
  149. package/docs/api/interfaces/ToastActionElement.md +1 -1
  150. package/docs/api/interfaces/ToastProps.md +1 -1
  151. package/docs/api/interfaces/UnifiedAuthContextType.md +94 -521
  152. package/docs/api/interfaces/UnifiedAuthProviderProps.md +16 -16
  153. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  154. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  155. package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
  156. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  157. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  158. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  159. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  160. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  161. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  162. package/docs/api/interfaces/UserEventAccess.md +11 -11
  163. package/docs/api/interfaces/UserMenuProps.md +1 -1
  164. package/docs/api/interfaces/UserProfile.md +1 -1
  165. package/docs/api/modules.md +251 -269
  166. package/docs/api-reference/components.md +193 -0
  167. package/docs/api-reference/hooks.md +265 -0
  168. package/docs/api-reference/providers.md +6 -0
  169. package/docs/api-reference/types.md +6 -0
  170. package/docs/api-reference/utilities.md +207 -0
  171. package/docs/architecture/README.md +6 -0
  172. package/docs/{database-schema-requirements.md → architecture/database-schema-requirements.md} +6 -0
  173. package/docs/architecture/rbac-security-architecture.md +258 -0
  174. package/docs/architecture/services.md +9 -1
  175. package/docs/best-practices/README.md +6 -0
  176. package/docs/best-practices/accessibility.md +6 -0
  177. package/docs/{common-patterns.md → best-practices/common-patterns.md} +6 -0
  178. package/docs/best-practices/deployment.md +6 -0
  179. package/docs/best-practices/performance.md +475 -2
  180. package/docs/best-practices/security.md +6 -0
  181. package/docs/best-practices/testing.md +6 -0
  182. package/docs/core-concepts/authentication.md +6 -0
  183. package/docs/core-concepts/events.md +6 -0
  184. package/docs/core-concepts/organisations.md +6 -0
  185. package/docs/core-concepts/permissions.md +6 -0
  186. package/docs/core-concepts/rbac-system.md +8 -0
  187. package/docs/documentation-index.md +121 -182
  188. package/docs/{consuming-app-vite-config.md → getting-started/consuming-app-vite-config.md} +6 -0
  189. package/docs/getting-started/documentation-index.md +40 -0
  190. package/docs/getting-started/examples/README.md +878 -35
  191. package/docs/{faq.md → getting-started/faq.md} +7 -1
  192. package/docs/getting-started/installation-guide.md +6 -0
  193. package/docs/{quick-reference.md → getting-started/quick-reference.md} +6 -0
  194. package/docs/implementation-guides/app-layout.md +6 -0
  195. package/docs/implementation-guides/authentication.md +1021 -0
  196. package/docs/implementation-guides/component-styling.md +6 -0
  197. package/docs/implementation-guides/data-tables.md +1264 -2076
  198. package/docs/implementation-guides/dynamic-colors.md +6 -0
  199. package/docs/implementation-guides/event-theming-summary.md +6 -0
  200. package/docs/{file-reference-system.md → implementation-guides/file-reference-system.md} +6 -0
  201. package/docs/implementation-guides/file-upload-storage.md +6 -0
  202. package/docs/implementation-guides/forms.md +6 -0
  203. package/docs/implementation-guides/inactivity-tracking.md +6 -0
  204. package/docs/implementation-guides/navigation.md +6 -0
  205. package/docs/implementation-guides/organisation-security.md +6 -0
  206. package/docs/implementation-guides/permission-enforcement.md +6 -0
  207. package/docs/implementation-guides/public-pages-advanced.md +6 -0
  208. package/docs/implementation-guides/public-pages.md +6 -0
  209. package/docs/migration/MIGRATION_GUIDE.md +827 -351
  210. package/docs/migration/README.md +7 -1
  211. package/docs/migration/organisation-context-timing-fix.md +6 -0
  212. package/docs/migration/rbac-migration.md +44 -1
  213. package/docs/migration/service-architecture.md +6 -0
  214. package/docs/migration/v0.4.15-tailwind-scanning.md +6 -0
  215. package/docs/migration/v0.4.16-css-first-approach.md +6 -0
  216. package/docs/migration/v0.4.17-source-path-fix.md +6 -0
  217. package/docs/rbac/README-rbac-rls-integration.md +6 -0
  218. package/docs/rbac/README.md +6 -0
  219. package/docs/rbac/advanced-patterns.md +6 -0
  220. package/docs/rbac/api-reference.md +7 -1
  221. package/docs/rbac/breaking-changes-v3.md +222 -0
  222. package/docs/rbac/examples/rbac-rls-integration-example.md +6 -0
  223. package/docs/rbac/examples.md +6 -0
  224. package/docs/rbac/getting-started.md +6 -0
  225. package/docs/rbac/migration-guide.md +260 -0
  226. package/docs/rbac/quick-start.md +70 -13
  227. package/docs/rbac/rbac-rls-integration.md +6 -0
  228. package/docs/rbac/super-admin-guide.md +6 -0
  229. package/docs/rbac/troubleshooting.md +6 -0
  230. package/docs/security/README.md +6 -0
  231. package/docs/security/checklist.md +6 -0
  232. package/docs/styles/README.md +7 -1
  233. package/docs/{usage.md → styles/usage.md} +6 -0
  234. package/docs/testing/README.md +6 -0
  235. package/docs/{visual-testing.md → testing/visual-testing.md} +6 -0
  236. package/docs/troubleshooting/README.md +387 -5
  237. package/docs/troubleshooting/cake-page-permission-guard-issue-summary.md +6 -0
  238. package/docs/troubleshooting/common-issues.md +6 -0
  239. package/docs/troubleshooting/database-view-compatibility.md +6 -0
  240. package/docs/troubleshooting/organisation-context-setup.md +6 -0
  241. package/docs/troubleshooting/react-hooks-issue-analysis.md +6 -0
  242. package/docs/troubleshooting/styling-issues.md +6 -0
  243. package/docs/troubleshooting/tailwind-content-scanning.md +6 -0
  244. package/package.json +1 -1
  245. package/src/__tests__/helpers/__tests__/test-providers.test.tsx +2 -1
  246. package/src/__tests__/helpers/test-providers.tsx +3 -53
  247. package/src/components/DataTable/DataTable.test.tsx +319 -0
  248. package/src/components/DataTable/DataTable.tsx +32 -11
  249. package/src/components/DataTable/__tests__/{DataTable.comprehensive.test.tsx → DataTable.comprehensive.test.tsx.skip} +6 -4
  250. package/src/components/DataTable/__tests__/{DataTable.test.tsx → DataTable.test.tsx.skip} +6 -4
  251. package/src/components/DataTable/__tests__/DataTableCore.test.tsx +31 -9
  252. package/src/components/DataTable/__tests__/a11y.basic.test.tsx +601 -0
  253. package/src/components/DataTable/__tests__/keyboard.test.tsx +615 -0
  254. package/src/components/DataTable/__tests__/pagination.modes.test.tsx +639 -0
  255. package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx.skip +330 -0
  256. package/src/components/DataTable/components/AccessDeniedPage.tsx +2 -2
  257. package/src/components/DataTable/components/ActionButtons.tsx +88 -104
  258. package/src/components/DataTable/components/DataTableCore.tsx +309 -337
  259. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +4 -2
  260. package/src/components/DataTable/components/DataTableModals.tsx +22 -1
  261. package/src/components/DataTable/components/EditableRow.tsx +69 -84
  262. package/src/components/DataTable/components/EmptyState.tsx +5 -1
  263. package/src/components/DataTable/components/ImportModal.tsx +65 -36
  264. package/src/components/DataTable/components/PaginationControls.tsx +40 -100
  265. package/src/components/DataTable/components/UnifiedTableBody.tsx +125 -148
  266. package/src/components/DataTable/context/DataTableContext.tsx +1 -1
  267. package/src/components/DataTable/core/ColumnFactory.ts +5 -0
  268. package/src/components/DataTable/examples/HierarchicalActionsExample.tsx +12 -10
  269. package/src/components/DataTable/examples/HierarchicalExample.tsx +1 -1
  270. package/src/components/DataTable/examples/InitialPageSizeExample.tsx +1 -0
  271. package/src/components/DataTable/examples/PerformanceExample.tsx +1 -0
  272. package/src/components/DataTable/hooks/__tests__/useColumnOrderPersistence.test.ts +1 -5
  273. package/src/components/DataTable/hooks/__tests__/useColumnVisibilityPersistence.test.ts +167 -0
  274. package/src/components/DataTable/hooks/index.ts +7 -0
  275. package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +32 -15
  276. package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +102 -0
  277. package/src/components/DataTable/hooks/useDataTableConfiguration.ts +89 -0
  278. package/src/components/DataTable/hooks/useDataTableDataPipeline.ts +117 -0
  279. package/src/components/DataTable/hooks/useDataTablePermissions.ts +71 -27
  280. package/src/components/DataTable/hooks/useDataTableState.ts +39 -11
  281. package/src/components/DataTable/hooks/useEffectiveColumnOrder.ts +33 -0
  282. package/src/components/DataTable/hooks/useHierarchicalState.ts +15 -1
  283. package/src/components/DataTable/hooks/useKeyboardNavigation.ts +447 -0
  284. package/src/components/DataTable/hooks/useServerSideDataEffect.ts +94 -0
  285. package/src/components/DataTable/hooks/useTableColumns.ts +10 -7
  286. package/src/components/DataTable/hooks/useTableHandlers.ts +174 -0
  287. package/src/components/DataTable/index.ts +12 -3
  288. package/src/components/DataTable/types.ts +129 -9
  289. package/src/components/DataTable/utils/__tests__/exportUtils.test.ts +159 -22
  290. package/src/components/DataTable/utils/__tests__/flexibleImport.test.ts +111 -0
  291. package/src/components/DataTable/utils/__tests__/rowUtils.test.ts +15 -29
  292. package/src/components/DataTable/utils/a11yUtils.ts +244 -0
  293. package/src/components/DataTable/utils/debugTools.ts +609 -0
  294. package/src/components/DataTable/utils/exportUtils.ts +114 -16
  295. package/src/components/DataTable/utils/flexibleImport.ts +202 -32
  296. package/src/components/DataTable/utils/hierarchicalUtils.ts +1 -1
  297. package/src/components/DataTable/utils/index.ts +2 -0
  298. package/src/components/DataTable/utils/paginationUtils.ts +350 -0
  299. package/src/components/DataTable/utils/rowUtils.ts +6 -5
  300. package/src/components/NavigationMenu/NavigationMenu.test.tsx +19 -24
  301. package/src/components/NavigationMenu/NavigationMenu.tsx +19 -8
  302. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.security.test.tsx +1 -23
  303. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +56 -6
  304. package/src/components/PaceLoginPage/PaceLoginPage.tsx +137 -13
  305. package/src/components/PublicLayout/__tests__/PublicPageHeader.test.tsx +1 -1
  306. package/src/components/Select/Select.tsx +1 -0
  307. package/src/components/examples/PermissionExample.tsx +173 -0
  308. package/src/examples/CorrectPublicPageImplementation.tsx +301 -0
  309. package/src/examples/PublicEventPage.tsx +274 -0
  310. package/src/examples/PublicPageApp.tsx +308 -0
  311. package/src/examples/PublicPageUsageExample.tsx +216 -0
  312. package/src/hooks/__tests__/useOrganisationPermissions.unit.test.tsx +12 -1
  313. package/src/hooks/__tests__/useOrganisationSecurity.unit.test.tsx +129 -17
  314. package/src/hooks/__tests__/useRBAC.unit.test.ts +151 -846
  315. package/src/hooks/useOrganisationPermissions.test.ts +42 -18
  316. package/src/hooks/useOrganisationPermissions.ts +12 -6
  317. package/src/hooks/useOrganisationSecurity.test.ts +138 -85
  318. package/src/hooks/useOrganisationSecurity.ts +41 -10
  319. package/src/index.ts +0 -1
  320. package/src/providers/AuthProvider.simplified.tsx +880 -0
  321. package/src/providers/UnifiedAuthProvider.test.simple.tsx +8 -8
  322. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +29 -19
  323. package/src/providers/index.ts +0 -1
  324. package/src/providers/services/EventServiceProvider.tsx +19 -15
  325. package/src/providers/services/InactivityServiceProvider.tsx +19 -15
  326. package/src/providers/services/OrganisationServiceProvider.tsx +19 -15
  327. package/src/providers/services/UnifiedAuthProvider.tsx +156 -127
  328. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +1 -1
  329. package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +3 -3
  330. package/src/rbac/README.md +1 -1
  331. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +25 -27
  332. package/src/rbac/__tests__/auth-rbac-security.integration.test.tsx +313 -0
  333. package/src/rbac/__tests__/engine.comprehensive.test.ts +114 -348
  334. package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +28 -110
  335. package/src/rbac/__tests__/rbac-engine-simplified.test.ts +33 -85
  336. package/src/rbac/__tests__/scenarios.user-role.test.tsx +2 -2
  337. package/src/rbac/adapters.tsx +26 -69
  338. package/src/rbac/api.test.ts +90 -27
  339. package/src/rbac/api.ts +61 -10
  340. package/src/rbac/audit.test.ts +33 -38
  341. package/src/rbac/audit.ts +21 -6
  342. package/src/rbac/cache.ts +33 -1
  343. package/src/rbac/components/NavigationGuard.tsx +11 -11
  344. package/src/rbac/components/NavigationProvider.test.tsx +11 -5
  345. package/src/rbac/components/NavigationProvider.tsx +37 -13
  346. package/src/rbac/components/PagePermissionGuard.tsx +111 -50
  347. package/src/rbac/components/PagePermissionProvider.tsx +5 -5
  348. package/src/rbac/components/PermissionEnforcer.tsx +11 -11
  349. package/src/rbac/components/RoleBasedRouter.tsx +5 -5
  350. package/src/rbac/components/SecureDataProvider.tsx +5 -5
  351. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +8 -8
  352. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +14 -14
  353. package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +12 -12
  354. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +6 -6
  355. package/src/rbac/engine.test.simple.ts +19 -13
  356. package/src/rbac/engine.test.ts +1 -0
  357. package/src/rbac/engine.ts +330 -766
  358. package/src/rbac/errors.ts +156 -0
  359. package/src/rbac/hooks/usePermissions.ts +32 -10
  360. package/src/rbac/hooks/useRBAC.test.ts +126 -512
  361. package/src/rbac/hooks/useRBAC.ts +147 -193
  362. package/src/rbac/hooks/useResolvedScope.ts +12 -0
  363. package/src/rbac/index.ts +7 -4
  364. package/src/rbac/security.ts +109 -18
  365. package/src/rbac/types.ts +12 -1
  366. package/src/services/AuthService.ts +2 -15
  367. package/src/services/EventService.ts +43 -46
  368. package/src/services/OrganisationService.ts +51 -31
  369. package/src/services/__tests__/AuthService.test.ts +1 -1
  370. package/src/services/__tests__/EventService.test.ts +1 -1
  371. package/src/services/__tests__/OrganisationService.test.ts +1 -1
  372. package/src/services/base/BaseService.ts +8 -0
  373. package/src/styles/base.css +208 -0
  374. package/src/styles/semantic.css +24 -0
  375. package/src/types/database.generated.ts +7347 -0
  376. package/src/types/database.ts +20 -0
  377. package/src/utils/logger.ts +179 -0
  378. package/src/utils/organisationContext.ts +11 -4
  379. package/src/utils/storage/__tests__/helpers.unit.test.ts +6 -2
  380. package/dist/appNameResolver-UURKN7NF.js +0 -22
  381. package/dist/audit-6TOCAMKO.js.map +0 -1
  382. package/dist/chunk-B2WTCLCV.js.map +0 -1
  383. package/dist/chunk-FGMFQSHX.js.map +0 -1
  384. package/dist/chunk-K34IM5CT.js.map +0 -1
  385. package/dist/chunk-KHJS6VIA.js.map +0 -1
  386. package/dist/chunk-KK73ZB4E.js.map +0 -1
  387. package/dist/chunk-M5IWZRBT.js.map +0 -1
  388. package/dist/chunk-ULBI5JGB.js +0 -109
  389. package/dist/chunk-ULBI5JGB.js.map +0 -1
  390. package/dist/chunk-WN6XJWOS.js.map +0 -1
  391. package/dist/chunk-XLZ7U46Z.js.map +0 -1
  392. package/dist/chunk-Y6TXWPJO.js.map +0 -1
  393. package/docs/DOCUMENTATION_CHECKLIST.md +0 -281
  394. package/docs/TERMINOLOGY.md +0 -231
  395. package/docs/api/interfaces/RBACContextType.md +0 -468
  396. package/docs/api/interfaces/RBACProviderProps.md +0 -107
  397. package/docs/best-practices/performance-expansion.md +0 -473
  398. package/docs/breaking-changes.md +0 -179
  399. package/docs/consuming-app-example.md +0 -290
  400. package/docs/documentation-templates.md +0 -539
  401. package/docs/examples/navigation-menu-auth-fix.md +0 -344
  402. package/docs/getting-started/examples/basic-auth-app.md +0 -520
  403. package/docs/getting-started/examples/full-featured-app.md +0 -616
  404. package/docs/getting-started/quick-start.md +0 -376
  405. package/docs/implementation-guides/datatable-filtering.md +0 -313
  406. package/docs/implementation-guides/datatable-rbac-usage.md +0 -317
  407. package/docs/implementation-guides/hierarchical-datatable.md +0 -850
  408. package/docs/implementation-guides/large-datasets.md +0 -281
  409. package/docs/implementation-guides/performance.md +0 -403
  410. package/docs/migration/quick-migration-guide.md +0 -320
  411. package/docs/migration-guide.md +0 -193
  412. package/docs/migration-guides/unified-auth-provider-mandatory-timeouts.md +0 -226
  413. package/docs/performance/README.md +0 -551
  414. package/docs/style-guide.md +0 -964
  415. package/docs/troubleshooting/authentication-issues.md +0 -334
  416. package/docs/troubleshooting/debugging.md +0 -1117
  417. package/docs/troubleshooting/migration.md +0 -918
  418. package/src/__tests__/hooks/usePermissions.test.ts +0 -261
  419. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.rbac.test.tsx +0 -574
  420. package/src/hooks/__tests__/ServiceHooks.test.tsx +0 -613
  421. package/src/hooks/services/__tests__/useServiceHooks.test.tsx +0 -137
  422. package/src/hooks/services/usePermissions.ts +0 -70
  423. package/src/hooks/services/useRBACService.ts +0 -30
  424. package/src/hooks/usePermissionCheck.ts +0 -150
  425. package/src/providers/__tests__/ServiceProviders.test.tsx +0 -477
  426. package/src/providers/services/RBACServiceProvider.tsx +0 -79
  427. package/src/rbac/__tests__/integration.authflow.test.tsx +0 -119
  428. package/src/rbac/__tests__/integration.navigation.test.tsx +0 -69
  429. package/src/rbac/__tests__/integration.securedata.test.tsx +0 -92
  430. package/src/rbac/__tests__/integration.smoke.test.tsx +0 -73
  431. package/src/rbac/providers/RBACProvider.tsx +0 -645
  432. package/src/rbac/providers/__tests__/RBACProvider.integration.test.tsx +0 -688
  433. package/src/rbac/providers/__tests__/RBACProvider.test.tsx +0 -1186
  434. package/src/rbac/providers/index.ts +0 -11
  435. package/src/services/RBACService.ts +0 -522
  436. package/src/services/__tests__/RBACService.test.ts +0 -492
  437. package/src/services/interfaces/IRBACService.ts +0 -62
  438. package/src/utils/appNameResolver.test 2.ts +0 -494
  439. /package/dist/{DataTable-4GAVPIEG.js.map → DataTable-ETGVF4Y5.js.map} +0 -0
  440. /package/dist/{UnifiedAuthProvider-3NKDOSOK.js.map → UnifiedAuthProvider-P5SOJAQ6.js.map} +0 -0
  441. /package/dist/{api-DDMUKIUD.js.map → api-KG4A2X7P.js.map} +0 -0
  442. /package/dist/{appNameResolver-UURKN7NF.js.map → audit-65VNHEV2.js.map} +0 -0
  443. /package/dist/{chunk-NTNILOBC.js.map → chunk-5BO3MI5Y.js.map} +0 -0
  444. /package/dist/{chunk-URUTVZ7N.js.map → chunk-FL4ZCQLD.js.map} +0 -0
  445. /package/dist/{chunk-LW7MMEAQ.js.map → chunk-FT2M4R4F.js.map} +0 -0
  446. /package/dist/{chunk-AFGTSUAD.js.map → chunk-VSOKOFRF.js.map} +0 -0
  447. /package/docs/{app.css.example → styles/app.css.example} +0 -0
@@ -0,0 +1,601 @@
1
+ /**
2
+ * @file DataTable Accessibility Tests
3
+ * @package @jmruthers/pace-core
4
+ * @module Components/DataTable/Tests
5
+ * @since 0.4.0
6
+ *
7
+ * Comprehensive accessibility tests for DataTable component.
8
+ * Tests WCAG 2.1 AA compliance and screen reader compatibility.
9
+ */
10
+
11
+ import React from 'react';
12
+ import { render, screen, fireEvent, waitFor } from '@testing-library/react';
13
+ import { axe, toHaveNoViolations } from 'jest-axe';
14
+ import userEvent from '@testing-library/user-event';
15
+ import { vi, beforeEach, afterEach } from 'vitest';
16
+ import { DataTable } from '../DataTable';
17
+ import { MockRBACProvider } from './mocks/MockRBACProvider';
18
+ import type { DataTableColumn } from '../types';
19
+
20
+ // Mock the RBAC hooks
21
+ vi.mock('../../../rbac/hooks', () => ({
22
+ useCan: vi.fn(() => ({ can: true, isLoading: false })),
23
+ useResolvedScope: vi.fn(() => ({
24
+ organisationId: 'test-org',
25
+ eventId: 'test-event',
26
+ appId: 'test-app'
27
+ })),
28
+ }));
29
+
30
+ // Mock the UnifiedAuthProvider
31
+ vi.mock('../../../providers/UnifiedAuthProvider', () => ({
32
+ useUnifiedAuth: vi.fn(() => ({
33
+ user: { id: 'test-user', name: 'Test User' },
34
+ isLoading: false,
35
+ error: null,
36
+ })),
37
+ }));
38
+
39
+ // Mock the toast hook
40
+ vi.mock('../../../hooks/useToast', () => ({
41
+ toast: vi.fn(),
42
+ }));
43
+
44
+ // Extend Jest matchers
45
+ expect.extend(toHaveNoViolations);
46
+
47
+ // Test data
48
+ interface TestUser {
49
+ id: string;
50
+ name: string;
51
+ email: string;
52
+ role: string;
53
+ status: 'active' | 'inactive';
54
+ }
55
+
56
+ const testUsers: TestUser[] = [
57
+ { id: '1', name: 'John Doe', email: 'john@example.com', role: 'Admin', status: 'active' },
58
+ { id: '2', name: 'Jane Smith', email: 'jane@example.com', role: 'User', status: 'active' },
59
+ { id: '3', name: 'Bob Johnson', email: 'bob@example.com', role: 'User', status: 'inactive' },
60
+ ];
61
+
62
+ const testColumns: DataTableColumn<TestUser>[] = [
63
+ {
64
+ accessorKey: 'name',
65
+ header: 'Name',
66
+ sortable: true,
67
+ searchable: true,
68
+ },
69
+ {
70
+ accessorKey: 'email',
71
+ header: 'Email',
72
+ sortable: true,
73
+ searchable: true,
74
+ },
75
+ {
76
+ accessorKey: 'role',
77
+ header: 'Role',
78
+ sortable: true,
79
+ },
80
+ {
81
+ accessorKey: 'status',
82
+ header: 'Status',
83
+ sortable: true,
84
+ },
85
+ ];
86
+
87
+ const defaultFeatures = {
88
+ search: true,
89
+ pagination: true,
90
+ sorting: true,
91
+ filtering: true,
92
+ export: false,
93
+ import: false,
94
+ selection: true,
95
+ creation: false,
96
+ editing: false,
97
+ deletion: false,
98
+ deleteSelected: false,
99
+ grouping: false,
100
+ columnVisibility: false,
101
+ columnReordering: false,
102
+ hierarchical: false,
103
+ };
104
+
105
+ // Test wrapper component
106
+ const TestWrapper: React.FC<{ children: React.ReactNode }> = ({ children }) => (
107
+ <MockRBACProvider>
108
+ {children}
109
+ </MockRBACProvider>
110
+ );
111
+
112
+ describe('DataTable Accessibility', () => {
113
+ beforeEach(() => {
114
+ // Clear any existing live regions
115
+ const existingLiveRegions = document.querySelectorAll('[aria-live]');
116
+ existingLiveRegions.forEach(region => {
117
+ if (region.parentNode) {
118
+ region.parentNode.removeChild(region);
119
+ }
120
+ });
121
+ });
122
+
123
+ afterEach(() => {
124
+ // Clean up live regions after each test
125
+ const liveRegions = document.querySelectorAll('[aria-live]');
126
+ liveRegions.forEach(region => {
127
+ if (region.parentNode) {
128
+ region.parentNode.removeChild(region);
129
+ }
130
+ });
131
+ });
132
+
133
+ describe('Semantic Structure', () => {
134
+ it('should use proper table semantics', () => {
135
+ render(
136
+ <TestWrapper>
137
+ <DataTable
138
+ data={testUsers}
139
+ columns={testColumns}
140
+ rbac={{ pageId: 'test-page' }}
141
+ features={defaultFeatures}
142
+ title="Users Table"
143
+ description="List of all users in the system"
144
+ />
145
+ </TestWrapper>
146
+ );
147
+
148
+ // Check for proper table structure
149
+ const table = screen.getByRole('table');
150
+ expect(table).toBeInTheDocument();
151
+ expect(table).toHaveAttribute('aria-label', 'Users Table');
152
+ expect(table).toHaveAttribute('aria-describedby', 'table-description');
153
+
154
+ // Check for table description
155
+ const description = screen.getByText('List of all users in the system');
156
+ expect(description).toHaveAttribute('id', 'table-description');
157
+ });
158
+
159
+ it('should have proper column headers with scope attributes', () => {
160
+ render(
161
+ <TestWrapper>
162
+ <DataTable
163
+ data={testUsers}
164
+ columns={testColumns}
165
+ rbac={{ pageId: 'test-page' }}
166
+ features={defaultFeatures}
167
+ />
168
+ </TestWrapper>
169
+ );
170
+
171
+ // Check column headers
172
+ const nameHeader = screen.getByRole('columnheader', { name: /name/i });
173
+ expect(nameHeader).toHaveAttribute('scope', 'col');
174
+ expect(nameHeader).toHaveAttribute('role', 'columnheader');
175
+
176
+ const emailHeader = screen.getByRole('columnheader', { name: /email/i });
177
+ expect(emailHeader).toHaveAttribute('scope', 'col');
178
+ expect(emailHeader).toHaveAttribute('role', 'columnheader');
179
+ });
180
+
181
+ it('should have proper row and cell structure', () => {
182
+ render(
183
+ <TestWrapper>
184
+ <DataTable
185
+ data={testUsers}
186
+ columns={testColumns}
187
+ rbac={{ pageId: 'test-page' }}
188
+ features={defaultFeatures}
189
+ />
190
+ </TestWrapper>
191
+ );
192
+
193
+ // Check for rows
194
+ const rows = screen.getAllByRole('row');
195
+ expect(rows.length).toBeGreaterThan(1); // Header row + data rows
196
+
197
+ // Check for cells
198
+ const cells = screen.getAllByRole('cell');
199
+ expect(cells.length).toBeGreaterThan(0);
200
+ });
201
+ });
202
+
203
+ describe('ARIA States and Properties', () => {
204
+ it('should have proper aria-sort attributes on sortable columns', () => {
205
+ render(
206
+ <TestWrapper>
207
+ <DataTable
208
+ data={testUsers}
209
+ columns={testColumns}
210
+ rbac={{ pageId: 'test-page' }}
211
+ features={defaultFeatures}
212
+ />
213
+ </TestWrapper>
214
+ );
215
+
216
+ // Check initial sort state
217
+ const nameHeader = screen.getByRole('columnheader', { name: /name/i });
218
+ expect(nameHeader).toHaveAttribute('aria-sort', 'none');
219
+ });
220
+
221
+ it('should update aria-sort when column is sorted', async () => {
222
+ const user = userEvent.setup();
223
+
224
+ render(
225
+ <TestWrapper>
226
+ <DataTable
227
+ data={testUsers}
228
+ columns={testColumns}
229
+ rbac={{ pageId: 'test-page' }}
230
+ features={defaultFeatures}
231
+ />
232
+ </TestWrapper>
233
+ );
234
+
235
+ const nameHeader = screen.getByRole('columnheader', { name: /name/i });
236
+ const sortButton = nameHeader.querySelector('button');
237
+
238
+ if (sortButton) {
239
+ // Click to sort ascending
240
+ await user.click(sortButton);
241
+
242
+ await waitFor(() => {
243
+ expect(nameHeader).toHaveAttribute('aria-sort', 'ascending');
244
+ });
245
+
246
+ // Click to sort descending
247
+ await user.click(sortButton);
248
+
249
+ await waitFor(() => {
250
+ expect(nameHeader).toHaveAttribute('aria-sort', 'descending');
251
+ });
252
+ }
253
+ });
254
+
255
+ it.skip('should have proper aria-selected on selectable rows', () => {
256
+ render(
257
+ <TestWrapper>
258
+ <DataTable
259
+ data={testUsers}
260
+ columns={testColumns}
261
+ rbac={{ pageId: 'test-page' }}
262
+ features={defaultFeatures}
263
+ />
264
+ </TestWrapper>
265
+ );
266
+
267
+ // Find data rows (exclude header row)
268
+ const rows = screen.getAllByRole('row');
269
+ const dataRows = rows.slice(1); // Skip header row
270
+
271
+ // Check that we have data rows and they have aria-selected
272
+ expect(dataRows.length).toBeGreaterThan(0);
273
+
274
+ dataRows.forEach((row, index) => {
275
+ // Initially, rows should not be selected
276
+ const ariaSelected = row.getAttribute('aria-selected');
277
+ console.log(`Row ${index}:`, ariaSelected);
278
+ expect(row).toHaveAttribute('aria-selected');
279
+ expect(ariaSelected).toBe('false'); // Should be false initially
280
+ });
281
+ });
282
+
283
+ it('should have aria-busy when loading', () => {
284
+ render(
285
+ <TestWrapper>
286
+ <DataTable
287
+ data={testUsers}
288
+ columns={testColumns}
289
+ rbac={{ pageId: 'test-page' }}
290
+ features={defaultFeatures}
291
+ isLoading={true}
292
+ />
293
+ </TestWrapper>
294
+ );
295
+
296
+ // When loading, the table might not be rendered, so check for loading state
297
+ const loadingElement = screen.getByText('Loading...');
298
+ expect(loadingElement).toBeInTheDocument();
299
+ expect(loadingElement).toHaveAttribute('aria-live', 'polite');
300
+ });
301
+
302
+ it('should not have aria-busy when not loading', () => {
303
+ render(
304
+ <TestWrapper>
305
+ <DataTable
306
+ data={testUsers}
307
+ columns={testColumns}
308
+ rbac={{ pageId: 'test-page' }}
309
+ features={defaultFeatures}
310
+ isLoading={false}
311
+ />
312
+ </TestWrapper>
313
+ );
314
+
315
+ const table = screen.getByRole('table');
316
+ expect(table).toHaveAttribute('aria-busy', 'false');
317
+ });
318
+ });
319
+
320
+ describe('Live Region Announcements', () => {
321
+ it('should create a live region for announcements', () => {
322
+ render(
323
+ <TestWrapper>
324
+ <DataTable
325
+ data={testUsers}
326
+ columns={testColumns}
327
+ rbac={{ pageId: 'test-page' }}
328
+ features={defaultFeatures}
329
+ />
330
+ </TestWrapper>
331
+ );
332
+
333
+ // Check for live region
334
+ const liveRegion = document.querySelector('[aria-live="polite"]');
335
+ expect(liveRegion).toBeInTheDocument();
336
+ });
337
+
338
+ it('should announce sort changes', async () => {
339
+ const user = userEvent.setup();
340
+
341
+ render(
342
+ <TestWrapper>
343
+ <DataTable
344
+ data={testUsers}
345
+ columns={testColumns}
346
+ rbac={{ pageId: 'test-page' }}
347
+ features={defaultFeatures}
348
+ />
349
+ </TestWrapper>
350
+ );
351
+
352
+ const nameHeader = screen.getByRole('columnheader', { name: /name/i });
353
+ const sortButton = nameHeader.querySelector('button');
354
+
355
+ if (sortButton) {
356
+ await user.click(sortButton);
357
+
358
+ // Wait for announcement
359
+ await waitFor(() => {
360
+ const liveRegion = document.querySelector('[aria-live="polite"]');
361
+ expect(liveRegion?.textContent).toContain('sorted by Name');
362
+ }, { timeout: 2000 });
363
+ }
364
+ });
365
+ });
366
+
367
+ describe('Empty State Accessibility', () => {
368
+ it('should have proper role and aria-live for empty state', () => {
369
+ render(
370
+ <TestWrapper>
371
+ <DataTable
372
+ data={[]}
373
+ columns={testColumns}
374
+ rbac={{ pageId: 'test-page' }}
375
+ features={defaultFeatures}
376
+ />
377
+ </TestWrapper>
378
+ );
379
+
380
+ const emptyStates = screen.getAllByRole('status');
381
+ expect(emptyStates.length).toBeGreaterThan(0);
382
+ // Check that at least one has aria-live
383
+ const liveEmptyState = emptyStates.find(state => state.hasAttribute('aria-live'));
384
+ expect(liveEmptyState).toBeInTheDocument();
385
+ expect(liveEmptyState).toHaveAttribute('aria-live', 'polite');
386
+ });
387
+ });
388
+
389
+ describe('Keyboard Navigation', () => {
390
+ it('should support keyboard navigation for sortable headers', async () => {
391
+ const user = userEvent.setup();
392
+
393
+ render(
394
+ <TestWrapper>
395
+ <DataTable
396
+ data={testUsers}
397
+ columns={testColumns}
398
+ rbac={{ pageId: 'test-page' }}
399
+ features={defaultFeatures}
400
+ />
401
+ </TestWrapper>
402
+ );
403
+
404
+ const nameHeader = screen.getByRole('columnheader', { name: /name/i });
405
+ const nameButton = nameHeader.querySelector('button');
406
+
407
+ // Focus the button inside the header
408
+ nameButton?.focus();
409
+ expect(nameButton).toHaveFocus();
410
+
411
+ // Press Enter to sort
412
+ await user.keyboard('{Enter}');
413
+
414
+ await waitFor(() => {
415
+ expect(nameHeader).toHaveAttribute('aria-sort', 'ascending');
416
+ });
417
+ });
418
+
419
+ it.skip('should have proper tab order', () => {
420
+ render(
421
+ <TestWrapper>
422
+ <DataTable
423
+ data={testUsers}
424
+ columns={testColumns}
425
+ rbac={{ pageId: 'test-page' }}
426
+ features={defaultFeatures}
427
+ />
428
+ </TestWrapper>
429
+ );
430
+
431
+ // Check that sortable headers are focusable
432
+ const sortableHeaders = screen.getAllByRole('columnheader').filter(header =>
433
+ header.hasAttribute('tabindex') && header.getAttribute('tabindex') === '0'
434
+ );
435
+
436
+ expect(sortableHeaders.length).toBeGreaterThan(0);
437
+ });
438
+ });
439
+
440
+ describe('Screen Reader Labels', () => {
441
+ it('should have proper aria-labels for sort buttons', () => {
442
+ render(
443
+ <TestWrapper>
444
+ <DataTable
445
+ data={testUsers}
446
+ columns={testColumns}
447
+ rbac={{ pageId: 'test-page' }}
448
+ features={defaultFeatures}
449
+ />
450
+ </TestWrapper>
451
+ );
452
+
453
+ const nameHeader = screen.getByRole('columnheader', { name: /name/i });
454
+ const sortButton = nameHeader.querySelector('button');
455
+
456
+ if (sortButton) {
457
+ expect(sortButton).toHaveAttribute('aria-label');
458
+ expect(sortButton.getAttribute('aria-label')).toContain('Sort');
459
+ expect(sortButton.getAttribute('aria-label')).toContain('Name');
460
+ }
461
+ });
462
+
463
+ it('should have proper aria-labels for expand/collapse buttons', async () => {
464
+ const hierarchicalData = [
465
+ { id: '1', name: 'Parent 1', isParent: true, parentId: null },
466
+ { id: '2', name: 'Child 1', isParent: false, parentId: '1' },
467
+ ];
468
+
469
+ const hierarchicalFeatures = {
470
+ ...defaultFeatures,
471
+ hierarchical: true,
472
+ };
473
+
474
+ render(
475
+ <TestWrapper>
476
+ <DataTable
477
+ data={hierarchicalData}
478
+ columns={testColumns}
479
+ rbac={{ pageId: 'test-page' }}
480
+ features={hierarchicalFeatures}
481
+ hierarchical={{
482
+ enabled: true,
483
+ defaultExpanded: false,
484
+ }}
485
+ />
486
+ </TestWrapper>
487
+ );
488
+
489
+ // Look for expand buttons
490
+ const expandButtons = screen.queryAllByLabelText(/expand row|collapse row/i);
491
+ expandButtons.forEach(button => {
492
+ expect(button).toHaveAttribute('aria-label');
493
+ expect(button).toHaveAttribute('title');
494
+ });
495
+ });
496
+ });
497
+
498
+ describe('Axe Accessibility Tests', () => {
499
+ it('should not have any accessibility violations', async () => {
500
+ const { container } = render(
501
+ <TestWrapper>
502
+ <DataTable
503
+ data={testUsers}
504
+ columns={testColumns}
505
+ rbac={{ pageId: 'test-page' }}
506
+ features={defaultFeatures}
507
+ title="Users Table"
508
+ description="List of all users in the system"
509
+ />
510
+ </TestWrapper>
511
+ );
512
+
513
+ const results = await axe(container);
514
+ expect(results).toHaveNoViolations();
515
+ });
516
+
517
+ it('should not have accessibility violations with empty data', async () => {
518
+ const { container } = render(
519
+ <TestWrapper>
520
+ <DataTable
521
+ data={[]}
522
+ columns={testColumns}
523
+ rbac={{ pageId: 'test-page' }}
524
+ features={defaultFeatures}
525
+ title="Empty Table"
526
+ />
527
+ </TestWrapper>
528
+ );
529
+
530
+ const results = await axe(container);
531
+ expect(results).toHaveNoViolations();
532
+ });
533
+
534
+ it('should not have accessibility violations when loading', async () => {
535
+ const { container } = render(
536
+ <TestWrapper>
537
+ <DataTable
538
+ data={testUsers}
539
+ columns={testColumns}
540
+ rbac={{ pageId: 'test-page' }}
541
+ features={defaultFeatures}
542
+ isLoading={true}
543
+ />
544
+ </TestWrapper>
545
+ );
546
+
547
+ const results = await axe(container);
548
+ expect(results).toHaveNoViolations();
549
+ });
550
+
551
+ it('should not have accessibility violations with selection enabled', async () => {
552
+ const { container } = render(
553
+ <TestWrapper>
554
+ <DataTable
555
+ data={testUsers}
556
+ columns={testColumns}
557
+ rbac={{ pageId: 'test-page' }}
558
+ features={{
559
+ ...defaultFeatures,
560
+ selection: true,
561
+ }}
562
+ />
563
+ </TestWrapper>
564
+ );
565
+
566
+ const results = await axe(container);
567
+ expect(results).toHaveNoViolations();
568
+ });
569
+ });
570
+
571
+ describe('Focus Management', () => {
572
+ it('should maintain focus when sorting', async () => {
573
+ const user = userEvent.setup();
574
+
575
+ render(
576
+ <TestWrapper>
577
+ <DataTable
578
+ data={testUsers}
579
+ columns={testColumns}
580
+ rbac={{ pageId: 'test-page' }}
581
+ features={defaultFeatures}
582
+ />
583
+ </TestWrapper>
584
+ );
585
+
586
+ const nameHeader = screen.getByRole('columnheader', { name: /name/i });
587
+ const sortButton = nameHeader.querySelector('button');
588
+
589
+ if (sortButton) {
590
+ // Focus and click the sort button
591
+ sortButton.focus();
592
+ await user.click(sortButton);
593
+
594
+ // Focus should remain on the button after sorting
595
+ await waitFor(() => {
596
+ expect(document.activeElement).toBe(sortButton);
597
+ });
598
+ }
599
+ });
600
+ });
601
+ });