@jmruthers/pace-core 0.5.53 → 0.5.55

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 (398) hide show
  1. package/README.md +0 -4
  2. package/dist/{DataTable-7FMFXA7A.js → DataTable-4T627QFJ.js} +11 -11
  3. package/dist/{PublicLoadingSpinner-Bq_-BeK-.d.ts → PublicLoadingSpinner-SL8WaQN7.d.ts} +2 -21
  4. package/dist/{api-H5A3H4IR.js → api-LUNF5O6M.js} +3 -3
  5. package/dist/{appConfig-BVGyuvI7.d.ts → appConfig-DjpeG6P-.d.ts} +9 -1
  6. package/dist/{appNameResolver-7GHF5ED2.js → appNameResolver-UURKN7NF.js} +2 -2
  7. package/dist/{audit-BUW3LMJB.js → audit-6TOCAMKO.js} +2 -2
  8. package/dist/{chunk-MZBUOP4P.js → chunk-5BSLGBYI.js} +4 -3
  9. package/dist/chunk-5BSLGBYI.js.map +1 -0
  10. package/dist/{chunk-I5Z3QH5X.js → chunk-66C4BSAY.js} +2 -2
  11. package/dist/{chunk-I5Z3QH5X.js.map → chunk-66C4BSAY.js.map} +1 -1
  12. package/dist/{chunk-MYP2EGHX.js → chunk-AJ2KMES7.js} +21 -14
  13. package/dist/chunk-AJ2KMES7.js.map +1 -0
  14. package/dist/{chunk-EL2O4IUX.js → chunk-AQFRLC7K.js} +16 -24
  15. package/dist/{chunk-EL2O4IUX.js.map → chunk-AQFRLC7K.js.map} +1 -1
  16. package/dist/{chunk-7BNPOCLL.js → chunk-B2WTCLCV.js} +6 -2
  17. package/dist/chunk-B2WTCLCV.js.map +1 -0
  18. package/dist/{chunk-WJARTBCT.js → chunk-D7ARGIA3.js} +16 -7
  19. package/dist/chunk-D7ARGIA3.js.map +1 -0
  20. package/dist/{chunk-NRK4AIHQ.js → chunk-KBRACSJI.js} +3 -3
  21. package/dist/{chunk-NYUJ4FJR.js → chunk-KJDPSM64.js} +7 -7
  22. package/dist/chunk-KJDPSM64.js.map +1 -0
  23. package/dist/{chunk-GWSBHC4J.js → chunk-KLPVOPRI.js} +261 -38
  24. package/dist/chunk-KLPVOPRI.js.map +1 -0
  25. package/dist/{chunk-TRIZ7IB7.js → chunk-MPQDF75X.js} +148 -288
  26. package/dist/chunk-MPQDF75X.js.map +1 -0
  27. package/dist/{chunk-MSFACPQQ.js → chunk-PAEM3OWN.js} +11 -11
  28. package/dist/{chunk-MSFACPQQ.js.map → chunk-PAEM3OWN.js.map} +1 -1
  29. package/dist/{chunk-GIO7BFE7.js → chunk-RQD3D2CO.js} +66 -169
  30. package/dist/{chunk-GIO7BFE7.js.map → chunk-RQD3D2CO.js.map} +1 -1
  31. package/dist/{chunk-YDJW5XTN.js → chunk-STT7INZR.js} +25 -1
  32. package/dist/chunk-STT7INZR.js.map +1 -0
  33. package/dist/{chunk-6MTY77WU.js → chunk-TNMXZLDR.js} +3 -3
  34. package/dist/{chunk-BC3S53OZ.js → chunk-UQE2Y64H.js} +30 -14
  35. package/dist/chunk-UQE2Y64H.js.map +1 -0
  36. package/dist/{chunk-22KLBHPS.js → chunk-W66AZIOH.js} +2 -2
  37. package/dist/chunk-W66AZIOH.js.map +1 -0
  38. package/dist/{chunk-SS3E6QLB.js → chunk-YNUBMSMV.js} +2 -2
  39. package/dist/chunk-YNUBMSMV.js.map +1 -0
  40. package/dist/{chunk-NZ655MWE.js → chunk-ZOD2ZY6X.js} +5 -4
  41. package/dist/chunk-ZOD2ZY6X.js.map +1 -0
  42. package/dist/{chunk-74C6SNEC.js → chunk-ZPK5656W.js} +3 -3
  43. package/dist/{chunk-74C6SNEC.js.map → chunk-ZPK5656W.js.map} +1 -1
  44. package/dist/components.d.ts +22 -899
  45. package/dist/components.js +436 -3118
  46. package/dist/components.js.map +1 -1
  47. package/dist/file-reference-9xUOnwyt.d.ts +70 -0
  48. package/dist/hooks.d.ts +2 -2
  49. package/dist/hooks.js +10 -10
  50. package/dist/hooks.js.map +1 -1
  51. package/dist/index.d.ts +49 -9
  52. package/dist/index.js +190 -25
  53. package/dist/index.js.map +1 -1
  54. package/dist/{organisation-CO3Sh3_D.d.ts → organisation-t-vvQC3g.d.ts} +1 -8
  55. package/dist/providers.d.ts +2 -2
  56. package/dist/providers.js +5 -5
  57. package/dist/rbac/index.d.ts +65 -46
  58. package/dist/rbac/index.js +10 -12
  59. package/dist/styles/core.css +0 -125
  60. package/dist/types.d.ts +2 -1
  61. package/dist/types.js +3 -1
  62. package/dist/types.js.map +1 -1
  63. package/dist/{usePublicRouteParams-B2OcAsur.d.ts → usePublicRouteParams-CdoFxnJK.d.ts} +1 -1
  64. package/dist/utils.d.ts +3 -4
  65. package/dist/utils.js +44 -13
  66. package/dist/utils.js.map +1 -1
  67. package/docs/FILE_REFERENCE_SYSTEM.md +440 -0
  68. package/docs/INDEX.md +7 -5
  69. package/docs/README.md +0 -1
  70. package/docs/api/README.md +0 -4
  71. package/docs/api/classes/ErrorBoundary.md +1 -1
  72. package/docs/api/classes/InvalidScopeError.md +1 -1
  73. package/docs/api/classes/MissingUserContextError.md +1 -1
  74. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  75. package/docs/api/classes/PermissionDeniedError.md +2 -2
  76. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  77. package/docs/api/classes/RBACAuditManager.md +12 -12
  78. package/docs/api/classes/RBACCache.md +1 -1
  79. package/docs/api/classes/RBACEngine.md +6 -6
  80. package/docs/api/classes/RBACError.md +1 -1
  81. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  82. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  83. package/docs/api/classes/StorageUtils.md +281 -0
  84. package/docs/api/interfaces/AggregateConfig.md +1 -1
  85. package/docs/api/interfaces/ButtonProps.md +1 -1
  86. package/docs/api/interfaces/CardProps.md +1 -1
  87. package/docs/api/interfaces/ColorPalette.md +1 -1
  88. package/docs/api/interfaces/ColorShade.md +1 -1
  89. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  90. package/docs/api/interfaces/DataTableAction.md +1 -1
  91. package/docs/api/interfaces/DataTableColumn.md +1 -1
  92. package/docs/api/interfaces/DataTableProps.md +1 -1
  93. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  94. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  95. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  96. package/docs/api/interfaces/EventContextType.md +1 -1
  97. package/docs/api/interfaces/EventLogoProps.md +1 -1
  98. package/docs/api/interfaces/EventProviderProps.md +1 -1
  99. package/docs/api/interfaces/FileSizeLimits.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 +2 -2
  107. package/docs/api/interfaces/NavigationContextType.md +1 -1
  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 +1 -1
  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 +1 -1
  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 +1 -1
  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/RBACContextType.md +1 -1
  133. package/docs/api/interfaces/RBACLogger.md +1 -1
  134. package/docs/api/interfaces/RBACProviderProps.md +1 -1
  135. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  136. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  137. package/docs/api/interfaces/RouteAccessRecord.md +2 -2
  138. package/docs/api/interfaces/RouteConfig.md +2 -2
  139. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  140. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  141. package/docs/api/interfaces/StorageConfig.md +1 -1
  142. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  143. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  144. package/docs/api/interfaces/StorageListOptions.md +1 -1
  145. package/docs/api/interfaces/StorageListResult.md +1 -1
  146. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  147. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  148. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  149. package/docs/api/interfaces/StyleImport.md +1 -1
  150. package/docs/api/interfaces/ToastActionElement.md +1 -1
  151. package/docs/api/interfaces/ToastProps.md +1 -1
  152. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  153. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  154. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  155. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  156. package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
  157. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  158. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  159. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  160. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  161. package/docs/api/interfaces/UserEventAccess.md +1 -1
  162. package/docs/api/interfaces/UserMenuProps.md +1 -1
  163. package/docs/api/interfaces/UserProfile.md +1 -1
  164. package/docs/api/modules.md +204 -200
  165. package/docs/api-reference/components.md +141 -163
  166. package/docs/api-reference/hooks.md +347 -0
  167. package/docs/core-concepts/rbac-system.md +69 -16
  168. package/docs/getting-started/examples/basic-auth-app.md +0 -1
  169. package/docs/implementation-guides/datatable-rbac-usage.md +12 -11
  170. package/docs/implementation-guides/file-upload-storage.md +733 -0
  171. package/docs/implementation-guides/inactivity-tracking.md +779 -0
  172. package/docs/implementation-guides/organisation-security.md +748 -0
  173. package/docs/implementation-guides/public-pages-advanced.md +1022 -0
  174. package/docs/migration/MIGRATION_GUIDE.md +684 -0
  175. package/docs/migration/README.md +13 -2
  176. package/docs/migration/rbac-migration.md +73 -0
  177. package/docs/rbac/examples/rbac-rls-integration-example.md +11 -13
  178. package/docs/style-guide.md +269 -1
  179. package/package.json +1 -1
  180. package/src/__tests__/TESTING_GUIDELINES.md +331 -18
  181. package/src/__tests__/helpers/supabaseMock.ts +99 -0
  182. package/src/__tests__/rbac/PagePermissionGuard.test.tsx +10 -7
  183. package/src/__tests__/shared.ts +6 -0
  184. package/src/components/DataTable/components/ActionButtons.tsx +2 -2
  185. package/src/components/DataTable/components/DataTableCore.tsx +2 -2
  186. package/src/components/DataTable/components/UnifiedTableBody.tsx +1 -1
  187. package/src/components/DataTable/utils/debugTools.ts +2 -2
  188. package/src/components/Dialog/Dialog.test.tsx +12 -2
  189. package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +6 -6
  190. package/src/components/ErrorBoundary/ErrorBoundary.tsx +2 -2
  191. package/src/components/FileDisplay.tsx +233 -0
  192. package/src/components/FileUpload.tsx +176 -0
  193. package/src/components/Footer/Footer.test.tsx +7 -7
  194. package/src/components/NavigationMenu/NavigationMenu.test.tsx +13 -6
  195. package/src/components/OrganisationSelector/OrganisationSelector.test.tsx +30 -3
  196. package/src/components/OrganisationSelector/OrganisationSelector.tsx +1 -1
  197. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.rbac.test.tsx +558 -0
  198. package/src/components/PublicLayout/PublicErrorBoundary.tsx +1 -1
  199. package/src/components/PublicLayout/PublicPageDebugger.tsx +2 -2
  200. package/src/components/PublicLayout/PublicPageDiagnostic.tsx +2 -2
  201. package/src/components/PublicLayout/PublicPageProvider.tsx +2 -2
  202. package/src/components/Select/Select.test.tsx +50 -15
  203. package/src/components/SuperAdminGuard.tsx +2 -2
  204. package/src/components/__tests__/SuperAdminGuard.test.tsx +559 -0
  205. package/src/components/index.ts +0 -183
  206. package/src/hooks/__tests__/useOrganisationPermissions.unit.test.tsx +2 -2
  207. package/src/hooks/__tests__/usePermissionCache.unit.test.ts +1 -1
  208. package/src/hooks/__tests__/useRBAC.unit.test.ts +191 -138
  209. package/src/hooks/public/usePublicEvent.ts +2 -2
  210. package/src/hooks/useAppConfig.ts +3 -3
  211. package/src/hooks/useComponentPerformance.ts +1 -1
  212. package/src/hooks/useDataTablePerformance.ts +1 -1
  213. package/src/hooks/useFileReference.ts +232 -0
  214. package/src/hooks/useOrganisationPermissions.test.ts +254 -344
  215. package/src/hooks/useOrganisationPermissions.ts +15 -7
  216. package/src/hooks/useOrganisationSecurity.test.ts +390 -402
  217. package/src/hooks/usePerformanceMonitor.ts +1 -1
  218. package/src/hooks/usePermissionCache.test.ts +264 -395
  219. package/src/hooks/usePermissionCache.ts +34 -4
  220. package/src/hooks/useSecureDataAccess.test.ts +486 -0
  221. package/src/hooks/useSecureDataAccess.ts +4 -1
  222. package/src/providers/InactivityProvider.tsx +2 -2
  223. package/src/providers/OrganisationProvider.test.simple.tsx +168 -0
  224. package/src/providers/OrganisationProvider.test.tsx +168 -0
  225. package/src/providers/OrganisationProvider.tsx +18 -31
  226. package/src/providers/UnifiedAuthProvider.test.simple.tsx +205 -0
  227. package/src/providers/UnifiedAuthProvider.test.tsx +128 -0
  228. package/src/providers/__tests__/InactivityProvider.test.tsx +3 -4
  229. package/src/providers/__tests__/OrganisationProvider.test.tsx +19 -14
  230. package/src/rbac/__tests__/integration.authflow.test.tsx +123 -0
  231. package/src/rbac/__tests__/integration.navigation.test.tsx +72 -0
  232. package/src/rbac/__tests__/integration.securedata.test.tsx +92 -0
  233. package/src/rbac/__tests__/integration.smoke.test.tsx +73 -0
  234. package/src/rbac/__tests__/rbac-core.test.tsx +26 -22
  235. package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +411 -0
  236. package/src/rbac/__tests__/rbac-engine-simplified.test.ts +285 -0
  237. package/src/rbac/__tests__/rbac-functions.test.ts +655 -0
  238. package/src/rbac/__tests__/rbac-integration.test.ts +532 -0
  239. package/src/rbac/__tests__/scenarios.user-role.test.tsx +196 -0
  240. package/src/rbac/api.test.ts +6 -6
  241. package/src/rbac/api.ts +2 -2
  242. package/src/rbac/audit.test.ts +485 -0
  243. package/src/rbac/audit.ts +7 -1
  244. package/src/rbac/cache-invalidation.ts +318 -0
  245. package/src/rbac/cache.test.ts +286 -0
  246. package/src/rbac/components/EnhancedNavigationMenu.test.tsx +559 -0
  247. package/src/rbac/components/EnhancedNavigationMenu.tsx +29 -23
  248. package/src/rbac/components/NavigationProvider.test.tsx +449 -0
  249. package/src/rbac/components/PagePermissionGuard.tsx +4 -4
  250. package/src/rbac/components/PagePermissionProvider.test.tsx +479 -0
  251. package/src/rbac/components/SecureDataProvider.test.tsx +511 -0
  252. package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +159 -430
  253. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +4 -5
  254. package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +112 -118
  255. package/src/rbac/config.test.ts +410 -0
  256. package/src/rbac/engine.test.simple.ts +237 -0
  257. package/src/rbac/engine.test.ts +233 -0
  258. package/src/rbac/engine.ts +37 -41
  259. package/src/rbac/examples/CompleteRBACExample.tsx +3 -3
  260. package/src/rbac/examples/EventBasedApp.tsx +4 -4
  261. package/src/rbac/hooks/useRBAC.simple.test.ts +16 -0
  262. package/src/rbac/hooks/useRBAC.test.ts +207 -455
  263. package/src/rbac/hooks/useRBAC.ts +30 -22
  264. package/src/rbac/permissions.test.ts +128 -0
  265. package/src/rbac/permissions.ts +56 -141
  266. package/src/rbac/providers/RBACProvider.tsx +1 -1
  267. package/src/rbac/secureClient.test.ts +444 -0
  268. package/src/rbac/security.test.ts +390 -0
  269. package/src/rbac/security.ts +1 -1
  270. package/src/rbac/types.test.ts +382 -0
  271. package/src/rbac/types.ts +2 -2
  272. package/src/styles/base.css +208 -0
  273. package/src/styles/core.css +0 -125
  274. package/src/styles/semantic.css +24 -0
  275. package/src/types/file-reference.ts +77 -0
  276. package/src/types/rbac-functions.ts +290 -0
  277. package/src/types/supabase.ts +10 -28
  278. package/src/types/unified.ts +4 -1
  279. package/src/utils/__tests__/bundleAnalysis.unit.test.ts +81 -55
  280. package/src/utils/__tests__/lazyLoad.unit.test.tsx +21 -12
  281. package/src/utils/__tests__/organisationContext.unit.test.ts +13 -7
  282. package/src/utils/__tests__/performanceBudgets.unit.test.ts +3 -3
  283. package/src/utils/__tests__/sessionTracking.unit.test.ts +32 -12
  284. package/src/utils/appConfig.ts +1 -1
  285. package/src/utils/appIdResolver.test.ts +503 -0
  286. package/src/utils/appIdResolver.ts +1 -1
  287. package/src/utils/appNameResolver.test.ts +494 -0
  288. package/src/utils/appNameResolver.ts +3 -2
  289. package/src/utils/bundleAnalysis.ts +3 -3
  290. package/src/utils/debugLogger.ts +1 -1
  291. package/src/utils/file-reference.ts +263 -0
  292. package/src/utils/formatDate.test.ts +2 -2
  293. package/src/utils/organisationContext.test.ts +340 -0
  294. package/src/utils/organisationContext.ts +19 -6
  295. package/src/utils/performanceBudgets.ts +2 -2
  296. package/src/utils/permissionUtils.test.ts +393 -0
  297. package/src/utils/permissionUtils.ts +5 -2
  298. package/src/utils/secureDataAccess.test.ts +715 -0
  299. package/src/utils/secureDataAccess.ts +21 -5
  300. package/src/utils/sessionTracking.ts +34 -4
  301. package/src/utils/storage/__tests__/helpers.unit.test.ts +328 -0
  302. package/src/utils/storage/__tests__/index.unit.test.ts +16 -0
  303. package/src/utils/storage/helpers.ts +20 -25
  304. package/src/utils/storage/index.ts +29 -1
  305. package/src/vite-env.d.ts +17 -0
  306. package/dist/chunk-22KLBHPS.js.map +0 -1
  307. package/dist/chunk-7BNPOCLL.js.map +0 -1
  308. package/dist/chunk-BC3S53OZ.js.map +0 -1
  309. package/dist/chunk-GWSBHC4J.js.map +0 -1
  310. package/dist/chunk-MYP2EGHX.js.map +0 -1
  311. package/dist/chunk-MZBUOP4P.js.map +0 -1
  312. package/dist/chunk-NYUJ4FJR.js.map +0 -1
  313. package/dist/chunk-NZ655MWE.js.map +0 -1
  314. package/dist/chunk-SS3E6QLB.js.map +0 -1
  315. package/dist/chunk-TRIZ7IB7.js.map +0 -1
  316. package/dist/chunk-WJARTBCT.js.map +0 -1
  317. package/dist/chunk-YDJW5XTN.js.map +0 -1
  318. package/docs/print-components/README.md +0 -258
  319. package/docs/print-components/api-reference.md +0 -636
  320. package/docs/print-components/examples/README.md +0 -204
  321. package/docs/print-components/examples/basic-report.tsx +0 -92
  322. package/docs/print-components/examples/card-catalog.tsx +0 -149
  323. package/docs/print-components/examples/cover-page-report.tsx +0 -163
  324. package/docs/print-components/quick-start.md +0 -363
  325. package/src/components/PrintButton/PrintButton.tsx +0 -321
  326. package/src/components/PrintButton/PrintButtonGroup.tsx +0 -84
  327. package/src/components/PrintButton/PrintToolbar.tsx +0 -94
  328. package/src/components/PrintButton/__tests__/PrintButton.test.tsx +0 -271
  329. package/src/components/PrintButton/examples/PrintButtonShowcase.tsx +0 -438
  330. package/src/components/PrintButton/index.ts +0 -33
  331. package/src/components/PrintButton/types.ts +0 -173
  332. package/src/components/PrintCard/PrintCard.tsx +0 -154
  333. package/src/components/PrintCard/PrintCardContent.tsx +0 -57
  334. package/src/components/PrintCard/PrintCardFooter.tsx +0 -60
  335. package/src/components/PrintCard/PrintCardGrid.tsx +0 -91
  336. package/src/components/PrintCard/PrintCardHeader.tsx +0 -78
  337. package/src/components/PrintCard/PrintCardImage.tsx +0 -81
  338. package/src/components/PrintCard/examples/PrintCardShowcase.tsx +0 -239
  339. package/src/components/PrintCard/index.ts +0 -34
  340. package/src/components/PrintCard/types.ts +0 -171
  341. package/src/components/PrintDataTable/PrintDataTable.tsx +0 -215
  342. package/src/components/PrintDataTable/PrintTableGroup.tsx +0 -90
  343. package/src/components/PrintDataTable/PrintTableRow.tsx +0 -76
  344. package/src/components/PrintDataTable/index.ts +0 -25
  345. package/src/components/PrintDataTable/types.ts +0 -67
  346. package/src/components/PrintFooter/PrintFooter.tsx +0 -183
  347. package/src/components/PrintFooter/PrintFooterContent.tsx +0 -71
  348. package/src/components/PrintFooter/PrintFooterInfo.tsx +0 -86
  349. package/src/components/PrintFooter/PrintPageNumber.tsx +0 -90
  350. package/src/components/PrintFooter/examples/PrintFooterShowcase.tsx +0 -390
  351. package/src/components/PrintFooter/index.ts +0 -30
  352. package/src/components/PrintFooter/types.ts +0 -149
  353. package/src/components/PrintGrid/PrintGrid.tsx +0 -180
  354. package/src/components/PrintGrid/PrintGridBreakpoint.tsx +0 -109
  355. package/src/components/PrintGrid/PrintGridContainer.tsx +0 -128
  356. package/src/components/PrintGrid/PrintGridItem.tsx +0 -220
  357. package/src/components/PrintGrid/examples/PrintGridShowcase.tsx +0 -359
  358. package/src/components/PrintGrid/index.ts +0 -31
  359. package/src/components/PrintGrid/types.ts +0 -159
  360. package/src/components/PrintHeader/PrintCoverHeader.tsx +0 -230
  361. package/src/components/PrintHeader/PrintHeader.tsx +0 -150
  362. package/src/components/PrintHeader/index.ts +0 -17
  363. package/src/components/PrintHeader/types.ts +0 -42
  364. package/src/components/PrintLayout/PrintLayout.tsx +0 -122
  365. package/src/components/PrintLayout/PrintLayoutContext.tsx +0 -66
  366. package/src/components/PrintLayout/PrintPageBreak.tsx +0 -52
  367. package/src/components/PrintLayout/examples/PrintShowcase.tsx +0 -230
  368. package/src/components/PrintLayout/index.ts +0 -19
  369. package/src/components/PrintLayout/types.ts +0 -37
  370. package/src/components/PrintPageBreak/PrintPageBreak.tsx +0 -120
  371. package/src/components/PrintPageBreak/PrintPageBreakGroup.tsx +0 -90
  372. package/src/components/PrintPageBreak/PrintPageBreakIndicator.tsx +0 -112
  373. package/src/components/PrintPageBreak/examples/PrintPageBreakShowcase.tsx +0 -279
  374. package/src/components/PrintPageBreak/index.ts +0 -23
  375. package/src/components/PrintPageBreak/types.ts +0 -94
  376. package/src/components/PrintSection/PrintColumn.tsx +0 -104
  377. package/src/components/PrintSection/PrintDivider.tsx +0 -101
  378. package/src/components/PrintSection/PrintSection.tsx +0 -129
  379. package/src/components/PrintSection/PrintSectionContent.tsx +0 -75
  380. package/src/components/PrintSection/PrintSectionHeader.tsx +0 -97
  381. package/src/components/PrintSection/examples/PrintSectionShowcase.tsx +0 -258
  382. package/src/components/PrintSection/index.ts +0 -33
  383. package/src/components/PrintSection/types.ts +0 -155
  384. package/src/components/PrintText/PrintText.tsx +0 -116
  385. package/src/components/PrintText/index.ts +0 -16
  386. package/src/components/PrintText/types.ts +0 -24
  387. package/src/rbac/__tests__/integration.test.tsx +0 -218
  388. package/src/utils/print/PrintDataProcessor.ts +0 -390
  389. package/src/utils/print/examples/PrintUtilitiesShowcase.tsx +0 -397
  390. package/src/utils/print/index.ts +0 -29
  391. package/src/utils/print/types.ts +0 -196
  392. package/src/utils/print/usePrintOptimization.ts +0 -272
  393. /package/dist/{DataTable-7FMFXA7A.js.map → DataTable-4T627QFJ.js.map} +0 -0
  394. /package/dist/{api-H5A3H4IR.js.map → api-LUNF5O6M.js.map} +0 -0
  395. /package/dist/{appNameResolver-7GHF5ED2.js.map → appNameResolver-UURKN7NF.js.map} +0 -0
  396. /package/dist/{audit-BUW3LMJB.js.map → audit-6TOCAMKO.js.map} +0 -0
  397. /package/dist/{chunk-NRK4AIHQ.js.map → chunk-KBRACSJI.js.map} +0 -0
  398. /package/dist/{chunk-6MTY77WU.js.map → chunk-TNMXZLDR.js.map} +0 -0
@@ -0,0 +1,559 @@
1
+ /**
2
+ * @file Enhanced Navigation Menu Tests
3
+ * @package @jmruthers/pace-core
4
+ * @module RBAC/Components/EnhancedNavigationMenu
5
+ * @since 2.0.0
6
+ *
7
+ * Comprehensive tests for the EnhancedNavigationMenu component covering all critical functionality.
8
+ */
9
+
10
+ import React from 'react';
11
+ import { render, screen, waitFor, fireEvent } from '@testing-library/react';
12
+ import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
13
+ import { EnhancedNavigationMenu } from './EnhancedNavigationMenu';
14
+ import { useNavigationPermissions, NavigationItem } from './NavigationProvider';
15
+ import { UUID, Scope, Permission } from '../types';
16
+
17
+ // Mock the navigation provider
18
+ vi.mock('./NavigationProvider', () => ({
19
+ useNavigationPermissions: vi.fn(),
20
+ NavigationProvider: ({ children }: { children: React.ReactNode }) => <div>{children}</div>
21
+ }));
22
+
23
+ // Mock the navigation guard
24
+ vi.mock('./NavigationGuard', () => ({
25
+ default: ({ children, navigationItem, onDenied }: {
26
+ children: React.ReactNode;
27
+ navigationItem: NavigationItem;
28
+ onDenied?: (item: NavigationItem) => void;
29
+ }) => {
30
+ // Simulate NavigationGuard behavior - call onDenied if user doesn't have permission
31
+ React.useEffect(() => {
32
+ if (onDenied) {
33
+ onDenied(navigationItem);
34
+ }
35
+ }, [onDenied, navigationItem]);
36
+
37
+ return <div data-testid={`nav-guard-${navigationItem.id}`}>{children}</div>;
38
+ }
39
+ }));
40
+
41
+ // Mock data
42
+ const mockScope: Scope = {
43
+ organisationId: 'org-123' as UUID,
44
+ eventId: 'event-456',
45
+ appId: 'app-789' as UUID
46
+ };
47
+
48
+ const mockNavigationItems: NavigationItem[] = [
49
+ {
50
+ id: 'dashboard',
51
+ label: 'Dashboard',
52
+ path: '/dashboard',
53
+ permissions: ['read:dashboard'] as Permission[],
54
+ pageId: 'page-dashboard',
55
+ meta: {
56
+ icon: 'dashboard-icon',
57
+ description: 'Main dashboard',
58
+ order: 1
59
+ }
60
+ },
61
+ {
62
+ id: 'users',
63
+ label: 'Users',
64
+ path: '/users',
65
+ permissions: ['read:users'] as Permission[],
66
+ pageId: 'page-users',
67
+ meta: {
68
+ icon: 'users-icon',
69
+ description: 'User management',
70
+ order: 2
71
+ }
72
+ },
73
+ {
74
+ id: 'admin',
75
+ label: 'Admin',
76
+ path: '/admin',
77
+ permissions: ['manage:admin'] as Permission[],
78
+ pageId: 'page-admin',
79
+ accessLevel: 'admin',
80
+ meta: {
81
+ icon: 'admin-icon',
82
+ description: 'Administration',
83
+ order: 3
84
+ }
85
+ }
86
+ ];
87
+
88
+ describe('EnhancedNavigationMenu', () => {
89
+ beforeEach(() => {
90
+ vi.clearAllMocks();
91
+ vi.mocked(useNavigationPermissions).mockReturnValue({
92
+ hasNavigationPermission: vi.fn().mockReturnValue(true),
93
+ getNavigationPermissions: vi.fn().mockReturnValue({}),
94
+ getFilteredNavigationItems: vi.fn().mockImplementation((items) => items), // Return items as-is by default
95
+ getNavigationAccessHistory: vi.fn().mockReturnValue([]),
96
+ clearNavigationAccessHistory: vi.fn(),
97
+ isEnabled: true,
98
+ isStrictMode: true,
99
+ isAuditLogEnabled: true
100
+ } as any);
101
+ });
102
+
103
+ afterEach(() => {
104
+ vi.restoreAllMocks();
105
+ });
106
+
107
+ describe('Rendering', () => {
108
+ it('renders navigation items correctly', () => {
109
+ render(
110
+ <EnhancedNavigationMenu items={mockNavigationItems} />
111
+ );
112
+
113
+ expect(screen.getByText('Dashboard')).toBeInTheDocument();
114
+ expect(screen.getByText('Users')).toBeInTheDocument();
115
+ expect(screen.getByText('Admin')).toBeInTheDocument();
116
+ });
117
+
118
+ it('renders with custom className', () => {
119
+ const { container } = render(
120
+ <EnhancedNavigationMenu
121
+ items={mockNavigationItems}
122
+ className="custom-nav-menu"
123
+ />
124
+ );
125
+
126
+ expect(container.firstChild).toHaveClass('custom-nav-menu');
127
+ });
128
+
129
+ it('renders with custom item className', () => {
130
+ render(
131
+ <EnhancedNavigationMenu
132
+ items={mockNavigationItems}
133
+ itemClassName="custom-nav-item"
134
+ />
135
+ );
136
+
137
+ const navItems = screen.getAllByRole('button');
138
+ navItems.forEach(item => {
139
+ expect(item).toHaveClass('custom-nav-item');
140
+ });
141
+ });
142
+
143
+ it('renders with custom active item className', () => {
144
+ render(
145
+ <EnhancedNavigationMenu
146
+ items={mockNavigationItems}
147
+ activeItemClassName="custom-active-item"
148
+ activePath="/dashboard"
149
+ />
150
+ );
151
+
152
+ const activeItem = screen.getByText('Dashboard').closest('button');
153
+ expect(activeItem).toHaveClass('custom-active-item');
154
+ });
155
+
156
+ it('renders with custom disabled item className', () => {
157
+ vi.mocked(useNavigationPermissions).mockReturnValue({
158
+ hasNavigationPermission: vi.fn().mockReturnValue(false),
159
+ getNavigationPermissions: vi.fn().mockReturnValue({}),
160
+ getFilteredNavigationItems: vi.fn().mockReturnValue(mockNavigationItems),
161
+ validateNavigationItem: vi.fn().mockReturnValue(true),
162
+ getNavigationAccessHistory: vi.fn().mockReturnValue([]),
163
+ clearNavigationAccessHistory: vi.fn(),
164
+ isEnabled: true,
165
+ isStrictMode: true,
166
+ isAuditLogEnabled: true
167
+ } as any);
168
+
169
+ render(
170
+ <EnhancedNavigationMenu
171
+ items={mockNavigationItems}
172
+ disabledItemClassName="custom-disabled-item"
173
+ />
174
+ );
175
+
176
+ const buttons = screen.getAllByRole('button');
177
+ buttons.forEach(button => {
178
+ expect(button).toHaveClass('custom-disabled-item');
179
+ });
180
+ });
181
+ });
182
+
183
+ describe('Permission Filtering', () => {
184
+ it('filters navigation items based on permissions', () => {
185
+ const filteredItems = [mockNavigationItems[0], mockNavigationItems[1]]; // Only dashboard and users
186
+ vi.mocked(useNavigationPermissions).mockReturnValue({
187
+ hasNavigationPermission: vi.fn().mockReturnValue(true),
188
+ getNavigationPermissions: vi.fn().mockReturnValue({}),
189
+ getFilteredNavigationItems: vi.fn().mockReturnValue(filteredItems),
190
+ validateNavigationItem: vi.fn().mockReturnValue(true),
191
+ getNavigationAccessHistory: vi.fn().mockReturnValue([]),
192
+ clearNavigationAccessHistory: vi.fn(),
193
+ isEnabled: true,
194
+ isStrictMode: true,
195
+ isAuditLogEnabled: true
196
+ } as any);
197
+
198
+ render(
199
+ <EnhancedNavigationMenu items={mockNavigationItems} />
200
+ );
201
+
202
+ expect(screen.getByText('Dashboard')).toBeInTheDocument();
203
+ expect(screen.getByText('Users')).toBeInTheDocument();
204
+ expect(screen.queryByText('Admin')).not.toBeInTheDocument();
205
+ });
206
+
207
+ it('hides unauthorized items when hideUnauthorizedItems is true', () => {
208
+ vi.mocked(useNavigationPermissions).mockReturnValue({
209
+ hasNavigationPermission: vi.fn()
210
+ .mockReturnValueOnce(true) // dashboard
211
+ .mockReturnValueOnce(true) // users
212
+ .mockReturnValueOnce(false), // admin
213
+ getNavigationPermissions: vi.fn().mockReturnValue({}),
214
+ getFilteredNavigationItems: vi.fn().mockImplementation((items) =>
215
+ items.filter(item => item.id !== 'admin') // Filter out admin item
216
+ ),
217
+ validateNavigationItem: vi.fn().mockReturnValue(true),
218
+ getNavigationAccessHistory: vi.fn().mockReturnValue([]),
219
+ clearNavigationAccessHistory: vi.fn(),
220
+ isEnabled: true,
221
+ isStrictMode: true,
222
+ isAuditLogEnabled: true
223
+ } as any);
224
+
225
+ render(
226
+ <EnhancedNavigationMenu
227
+ items={mockNavigationItems}
228
+ hideUnauthorizedItems={true}
229
+ />
230
+ );
231
+
232
+ expect(screen.getByText('Dashboard')).toBeInTheDocument();
233
+ expect(screen.getByText('Users')).toBeInTheDocument();
234
+ expect(screen.queryByText('Admin')).not.toBeInTheDocument();
235
+ });
236
+
237
+ it('shows unauthorized items as disabled when hideUnauthorizedItems is false', () => {
238
+ vi.mocked(useNavigationPermissions).mockReturnValue({
239
+ hasNavigationPermission: vi.fn()
240
+ .mockReturnValueOnce(true) // dashboard
241
+ .mockReturnValueOnce(true) // users
242
+ .mockReturnValueOnce(false), // admin
243
+ getNavigationPermissions: vi.fn().mockReturnValue({}),
244
+ getFilteredNavigationItems: vi.fn().mockReturnValue(mockNavigationItems),
245
+ validateNavigationItem: vi.fn().mockReturnValue(true),
246
+ getNavigationAccessHistory: vi.fn().mockReturnValue([]),
247
+ clearNavigationAccessHistory: vi.fn(),
248
+ isEnabled: true,
249
+ isStrictMode: true,
250
+ isAuditLogEnabled: true
251
+ } as any);
252
+
253
+ render(
254
+ <EnhancedNavigationMenu
255
+ items={mockNavigationItems}
256
+ hideUnauthorizedItems={false}
257
+ />
258
+ );
259
+
260
+ expect(screen.getByText('Dashboard')).toBeInTheDocument();
261
+ expect(screen.getByText('Users')).toBeInTheDocument();
262
+ expect(screen.getByText('Admin')).toBeInTheDocument();
263
+ });
264
+ });
265
+
266
+ describe('Custom Rendering', () => {
267
+ it('uses custom render function for navigation items', () => {
268
+ const customRenderItem = vi.fn((item: NavigationItem, isAuthorized: boolean) => (
269
+ <div key={item.id} data-testid={`custom-${item.id}`}>
270
+ {item.label} - {isAuthorized ? 'Authorized' : 'Unauthorized'}
271
+ </div>
272
+ ));
273
+
274
+ render(
275
+ <EnhancedNavigationMenu
276
+ items={mockNavigationItems}
277
+ renderItem={customRenderItem}
278
+ />
279
+ );
280
+
281
+ expect(customRenderItem).toHaveBeenCalledTimes(3);
282
+ expect(screen.getByTestId('custom-dashboard')).toBeInTheDocument();
283
+ expect(screen.getByTestId('custom-users')).toBeInTheDocument();
284
+ expect(screen.getByTestId('custom-admin')).toBeInTheDocument();
285
+ });
286
+
287
+ it('passes correct parameters to custom render function', () => {
288
+ const customRenderItem = vi.fn((item: NavigationItem, isAuthorized: boolean) => (
289
+ <div key={item.id}>{item.label}</div>
290
+ ));
291
+
292
+ render(
293
+ <EnhancedNavigationMenu
294
+ items={mockNavigationItems}
295
+ renderItem={customRenderItem}
296
+ />
297
+ );
298
+
299
+ expect(customRenderItem).toHaveBeenCalledWith(mockNavigationItems[0], true);
300
+ expect(customRenderItem).toHaveBeenCalledWith(mockNavigationItems[1], true);
301
+ expect(customRenderItem).toHaveBeenCalledWith(mockNavigationItems[2], true);
302
+ });
303
+ });
304
+
305
+ describe('Event Handling', () => {
306
+ it('calls onItemClick when navigation item is clicked', () => {
307
+ const onItemClick = vi.fn();
308
+
309
+ render(
310
+ <EnhancedNavigationMenu
311
+ items={mockNavigationItems}
312
+ onItemClick={onItemClick}
313
+ />
314
+ );
315
+
316
+ const dashboardLink = screen.getByText('Dashboard').closest('button');
317
+ fireEvent.click(dashboardLink!);
318
+
319
+ expect(onItemClick).toHaveBeenCalledWith(mockNavigationItems[0]);
320
+ });
321
+
322
+ it('calls onNavigationAccess when navigation access is attempted', () => {
323
+ const onNavigationAccess = vi.fn();
324
+ vi.mocked(useNavigationPermissions).mockReturnValue({
325
+ hasNavigationPermission: vi.fn().mockReturnValue(true),
326
+ getNavigationPermissions: vi.fn().mockReturnValue({}),
327
+ getFilteredNavigationItems: vi.fn().mockReturnValue(mockNavigationItems),
328
+ validateNavigationItem: vi.fn().mockReturnValue(true),
329
+ getNavigationAccessHistory: vi.fn().mockReturnValue([]),
330
+ clearNavigationAccessHistory: vi.fn(),
331
+ isEnabled: true,
332
+ isStrictMode: true,
333
+ isAuditLogEnabled: true
334
+ } as any);
335
+
336
+ render(
337
+ <EnhancedNavigationMenu
338
+ items={mockNavigationItems}
339
+ onNavigationAccess={onNavigationAccess}
340
+ />
341
+ );
342
+
343
+ // Click on a navigation item to trigger the event
344
+ const dashboardButton = screen.getByText('Dashboard').closest('button');
345
+ fireEvent.click(dashboardButton!);
346
+
347
+ expect(onNavigationAccess).toHaveBeenCalled();
348
+ });
349
+
350
+ it('calls onStrictModeViolation when strict mode is violated', () => {
351
+ const onStrictModeViolation = vi.fn();
352
+ vi.mocked(useNavigationPermissions).mockReturnValue({
353
+ hasNavigationPermission: vi.fn().mockReturnValue(false),
354
+ getNavigationPermissions: vi.fn().mockReturnValue({}),
355
+ getFilteredNavigationItems: vi.fn().mockReturnValue(mockNavigationItems),
356
+ validateNavigationItem: vi.fn().mockReturnValue(true),
357
+ getNavigationAccessHistory: vi.fn().mockReturnValue([]),
358
+ clearNavigationAccessHistory: vi.fn(),
359
+ isEnabled: true,
360
+ isStrictMode: true,
361
+ isAuditLogEnabled: true
362
+ } as any);
363
+
364
+ render(
365
+ <EnhancedNavigationMenu
366
+ items={mockNavigationItems}
367
+ onStrictModeViolation={onStrictModeViolation}
368
+ strictMode={true}
369
+ />
370
+ );
371
+
372
+ // The NavigationGuard should be configured with the onStrictModeViolation handler
373
+ // Since the user doesn't have permission, the button should be disabled
374
+ const dashboardButton = screen.getByText('Dashboard').closest('button');
375
+ expect(dashboardButton).toBeDisabled();
376
+
377
+ // The NavigationGuard should call onStrictModeViolation when it detects unauthorized access
378
+ // This happens during the component render, not on click
379
+ expect(onStrictModeViolation).toHaveBeenCalled();
380
+ });
381
+ });
382
+
383
+ describe('Loading and Error States', () => {
384
+ it('renders loading state when permissions are being checked', () => {
385
+ vi.mocked(useNavigationPermissions).mockReturnValue({
386
+ hasNavigationPermission: vi.fn().mockReturnValue(false),
387
+ getNavigationPermissions: vi.fn().mockReturnValue({}),
388
+ getFilteredNavigationItems: vi.fn().mockReturnValue([]),
389
+ validateNavigationItem: vi.fn().mockReturnValue(true),
390
+ getNavigationAccessHistory: vi.fn().mockReturnValue([]),
391
+ clearNavigationAccessHistory: vi.fn(),
392
+ isEnabled: true,
393
+ isStrictMode: true,
394
+ isAuditLogEnabled: true
395
+ } as any);
396
+
397
+ render(
398
+ <EnhancedNavigationMenu
399
+ items={mockNavigationItems}
400
+ />
401
+ );
402
+
403
+ // When no items are returned, the component renders an empty nav
404
+ expect(screen.getByRole('navigation')).toBeInTheDocument();
405
+ });
406
+
407
+ it('renders error state when permission check fails', () => {
408
+ vi.mocked(useNavigationPermissions).mockReturnValue({
409
+ hasNavigationPermission: vi.fn().mockImplementation(() => {
410
+ throw new Error('Permission check failed');
411
+ }),
412
+ getNavigationPermissions: vi.fn().mockReturnValue({}),
413
+ getFilteredNavigationItems: vi.fn().mockReturnValue([]),
414
+ validateNavigationItem: vi.fn().mockReturnValue(true),
415
+ getNavigationAccessHistory: vi.fn().mockReturnValue([]),
416
+ clearNavigationAccessHistory: vi.fn(),
417
+ isEnabled: true,
418
+ isStrictMode: true,
419
+ isAuditLogEnabled: true
420
+ } as any);
421
+
422
+ render(
423
+ <EnhancedNavigationMenu
424
+ items={mockNavigationItems}
425
+ />
426
+ );
427
+
428
+ // When permission check fails, the component should still render
429
+ expect(screen.getByRole('navigation')).toBeInTheDocument();
430
+ });
431
+
432
+ it('renders empty state when no navigation items are available', () => {
433
+ vi.mocked(useNavigationPermissions).mockReturnValue({
434
+ hasNavigationPermission: vi.fn().mockReturnValue(false),
435
+ getNavigationPermissions: vi.fn().mockReturnValue({}),
436
+ getFilteredNavigationItems: vi.fn().mockReturnValue([]),
437
+ validateNavigationItem: vi.fn().mockReturnValue(true),
438
+ getNavigationAccessHistory: vi.fn().mockReturnValue([]),
439
+ clearNavigationAccessHistory: vi.fn(),
440
+ isEnabled: true,
441
+ isStrictMode: true,
442
+ isAuditLogEnabled: true
443
+ } as any);
444
+
445
+ render(
446
+ <EnhancedNavigationMenu
447
+ items={[]}
448
+ />
449
+ );
450
+
451
+ // When no items are provided, the component renders an empty nav
452
+ expect(screen.getByRole('navigation')).toBeInTheDocument();
453
+ });
454
+ });
455
+
456
+ describe('Accessibility', () => {
457
+ it('renders navigation with proper ARIA attributes', () => {
458
+ render(
459
+ <EnhancedNavigationMenu
460
+ items={mockNavigationItems}
461
+ />
462
+ );
463
+
464
+ const nav = screen.getByRole('navigation');
465
+ expect(nav).toBeInTheDocument();
466
+ });
467
+
468
+ it('renders navigation items with proper accessibility attributes', () => {
469
+ render(
470
+ <EnhancedNavigationMenu items={mockNavigationItems} />
471
+ );
472
+
473
+ const buttons = screen.getAllByRole('button');
474
+ buttons.forEach(button => {
475
+ expect(button).not.toBeDisabled();
476
+ });
477
+ });
478
+
479
+ it('handles keyboard navigation correctly', () => {
480
+ const onItemClick = vi.fn();
481
+
482
+ render(
483
+ <EnhancedNavigationMenu
484
+ items={mockNavigationItems}
485
+ onItemClick={onItemClick}
486
+ />
487
+ );
488
+
489
+ const dashboardLink = screen.getByText('Dashboard').closest('button');
490
+ fireEvent.click(dashboardLink!);
491
+
492
+ expect(onItemClick).toHaveBeenCalledWith(mockNavigationItems[0]);
493
+ });
494
+ });
495
+
496
+ describe('Performance', () => {
497
+ it('memoizes filtered navigation items', () => {
498
+ const getFilteredNavigationItems = vi.fn().mockReturnValue(mockNavigationItems);
499
+ vi.mocked(useNavigationPermissions).mockReturnValue({
500
+ hasNavigationPermission: vi.fn().mockReturnValue(true),
501
+ getNavigationPermissions: vi.fn().mockReturnValue({}),
502
+ getFilteredNavigationItems,
503
+ validateNavigationItem: vi.fn().mockReturnValue(true),
504
+ getNavigationAccessHistory: vi.fn().mockReturnValue([]),
505
+ clearNavigationAccessHistory: vi.fn(),
506
+ isEnabled: true,
507
+ isStrictMode: true,
508
+ isAuditLogEnabled: true
509
+ } as any);
510
+
511
+ const { rerender } = render(
512
+ <EnhancedNavigationMenu items={mockNavigationItems} />
513
+ );
514
+
515
+ const initialCallCount = getFilteredNavigationItems.mock.calls.length;
516
+
517
+ // Re-render with same props
518
+ rerender(
519
+ <EnhancedNavigationMenu items={mockNavigationItems} />
520
+ );
521
+
522
+ // Should not call filterNavigationItems again due to memoization
523
+ expect(getFilteredNavigationItems).toHaveBeenCalledTimes(initialCallCount);
524
+ });
525
+ });
526
+
527
+ describe('Integration with NavigationProvider', () => {
528
+ it('integrates with useNavigationPermissions hook', () => {
529
+ render(
530
+ <EnhancedNavigationMenu items={mockNavigationItems} />
531
+ );
532
+
533
+ expect(vi.mocked(useNavigationPermissions)).toHaveBeenCalled();
534
+ });
535
+
536
+ it('uses filtered navigation items from provider', () => {
537
+ const filteredItems = [mockNavigationItems[0]];
538
+ vi.mocked(useNavigationPermissions).mockReturnValue({
539
+ hasNavigationPermission: vi.fn().mockReturnValue(true),
540
+ getNavigationPermissions: vi.fn().mockReturnValue({}),
541
+ getFilteredNavigationItems: vi.fn().mockReturnValue(filteredItems),
542
+ validateNavigationItem: vi.fn().mockReturnValue(true),
543
+ getNavigationAccessHistory: vi.fn().mockReturnValue([]),
544
+ clearNavigationAccessHistory: vi.fn(),
545
+ isEnabled: true,
546
+ isStrictMode: true,
547
+ isAuditLogEnabled: true
548
+ } as any);
549
+
550
+ render(
551
+ <EnhancedNavigationMenu items={mockNavigationItems} />
552
+ );
553
+
554
+ expect(screen.getByText('Dashboard')).toBeInTheDocument();
555
+ expect(screen.queryByText('Users')).not.toBeInTheDocument();
556
+ expect(screen.queryByText('Admin')).not.toBeInTheDocument();
557
+ });
558
+ });
559
+ });
@@ -141,29 +141,6 @@ export function EnhancedNavigationMenu({
141
141
  return getFilteredNavigationItems(items);
142
142
  }, [isEnabled, items, getFilteredNavigationItems]);
143
143
 
144
- // Handle navigation item click
145
- const handleItemClick = useCallback((item: NavigationItem) => {
146
- if (onItemClick) {
147
- onItemClick(item);
148
- }
149
-
150
- // Record navigation attempt
151
- if (auditLog) {
152
- console.log(`[EnhancedNavigationMenu] Navigation item clicked:`, {
153
- item: item.id,
154
- path: item.path,
155
- permissions: item.permissions,
156
- timestamp: new Date().toISOString()
157
- });
158
- }
159
-
160
- // Add to navigation history
161
- setNavigationHistory(prev => {
162
- const newHistory = [item, ...prev.filter(i => i.id !== item.id)];
163
- return newHistory.slice(0, 10); // Keep last 10 items
164
- });
165
- }, [onItemClick, auditLog]);
166
-
167
144
  // Handle navigation access attempt
168
145
  const handleNavigationAccess = useCallback((item: NavigationItem, allowed: boolean) => {
169
146
  if (onNavigationAccess) {
@@ -196,6 +173,35 @@ export function EnhancedNavigationMenu({
196
173
  }
197
174
  }, [onStrictModeViolation, strictMode]);
198
175
 
176
+ // Handle navigation item click
177
+ const handleItemClick = useCallback((item: NavigationItem) => {
178
+ // Check if user has permission for this item
179
+ const isAuthorized = hasNavigationPermission(item);
180
+
181
+ // Call navigation access handler
182
+ handleNavigationAccess(item, isAuthorized);
183
+
184
+ if (onItemClick) {
185
+ onItemClick(item);
186
+ }
187
+
188
+ // Record navigation attempt
189
+ if (auditLog) {
190
+ console.log(`[EnhancedNavigationMenu] Navigation item clicked:`, {
191
+ item: item.id,
192
+ path: item.path,
193
+ permissions: item.permissions,
194
+ timestamp: new Date().toISOString()
195
+ });
196
+ }
197
+
198
+ // Add to navigation history
199
+ setNavigationHistory(prev => {
200
+ const newHistory = [item, ...prev.filter(i => i.id !== item.id)];
201
+ return newHistory.slice(0, 10); // Keep last 10 items
202
+ });
203
+ }, [onItemClick, auditLog, hasNavigationPermission, handleNavigationAccess]);
204
+
199
205
  // Default render function for navigation items
200
206
  const defaultRenderItem = useCallback((item: NavigationItem, isAuthorized: boolean) => {
201
207
  const isActive = activePath === item.path;