@jmruthers/pace-core 0.5.191 → 0.6.1

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 (380) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +7 -1
  3. package/cursor-rules/00-pace-core-compliance.mdc +372 -0
  4. package/cursor-rules/01-standards-compliance.mdc +275 -0
  5. package/cursor-rules/02-project-structure.mdc +200 -0
  6. package/cursor-rules/03-solid-principles.mdc +341 -0
  7. package/cursor-rules/04-testing-standards.mdc +315 -0
  8. package/cursor-rules/05-bug-reports-and-features.mdc +246 -0
  9. package/cursor-rules/06-code-quality.mdc +392 -0
  10. package/cursor-rules/07-tech-stack-compliance.mdc +309 -0
  11. package/cursor-rules/CHANGELOG.md +101 -0
  12. package/cursor-rules/README.md +191 -0
  13. package/dist/{AuthService-CbP_utw2.d.ts → AuthService-DjnJHDtC.d.ts} +1 -0
  14. package/dist/{DataTable-Be6dH_dR.d.ts → DataTable-CH1U5Tpy.d.ts} +1 -1
  15. package/dist/{DataTable-WKRZD47S.js → DataTable-DQ7RSOHE.js} +8 -7
  16. package/dist/{PublicPageProvider-ULXC_u6U.d.ts → PublicPageProvider-ce4xlHYA.d.ts} +37 -156
  17. package/dist/{UnifiedAuthProvider-BYA9qB-o.d.ts → UnifiedAuthProvider-185Ih4dj.d.ts} +2 -0
  18. package/dist/{UnifiedAuthProvider-FTSG5XH7.js → UnifiedAuthProvider-ATAP5UTR.js} +3 -3
  19. package/dist/{api-IHKALJZD.js → api-N774RPUA.js} +2 -2
  20. package/dist/{chunk-6C4YBBJM.js → chunk-3QRJFVBR.js} +1 -1
  21. package/dist/chunk-3QRJFVBR.js.map +1 -0
  22. package/dist/{chunk-OETXORNB.js → chunk-3XTALGJF.js} +211 -136
  23. package/dist/chunk-3XTALGJF.js.map +1 -0
  24. package/dist/{chunk-6TQDD426.js → chunk-4N5C5XZU.js} +47 -228
  25. package/dist/chunk-4N5C5XZU.js.map +1 -0
  26. package/dist/{chunk-LOMZXPSN.js → chunk-4ZC4GX36.js} +47 -74
  27. package/dist/chunk-4ZC4GX36.js.map +1 -0
  28. package/dist/{chunk-6LTQQAT6.js → chunk-BYFSK72L.js} +357 -158
  29. package/dist/chunk-BYFSK72L.js.map +1 -0
  30. package/dist/{chunk-XYXSXPUK.js → chunk-EXUD6RNJ.js} +50 -10
  31. package/dist/chunk-EXUD6RNJ.js.map +1 -0
  32. package/dist/{chunk-VKB2CO4Z.js → chunk-GLK6VM3F.js} +244 -249
  33. package/dist/chunk-GLK6VM3F.js.map +1 -0
  34. package/dist/{chunk-HW3OVDUF.js → chunk-J36DSWQK.js} +1 -1
  35. package/dist/{chunk-HW3OVDUF.js.map → chunk-J36DSWQK.js.map} +1 -1
  36. package/dist/{chunk-XNYQOL3Z.js → chunk-JBKQ3SAO.js} +9 -18
  37. package/dist/chunk-JBKQ3SAO.js.map +1 -0
  38. package/dist/{chunk-ROXMHMY2.js → chunk-KNC55RTG.js} +13 -3
  39. package/dist/{chunk-ROXMHMY2.js.map → chunk-KNC55RTG.js.map} +1 -1
  40. package/dist/{chunk-QWWZ5CAQ.js → chunk-LXQLPRQ2.js} +2 -2
  41. package/dist/{chunk-ULHIJK66.js → chunk-T33XF5ZC.js} +255 -140
  42. package/dist/chunk-T33XF5ZC.js.map +1 -0
  43. package/dist/{chunk-VRGWKHDB.js → chunk-XM25TVIE.js} +100 -33
  44. package/dist/chunk-XM25TVIE.js.map +1 -0
  45. package/dist/components.d.ts +4 -4
  46. package/dist/components.js +9 -9
  47. package/dist/hooks.d.ts +6 -6
  48. package/dist/hooks.js +20 -25
  49. package/dist/hooks.js.map +1 -1
  50. package/dist/index.d.ts +11 -11
  51. package/dist/index.js +18 -21
  52. package/dist/index.js.map +1 -1
  53. package/dist/providers.d.ts +3 -3
  54. package/dist/providers.js +2 -2
  55. package/dist/rbac/index.d.ts +2 -20
  56. package/dist/rbac/index.js +7 -9
  57. package/dist/{usePublicRouteParams-TZe0gy-4.d.ts → usePublicRouteParams-BJAlWfuJ.d.ts} +3 -3
  58. package/dist/{useToast-C8gR5ir4.d.ts → useToast-AyaT-x7p.d.ts} +2 -2
  59. package/dist/utils.d.ts +1 -1
  60. package/dist/utils.js +3 -3
  61. package/docs/api/classes/ColumnFactory.md +1 -1
  62. package/docs/api/classes/ErrorBoundary.md +1 -1
  63. package/docs/api/classes/InvalidScopeError.md +1 -1
  64. package/docs/api/classes/Logger.md +1 -1
  65. package/docs/api/classes/MissingUserContextError.md +1 -1
  66. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  67. package/docs/api/classes/PermissionDeniedError.md +2 -2
  68. package/docs/api/classes/RBACAuditManager.md +2 -2
  69. package/docs/api/classes/RBACCache.md +1 -1
  70. package/docs/api/classes/RBACEngine.md +2 -2
  71. package/docs/api/classes/RBACError.md +1 -1
  72. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  73. package/docs/api/classes/SecureSupabaseClient.md +10 -10
  74. package/docs/api/classes/StorageUtils.md +1 -1
  75. package/docs/api/enums/FileCategory.md +1 -1
  76. package/docs/api/enums/LogLevel.md +1 -1
  77. package/docs/api/enums/RBACErrorCode.md +1 -1
  78. package/docs/api/enums/RPCFunction.md +1 -1
  79. package/docs/api/interfaces/AddressFieldProps.md +1 -1
  80. package/docs/api/interfaces/AddressFieldRef.md +1 -1
  81. package/docs/api/interfaces/AggregateConfig.md +1 -1
  82. package/docs/api/interfaces/AutocompleteOptions.md +1 -1
  83. package/docs/api/interfaces/AvatarProps.md +1 -1
  84. package/docs/api/interfaces/BadgeProps.md +1 -1
  85. package/docs/api/interfaces/ButtonProps.md +1 -1
  86. package/docs/api/interfaces/CalendarProps.md +1 -1
  87. package/docs/api/interfaces/CardProps.md +1 -1
  88. package/docs/api/interfaces/ColorPalette.md +1 -1
  89. package/docs/api/interfaces/ColorShade.md +1 -1
  90. package/docs/api/interfaces/ComplianceResult.md +1 -1
  91. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  92. package/docs/api/interfaces/DataRecord.md +1 -1
  93. package/docs/api/interfaces/DataTableAction.md +1 -1
  94. package/docs/api/interfaces/DataTableColumn.md +1 -1
  95. package/docs/api/interfaces/DataTableProps.md +1 -1
  96. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  97. package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
  98. package/docs/api/interfaces/DatabaseIssue.md +1 -1
  99. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  100. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  101. package/docs/api/interfaces/EventAppRoleData.md +1 -1
  102. package/docs/api/interfaces/ExportColumn.md +1 -1
  103. package/docs/api/interfaces/ExportOptions.md +1 -1
  104. package/docs/api/interfaces/FileDisplayProps.md +24 -11
  105. package/docs/api/interfaces/FileMetadata.md +1 -1
  106. package/docs/api/interfaces/FileReference.md +1 -1
  107. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  108. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  109. package/docs/api/interfaces/FileUploadProps.md +1 -1
  110. package/docs/api/interfaces/FooterProps.md +1 -1
  111. package/docs/api/interfaces/FormFieldProps.md +1 -1
  112. package/docs/api/interfaces/FormProps.md +1 -1
  113. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  114. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  115. package/docs/api/interfaces/InputProps.md +1 -1
  116. package/docs/api/interfaces/LabelProps.md +1 -1
  117. package/docs/api/interfaces/LoggerConfig.md +1 -1
  118. package/docs/api/interfaces/LoginFormProps.md +1 -1
  119. package/docs/api/interfaces/NavigationAccessRecord.md +2 -2
  120. package/docs/api/interfaces/NavigationContextType.md +1 -1
  121. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  122. package/docs/api/interfaces/NavigationItem.md +1 -1
  123. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  124. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  125. package/docs/api/interfaces/Organisation.md +1 -1
  126. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  127. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  128. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  129. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  130. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  131. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  132. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  133. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  134. package/docs/api/interfaces/PagePermissionGuardProps.md +2 -2
  135. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  136. package/docs/api/interfaces/PaletteData.md +1 -1
  137. package/docs/api/interfaces/ParsedAddress.md +1 -1
  138. package/docs/api/interfaces/PermissionEnforcerProps.md +4 -4
  139. package/docs/api/interfaces/ProgressProps.md +1 -1
  140. package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
  141. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  142. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  143. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  144. package/docs/api/interfaces/QuickFix.md +1 -1
  145. package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
  146. package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
  147. package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
  148. package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
  149. package/docs/api/interfaces/RBACConfig.md +2 -2
  150. package/docs/api/interfaces/RBACContext.md +1 -1
  151. package/docs/api/interfaces/RBACLogger.md +1 -1
  152. package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
  153. package/docs/api/interfaces/RBACPerformanceMetrics.md +1 -1
  154. package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
  155. package/docs/api/interfaces/RBACPermissionCheckResult.md +2 -2
  156. package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
  157. package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
  158. package/docs/api/interfaces/RBACResult.md +1 -1
  159. package/docs/api/interfaces/RBACRoleGrantParams.md +2 -2
  160. package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
  161. package/docs/api/interfaces/RBACRoleRevokeParams.md +2 -2
  162. package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
  163. package/docs/api/interfaces/RBACRoleValidateParams.md +2 -2
  164. package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
  165. package/docs/api/interfaces/RBACRolesListParams.md +1 -1
  166. package/docs/api/interfaces/RBACRolesListResult.md +2 -2
  167. package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
  168. package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
  169. package/docs/api/interfaces/ResourcePermissions.md +1 -1
  170. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  171. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  172. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  173. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  174. package/docs/api/interfaces/RouteAccessRecord.md +2 -2
  175. package/docs/api/interfaces/RouteConfig.md +2 -2
  176. package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
  177. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  178. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  179. package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
  180. package/docs/api/interfaces/SetupIssue.md +1 -1
  181. package/docs/api/interfaces/StorageConfig.md +1 -1
  182. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  183. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  184. package/docs/api/interfaces/StorageListOptions.md +1 -1
  185. package/docs/api/interfaces/StorageListResult.md +1 -1
  186. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  187. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  188. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  189. package/docs/api/interfaces/StyleImport.md +1 -1
  190. package/docs/api/interfaces/SwitchProps.md +1 -1
  191. package/docs/api/interfaces/TabsContentProps.md +1 -1
  192. package/docs/api/interfaces/TabsListProps.md +1 -1
  193. package/docs/api/interfaces/TabsProps.md +1 -1
  194. package/docs/api/interfaces/TabsTriggerProps.md +1 -1
  195. package/docs/api/interfaces/TextareaProps.md +1 -1
  196. package/docs/api/interfaces/ToastActionElement.md +1 -1
  197. package/docs/api/interfaces/ToastProps.md +1 -1
  198. package/docs/api/interfaces/UnifiedAuthContextType.md +60 -38
  199. package/docs/api/interfaces/UnifiedAuthProviderProps.md +13 -13
  200. package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
  201. package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
  202. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  203. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  204. package/docs/api/interfaces/UsePublicEventLogoOptions.md +2 -2
  205. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  206. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  207. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  208. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +2 -2
  209. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
  210. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  211. package/docs/api/interfaces/UseResolvedScopeOptions.md +2 -2
  212. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  213. package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
  214. package/docs/api/interfaces/UserEventAccess.md +1 -1
  215. package/docs/api/interfaces/UserMenuProps.md +1 -1
  216. package/docs/api/interfaces/UserProfile.md +1 -1
  217. package/docs/api/modules.md +194 -209
  218. package/docs/getting-started/cursor-rules.md +262 -0
  219. package/docs/getting-started/installation-guide.md +6 -1
  220. package/docs/getting-started/quick-start.md +6 -1
  221. package/docs/migration/MIGRATION_GUIDE.md +4 -4
  222. package/docs/migration/REACT_19_MIGRATION.md +227 -0
  223. package/docs/migration/database-changes-december-2025.md +2 -1
  224. package/docs/rbac/event-based-apps.md +124 -6
  225. package/docs/standards/README.md +39 -0
  226. package/docs/troubleshooting/migration.md +4 -4
  227. package/examples/PublicPages/PublicEventPage.tsx +1 -1
  228. package/package.json +11 -6
  229. package/scripts/audit-consuming-app.cjs +961 -0
  230. package/scripts/check-pace-core-compliance.cjs +315 -61
  231. package/scripts/install-cursor-rules.cjs +236 -0
  232. package/src/__tests__/helpers/test-providers.tsx +1 -1
  233. package/src/__tests__/helpers/test-utils.tsx +1 -1
  234. package/src/__tests__/rls-policies.test.ts +3 -1
  235. package/src/components/Badge/Badge.tsx +2 -4
  236. package/src/components/Button/Button.tsx +5 -4
  237. package/src/components/Calendar/Calendar.tsx +1 -1
  238. package/src/components/DataTable/DataTable.test.tsx +57 -93
  239. package/src/components/DataTable/DataTable.tsx +2 -2
  240. package/src/components/DataTable/__tests__/DataTable.default-state.test.tsx +172 -45
  241. package/src/components/DataTable/__tests__/DataTable.grouping-aggregation.test.tsx +121 -28
  242. package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +9 -8
  243. package/src/components/DataTable/__tests__/DataTableCore.test.tsx +20 -52
  244. package/src/components/DataTable/__tests__/a11y.basic.test.tsx +170 -34
  245. package/src/components/DataTable/__tests__/keyboard.test.tsx +75 -12
  246. package/src/components/DataTable/__tests__/pagination.modes.test.tsx +88 -16
  247. package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx +12 -12
  248. package/src/components/DataTable/components/AccessDeniedPage.tsx +1 -1
  249. package/src/components/DataTable/components/BulkOperationsDropdown.tsx +1 -1
  250. package/src/components/DataTable/components/DataTableCore.tsx +4 -7
  251. package/src/components/DataTable/components/DataTableModals.tsx +1 -1
  252. package/src/components/DataTable/components/EditableRow.tsx +1 -1
  253. package/src/components/DataTable/components/UnifiedTableBody.tsx +86 -17
  254. package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +23 -23
  255. package/src/components/DataTable/components/__tests__/EditableRow.test.tsx +11 -11
  256. package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +36 -36
  257. package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +27 -27
  258. package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +39 -39
  259. package/src/components/DataTable/components/__tests__/UnifiedTableBody.test.tsx +33 -33
  260. package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +29 -29
  261. package/src/components/DataTable/hooks/useColumnReordering.ts +2 -2
  262. package/src/components/DataTable/hooks/useDataTablePermissions.ts +75 -10
  263. package/src/components/DataTable/hooks/useKeyboardNavigation.ts +2 -2
  264. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +8 -14
  265. package/src/components/Dialog/Dialog.tsx +6 -5
  266. package/src/components/ErrorBoundary/ErrorBoundary.tsx +1 -1
  267. package/src/components/EventSelector/EventSelector.tsx +1 -1
  268. package/src/components/FileDisplay/FileDisplay.test.tsx +4 -3
  269. package/src/components/FileDisplay/FileDisplay.tsx +16 -4
  270. package/src/components/Footer/Footer.tsx +1 -1
  271. package/src/components/Form/Form.test.tsx +36 -15
  272. package/src/components/Form/Form.tsx +30 -26
  273. package/src/components/Header/Header.tsx +1 -1
  274. package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +40 -40
  275. package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +1 -1
  276. package/src/components/Input/Input.tsx +28 -30
  277. package/src/components/Label/Label.tsx +1 -1
  278. package/src/components/LoadingSpinner/LoadingSpinner.tsx +1 -1
  279. package/src/components/LoginForm/LoginForm.test.tsx +42 -42
  280. package/src/components/LoginForm/LoginForm.tsx +8 -8
  281. package/src/components/NavigationMenu/NavigationMenu.test.tsx +6 -4
  282. package/src/components/NavigationMenu/NavigationMenu.tsx +2 -11
  283. package/src/components/OrganisationSelector/OrganisationSelector.tsx +0 -1
  284. package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +1 -1
  285. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +75 -52
  286. package/src/components/PaceAppLayout/PaceAppLayout.tsx +98 -69
  287. package/src/components/PaceAppLayout/README.md +1 -1
  288. package/src/components/PaceLoginPage/PaceLoginPage.tsx +1 -8
  289. package/src/components/PasswordChange/PasswordChangeForm.test.tsx +33 -33
  290. package/src/components/PasswordChange/PasswordChangeForm.tsx +1 -1
  291. package/src/components/Progress/Progress.tsx +1 -1
  292. package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +5 -9
  293. package/src/components/ProtectedRoute/ProtectedRoute.tsx +0 -1
  294. package/src/components/PublicLayout/PublicPageLayout.tsx +1 -1
  295. package/src/components/PublicLayout/PublicPageProvider.tsx +0 -1
  296. package/src/components/Select/Select.tsx +33 -22
  297. package/src/components/SessionRestorationLoader/SessionRestorationLoader.tsx +1 -1
  298. package/src/components/Table/Table.tsx +1 -1
  299. package/src/components/Textarea/Textarea.tsx +27 -29
  300. package/src/components/Toast/Toast.tsx +1 -1
  301. package/src/components/Tooltip/Tooltip.tsx +1 -1
  302. package/src/components/UserMenu/UserMenu.tsx +1 -1
  303. package/src/hooks/__tests__/hooks.integration.test.tsx +80 -55
  304. package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +14 -7
  305. package/src/hooks/__tests__/useStorage.unit.test.ts +36 -36
  306. package/src/hooks/public/usePublicEvent.ts +1 -1
  307. package/src/hooks/public/usePublicEventLogo.ts +1 -1
  308. package/src/hooks/public/usePublicRouteParams.ts +1 -1
  309. package/src/hooks/services/useAuthService.ts +21 -3
  310. package/src/hooks/services/useEventService.ts +21 -3
  311. package/src/hooks/services/useInactivityService.ts +21 -3
  312. package/src/hooks/services/useOrganisationService.ts +21 -3
  313. package/src/hooks/useDataTableState.ts +8 -18
  314. package/src/hooks/useFileDisplay.ts +10 -17
  315. package/src/hooks/useFocusManagement.ts +2 -2
  316. package/src/hooks/useFocusTrap.ts +4 -4
  317. package/src/hooks/useFormDialog.ts +8 -7
  318. package/src/hooks/useInactivityTracker.ts +1 -1
  319. package/src/hooks/usePermissionCache.ts +1 -1
  320. package/src/hooks/useSecureDataAccess.test.ts +16 -9
  321. package/src/hooks/useSecureDataAccess.ts +22 -6
  322. package/src/hooks/useToast.ts +2 -2
  323. package/src/providers/__tests__/OrganisationProvider.test.tsx +57 -13
  324. package/src/providers/__tests__/ProviderLifecycle.test.tsx +21 -6
  325. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +10 -10
  326. package/src/providers/services/EventServiceProvider.tsx +0 -8
  327. package/src/providers/services/UnifiedAuthProvider.tsx +196 -46
  328. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +13 -3
  329. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +34 -40
  330. package/src/rbac/__tests__/isSuperAdmin.real.test.ts +82 -0
  331. package/src/rbac/adapters.tsx +3 -22
  332. package/src/rbac/api.test.ts +2 -2
  333. package/src/rbac/api.ts +7 -1
  334. package/src/rbac/components/EnhancedNavigationMenu.tsx +3 -16
  335. package/src/rbac/components/NavigationGuard.tsx +2 -11
  336. package/src/rbac/components/NavigationProvider.tsx +1 -2
  337. package/src/rbac/components/PagePermissionGuard.tsx +1 -1
  338. package/src/rbac/components/PagePermissionProvider.tsx +1 -1
  339. package/src/rbac/components/PermissionEnforcer.tsx +46 -13
  340. package/src/rbac/components/RoleBasedRouter.tsx +1 -1
  341. package/src/rbac/components/SecureDataProvider.tsx +1 -2
  342. package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +7 -43
  343. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +4 -11
  344. package/src/rbac/components/__tests__/NavigationProvider.test.tsx +3 -3
  345. package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +1 -1
  346. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +1 -1
  347. package/src/rbac/engine.ts +14 -2
  348. package/src/rbac/hooks/index.ts +0 -3
  349. package/src/rbac/hooks/usePermissions.ts +51 -11
  350. package/src/rbac/hooks/useRBAC.ts +3 -13
  351. package/src/rbac/hooks/useResolvedScope.test.ts +75 -54
  352. package/src/rbac/hooks/useResolvedScope.ts +58 -33
  353. package/src/rbac/hooks/useSecureSupabase.ts +4 -9
  354. package/src/rbac/secureClient.ts +43 -0
  355. package/src/services/EventService.ts +4 -57
  356. package/src/services/InactivityService.ts +127 -34
  357. package/src/services/OrganisationService.ts +68 -10
  358. package/src/utils/security/secureDataAccess.test.ts +31 -20
  359. package/src/utils/security/secureDataAccess.ts +4 -3
  360. package/dist/chunk-6C4YBBJM.js.map +0 -1
  361. package/dist/chunk-6LTQQAT6.js.map +0 -1
  362. package/dist/chunk-6TQDD426.js.map +0 -1
  363. package/dist/chunk-LOMZXPSN.js.map +0 -1
  364. package/dist/chunk-OETXORNB.js.map +0 -1
  365. package/dist/chunk-ULHIJK66.js.map +0 -1
  366. package/dist/chunk-VKB2CO4Z.js.map +0 -1
  367. package/dist/chunk-VRGWKHDB.js.map +0 -1
  368. package/dist/chunk-XNYQOL3Z.js.map +0 -1
  369. package/dist/chunk-XYXSXPUK.js.map +0 -1
  370. package/scripts/check-pace-core-compliance.js +0 -512
  371. package/src/rbac/hooks/useSuperAdminBypass.ts +0 -126
  372. package/src/utils/context/superAdminOverride.ts +0 -58
  373. /package/dist/{DataTable-WKRZD47S.js.map → DataTable-DQ7RSOHE.js.map} +0 -0
  374. /package/dist/{UnifiedAuthProvider-FTSG5XH7.js.map → UnifiedAuthProvider-ATAP5UTR.js.map} +0 -0
  375. /package/dist/{api-IHKALJZD.js.map → api-N774RPUA.js.map} +0 -0
  376. /package/dist/{chunk-QWWZ5CAQ.js.map → chunk-LXQLPRQ2.js.map} +0 -0
  377. /package/examples/{rbac → RBAC}/CompleteRBACExample.tsx +0 -0
  378. /package/examples/{rbac → RBAC}/EventBasedApp.tsx +0 -0
  379. /package/examples/{rbac → RBAC}/PermissionExample.tsx +0 -0
  380. /package/examples/{rbac → RBAC}/index.ts +0 -0
@@ -9,7 +9,8 @@
9
9
  */
10
10
 
11
11
  import React from 'react';
12
- import { render, screen, fireEvent, waitFor } from '@testing-library/react';
12
+ import { render, screen, fireEvent, waitFor, act } from '@testing-library/react';
13
+ import userEvent from '@testing-library/user-event';
13
14
  import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
14
15
  import { DataTable } from '../DataTable';
15
16
  import { getPaginationBinding, getPageSizeOptions, calculateOptimalPageSize } from '../utils/paginationUtils';
@@ -76,8 +77,47 @@ vi.mock('../../../providers/services/UnifiedAuthProvider', () => ({
76
77
 
77
78
  // Mock RBAC hooks
78
79
  vi.mock('../../../rbac/hooks', () => ({
79
- useCan: () => ({ can: true, reason: null }),
80
- useResolvedScope: () => ({ pageId: 'test-page-id' }),
80
+ useCan: () => ({ can: true, isLoading: false, error: null }),
81
+ useResolvedScope: () => ({
82
+ resolvedScope: {
83
+ organisationId: 'test-org',
84
+ eventId: 'test-event',
85
+ appId: 'test-app'
86
+ },
87
+ isLoading: false
88
+ }),
89
+ }));
90
+
91
+ // Mock useDataTablePermissions to return non-loading permissions
92
+ const mockUseDataTablePermissions = vi.hoisted(() => vi.fn(() => ({
93
+ permissions: {
94
+ canRead: { can: true, isLoading: false, error: null },
95
+ canCreate: { can: true, isLoading: false, error: null },
96
+ canUpdate: { can: true, isLoading: false, error: null },
97
+ canDelete: { can: true, isLoading: false, error: null },
98
+ canExport: { can: true, isLoading: false, error: null },
99
+ canImport: { can: true, isLoading: false, error: null },
100
+ },
101
+ secureFeatures: {
102
+ search: true,
103
+ sorting: true,
104
+ pagination: true,
105
+ selection: true,
106
+ creation: true,
107
+ editing: true,
108
+ deletion: true,
109
+ deleteSelected: true,
110
+ export: true,
111
+ import: true,
112
+ columnVisibility: true,
113
+ filtering: true,
114
+ grouping: true,
115
+ },
116
+ effectivePageId: 'test-pagination',
117
+ })));
118
+
119
+ vi.mock('../hooks/useDataTablePermissions', () => ({
120
+ useDataTablePermissions: mockUseDataTablePermissions
81
121
  }));
82
122
 
83
123
  // Mock toast
@@ -300,7 +340,7 @@ describe('DataTable Pagination Integration', () => {
300
340
  const largeDataset = generateTestData(100);
301
341
 
302
342
  describe('Client Mode Integration', () => {
303
- it('should render pagination controls for client mode', () => {
343
+ it('should render pagination controls for client mode', async () => {
304
344
  render(
305
345
  <DataTable
306
346
  data={smallDataset}
@@ -328,11 +368,17 @@ describe('DataTable Pagination Integration', () => {
328
368
  />
329
369
  );
330
370
 
331
- expect(screen.getByText('Page 1 of 3')).toBeInTheDocument();
371
+ // Wait for table to render
372
+ await waitFor(() => {
373
+ expect(screen.getByRole('table')).toBeInTheDocument();
374
+ });
375
+
376
+ expect(await screen.findByText('Page 1 of 3')).toBeInTheDocument();
332
377
  expect(screen.getByText('Rows per page')).toBeInTheDocument();
333
378
  });
334
379
 
335
380
  it('should navigate pages correctly in client mode', async () => {
381
+ const user = userEvent.setup();
336
382
  render(
337
383
  <DataTable
338
384
  data={smallDataset}
@@ -360,16 +406,27 @@ describe('DataTable Pagination Integration', () => {
360
406
  />
361
407
  );
362
408
 
409
+ // Wait for table to render
410
+ await waitFor(() => {
411
+ expect(screen.getByRole('table')).toBeInTheDocument();
412
+ });
413
+
363
414
  // Should start on page 1
364
- expect(screen.getByText('Page 1 of 3')).toBeInTheDocument();
415
+ expect(await screen.findByText('Page 1 of 3')).toBeInTheDocument();
365
416
 
366
- // Click next page
417
+ // Click next page using userEvent
367
418
  const nextButton = screen.getByLabelText('Go to next page');
368
- fireEvent.click(nextButton);
369
-
419
+
420
+ // Click the button - this triggers table.nextPage() which calls onPaginationChange
421
+ // The onPaginationChange handler updates React state, which should trigger a re-render
422
+ await user.click(nextButton);
423
+
424
+ // Wait for the page text to update - TanStack Table updates state synchronously,
425
+ // but React needs to process the state update and re-render
426
+ // We wait for the text to change, which indicates the component has re-rendered
370
427
  await waitFor(() => {
371
428
  expect(screen.getByText('Page 2 of 3')).toBeInTheDocument();
372
- });
429
+ }, { timeout: 10000, interval: 100 });
373
430
  });
374
431
 
375
432
  // Note: Page size change test removed due to test environment issues with Select component
@@ -493,7 +550,7 @@ describe('DataTable Pagination Integration', () => {
493
550
  });
494
551
 
495
552
  describe('Edge Cases', () => {
496
- it('should handle empty datasets gracefully', () => {
553
+ it('should handle empty datasets gracefully', async () => {
497
554
  render(
498
555
  <DataTable
499
556
  data={[]}
@@ -520,10 +577,15 @@ describe('DataTable Pagination Integration', () => {
520
577
  />
521
578
  );
522
579
 
523
- expect(screen.getByText('Page 1 of 1')).toBeInTheDocument();
580
+ // Wait for table to render
581
+ await waitFor(() => {
582
+ expect(screen.getByRole('table')).toBeInTheDocument();
583
+ });
584
+
585
+ expect(await screen.findByText('Page 1 of 1')).toBeInTheDocument();
524
586
  });
525
587
 
526
- it('should handle single page datasets', () => {
588
+ it('should handle single page datasets', async () => {
527
589
  const singlePageData = generateTestData(5);
528
590
 
529
591
  render(
@@ -553,7 +615,12 @@ describe('DataTable Pagination Integration', () => {
553
615
  />
554
616
  );
555
617
 
556
- expect(screen.getByText('Page 1 of 1')).toBeInTheDocument();
618
+ // Wait for table to render
619
+ await waitFor(() => {
620
+ expect(screen.getByRole('table')).toBeInTheDocument();
621
+ });
622
+
623
+ expect(await screen.findByText('Page 1 of 1')).toBeInTheDocument();
557
624
 
558
625
  // Navigation buttons should be disabled
559
626
  expect(screen.getByLabelText('Go to previous page')).toBeDisabled();
@@ -587,7 +654,7 @@ describe('DataTable Pagination Integration', () => {
587
654
  // ============================================================================
588
655
 
589
656
  describe('Pagination Performance', () => {
590
- it('should handle large datasets efficiently in client mode', () => {
657
+ it('should handle large datasets efficiently in client mode', async () => {
591
658
  const largeDataset = generateTestData(10000);
592
659
 
593
660
  const startTime = performance.now();
@@ -624,7 +691,12 @@ describe('Pagination Performance', () => {
624
691
 
625
692
  // Should render within reasonable time (adjust threshold as needed)
626
693
  expect(renderTime).toBeLessThan(1000); // 1 second
627
- expect(screen.getByText('Page 1 of 200')).toBeInTheDocument();
694
+ // Wait for table to render
695
+ await waitFor(() => {
696
+ expect(screen.getByRole('table')).toBeInTheDocument();
697
+ });
698
+
699
+ expect(await screen.findByText('Page 1 of 200')).toBeInTheDocument();
628
700
  });
629
701
 
630
702
  it('should use appropriate page sizes for different modes', () => {
@@ -4,7 +4,7 @@
4
4
  * @module Components/DataTable/__tests__
5
5
  * @since 2.0.0
6
6
  *
7
- * Tests for SSR safety and React 18 strict mode compatibility.
7
+ * Tests for SSR safety and React 19 strict mode compatibility.
8
8
  */
9
9
 
10
10
  import React from 'react';
@@ -39,7 +39,7 @@ const testColumns: DataTableColumn<TestData>[] = [
39
39
  },
40
40
  ];
41
41
 
42
- const defaultProps = {
42
+ const baseProps = {
43
43
  data: testData,
44
44
  columns: testColumns,
45
45
  rbac: { pageId: 'test' },
@@ -92,7 +92,7 @@ describe('DataTable SSR and Strict Mode Compatibility', () => {
92
92
  (global as any).window = {} as any;
93
93
 
94
94
  expect(() => {
95
- React.createElement(SafeDataTable, defaultProps);
95
+ React.createElement(SafeDataTable, baseProps);
96
96
  }).not.toThrow();
97
97
 
98
98
  global.window = originalWindow;
@@ -104,7 +104,7 @@ describe('DataTable SSR and Strict Mode Compatibility', () => {
104
104
  (global as any).localStorage = undefined as any;
105
105
 
106
106
  expect(() => {
107
- React.createElement(SafeDataTable, defaultProps);
107
+ React.createElement(SafeDataTable, baseProps);
108
108
  }).not.toThrow();
109
109
 
110
110
  global.localStorage = originalLocalStorage;
@@ -116,20 +116,20 @@ describe('DataTable SSR and Strict Mode Compatibility', () => {
116
116
  (global as any).document = {} as any;
117
117
 
118
118
  expect(() => {
119
- React.createElement(SafeDataTable, defaultProps);
119
+ React.createElement(SafeDataTable, baseProps);
120
120
  }).not.toThrow();
121
121
 
122
122
  global.document = originalDocument;
123
123
  });
124
124
  });
125
125
 
126
- describe('React 18 Strict Mode Compatibility', () => {
126
+ describe('React 19 Strict Mode Compatibility', () => {
127
127
  it('should handle strict mode double-invoke without errors', () => {
128
128
  let renderCount = 0;
129
129
 
130
130
  const TestComponent = () => {
131
131
  renderCount++;
132
- return <SafeDataTable {...defaultProps} />;
132
+ return <SafeDataTable {...baseProps} />;
133
133
  };
134
134
 
135
135
  expect(() => {
@@ -153,7 +153,7 @@ describe('DataTable SSR and Strict Mode Compatibility', () => {
153
153
  };
154
154
  }, []);
155
155
 
156
- return <SafeDataTable {...defaultProps} />;
156
+ return <SafeDataTable {...baseProps} />;
157
157
  };
158
158
 
159
159
  expect(() => {
@@ -176,7 +176,7 @@ describe('DataTable SSR and Strict Mode Compatibility', () => {
176
176
  return (
177
177
  <div>
178
178
  <span data-testid="count">{count}</span>
179
- <SafeDataTable {...defaultProps} />
179
+ <SafeDataTable {...baseProps} />
180
180
  </div>
181
181
  );
182
182
  };
@@ -270,7 +270,7 @@ describe('DataTable SSR and Strict Mode Compatibility', () => {
270
270
 
271
271
  return (
272
272
  <div>
273
- <SafeDataTable {...defaultProps} />
273
+ <SafeDataTable {...baseProps} />
274
274
  {mounted && <div data-testid="client-only">Client Only</div>}
275
275
  </div>
276
276
  );
@@ -291,7 +291,7 @@ describe('DataTable SSR and Strict Mode Compatibility', () => {
291
291
 
292
292
  return (
293
293
  <div>
294
- <SafeDataTable {...defaultProps} />
294
+ <SafeDataTable {...baseProps} />
295
295
  <div data-testid="client-state">
296
296
  {isClient ? 'Client' : 'Server'}
297
297
  </div>
@@ -311,7 +311,7 @@ describe('DataTable SSR and Strict Mode Compatibility', () => {
311
311
 
312
312
  render(
313
313
  <React.StrictMode>
314
- <SafeDataTable {...defaultProps} />
314
+ <SafeDataTable {...baseProps} />
315
315
  </React.StrictMode>
316
316
  );
317
317
 
@@ -33,7 +33,7 @@
33
33
  * - Card component for layout
34
34
  * - Button component for actions
35
35
  * - Lucide React icons
36
- * - React 18+ hooks
36
+ * - React 19+ hooks
37
37
  */
38
38
 
39
39
  import React from 'react';
@@ -38,7 +38,7 @@
38
38
  * - Select components (formerly DropdownMenu)
39
39
  * - Button component
40
40
  * - Lucide React icons
41
- * - React 18+ hooks
41
+ * - React 19+ hooks
42
42
  */
43
43
 
44
44
  import React from 'react';
@@ -8,12 +8,10 @@
8
8
  * This is the main component that consumers will use.
9
9
  */
10
10
 
11
- import React, { useMemo, useCallback, useEffect, useState, useRef } from 'react';
12
- import { useReactTable, flexRender } from '@tanstack/react-table';
11
+ import React, { useMemo, useCallback, useEffect, useRef } from 'react';
12
+ import { useReactTable } from '@tanstack/react-table';
13
13
  import type {
14
14
  SortingState,
15
- ColumnFiltersState,
16
- VisibilityState,
17
15
  GroupingState,
18
16
  ExpandedState,
19
17
  PaginationState,
@@ -159,7 +157,7 @@ export interface DataTableCoreProps<TData extends DataRecord> {
159
157
  // Utilities
160
158
  getRowId?: GetRowId<TData>;
161
159
  isLoading?: boolean;
162
- emptyState?: EmptyStateConfig | React.ReactElement;
160
+ emptyState?: EmptyStateConfig | React.ReactElement<any>;
163
161
  aggregates?: AggregateConfig[];
164
162
  importModalConfig?: ImportModalConfig;
165
163
  actions?: DataTableAction<TData>[];
@@ -220,7 +218,7 @@ function DataTableInternal<TData extends DataRecord>(props: DataTableCoreProps<T
220
218
  onLayoutChange,
221
219
  } = props;
222
220
 
223
- const logger = React.useMemo(() => createLogger('DataTableCore'), []);
221
+ const logger = createLogger('DataTableCore');
224
222
 
225
223
  // ============================================================================
226
224
  // ALL HOOKS MUST BE CALLED IN THE SAME ORDER EVERY RENDER
@@ -1234,7 +1232,6 @@ function DataTableInternal<TData extends DataRecord>(props: DataTableCoreProps<T
1234
1232
  )}
1235
1233
 
1236
1234
  </table>
1237
-
1238
1235
  {/* Modal Dialogs */}
1239
1236
  <DataTableModals
1240
1237
  showImportModal={state.showImportModal}
@@ -184,7 +184,7 @@ export function DataTableModals<TData extends Record<string, unknown> = Record<s
184
184
  onStoreFocus,
185
185
  onRestoreFocus,
186
186
  }: DataTableModalsProps<TData>) {
187
- const logger = React.useMemo(() => createLogger('DataTableModals'), []);
187
+ const logger = createLogger('DataTableModals');
188
188
  // Handle focus management for import modal
189
189
  useEffect(() => {
190
190
  if (showImportModal) {
@@ -45,7 +45,7 @@ function SelectEditField<TData extends DataRecord>({
45
45
  onChange: (value: CellValue) => void;
46
46
  className?: string;
47
47
  }) {
48
- const logger = React.useMemo(() => createLogger('SelectEditField'), []);
48
+ const logger = createLogger('SelectEditField');
49
49
  // Determine if searchable - explicitly check for true to ensure visible search input appears
50
50
  // When selectSearchable is true or undefined, show the visible search input box
51
51
  // When selectSearchable is false, hide the search input (type-to-search still works via SelectContent internals)
@@ -23,6 +23,7 @@ import { getTableCellClasses, getTableHeadClasses, getTableRowClasses } from '..
23
23
  import { Input } from '../../Input/Input';
24
24
  import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, SelectGroup, SelectLabel, SelectSeparator } from '../../Select/Select';
25
25
  import { createLogger } from '../../../utils/core/logger';
26
+ import { cn } from '../../../utils/core/cn';
26
27
  import type {
27
28
  AggregateConfig,
28
29
  DataRecord,
@@ -136,7 +137,7 @@ function SelectEditField<TData extends DataRecord>({
136
137
  placeholder?: string;
137
138
  onChange: (value: CellValue) => void;
138
139
  }) {
139
- const logger = React.useMemo(() => createLogger('SelectEditField'), []);
140
+ const logger = createLogger('SelectEditField');
140
141
  // Determine if searchable - explicitly check for true to ensure visible search input appears
141
142
  // When selectSearchable is true or undefined, show the visible search input box
142
143
  // When selectSearchable is false, hide the search input (type-to-search still works via SelectContent internals)
@@ -516,7 +517,7 @@ const RowComponent = React.memo(({
516
517
  }: RowProps) => {
517
518
  const rowRef = useRef<HTMLTableRowElement>(null);
518
519
  const firstInputRef = useRef<HTMLInputElement>(null);
519
- const logger = React.useMemo(() => createLogger('RowComponent'), []);
520
+ const logger = createLogger('RowComponent');
520
521
 
521
522
  const rowId = getRowIdSafe(row.original, row.index, getRowId);
522
523
 
@@ -526,6 +527,10 @@ const RowComponent = React.memo(({
526
527
  const isParent = isHierarchical && hierarchicalRow.isParent;
527
528
  const isChild = isHierarchical && !hierarchicalRow.isParent;
528
529
 
530
+ // React Compiler handles memoization automatically
531
+ const visibleCells = row.getVisibleCells();
532
+ const isSelected = typeof row.getIsSelected === 'function' ? row.getIsSelected() : false;
533
+
529
534
  // Auto-focus first editable field when entering edit mode
530
535
  useEffect(() => {
531
536
  if (isEditing && firstInputRef.current) {
@@ -565,7 +570,6 @@ const RowComponent = React.memo(({
565
570
  const groupValue = row.getValue(grouping[0]);
566
571
  const subRowsCount = row.subRows?.length || 0;
567
572
  const isExpanded = row.getIsExpanded();
568
- const visibleCells = row.getVisibleCells();
569
573
 
570
574
  // Get child rows for aggregation (convert TanStack Row to original data)
571
575
  const childRows: DataRecord[] = row.subRows?.map((subRow: any) => subRow.original) || [];
@@ -709,20 +713,30 @@ const RowComponent = React.memo(({
709
713
  }
710
714
 
711
715
  // Regular row (not in edit mode)
712
- const visibleCells = row.getVisibleCells();
713
- const allCells = row.getAllCells ? row.getAllCells() : [];
716
+ // visibleCells is already memoized above
714
717
 
715
718
  // Calculate indentation for child rows
716
719
  const indentSize = hierarchical?.indentSize || 24;
717
- const indentation = isChild && hierarchical?.state ?
718
- calculateIndentation(hierarchicalRow, [], indentSize) : 0;
720
+ const indentation = React.useMemo(() => {
721
+ return isChild && hierarchical?.state ?
722
+ calculateIndentation(hierarchicalRow, [], indentSize) : 0;
723
+ }, [isChild, hierarchical?.state, hierarchicalRow, indentSize]);
719
724
 
720
- // Apply hierarchical row classes
721
- const rowClassName = isHierarchical ? (
722
- isParent
723
- ? hierarchical?.parentRowClassName || 'bg-main-50 hover:bg-main-100 font-medium'
724
- : hierarchical?.childRowClassName || 'bg-sec-25 hover:bg-sec-50'
725
- ) : '';
725
+ // Apply hierarchical row classes - combine with standard row classes
726
+ const rowClassName = React.useMemo(() => {
727
+ if (isHierarchical) {
728
+ const hierarchicalClass = isParent
729
+ ? hierarchical?.parentRowClassName || 'bg-main-50 hover:bg-main-100 font-medium'
730
+ : hierarchical?.childRowClassName || 'bg-sec-25 hover:bg-sec-50';
731
+ // Combine with standard row classes for consistent hover behavior
732
+ return cn(
733
+ getTableRowClasses({ isSelected, isVirtualized: false }),
734
+ hierarchicalClass
735
+ );
736
+ }
737
+ // Use standard row classes when not hierarchical
738
+ return getTableRowClasses({ isSelected, isVirtualized: false });
739
+ }, [isHierarchical, isParent, isSelected, hierarchical]);
726
740
 
727
741
  return (
728
742
  <tr
@@ -734,7 +748,7 @@ const RowComponent = React.memo(({
734
748
  ...(isChild && indentation > 0 ? { paddingLeft: `${indentation}px` } : {})
735
749
  }}
736
750
  className={rowClassName}
737
- aria-selected={typeof row.getIsSelected === 'function' ? (row.getIsSelected() ? 'true' : 'false') : 'false'}
751
+ aria-selected={isSelected ? 'true' : 'false'}
738
752
  >
739
753
  {visibleCells.map((cell: any, cellIndex: number) => {
740
754
  const isFirstCell = cellIndex === 0;
@@ -797,8 +811,63 @@ const RowComponent = React.memo(({
797
811
 
798
812
  RowComponent.displayName = 'RowComponent';
799
813
 
800
- // Use the already memoized RowComponent
801
- const MemoizedRow = RowComponent;
814
+ // Custom comparison function for React.memo to prevent unnecessary re-renders
815
+ // This compares the actual row data and props, not just object references
816
+ const areRowPropsEqual = (prevProps: RowProps, nextProps: RowProps): boolean => {
817
+ // Compare row by ID and index (stable identifiers)
818
+ if (prevProps.row.id !== nextProps.row.id || prevProps.row.index !== nextProps.row.index) {
819
+ return false;
820
+ }
821
+
822
+ // Compare editing state
823
+ if (prevProps.isEditing !== nextProps.isEditing) {
824
+ return false;
825
+ }
826
+
827
+ if (prevProps.editingRowId !== nextProps.editingRowId) {
828
+ return false;
829
+ }
830
+
831
+ // Compare style object (shallow comparison)
832
+ if (prevProps.style !== nextProps.style) {
833
+ if (!prevProps.style || !nextProps.style) {
834
+ return false;
835
+ }
836
+ // Convert to records for comparison
837
+ const prevStyle = prevProps.style as Record<string, unknown>;
838
+ const nextStyle = nextProps.style as Record<string, unknown>;
839
+ const styleKeys = new Set([...Object.keys(prevStyle), ...Object.keys(nextStyle)]);
840
+ for (const key of styleKeys) {
841
+ if (prevStyle[key] !== nextStyle[key]) {
842
+ return false;
843
+ }
844
+ }
845
+ }
846
+
847
+ // Compare other primitive props
848
+ if (prevProps.grouping.length !== nextProps.grouping.length ||
849
+ prevProps.grouping.some((id, i) => id !== nextProps.grouping[i])) {
850
+ return false;
851
+ }
852
+
853
+ // For hierarchical state, compare the enabled flag and state functions
854
+ if (prevProps.hierarchical?.enabled !== nextProps.hierarchical?.enabled) {
855
+ return false;
856
+ }
857
+
858
+ // Compare row selection state by checking the function result
859
+ const prevSelected = typeof prevProps.row.getIsSelected === 'function' ? prevProps.row.getIsSelected() : false;
860
+ const nextSelected = typeof nextProps.row.getIsSelected === 'function' ? nextProps.row.getIsSelected() : false;
861
+ if (prevSelected !== nextSelected) {
862
+ return false;
863
+ }
864
+
865
+ // If all checks pass, props are equal
866
+ return true;
867
+ };
868
+
869
+ // Use the memoized RowComponent with custom comparison
870
+ const MemoizedRow = React.memo(RowComponent, areRowPropsEqual);
802
871
 
803
872
  /**
804
873
  * Unified table body component with intelligent virtualization
@@ -831,7 +900,7 @@ export function UnifiedTableBody<TData extends Record<string, any>>({
831
900
  rbac,
832
901
  permissions
833
902
  }: UnifiedTableBodyProps<TData>) {
834
- const logger = React.useMemo(() => createLogger('UnifiedTableBody'), []);
903
+ const logger = createLogger('UnifiedTableBody');
835
904
 
836
905
  const headerRef = useRef<HTMLTableSectionElement>(null);
837
906
  const bodyRef = useRef<HTMLTableSectionElement>(null);