@jmruthers/pace-core 0.5.121 → 0.5.124

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 (255) hide show
  1. package/dist/{AuthService-D4646R4b.d.ts → AuthService-DYuQPJj6.d.ts} +0 -9
  2. package/dist/{DataTable-DGZDJUYM.js → DataTable-OKDYRW2S.js} +7 -8
  3. package/dist/{PublicLoadingSpinner-DgDWTFqn.d.ts → PublicLoadingSpinner-CaoRbHvJ.d.ts} +30 -4
  4. package/dist/{UnifiedAuthProvider-UACKFATV.js → UnifiedAuthProvider-6C47WIML.js} +3 -4
  5. package/dist/{chunk-D6BOFXYR.js → chunk-35ZDPMBM.js} +3 -3
  6. package/dist/{chunk-CGURJ27Z.js → chunk-4MXVZVNS.js} +2 -2
  7. package/dist/{chunk-ZYJ6O5CA.js → chunk-C43QIDN3.js} +2 -2
  8. package/dist/{chunk-VKOCWWVY.js → chunk-CX5M4ZAG.js} +1 -6
  9. package/dist/{chunk-VKOCWWVY.js 3.map → chunk-CX5M4ZAG.js.map} +1 -1
  10. package/dist/{chunk-RIEJGKD3.js → chunk-ESJTIADP.js} +15 -6
  11. package/dist/{chunk-RIEJGKD3.js.map → chunk-ESJTIADP.js.map} +1 -1
  12. package/dist/{chunk-HFBOFZ3Z.js → chunk-GBGYYMC6.js} +317 -251
  13. package/dist/chunk-GBGYYMC6.js.map +1 -0
  14. package/dist/{chunk-SMJZMKYN.js → chunk-GEVIB2UB.js} +43 -10
  15. package/dist/chunk-GEVIB2UB.js.map +1 -0
  16. package/dist/{chunk-TDNI6ZWL.js → chunk-IJOZZOGT.js} +7 -7
  17. package/dist/chunk-IJOZZOGT.js.map +1 -0
  18. package/dist/{chunk-GZRXOUBE.js → chunk-M6DDYFUD.js} +2 -2
  19. package/dist/chunk-M6DDYFUD.js.map +1 -0
  20. package/dist/{chunk-B4GZ2BXO.js → chunk-NZGLXZGP.js} +3 -3
  21. package/dist/{chunk-NZ32EONV.js → chunk-QWNJCQXZ.js} +2 -2
  22. package/dist/{chunk-QPI2CCBA.js → chunk-VPUCTHTY.js} +149 -96
  23. package/dist/chunk-VPUCTHTY.js.map +1 -0
  24. package/dist/{chunk-FKFHZUGF.js → chunk-XN6GWKMV.js} +43 -56
  25. package/dist/chunk-XN6GWKMV.js.map +1 -0
  26. package/dist/{chunk-BHWIUEYH.js → chunk-ZBLK676C.js} +1 -61
  27. package/dist/chunk-ZBLK676C.js.map +1 -0
  28. package/dist/components.d.ts +1 -1
  29. package/dist/components.js +11 -11
  30. package/dist/{formatting-B1jSqgl-.d.ts → formatting-DFcCxUEk.d.ts} +1 -1
  31. package/dist/hooks.d.ts +1 -1
  32. package/dist/hooks.js +9 -8
  33. package/dist/hooks.js.map +1 -1
  34. package/dist/index.d.ts +6 -6
  35. package/dist/index.js +19 -17
  36. package/dist/index.js.map +1 -1
  37. package/dist/providers.d.ts +2 -2
  38. package/dist/providers.js +2 -3
  39. package/dist/rbac/index.js +7 -8
  40. package/dist/styles/index.d.ts +1 -1
  41. package/dist/styles/index.js +5 -3
  42. package/dist/theming/runtime.d.ts +73 -1
  43. package/dist/theming/runtime.js +5 -5
  44. package/dist/{usePublicRouteParams-BdF8bZgs.d.ts → usePublicRouteParams-Dyt1tzI9.d.ts} +60 -8
  45. package/dist/utils.d.ts +1 -1
  46. package/dist/utils.js +5 -5
  47. package/docs/api/classes/ColumnFactory.md +1 -1
  48. package/docs/api/classes/ErrorBoundary.md +1 -1
  49. package/docs/api/classes/InvalidScopeError.md +1 -1
  50. package/docs/api/classes/MissingUserContextError.md +1 -1
  51. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  52. package/docs/api/classes/PermissionDeniedError.md +1 -1
  53. package/docs/api/classes/PublicErrorBoundary.md +6 -6
  54. package/docs/api/classes/RBACAuditManager.md +1 -1
  55. package/docs/api/classes/RBACCache.md +1 -1
  56. package/docs/api/classes/RBACEngine.md +1 -1
  57. package/docs/api/classes/RBACError.md +1 -1
  58. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  59. package/docs/api/classes/SecureSupabaseClient.md +6 -6
  60. package/docs/api/classes/StorageUtils.md +1 -1
  61. package/docs/api/enums/FileCategory.md +1 -1
  62. package/docs/api/interfaces/AggregateConfig.md +1 -1
  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/FileDisplayProps.md +1 -1
  77. package/docs/api/interfaces/FileMetadata.md +1 -1
  78. package/docs/api/interfaces/FileReference.md +1 -1
  79. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  80. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  81. package/docs/api/interfaces/FileUploadProps.md +1 -1
  82. package/docs/api/interfaces/FooterProps.md +1 -1
  83. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  84. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  85. package/docs/api/interfaces/InputProps.md +1 -1
  86. package/docs/api/interfaces/LabelProps.md +1 -1
  87. package/docs/api/interfaces/LoginFormProps.md +1 -1
  88. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  89. package/docs/api/interfaces/NavigationContextType.md +1 -1
  90. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  91. package/docs/api/interfaces/NavigationItem.md +1 -1
  92. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  93. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  94. package/docs/api/interfaces/Organisation.md +1 -1
  95. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  96. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  97. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  98. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  99. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  100. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  101. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  102. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  103. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  104. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  105. package/docs/api/interfaces/PaletteData.md +1 -1
  106. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  107. package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
  108. package/docs/api/interfaces/PublicErrorBoundaryProps.md +7 -7
  109. package/docs/api/interfaces/PublicErrorBoundaryState.md +5 -5
  110. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +7 -7
  111. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  112. package/docs/api/interfaces/PublicPageHeaderProps.md +51 -12
  113. package/docs/api/interfaces/PublicPageLayoutProps.md +72 -12
  114. package/docs/api/interfaces/RBACConfig.md +1 -1
  115. package/docs/api/interfaces/RBACLogger.md +1 -1
  116. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  117. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  118. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  119. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  120. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  121. package/docs/api/interfaces/RouteConfig.md +1 -1
  122. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  123. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  124. package/docs/api/interfaces/StorageConfig.md +1 -1
  125. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  126. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  127. package/docs/api/interfaces/StorageListOptions.md +1 -1
  128. package/docs/api/interfaces/StorageListResult.md +1 -1
  129. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  130. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  131. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  132. package/docs/api/interfaces/StyleImport.md +1 -1
  133. package/docs/api/interfaces/SwitchProps.md +1 -1
  134. package/docs/api/interfaces/ToastActionElement.md +1 -1
  135. package/docs/api/interfaces/ToastProps.md +1 -1
  136. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  137. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  138. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  139. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  140. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  141. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  142. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +1 -1
  143. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
  144. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  145. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  146. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  147. package/docs/api/interfaces/UserEventAccess.md +1 -1
  148. package/docs/api/interfaces/UserMenuProps.md +1 -1
  149. package/docs/api/interfaces/UserProfile.md +1 -1
  150. package/docs/api/modules.md +140 -30
  151. package/docs/best-practices/README.md +1 -1
  152. package/docs/implementation-guides/datatable-filtering.md +313 -0
  153. package/docs/implementation-guides/datatable-rbac-usage.md +317 -0
  154. package/docs/implementation-guides/hierarchical-datatable.md +850 -0
  155. package/docs/implementation-guides/large-datasets.md +281 -0
  156. package/docs/implementation-guides/performance.md +403 -0
  157. package/docs/implementation-guides/public-pages.md +4 -4
  158. package/docs/migration/quick-migration-guide.md +320 -0
  159. package/docs/rbac/quick-start.md +16 -16
  160. package/docs/troubleshooting/README.md +4 -4
  161. package/docs/troubleshooting/cake-page-permission-guard-issue-summary.md +1 -1
  162. package/docs/troubleshooting/debugging.md +1117 -0
  163. package/docs/troubleshooting/migration.md +918 -0
  164. package/examples/public-pages/CorrectPublicPageImplementation.tsx +30 -30
  165. package/examples/public-pages/PublicEventPage.tsx +41 -41
  166. package/examples/public-pages/PublicPageApp.tsx +33 -33
  167. package/examples/public-pages/PublicPageUsageExample.tsx +30 -30
  168. package/package.json +4 -4
  169. package/src/__tests__/hooks/usePermissions.test.ts +265 -0
  170. package/src/components/DataTable/DataTable.test.tsx +9 -38
  171. package/src/components/DataTable/DataTable.tsx +0 -7
  172. package/src/components/DataTable/components/DataTableCore.tsx +125 -144
  173. package/src/components/DataTable/components/DataTableModals.tsx +25 -22
  174. package/src/components/DataTable/components/DataTableToolbar.tsx +14 -1
  175. package/src/components/DataTable/components/EditableRow.tsx +118 -42
  176. package/src/components/DataTable/components/UnifiedTableBody.tsx +129 -76
  177. package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +33 -14
  178. package/src/components/DataTable/utils/__tests__/exportUtils.test.ts +17 -5
  179. package/src/components/DataTable/utils/exportUtils.ts +3 -2
  180. package/src/components/Dialog/Dialog.tsx +1 -1
  181. package/src/components/Dialog/README.md +24 -24
  182. package/src/components/Dialog/examples/BasicHtmlTest.tsx +2 -2
  183. package/src/components/Dialog/examples/DebugHtmlExample.tsx +6 -6
  184. package/src/components/Dialog/examples/HtmlDialogExample.tsx +2 -2
  185. package/src/components/Dialog/examples/SimpleHtmlTest.tsx +3 -3
  186. package/src/components/Dialog/examples/__tests__/SimpleHtmlTest.test.tsx +4 -4
  187. package/src/components/PaceAppLayout/PaceAppLayout.tsx +12 -1
  188. package/src/components/PublicLayout/EventLogo.tsx +175 -0
  189. package/src/components/PublicLayout/PublicErrorBoundary.tsx +22 -18
  190. package/src/components/PublicLayout/PublicLoadingSpinner.tsx +22 -14
  191. package/src/components/PublicLayout/PublicPageHeader.tsx +133 -40
  192. package/src/components/PublicLayout/PublicPageLayout.tsx +75 -72
  193. package/src/components/PublicLayout/__tests__/PublicErrorBoundary.test.tsx +1 -1
  194. package/src/components/PublicLayout/__tests__/PublicLoadingSpinner.test.tsx +8 -8
  195. package/src/components/PublicLayout/__tests__/PublicPageHeader.test.tsx +23 -16
  196. package/src/components/PublicLayout/__tests__/PublicPageLayout.test.tsx +86 -14
  197. package/src/examples/CorrectPublicPageImplementation.tsx +30 -30
  198. package/src/examples/PublicEventPage.tsx +41 -41
  199. package/src/examples/PublicPageApp.tsx +33 -33
  200. package/src/examples/PublicPageUsageExample.tsx +30 -30
  201. package/src/hooks/__tests__/usePublicEvent.unit.test.ts +583 -0
  202. package/src/hooks/__tests__/usePublicRouteParams.unit.test.ts +10 -3
  203. package/src/hooks/index.ts +1 -1
  204. package/src/hooks/public/usePublicEventLogo.ts +285 -0
  205. package/src/hooks/public/usePublicRouteParams.ts +21 -4
  206. package/src/hooks/useEventTheme.test.ts +119 -43
  207. package/src/hooks/useEventTheme.ts +84 -55
  208. package/src/index.ts +3 -1
  209. package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +630 -0
  210. package/src/rbac/components/__tests__/NavigationProvider.test.tsx +667 -0
  211. package/src/rbac/components/__tests__/PagePermissionProvider.test.tsx +647 -0
  212. package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +496 -0
  213. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +496 -0
  214. package/src/rbac/secureClient.ts +4 -2
  215. package/src/services/EventService.ts +0 -66
  216. package/src/services/__tests__/EventService.eventColours.test.ts +44 -40
  217. package/src/styles/index.ts +1 -1
  218. package/src/theming/__tests__/parseEventColours.test.ts +209 -0
  219. package/src/theming/parseEventColours.ts +123 -0
  220. package/src/theming/runtime.ts +3 -0
  221. package/src/types/__tests__/file-reference.test.ts +447 -0
  222. package/src/utils/formatDate.test.ts +11 -11
  223. package/src/utils/formatting.ts +3 -2
  224. package/dist/chunk-BDZUMRBD.js 3.map +0 -1
  225. package/dist/chunk-BHWIUEYH.js.map +0 -1
  226. package/dist/chunk-CGURJ27Z.js.map +0 -1
  227. package/dist/chunk-FKFHZUGF.js.map +0 -1
  228. package/dist/chunk-GKHF54DI 2.js +0 -619
  229. package/dist/chunk-GKHF54DI.js 2.map +0 -1
  230. package/dist/chunk-GZRXOUBE.js.map +0 -1
  231. package/dist/chunk-HFBOFZ3Z.js.map +0 -1
  232. package/dist/chunk-NZ32EONV.js.map +0 -1
  233. package/dist/chunk-O3NWNXDY 2.js +0 -76
  234. package/dist/chunk-QPI2CCBA.js.map +0 -1
  235. package/dist/chunk-SMJZMKYN.js.map +0 -1
  236. package/dist/chunk-TDNI6ZWL.js 2.map +0 -1
  237. package/dist/chunk-TDNI6ZWL.js.map +0 -1
  238. package/dist/chunk-VKOCWWVY.js.map +0 -1
  239. package/dist/chunk-WP5I5GLN 2.js +0 -1564
  240. package/dist/index 3.js +0 -856
  241. package/dist/providers 3.js +0 -38
  242. package/dist/providers.js 3.map +0 -1
  243. package/dist/types 3.js +0 -128
  244. package/dist/types.js 3.map +0 -1
  245. package/dist/useInactivityTracker-MRUU55XI.js 3.map +0 -1
  246. package/dist/utils.js 3.map +0 -1
  247. package/dist/validation 3.js +0 -479
  248. package/src/styles/semantic.css +0 -24
  249. /package/dist/{DataTable-DGZDJUYM.js.map → DataTable-OKDYRW2S.js.map} +0 -0
  250. /package/dist/{UnifiedAuthProvider-UACKFATV.js.map → UnifiedAuthProvider-6C47WIML.js.map} +0 -0
  251. /package/dist/{chunk-D6BOFXYR.js.map → chunk-35ZDPMBM.js.map} +0 -0
  252. /package/dist/{chunk-CGURJ27Z.js 2.map → chunk-4MXVZVNS.js.map} +0 -0
  253. /package/dist/{chunk-ZYJ6O5CA.js.map → chunk-C43QIDN3.js.map} +0 -0
  254. /package/dist/{chunk-B4GZ2BXO.js.map → chunk-NZGLXZGP.js.map} +0 -0
  255. /package/dist/{chunk-NZ32EONV.js 2.map → chunk-QWNJCQXZ.js.map} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jmruthers/pace-core",
3
- "version": "0.5.121",
3
+ "version": "0.5.124",
4
4
  "description": "Clean, modern React component library with Tailwind v4 styling and native utilities",
5
5
  "private": false,
6
6
  "publishConfig": {
@@ -138,12 +138,12 @@
138
138
  "prepublishOnly": "npm run build:all",
139
139
  "clean": "rimraf dist",
140
140
  "_comment_test": "Test suite with various configurations",
141
- "test": "NODE_OPTIONS='--max-old-space-size=8192 --max-semi-space-size=128' vitest run --config ../../vitest.config.ts",
141
+ "test": "cross-env NODE_OPTIONS=\"--max-old-space-size=8192 --max-semi-space-size=128\" vitest run --config ../../vitest.config.ts",
142
142
  "test:memory-optimized": "node ../../scripts/run-tests-memory-optimized.js",
143
143
  "test:watch": "vitest --watch --config ../../vitest.config.ts",
144
- "test:coverage": "NODE_OPTIONS='--max-old-space-size=8192 --max-semi-space-size=128' vitest run --coverage --config ../../vitest.config.ts --reporter=dot",
144
+ "test:coverage": "cross-env NODE_OPTIONS=\"--max-old-space-size=8192 --max-semi-space-size=128\" vitest run --coverage --config ../../vitest.config.ts --reporter=dot",
145
145
  "test:ui": "vitest --ui --config ../../vitest.config.ts",
146
- "test:new": "NODE_OPTIONS='--max-old-space-size=8192 --max-semi-space-size=128' vitest run --config vitest.new.config.ts",
146
+ "test:new": "cross-env NODE_OPTIONS=\"--max-old-space-size=8192 --max-semi-space-size=128\" vitest run --config vitest.new.config.ts",
147
147
  "test:new:watch": "vitest --watch --config vitest.new.config.ts",
148
148
  "migrate-tests": "node scripts/migrate-tests.js",
149
149
  "test:unit": "vitest src/**/*.test.{ts,tsx} --config ../../vitest.config.ts",
@@ -0,0 +1,265 @@
1
+ /**
2
+ * @file usePermissions Hook Tests
3
+ * @package @jmruthers/pace-core
4
+ * @module RBAC/Hooks/usePermissions
5
+ * @since 1.0.0
6
+ *
7
+ * Tests for usePermissions hook to ensure:
8
+ * - Hook returns stable references when data hasn't changed
9
+ * - No infinite re-renders occur
10
+ * - Proper memoization is working
11
+ */
12
+
13
+ import { renderHook, act } from '@testing-library/react';
14
+ import { vi } from 'vitest';
15
+ import { usePermissions, useCan } from '../../rbac/hooks/usePermissions';
16
+
17
+ // Mock the API functions
18
+ vi.mock('../../rbac/api', () => ({
19
+ getPermissionMap: vi.fn(),
20
+ isPermitted: vi.fn(),
21
+ isPermittedCached: vi.fn(),
22
+ }));
23
+
24
+ import { getPermissionMap, isPermitted, isPermittedCached } from '../../rbac/api';
25
+
26
+ const mockGetPermissionMap = vi.mocked(getPermissionMap);
27
+ const mockIsPermitted = vi.mocked(isPermitted);
28
+ const mockIsPermittedCached = vi.mocked(isPermittedCached);
29
+
30
+ describe('usePermissions Hook Stability', () => {
31
+ const mockUserId = 'user-123';
32
+ const mockScope = {
33
+ organisationId: 'org-123',
34
+ eventId: 'event-123',
35
+ appId: 'app-123'
36
+ };
37
+
38
+ beforeEach(() => {
39
+ vi.clearAllMocks();
40
+ });
41
+
42
+ describe('usePermissions', () => {
43
+ it('should return stable references when data hasn\'t changed', async () => {
44
+ const mockPermissions = {
45
+ 'read:users': true,
46
+ 'create:users': false
47
+ };
48
+
49
+ mockGetPermissionMap.mockResolvedValue(mockPermissions);
50
+
51
+ const { result, rerender } = renderHook(() =>
52
+ usePermissions(mockUserId, mockScope)
53
+ );
54
+
55
+ // Wait for initial load
56
+ await act(async () => {
57
+ await new Promise(resolve => setTimeout(resolve, 0));
58
+ });
59
+
60
+ const firstResult = result.current;
61
+
62
+ // Re-render with same props
63
+ rerender();
64
+
65
+ const secondResult = result.current;
66
+
67
+ // References should be stable
68
+ expect(firstResult.permissions).toBe(secondResult.permissions);
69
+ expect(firstResult.isLoading).toBe(secondResult.isLoading);
70
+ expect(firstResult.error).toBe(secondResult.error);
71
+ expect(firstResult.hasPermission).toBe(secondResult.hasPermission);
72
+ expect(firstResult.hasAnyPermission).toBe(secondResult.hasAnyPermission);
73
+ expect(firstResult.hasAllPermissions).toBe(secondResult.hasAllPermissions);
74
+ expect(firstResult.refetch).toBe(secondResult.refetch);
75
+ });
76
+
77
+ it('should update references when data changes', async () => {
78
+ const initialPermissions = {
79
+ 'read:users': true,
80
+ 'create:users': false
81
+ };
82
+
83
+ const updatedPermissions = {
84
+ 'read:users': true,
85
+ 'create:users': true
86
+ };
87
+
88
+ mockGetPermissionMap
89
+ .mockResolvedValueOnce(initialPermissions)
90
+ .mockResolvedValueOnce(updatedPermissions);
91
+
92
+ const { result, rerender } = renderHook(() =>
93
+ usePermissions(mockUserId, mockScope)
94
+ );
95
+
96
+ // Wait for initial load
97
+ await act(async () => {
98
+ await new Promise(resolve => setTimeout(resolve, 0));
99
+ });
100
+
101
+ const firstResult = result.current;
102
+
103
+ // Trigger refetch
104
+ await act(async () => {
105
+ await firstResult.refetch();
106
+ });
107
+
108
+ const secondResult = result.current;
109
+
110
+ // References should be different due to data change
111
+ expect(firstResult.permissions).not.toBe(secondResult.permissions);
112
+ expect(firstResult.permissions).toEqual(initialPermissions);
113
+ expect(secondResult.permissions).toEqual(updatedPermissions);
114
+ });
115
+
116
+ it('should not cause infinite re-renders', async () => {
117
+ let renderCount = 0;
118
+
119
+ mockGetPermissionMap.mockResolvedValue({
120
+ 'read:users': true
121
+ });
122
+
123
+ const { result } = renderHook(() => {
124
+ renderCount++;
125
+ return usePermissions(mockUserId, mockScope);
126
+ });
127
+
128
+ // Wait for initial load
129
+ await act(async () => {
130
+ await new Promise(resolve => setTimeout(resolve, 0));
131
+ });
132
+
133
+ // Should not have excessive re-renders
134
+ expect(renderCount).toBeLessThan(5);
135
+ });
136
+ });
137
+
138
+ describe('useCan', () => {
139
+ it('should return stable references when data hasn\'t changed', async () => {
140
+ mockIsPermittedCached.mockResolvedValue(true);
141
+
142
+ const { result, rerender } = renderHook(() =>
143
+ useCan(mockUserId, mockScope, 'read:users', 'page-123', true)
144
+ );
145
+
146
+ // Wait for initial load
147
+ await act(async () => {
148
+ await new Promise(resolve => setTimeout(resolve, 0));
149
+ });
150
+
151
+ const firstResult = result.current;
152
+
153
+ // Re-render with same props
154
+ rerender();
155
+
156
+ const secondResult = result.current;
157
+
158
+ // References should be stable
159
+ expect(firstResult.can).toBe(secondResult.can);
160
+ expect(firstResult.isLoading).toBe(secondResult.isLoading);
161
+ expect(firstResult.error).toBe(secondResult.error);
162
+ expect(firstResult.refetch).toBe(secondResult.refetch);
163
+ });
164
+
165
+ it('should update references when permission result changes', async () => {
166
+ mockIsPermittedCached
167
+ .mockResolvedValueOnce(false)
168
+ .mockResolvedValueOnce(true);
169
+
170
+ const { result, rerender } = renderHook(() =>
171
+ useCan(mockUserId, mockScope, 'read:users', 'page-123', true)
172
+ );
173
+
174
+ // Wait for initial load
175
+ await act(async () => {
176
+ await new Promise(resolve => setTimeout(resolve, 0));
177
+ });
178
+
179
+ const firstResult = result.current;
180
+
181
+ // Trigger refetch
182
+ await act(async () => {
183
+ await firstResult.refetch();
184
+ });
185
+
186
+ const secondResult = result.current;
187
+
188
+ // References should be different due to permission change
189
+ expect(firstResult.can).toBe(false);
190
+ expect(secondResult.can).toBe(true);
191
+ });
192
+
193
+ it('should not cause infinite re-renders', async () => {
194
+ let renderCount = 0;
195
+
196
+ mockIsPermittedCached.mockResolvedValue(true);
197
+
198
+ const { result } = renderHook(() => {
199
+ renderCount++;
200
+ return useCan(mockUserId, mockScope, 'read:users', 'page-123', true);
201
+ });
202
+
203
+ // Wait for initial load
204
+ await act(async () => {
205
+ await new Promise(resolve => setTimeout(resolve, 0));
206
+ });
207
+
208
+ // Should not have excessive re-renders
209
+ expect(renderCount).toBeLessThan(5);
210
+ });
211
+ });
212
+
213
+ describe('Hook Dependencies', () => {
214
+ it('should re-run when userId changes', async () => {
215
+ mockGetPermissionMap.mockResolvedValue({ 'read:users': true });
216
+
217
+ const { result, rerender } = renderHook(
218
+ ({ userId }) => usePermissions(userId, mockScope),
219
+ { initialProps: { userId: 'user-1' } }
220
+ );
221
+
222
+ await act(async () => {
223
+ await new Promise(resolve => setTimeout(resolve, 0));
224
+ });
225
+
226
+ const firstResult = result.current;
227
+
228
+ // Change userId
229
+ rerender({ userId: 'user-2' });
230
+
231
+ await act(async () => {
232
+ await new Promise(resolve => setTimeout(resolve, 0));
233
+ });
234
+
235
+ const secondResult = result.current;
236
+
237
+ // Should have re-fetched due to userId change
238
+ expect(mockGetPermissionMap).toHaveBeenCalledTimes(2);
239
+ expect(firstResult).not.toBe(secondResult);
240
+ });
241
+
242
+ it('should re-run when scope changes', async () => {
243
+ mockGetPermissionMap.mockResolvedValue({ 'read:users': true });
244
+
245
+ const { result, rerender } = renderHook(
246
+ ({ scope }) => usePermissions(mockUserId, scope),
247
+ { initialProps: { scope: { ...mockScope, organisationId: 'org-1' } } }
248
+ );
249
+
250
+ await act(async () => {
251
+ await new Promise(resolve => setTimeout(resolve, 0));
252
+ });
253
+
254
+ // Change scope
255
+ rerender({ scope: { ...mockScope, organisationId: 'org-2' } });
256
+
257
+ await act(async () => {
258
+ await new Promise(resolve => setTimeout(resolve, 0));
259
+ });
260
+
261
+ // Should have re-fetched due to scope change
262
+ expect(mockGetPermissionMap).toHaveBeenCalledTimes(2);
263
+ });
264
+ });
265
+ });
@@ -221,16 +221,12 @@ describe('[component] DataTable', () => {
221
221
 
222
222
  describe('Validation and Warnings', () => {
223
223
  it('logs debug information in development mode', () => {
224
+ // Note: Debug logging has been removed from DataTable component
225
+ // The component now uses a logger utility instead of console.log
224
226
  render(<DataTable {...defaultProps} />);
225
227
 
226
- expect(consoleLogSpy).toHaveBeenCalledWith(
227
- '[DataTable] 🎯 DataTable WRAPPER called:',
228
- expect.objectContaining({
229
- dataLength: testData.length,
230
- columnsCount: testColumns.length,
231
- pageName: defaultRBAC.pageId,
232
- })
233
- );
228
+ // Verify component renders correctly
229
+ expect(screen.getByTestId('data-table-core')).toBeInTheDocument();
234
230
  });
235
231
 
236
232
  it('logs info message when features are not provided in development', async () => {
@@ -369,41 +365,26 @@ describe('[component] DataTable', () => {
369
365
 
370
366
  describe('Data Handling', () => {
371
367
  it('handles empty data array', () => {
368
+ // Note: Debug logging has been removed from DataTable component
372
369
  render(<DataTable {...defaultProps} data={[]} />);
373
370
 
374
371
  expect(screen.getByTestId('data-table-core')).toBeInTheDocument();
375
- expect(consoleLogSpy).toHaveBeenCalledWith(
376
- expect.stringContaining('[DataTable]'),
377
- expect.objectContaining({
378
- dataLength: 0,
379
- })
380
- );
381
372
  });
382
373
 
383
374
  it('handles single data item', () => {
375
+ // Note: Debug logging has been removed from DataTable component
384
376
  const singleData = testDataScenarios.single;
385
377
  render(<DataTable {...defaultProps} data={singleData} />);
386
378
 
387
379
  expect(screen.getByTestId('data-table-core')).toBeInTheDocument();
388
- expect(consoleLogSpy).toHaveBeenCalledWith(
389
- expect.stringContaining('[DataTable]'),
390
- expect.objectContaining({
391
- dataLength: 1,
392
- })
393
- );
394
380
  });
395
381
 
396
382
  it('handles large dataset', () => {
383
+ // Note: Debug logging has been removed from DataTable component
397
384
  const largeData = testDataScenarios.large;
398
385
  render(<DataTable {...defaultProps} data={largeData} />);
399
386
 
400
387
  expect(screen.getByTestId('data-table-core')).toBeInTheDocument();
401
- expect(consoleLogSpy).toHaveBeenCalledWith(
402
- expect.stringContaining('[DataTable]'),
403
- expect.objectContaining({
404
- dataLength: largeData.length,
405
- })
406
- );
407
388
  });
408
389
 
409
390
  it('handles data with null values', () => {
@@ -532,27 +513,17 @@ describe('[component] DataTable', () => {
532
513
 
533
514
  describe('Edge Cases', () => {
534
515
  it('handles missing columns gracefully', () => {
516
+ // Note: Debug logging has been removed from DataTable component
535
517
  render(<DataTable {...defaultProps} columns={[]} />);
536
518
 
537
519
  expect(screen.getByTestId('data-table-core')).toBeInTheDocument();
538
- expect(consoleLogSpy).toHaveBeenCalledWith(
539
- expect.stringContaining('[DataTable]'),
540
- expect.objectContaining({
541
- columnsCount: 0,
542
- })
543
- );
544
520
  });
545
521
 
546
522
  it('handles undefined rbac pageId and pageName', () => {
523
+ // Note: Debug logging has been removed from DataTable component
547
524
  render(<DataTable {...defaultProps} rbac={{}} />);
548
525
 
549
526
  expect(screen.getByTestId('data-table-core')).toBeInTheDocument();
550
- expect(consoleLogSpy).toHaveBeenCalledWith(
551
- expect.stringContaining('[DataTable]'),
552
- expect.objectContaining({
553
- pageName: undefined,
554
- })
555
- );
556
527
  });
557
528
 
558
529
  it('handles rapid feature prop changes', () => {
@@ -464,13 +464,6 @@ export function DataTable<TData extends DataRecord>(props: DataTableProps<TData>
464
464
  const logger = React.useMemo(() => createLogger('DataTable'), []);
465
465
  const { features, ...rest } = props;
466
466
 
467
- // CRITICAL DEBUG: Log when DataTable wrapper is called
468
- console.log('[DataTable] 🎯 DataTable WRAPPER called:', {
469
- dataLength: props.data?.length || 0,
470
- columnsCount: props.columns?.length || 0,
471
- pageName: props.rbac?.pageName || props.rbac?.pageId,
472
- });
473
-
474
467
  const normalizedFeatures = React.useMemo(
475
468
  () => normalizeDataTableFeatures(features),
476
469
  [features]