@jmruthers/pace-core 0.6.6 → 0.6.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (292) hide show
  1. package/{scripts/audit/audit-dependencies.cjs → audit-tool/00-dependencies.cjs} +227 -22
  2. package/audit-tool/audits/01-pace-core-compliance.cjs +556 -0
  3. package/audit-tool/audits/02-project-structure.cjs +240 -0
  4. package/audit-tool/audits/03-architecture.cjs +224 -0
  5. package/audit-tool/audits/04-code-quality.cjs +149 -0
  6. package/audit-tool/audits/05-styling.cjs +224 -0
  7. package/audit-tool/audits/06-security-rbac.cjs +554 -0
  8. package/audit-tool/audits/07-api-tech-stack.cjs +355 -0
  9. package/audit-tool/audits/08-testing-documentation.cjs +202 -0
  10. package/audit-tool/audits/09-operations.cjs +208 -0
  11. package/audit-tool/index.cjs +295 -0
  12. package/audit-tool/utils/code-utils.cjs +218 -0
  13. package/audit-tool/utils/file-utils.cjs +230 -0
  14. package/audit-tool/utils/report-utils.cjs +380 -0
  15. package/cursor-rules/00-standards-overview.mdc +156 -0
  16. package/cursor-rules/{00-pace-core-compliance.mdc → 01-pace-core-compliance.mdc} +187 -34
  17. package/cursor-rules/02-project-structure.mdc +37 -5
  18. package/cursor-rules/{03-solid-principles.mdc → 03-architecture.mdc} +125 -11
  19. package/cursor-rules/04-code-quality.mdc +419 -0
  20. package/cursor-rules/{08-markup-quality.mdc → 05-styling.mdc} +55 -10
  21. package/cursor-rules/{09-rbac-compliance.mdc → 06-security-rbac.mdc} +62 -6
  22. package/cursor-rules/07-api-tech-stack.mdc +377 -0
  23. package/cursor-rules/08-testing-documentation.mdc +324 -0
  24. package/cursor-rules/09-operations.mdc +365 -0
  25. package/dist/DataTable-6RMSCQJ6.js +15 -0
  26. package/dist/{DataTable-2N_tqbfq.d.ts → DataTable-DRUIgtUH.d.ts} +1 -1
  27. package/dist/{PublicPageProvider-BBH6Vqg7.d.ts → PublicPageProvider-CIGSujI2.d.ts} +40 -24
  28. package/dist/{UnifiedAuthProvider-ZT6TIGM7.js → UnifiedAuthProvider-7SNDOWYD.js} +2 -2
  29. package/dist/{api-Y4MQWOFW.js → api-7P7DI652.js} +1 -1
  30. package/dist/{chunk-MAGBIDNS.js → chunk-4DDCYDQ3.js} +8 -7
  31. package/dist/{chunk-BVP2BCJF.js → chunk-5W2A3DRC.js} +10 -9
  32. package/dist/{chunk-SD6WQY43.js → chunk-7ILTDCL2.js} +9 -1
  33. package/dist/{chunk-3QC3KRHK.js → chunk-A3W6LW53.js} +16 -1
  34. package/dist/{chunk-3O3WHILE.js → chunk-EF2UGZWY.js} +239 -63
  35. package/dist/{chunk-LAZMKTTF.js → chunk-EURB7QFZ.js} +341 -337
  36. package/dist/{chunk-2HGJFNAH.js → chunk-FEJLJNWA.js} +1 -15
  37. package/dist/{chunk-7TYHROIV.js → chunk-GS5672WG.js} +55 -13
  38. package/dist/{chunk-UIYSCEV7.js → chunk-IUBRCBSY.js} +1 -1
  39. package/dist/{chunk-ZFYPMX46.js → chunk-LX6U42O3.js} +1 -1
  40. package/dist/{chunk-FENMYN2U.js → chunk-MPBLMWVR.js} +3 -3
  41. package/dist/{chunk-ZS5VO5JB.js → chunk-NKHKXPI4.js} +408 -453
  42. package/dist/{chunk-A55DK444.js → chunk-OJ4SKRSV.js} +1 -7
  43. package/dist/{chunk-4T7OBVTU.js → chunk-S6ZQKDY6.js} +1 -1
  44. package/dist/{chunk-FTCRZOG2.js → chunk-T5CVK4R3.js} +5 -5
  45. package/dist/{chunk-OHIK3MIO.js → chunk-Z2FNRKF3.js} +13 -13
  46. package/dist/components.d.ts +5 -4
  47. package/dist/components.js +29 -34
  48. package/dist/eslint-rules/index.cjs +22 -9
  49. package/{src/eslint-rules/rules/compliance.cjs → dist/eslint-rules/rules/01-pace-core-compliance.cjs} +184 -23
  50. package/dist/eslint-rules/rules/04-code-quality.cjs +346 -0
  51. package/dist/eslint-rules/rules/05-styling.cjs +61 -0
  52. package/dist/eslint-rules/rules/{rbac.cjs → 06-security-rbac.cjs} +34 -13
  53. package/dist/eslint-rules/rules/07-api-tech-stack.cjs +385 -0
  54. package/dist/eslint-rules/rules/08-testing.cjs +94 -0
  55. package/dist/{functions-DHebl8-F.d.ts → functions-lBy5L2ry.d.ts} +1 -1
  56. package/dist/hooks.d.ts +5 -5
  57. package/dist/hooks.js +8 -8
  58. package/dist/index.d.ts +7 -7
  59. package/dist/index.js +21 -20
  60. package/dist/providers.js +2 -2
  61. package/dist/rbac/index.d.ts +1 -1
  62. package/dist/rbac/index.js +8 -8
  63. package/dist/theming/runtime.d.ts +61 -1
  64. package/dist/theming/runtime.js +1 -1
  65. package/dist/{types-B-K_5VnO.d.ts → types-DXstZpNI.d.ts} +0 -17
  66. package/dist/types.d.ts +2 -2
  67. package/dist/{usePublicRouteParams-COZ28Mvq.d.ts → usePublicRouteParams-MamNgwqe.d.ts} +19 -19
  68. package/dist/utils.d.ts +2 -2
  69. package/dist/utils.js +8 -8
  70. package/docs/README.md +1 -1
  71. package/docs/api/modules.md +106 -41
  72. package/docs/api-reference/components.md +18 -20
  73. package/docs/api-reference/hooks.md +80 -80
  74. package/docs/api-reference/types.md +1 -1
  75. package/docs/api-reference/utilities.md +1 -1
  76. package/docs/architecture/README.md +1 -1
  77. package/docs/core-concepts/events.md +3 -3
  78. package/docs/core-concepts/organisations.md +6 -6
  79. package/docs/core-concepts/permissions.md +6 -6
  80. package/docs/documentation-index.md +12 -18
  81. package/docs/getting-started/dependencies.md +23 -0
  82. package/docs/getting-started/documentation-index.md +1 -1
  83. package/docs/getting-started/examples/README.md +4 -4
  84. package/docs/getting-started/examples/full-featured-app.md +1 -1
  85. package/docs/getting-started/faq.md +2 -2
  86. package/docs/getting-started/quick-reference.md +4 -4
  87. package/docs/implementation-guides/app-layout.md +1 -1
  88. package/docs/implementation-guides/authentication.md +15 -15
  89. package/docs/implementation-guides/component-styling.md +1 -1
  90. package/docs/implementation-guides/data-tables.md +127 -34
  91. package/docs/implementation-guides/datatable-rbac-usage.md +1 -1
  92. package/docs/implementation-guides/dynamic-colors.md +3 -3
  93. package/docs/implementation-guides/file-upload-storage.md +2 -2
  94. package/docs/implementation-guides/hierarchical-datatable.md +40 -60
  95. package/docs/implementation-guides/inactivity-tracking.md +3 -3
  96. package/docs/implementation-guides/large-datasets.md +3 -2
  97. package/docs/implementation-guides/organisation-security.md +2 -2
  98. package/docs/implementation-guides/performance.md +2 -2
  99. package/docs/implementation-guides/permission-enforcement.md +1 -1
  100. package/docs/migration/V0.3.44_organisation-context-timing-fix.md +1 -1
  101. package/docs/migration/V0.4.0_rbac-migration.md +6 -6
  102. package/docs/rbac/README.md +5 -5
  103. package/docs/rbac/advanced-patterns.md +6 -6
  104. package/docs/rbac/api-reference.md +20 -20
  105. package/docs/rbac/event-based-apps.md +3 -3
  106. package/docs/rbac/examples.md +41 -41
  107. package/docs/rbac/getting-started.md +37 -37
  108. package/docs/rbac/performance.md +1 -1
  109. package/docs/rbac/quick-start.md +52 -52
  110. package/docs/rbac/secure-client-protection.md +1 -1
  111. package/docs/rbac/troubleshooting.md +1 -1
  112. package/docs/security/README.md +5 -5
  113. package/docs/standards/0-standards-overview.md +220 -0
  114. package/docs/standards/{00-pace-core-compliance.md → 1-pace-core-compliance-standards.md} +241 -185
  115. package/docs/standards/{02-project-structure.md → 2-project-structure-standards.md} +11 -47
  116. package/docs/standards/3-architecture-standards.md +606 -0
  117. package/docs/standards/4-code-quality-standards.md +728 -0
  118. package/docs/standards/{08-markup-quality.md → 5-styling-standards.md} +12 -9
  119. package/docs/standards/{09-rbac-compliance.md → 6-security-rbac-standards.md} +126 -18
  120. package/docs/standards/7-api-tech-stack-standards.md +662 -0
  121. package/docs/standards/8-testing-documentation-standards.md +401 -0
  122. package/docs/standards/9-operations-standards.md +1102 -0
  123. package/docs/standards/README.md +203 -104
  124. package/docs/troubleshooting/README.md +4 -4
  125. package/docs/troubleshooting/common-issues.md +2 -2
  126. package/docs/troubleshooting/debugging.md +9 -9
  127. package/docs/troubleshooting/migration.md +4 -4
  128. package/eslint-config-pace-core.cjs +50 -20
  129. package/package.json +50 -19
  130. package/scripts/eslint-audit.cjs +123 -0
  131. package/scripts/install-cursor-rules.cjs +11 -243
  132. package/scripts/install-eslint-config.cjs +349 -0
  133. package/scripts/validate-dependencies.cjs +248 -0
  134. package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +2 -2
  135. package/src/__tests__/helpers/__tests__/test-providers.test.tsx +2 -2
  136. package/src/__tests__/helpers/__tests__/test-utils.test.tsx +30 -18
  137. package/src/__tests__/integration/UserProfile.test.tsx +14 -14
  138. package/src/__tests__/rbac/PagePermissionGuard.test.tsx +6 -6
  139. package/src/__tests__/templates/accessibility.test.template.tsx +10 -9
  140. package/src/__tests__/templates/component.test.template.tsx +18 -15
  141. package/src/components/AddressField/AddressField.tsx +26 -1
  142. package/src/components/Alert/Alert.test.tsx +86 -22
  143. package/src/components/Alert/Alert.tsx +19 -11
  144. package/src/components/Badge/Badge.tsx +1 -1
  145. package/src/components/Calendar/Calendar.tsx +201 -47
  146. package/src/components/Checkbox/Checkbox.test.tsx +2 -1
  147. package/src/components/ContextSelector/ContextSelector.tsx +108 -126
  148. package/src/components/DataTable/AUDIT_REPORT.md +293 -0
  149. package/src/components/DataTable/DataTable.tsx +1 -19
  150. package/src/components/DataTable/__tests__/DataTableCore.test.tsx +6 -2
  151. package/src/components/DataTable/__tests__/a11y.basic.test.tsx +21 -6
  152. package/src/components/DataTable/__tests__/pagination.modes.test.tsx +3 -2
  153. package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +9 -9
  154. package/src/components/DataTable/components/ColumnFilter.tsx +63 -74
  155. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +43 -41
  156. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +9 -11
  157. package/src/components/DataTable/components/DataTableLayout.tsx +5 -16
  158. package/src/components/DataTable/components/EditableRow.tsx +5 -7
  159. package/src/components/DataTable/components/EmptyState.tsx +11 -10
  160. package/src/components/DataTable/components/FilterRow.tsx +2 -4
  161. package/src/components/DataTable/components/ImportModal.tsx +124 -126
  162. package/src/components/DataTable/components/LoadingState.tsx +5 -6
  163. package/src/components/DataTable/components/SortIndicator.tsx +50 -0
  164. package/src/components/DataTable/components/__tests__/COVERAGE_NOTE.md +4 -4
  165. package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +23 -82
  166. package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +37 -9
  167. package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +7 -4
  168. package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +12 -4
  169. package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +45 -27
  170. package/src/components/DataTable/components/index.ts +2 -1
  171. package/src/components/DataTable/types.ts +0 -18
  172. package/src/components/DataTable/utils/a11yUtils.ts +17 -0
  173. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +1 -1
  174. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +11 -15
  175. package/src/components/DateTimeField/DateTimeField.tsx +7 -8
  176. package/src/components/Dialog/Dialog.test.tsx +1 -0
  177. package/src/components/Dialog/Dialog.tsx +25 -8
  178. package/src/components/ErrorBoundary/ErrorBoundary.tsx +77 -79
  179. package/src/components/FileUpload/FileUpload.test.tsx +45 -16
  180. package/src/components/FileUpload/FileUpload.tsx +141 -130
  181. package/src/components/NavigationMenu/NavigationMenu.test.tsx +48 -12
  182. package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +9 -9
  183. package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +30 -30
  184. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +4 -4
  185. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +7 -1
  186. package/src/components/Progress/Progress.tsx +2 -4
  187. package/src/components/ProtectedRoute/ProtectedRoute.tsx +8 -8
  188. package/src/components/Select/Select.tsx +86 -77
  189. package/src/components/Select/types.ts +3 -0
  190. package/src/hooks/__tests__/ServiceHooks.test.tsx +16 -16
  191. package/src/hooks/__tests__/hooks.integration.test.tsx +49 -49
  192. package/src/hooks/__tests__/useDataTablePerformance.unit.test.ts +8 -5
  193. package/src/hooks/__tests__/useFileUrl.unit.test.ts +4 -0
  194. package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +99 -99
  195. package/src/hooks/__tests__/useInactivityTracker.unit.test.ts +45 -8
  196. package/src/hooks/__tests__/usePerformanceMonitor.unit.test.ts +22 -2
  197. package/src/hooks/public/usePublicEvent.ts +5 -5
  198. package/src/hooks/public/usePublicEventLogo.ts +5 -5
  199. package/src/hooks/public/usePublicFileDisplay.ts +2 -2
  200. package/src/hooks/public/usePublicRouteParams.ts +13 -9
  201. package/src/hooks/useAddressAutocomplete.test.ts +18 -18
  202. package/src/hooks/useAppConfig.ts +2 -2
  203. package/src/hooks/useEventTheme.test.ts +7 -7
  204. package/src/hooks/useEventTheme.ts +2 -1
  205. package/src/hooks/useFileDisplay.ts +2 -2
  206. package/src/hooks/useFileUrl.ts +52 -8
  207. package/src/hooks/useOrganisationSecurity.test.ts +2 -1
  208. package/src/providers/UnifiedAuthProvider.smoke.test.tsx +21 -21
  209. package/src/providers/__tests__/AuthProvider.test.tsx +21 -21
  210. package/src/providers/__tests__/EventProvider.test.tsx +61 -61
  211. package/src/providers/__tests__/InactivityProvider.test.tsx +56 -56
  212. package/src/providers/__tests__/OrganisationProvider.test.tsx +75 -75
  213. package/src/providers/__tests__/ProviderLifecycle.test.tsx +38 -38
  214. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +103 -103
  215. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +7 -7
  216. package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +10 -10
  217. package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +15 -6
  218. package/src/rbac/__tests__/rbac-functions.test.ts +3 -3
  219. package/src/rbac/api.test.ts +104 -0
  220. package/src/rbac/engine.ts +1 -1
  221. package/src/rbac/hooks/useCan.test.ts +2 -2
  222. package/src/rbac/secureClient.ts +1 -1
  223. package/src/rbac/types/functions.ts +1 -1
  224. package/src/styles/core.css +7 -0
  225. package/src/theming/__tests__/parseEventColours.test.ts +118 -3
  226. package/src/theming/parseEventColours.ts +77 -11
  227. package/src/types/supabase.ts +2 -3
  228. package/src/utils/__tests__/bundleAnalysis.unit.test.ts +9 -9
  229. package/src/utils/__tests__/lazyLoad.unit.test.tsx +42 -39
  230. package/src/utils/file-reference/__tests__/file-reference.test.ts +4 -0
  231. package/src/utils/formatting/formatDate.test.ts +3 -2
  232. package/src/utils/formatting/formatDateTime.test.ts +2 -2
  233. package/src/utils/google-places/googlePlacesUtils.test.ts +36 -24
  234. package/src/utils/storage/README.md +1 -1
  235. package/src/utils/storage/__tests__/helpers.unit.test.ts +19 -12
  236. package/src/utils/storage/helpers.test.ts +69 -3
  237. package/cursor-rules/01-standards-compliance.mdc +0 -285
  238. package/cursor-rules/04-testing-standards.mdc +0 -270
  239. package/cursor-rules/05-bug-reports-and-features.mdc +0 -248
  240. package/cursor-rules/06-code-quality.mdc +0 -311
  241. package/cursor-rules/07-tech-stack-compliance.mdc +0 -216
  242. package/cursor-rules/10-error-handling-patterns.mdc +0 -179
  243. package/cursor-rules/11-performance-optimization.mdc +0 -169
  244. package/cursor-rules/12-ci-cd-integration.mdc +0 -150
  245. package/dist/DataTable-LRJL4IRV.js +0 -15
  246. package/dist/eslint-rules/rules/compliance.cjs +0 -348
  247. package/dist/eslint-rules/rules/components.cjs +0 -113
  248. package/dist/eslint-rules/rules/imports.cjs +0 -102
  249. package/docs/best-practices/README.md +0 -472
  250. package/docs/best-practices/accessibility.md +0 -604
  251. package/docs/best-practices/common-patterns.md +0 -516
  252. package/docs/best-practices/deployment.md +0 -1103
  253. package/docs/best-practices/performance.md +0 -1328
  254. package/docs/best-practices/security.md +0 -940
  255. package/docs/best-practices/testing.md +0 -1034
  256. package/docs/rbac/compliance/compliance-guide.md +0 -544
  257. package/docs/standards/01-standards-compliance.md +0 -188
  258. package/docs/standards/03-solid-principles.md +0 -39
  259. package/docs/standards/04-testing-standards.md +0 -36
  260. package/docs/standards/05-bug-reports-and-features.md +0 -27
  261. package/docs/standards/06-code-quality.md +0 -34
  262. package/docs/standards/07-tech-stack-compliance.md +0 -30
  263. package/docs/standards/10-error-handling-patterns.md +0 -401
  264. package/docs/standards/11-performance-optimization.md +0 -348
  265. package/docs/standards/12-ci-cd-integration.md +0 -370
  266. package/docs/standards/ALIGNMENT_REVIEW_SUMMARY.md +0 -192
  267. package/scripts/audit/audit-compliance.cjs +0 -1295
  268. package/scripts/audit/audit-components.cjs +0 -260
  269. package/scripts/audit/audit-rbac.cjs +0 -954
  270. package/scripts/audit/audit-standards.cjs +0 -1268
  271. package/scripts/audit/index.cjs +0 -1927
  272. package/src/components/DataTable/components/DataTableBody.tsx +0 -478
  273. package/src/components/DataTable/components/DraggableColumnHeader.tsx +0 -156
  274. package/src/components/DataTable/components/ExpandButton.tsx +0 -113
  275. package/src/components/DataTable/components/GroupHeader.tsx +0 -54
  276. package/src/components/DataTable/components/ViewRowModal.tsx +0 -68
  277. package/src/components/DataTable/components/VirtualizedDataTable.tsx +0 -525
  278. package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +0 -462
  279. package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +0 -393
  280. package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +0 -476
  281. package/src/components/DataTable/components/__tests__/VirtualizedDataTable.test.tsx +0 -128
  282. package/src/components/DataTable/core/DataTableContext.tsx +0 -216
  283. package/src/components/DataTable/core/__tests__/DataTableContext.test.tsx +0 -136
  284. package/src/components/DataTable/hooks/__tests__/useColumnReordering.test.ts +0 -570
  285. package/src/components/DataTable/hooks/useColumnReordering.ts +0 -123
  286. package/src/components/DataTable/utils/debugTools.ts +0 -514
  287. package/src/eslint-rules/index.cjs +0 -22
  288. package/src/eslint-rules/rules/components.cjs +0 -113
  289. package/src/eslint-rules/rules/imports.cjs +0 -102
  290. package/src/eslint-rules/rules/rbac.cjs +0 -790
  291. package/src/eslint-rules/utils/helpers.cjs +0 -42
  292. package/src/eslint-rules/utils/manifest-loader.cjs +0 -75
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import React from 'react';
7
- import { screen, waitFor } from '@testing-library/react';
7
+ import { screen, waitFor, act } from '@testing-library/react';
8
8
  import userEvent from '@testing-library/user-event';
9
9
  import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
10
10
  import { NavigationMenu } from './NavigationMenu';
@@ -716,7 +716,7 @@ describe('NavigationMenu Component', () => {
716
716
  expect(screen.getByText('Home')).toBeInTheDocument();
717
717
  }, { interval: 10 });
718
718
 
719
- const homeItem = screen.getByText('Home');
719
+ const homeItem = screen.getByRole('option', { name: 'Home' });
720
720
  await user.click(homeItem);
721
721
 
722
722
  expect(mockNavigate).toHaveBeenCalledWith(
@@ -748,7 +748,7 @@ describe('NavigationMenu Component', () => {
748
748
  expect(screen.getByText('Home')).toBeInTheDocument();
749
749
  }, { interval: 10 });
750
750
 
751
- const homeItem = screen.getByText('Home');
751
+ const homeItem = screen.getByRole('option', { name: 'Home' });
752
752
  await user.click(homeItem);
753
753
 
754
754
  expect(window.location.href).toBe('/');
@@ -1350,14 +1350,16 @@ describe('NavigationMenu Component', () => {
1350
1350
  items={basicNavItems}
1351
1351
  onNavigate={mockNavigate}
1352
1352
  buttonText="Menu"
1353
+ itemsPreFiltered={true}
1353
1354
  />
1354
1355
  );
1355
1356
 
1356
1357
  const openMenu = async () => {
1357
1358
  await user.click(screen.getByRole('combobox'));
1358
1359
  await waitFor(() => {
1359
- expect(screen.getByText('Home')).toBeInTheDocument();
1360
- }, { interval: 10 });
1360
+ // Items should appear as options in the listbox
1361
+ expect(screen.getByRole('option', { name: 'Home' })).toBeInTheDocument();
1362
+ }, { interval: 10, timeout: 3000 });
1361
1363
  };
1362
1364
 
1363
1365
  await openMenu();
@@ -1367,10 +1369,38 @@ describe('NavigationMenu Component', () => {
1367
1369
  items={basicNavItems}
1368
1370
  onNavigate={mockNavigate}
1369
1371
  buttonText="Menu"
1372
+ itemsPreFiltered={true}
1370
1373
  />
1371
1374
  );
1372
1375
 
1373
- await openMenu();
1376
+ // Wait for rerender to complete and component to be ready
1377
+ await waitFor(() => {
1378
+ const combobox = screen.getByRole('combobox');
1379
+ expect(combobox).toBeInTheDocument();
1380
+ expect(combobox).not.toBeDisabled();
1381
+ }, { interval: 10, timeout: 1000 });
1382
+
1383
+ // The Select component may keep the menu open after rerender
1384
+ // Check if menu is still open, and if so, verify items are still visible
1385
+ // If closed, open it again to verify items remain visible
1386
+ const combobox = screen.getByRole('combobox');
1387
+ const isOpen = combobox.getAttribute('aria-expanded') === 'true';
1388
+
1389
+ if (!isOpen) {
1390
+ // Menu closed, open it again
1391
+ await user.click(combobox);
1392
+ await waitFor(() => {
1393
+ const updatedCombobox = screen.getByRole('combobox');
1394
+ expect(updatedCombobox).toHaveAttribute('aria-expanded', 'true');
1395
+ }, { interval: 10, timeout: 3000 });
1396
+ }
1397
+
1398
+ // Now wait for items to appear as options in the visible listbox
1399
+ // This verifies that items remain visible after rerender during permission reload
1400
+ await waitFor(() => {
1401
+ // Items should appear as options in the listbox after rerender
1402
+ expect(screen.getByRole('option', { name: 'Home' })).toBeInTheDocument();
1403
+ }, { interval: 10, timeout: 3000 });
1374
1404
  });
1375
1405
 
1376
1406
  it('renders no selectable items when auth and RBAC providers are unavailable', async () => {
@@ -1423,14 +1453,16 @@ describe('NavigationMenu Component', () => {
1423
1453
  items={basicNavItems}
1424
1454
  onNavigate={mockNavigate}
1425
1455
  buttonText="Menu"
1456
+ itemsPreFiltered={true}
1426
1457
  />
1427
1458
  );
1428
1459
 
1429
1460
  await user.click(screen.getByRole('combobox'));
1430
1461
 
1431
1462
  await waitFor(() => {
1432
- expect(screen.getByText('Home')).toBeInTheDocument();
1433
- expect(screen.getByText('Dashboard')).toBeInTheDocument();
1463
+ // Items should appear as options in the listbox
1464
+ expect(screen.getByRole('option', { name: 'Home' })).toBeInTheDocument();
1465
+ expect(screen.getByRole('option', { name: 'Dashboard' })).toBeInTheDocument();
1434
1466
  }, { interval: 10 });
1435
1467
  });
1436
1468
  });
@@ -1445,6 +1477,7 @@ describe('NavigationMenu Component', () => {
1445
1477
  onNavigate={mockNavigate}
1446
1478
  auditLog={true}
1447
1479
  buttonText="Menu"
1480
+ itemsPreFiltered={true}
1448
1481
  />
1449
1482
  );
1450
1483
 
@@ -1452,10 +1485,11 @@ describe('NavigationMenu Component', () => {
1452
1485
  await user.click(trigger);
1453
1486
 
1454
1487
  await waitFor(() => {
1455
- expect(screen.getByText('Home')).toBeInTheDocument();
1488
+ // Items should appear as options in the listbox
1489
+ expect(screen.getByRole('option', { name: 'Home' })).toBeInTheDocument();
1456
1490
  }, { interval: 10 });
1457
1491
 
1458
- const homeItem = screen.getByText('Home');
1492
+ const homeItem = screen.getByRole('option', { name: 'Home' });
1459
1493
  await user.click(homeItem);
1460
1494
 
1461
1495
  // Note: NavigationMenu audit logging is currently commented out in the component
@@ -1479,6 +1513,7 @@ describe('NavigationMenu Component', () => {
1479
1513
  onNavigate={mockNavigate}
1480
1514
  auditLog={false}
1481
1515
  buttonText="Menu"
1516
+ itemsPreFiltered={true}
1482
1517
  />
1483
1518
  );
1484
1519
 
@@ -1486,10 +1521,11 @@ describe('NavigationMenu Component', () => {
1486
1521
  await user.click(trigger);
1487
1522
 
1488
1523
  await waitFor(() => {
1489
- expect(screen.getByText('Home')).toBeInTheDocument();
1524
+ // Items should appear as options in the listbox
1525
+ expect(screen.getByRole('option', { name: 'Home' })).toBeInTheDocument();
1490
1526
  }, { interval: 10 });
1491
1527
 
1492
- const homeItem = screen.getByText('Home');
1528
+ const homeItem = screen.getByRole('option', { name: 'Home' });
1493
1529
  await user.click(homeItem);
1494
1530
 
1495
1531
  // Logger.debug should not be called when auditLog is disabled
@@ -439,7 +439,7 @@ const TestWrapper = ({ children }: { children: React.ReactNode }) => (
439
439
  });
440
440
 
441
441
  describe('Permission Check Performance', () => {
442
- it('performs permission checks within threshold', async () => {
442
+ it('performs permission checks within threshold', { timeout: 6000 }, async () => {
443
443
  // Ensure super admin check resolves immediately for performance testing
444
444
  mockIsSuperAdmin.mockResolvedValue(false);
445
445
 
@@ -452,7 +452,7 @@ const TestWrapper = ({ children }: { children: React.ReactNode }) => (
452
452
  // Wait for permission check to complete and component to render
453
453
  await waitFor(() => {
454
454
  expect(screen.getByTestId('mock-header')).toBeInTheDocument();
455
- }, { timeout: 5000 });
455
+ });
456
456
 
457
457
  // Use real performance.now() for accurate timing (temporarily restore the spy)
458
458
  // Note: We're measuring after mount, so this should be very fast
@@ -476,9 +476,9 @@ const TestWrapper = ({ children }: { children: React.ReactNode }) => (
476
476
  // In coverage mode, just verify the component rendered successfully
477
477
  expect(screen.getByTestId('mock-header')).toBeInTheDocument();
478
478
  }
479
- }, { timeout: 6000 });
479
+ });
480
480
 
481
- it('handles multiple permission checks efficiently', async () => {
481
+ it('handles multiple permission checks efficiently', { timeout: 6000 }, async () => {
482
482
  // Ensure super admin check resolves immediately for performance testing
483
483
  mockIsSuperAdmin.mockResolvedValue(false);
484
484
 
@@ -501,7 +501,7 @@ const TestWrapper = ({ children }: { children: React.ReactNode }) => (
501
501
  // Wait for component to fully mount (including super admin check)
502
502
  await waitFor(() => {
503
503
  expect(screen.getByTestId('mock-header')).toBeInTheDocument();
504
- }, { timeout: 5000 });
504
+ });
505
505
 
506
506
  // Use real performance.now() for accurate timing (temporarily restore the spy)
507
507
  performanceNowSpy?.mockRestore();
@@ -527,7 +527,7 @@ const TestWrapper = ({ children }: { children: React.ReactNode }) => (
527
527
  // In coverage mode, just verify the component rendered successfully
528
528
  expect(screen.getByTestId('mock-header')).toBeInTheDocument();
529
529
  }
530
- }, { timeout: 6000 });
530
+ });
531
531
 
532
532
  it('handles permission check errors efficiently', async () => {
533
533
  mockUseCanFn.mockReturnValue({
@@ -545,7 +545,7 @@ const TestWrapper = ({ children }: { children: React.ReactNode }) => (
545
545
 
546
546
  await waitFor(() => {
547
547
  expect(screen.getByText('Permission check failed')).toBeInTheDocument();
548
- }, { timeout: 5000 });
548
+ });
549
549
  });
550
550
  });
551
551
 
@@ -939,7 +939,7 @@ const TestWrapper = ({ children }: { children: React.ReactNode }) => (
939
939
  await waitFor(() => {
940
940
  expect(screen.getByTestId('header-actions')).toBeInTheDocument();
941
941
  expect(screen.getByTestId('custom-user-menu')).toBeInTheDocument();
942
- }, { timeout: 2000 });
942
+ });
943
943
 
944
944
  const endTime = performance.now();
945
945
  const renderTime = endTime - startTime;
@@ -953,6 +953,6 @@ const TestWrapper = ({ children }: { children: React.ReactNode }) => (
953
953
  // In coverage mode, verify component renders successfully (behavioral check)
954
954
  expect(screen.getByTestId('mock-header')).toBeInTheDocument();
955
955
  }
956
- }, { timeout: 3000 });
956
+ });
957
957
  });
958
958
  });
@@ -444,7 +444,7 @@ describe('PaceAppLayout Security', () => {
444
444
  });
445
445
  });
446
446
 
447
- it('prevents access when user lacks permission', async () => {
447
+ it('prevents access when user lacks permission', { timeout: 3000 }, async () => {
448
448
  // Ensure super admin check completes first
449
449
  mockIsSuperAdmin.mockResolvedValueOnce(false);
450
450
 
@@ -465,7 +465,7 @@ describe('PaceAppLayout Security', () => {
465
465
  // Wait for super admin check to complete and component to re-render
466
466
  await waitFor(() => {
467
467
  expect(mockIsSuperAdmin).toHaveBeenCalled();
468
- }, { timeout: 1000 });
468
+ });
469
469
 
470
470
  // Wait a bit for the component to process the super admin check result
471
471
  await new Promise(resolve => setTimeout(resolve, 100));
@@ -474,8 +474,8 @@ describe('PaceAppLayout Security', () => {
474
474
  await waitFor(() => {
475
475
  expect(screen.getByText('Access Denied')).toBeInTheDocument();
476
476
  expect(screen.getByText("You don't have permission to access this page.")).toBeInTheDocument();
477
- }, { timeout: 2000 });
478
- }, { timeout: 3000 });
477
+ });
478
+ });
479
479
 
480
480
  it('enforces route-specific permissions', async () => {
481
481
  const routePermissions: Record<string, Operation> = {
@@ -501,7 +501,7 @@ describe('PaceAppLayout Security', () => {
501
501
  });
502
502
  });
503
503
 
504
- it('handles permission check failures securely', async () => {
504
+ it('handles permission check failures securely', { timeout: 3000 }, async () => {
505
505
  // Ensure super admin check completes first
506
506
  mockIsSuperAdmin.mockResolvedValueOnce(false);
507
507
 
@@ -522,7 +522,7 @@ describe('PaceAppLayout Security', () => {
522
522
  // Wait for super admin check to complete and component to re-render
523
523
  await waitFor(() => {
524
524
  expect(mockIsSuperAdmin).toHaveBeenCalled();
525
- }, { timeout: 1000 });
525
+ });
526
526
 
527
527
  // Wait a bit for the component to process the super admin check result
528
528
  await new Promise(resolve => setTimeout(resolve, 100));
@@ -531,8 +531,8 @@ describe('PaceAppLayout Security', () => {
531
531
  // When permission check throws an error, should show Permission Error page
532
532
  expect(screen.getByText('Permission Error')).toBeInTheDocument();
533
533
  expect(screen.getByText('Permission check failed')).toBeInTheDocument();
534
- }, { timeout: 2000 });
535
- }, { timeout: 3000 });
534
+ });
535
+ });
536
536
 
537
537
  it('prevents bypassing permission checks', async () => {
538
538
  // Test that permission checks cannot be bypassed by manipulating props
@@ -562,7 +562,7 @@ describe('PaceAppLayout Security', () => {
562
562
  });
563
563
 
564
564
  describe('Navigation Security', () => {
565
- it('filters navigation items based on permissions', async () => {
565
+ it('filters navigation items based on permissions', { timeout: 3000 }, async () => {
566
566
  // Mock permission check to allow all permissions for navigation filtering
567
567
  // The navigation filtering uses getPermissionMap which is already mocked
568
568
  render(
@@ -582,8 +582,8 @@ describe('PaceAppLayout Security', () => {
582
582
  // With permission enforcement enabled, the component should render normally
583
583
  expect(screen.getByTestId('mock-header')).toBeInTheDocument();
584
584
  expect(screen.getByTestId('mock-outlet')).toBeInTheDocument();
585
- }, { timeout: 2000 });
586
- }, { timeout: 3000 });
585
+ });
586
+ });
587
587
 
588
588
  it('prevents navigation to unauthorized routes', () => {
589
589
  render(
@@ -843,7 +843,7 @@ describe('PaceAppLayout Security', () => {
843
843
  });
844
844
  });
845
845
 
846
- it('handles permission errors securely', async () => {
846
+ it('handles permission errors securely', { timeout: 3000 }, async () => {
847
847
  // Ensure super admin check completes first
848
848
  mockIsSuperAdmin.mockResolvedValueOnce(false);
849
849
 
@@ -864,17 +864,17 @@ describe('PaceAppLayout Security', () => {
864
864
  // Wait for super admin check to complete and component to re-render
865
865
  await waitFor(() => {
866
866
  expect(mockIsSuperAdmin).toHaveBeenCalled();
867
- }, { timeout: 1000 });
867
+ });
868
868
 
869
869
  // Wait a bit for the component to process the super admin check result
870
870
  await new Promise(resolve => setTimeout(resolve, 100));
871
871
 
872
872
  await waitFor(() => {
873
873
  expect(screen.getByText('Access Denied')).toBeInTheDocument();
874
- }, { timeout: 2000 });
875
- }, { timeout: 3000 });
874
+ });
875
+ });
876
876
 
877
- it('prevents information leakage in error messages', async () => {
877
+ it('prevents information leakage in error messages', { timeout: 3000 }, async () => {
878
878
  // Ensure super admin check completes first
879
879
  mockIsSuperAdmin.mockResolvedValueOnce(false);
880
880
 
@@ -902,7 +902,7 @@ describe('PaceAppLayout Security', () => {
902
902
  // Wait for super admin check to complete and component to re-render
903
903
  await waitFor(() => {
904
904
  expect(mockIsSuperAdmin).toHaveBeenCalled();
905
- }, { timeout: 1000 });
905
+ });
906
906
 
907
907
  // Wait a bit for the component to process the super admin check result
908
908
  await new Promise(resolve => setTimeout(resolve, 100));
@@ -911,8 +911,8 @@ describe('PaceAppLayout Security', () => {
911
911
  expect(screen.getByText('Permission Error')).toBeInTheDocument();
912
912
  // Should not expose sensitive information
913
913
  expect(screen.queryByText('password=secret123')).not.toBeInTheDocument();
914
- }, { timeout: 2000 });
915
- }, { timeout: 3000 });
914
+ });
915
+ });
916
916
  });
917
917
 
918
918
  describe('Session Security', () => {
@@ -987,7 +987,7 @@ describe('PaceAppLayout Security', () => {
987
987
  });
988
988
  });
989
989
 
990
- it('allows super admin to bypass all permission checks', async () => {
990
+ it('allows super admin to bypass all permission checks', { timeout: 3000 }, async () => {
991
991
  // Mock super admin status
992
992
  mockIsSuperAdmin.mockResolvedValueOnce(true);
993
993
 
@@ -1017,10 +1017,10 @@ describe('PaceAppLayout Security', () => {
1017
1017
  expect(screen.getByTestId('mock-outlet')).toBeInTheDocument();
1018
1018
  // Should NOT show access denied
1019
1019
  expect(screen.queryByText('Access Denied')).not.toBeInTheDocument();
1020
- }, { timeout: 2000 });
1021
- }, { timeout: 3000 });
1020
+ });
1021
+ });
1022
1022
 
1023
- it('does not log strict mode violations for super admins', async () => {
1023
+ it('does not log strict mode violations for super admins', { timeout: 5000 }, async () => {
1024
1024
  // Mock super admin status (resolve immediately)
1025
1025
  mockIsSuperAdmin.mockResolvedValueOnce(true);
1026
1026
 
@@ -1052,12 +1052,12 @@ describe('PaceAppLayout Security', () => {
1052
1052
  // Wait for super admin check to complete
1053
1053
  await waitFor(() => {
1054
1054
  expect(mockIsSuperAdmin).toHaveBeenCalled();
1055
- }, { timeout: 1000 });
1055
+ });
1056
1056
 
1057
1057
  // Wait for component to fully render (super admin should bypass checks)
1058
1058
  await waitFor(() => {
1059
1059
  expect(screen.getByTestId('mock-header')).toBeInTheDocument();
1060
- }, { timeout: 2000 });
1060
+ });
1061
1061
 
1062
1062
  // Clear any violations that might have been logged before super admin check completed
1063
1063
  consoleSpy.mockClear();
@@ -1073,9 +1073,9 @@ describe('PaceAppLayout Security', () => {
1073
1073
  expect(violationLogs).toHaveLength(0);
1074
1074
 
1075
1075
  consoleSpy.mockRestore();
1076
- }, { timeout: 5000 });
1076
+ });
1077
1077
 
1078
- it('prevents privilege escalation', async () => {
1078
+ it('prevents privilege escalation', { timeout: 3000 }, async () => {
1079
1079
  // Test that users cannot escalate their privileges
1080
1080
  // Create a test wrapper with admin path for privilege escalation test
1081
1081
  const AdminTestWrapper = ({ children }: { children: React.ReactNode }) => (
@@ -1115,7 +1115,7 @@ describe('PaceAppLayout Security', () => {
1115
1115
  // Wait for super admin check to complete and component to re-render
1116
1116
  await waitFor(() => {
1117
1117
  expect(mockIsSuperAdmin).toHaveBeenCalled();
1118
- }, { timeout: 1000 });
1118
+ });
1119
1119
 
1120
1120
  // Wait a bit for the component to process the super admin check result
1121
1121
  await new Promise(resolve => setTimeout(resolve, 100));
@@ -1124,7 +1124,7 @@ describe('PaceAppLayout Security', () => {
1124
1124
  // With privilege escalation prevention, should show access denied for admin
1125
1125
  expect(screen.getByText('Access Denied')).toBeInTheDocument();
1126
1126
  expect(screen.getByText("You don't have permission to access this page.")).toBeInTheDocument();
1127
- }, { timeout: 2000 });
1128
- }, { timeout: 3000 });
1127
+ });
1128
+ });
1129
1129
  });
1130
1130
  });
@@ -873,7 +873,7 @@ describe('PaceAppLayout Component', () => {
873
873
  });
874
874
 
875
875
  describe('Route-Specific Permissions', () => {
876
- it('uses route-specific permissions when provided', async () => {
876
+ it('uses route-specific permissions when provided', { timeout: 3000 }, async () => {
877
877
  renderWithProviders(
878
878
  <TestWrapper>
879
879
  <PaceAppLayout
@@ -906,9 +906,9 @@ describe('PaceAppLayout Component', () => {
906
906
  'Test App'
907
907
  );
908
908
  }, { timeout: 2000 });
909
- }, { timeout: 3000 });
909
+ });
910
910
 
911
- it('uses default permission when route not in routePermissions', async () => {
911
+ it('uses default permission when route not in routePermissions', { timeout: 3000 }, async () => {
912
912
  renderWithProviders(
913
913
  <TestWrapper>
914
914
  <PaceAppLayout
@@ -940,7 +940,7 @@ describe('PaceAppLayout Component', () => {
940
940
  'Test App'
941
941
  );
942
942
  }, { timeout: 2000 });
943
- }, { timeout: 3000 });
943
+ });
944
944
  });
945
945
 
946
946
  describe('Super Admin Bypass', () => {
@@ -673,7 +673,9 @@ describe('PaceLoginPage Component', () => {
673
673
  });
674
674
 
675
675
  it('shows access error when required app configuration is missing', async () => {
676
+ vi.mocked(isSuperAdmin).mockResolvedValue(false);
676
677
  mockAuthContext.isAuthenticated = true;
678
+ mockAuthContext.isLoading = false;
677
679
  mockAuthContext.user = { id: 'user-1' } as User;
678
680
  const missingAppSupabase = {
679
681
  from: vi.fn((table: string) => {
@@ -707,12 +709,15 @@ describe('PaceLoginPage Component', () => {
707
709
  );
708
710
 
709
711
  await waitFor(() => {
712
+ // Error message format: Application "${appName}" is not configured. Please contact your administrator.
710
713
  expect(screen.getByText(/is not configured/i)).toBeInTheDocument();
711
- });
714
+ }, { timeout: 5000 });
712
715
  });
713
716
 
714
717
  it('shows organisation access error when user lacks active organisations', async () => {
718
+ vi.mocked(isSuperAdmin).mockResolvedValue(false);
715
719
  mockAuthContext.isAuthenticated = true;
720
+ mockAuthContext.isLoading = false;
716
721
  mockAuthContext.user = { id: 'user-2' } as User;
717
722
 
718
723
  const createOrganisationRolesQuery = (result: { data: unknown; error: unknown }) => {
@@ -779,6 +784,7 @@ describe('PaceLoginPage Component', () => {
779
784
  );
780
785
 
781
786
  await waitFor(() => {
787
+ // Error message format: You do not have permission to access ${appName}. You are not assigned to any organisation. Please contact your administrator.
782
788
  expect(screen.getByText(/not assigned to any organisation/i)).toBeInTheDocument();
783
789
  }, { timeout: 5000 });
784
790
  });
@@ -97,10 +97,8 @@ const Progress = React.forwardRef<
97
97
  <progress
98
98
  ref={ref}
99
99
  className={cn(
100
- 'appearance-none border-0 h-2 w-full rounded-full overflow-hidden transition-all accent-primary',
101
- isIndeterminate
102
- ? 'bg-gradient-to-r from-primary/10 via-primary/90 to-primary/10'
103
- : 'bg-primary/20',
100
+ 'appearance-none border-0 h-2 w-full rounded-full overflow-hidden transition-all accent-main-800',
101
+ !isIndeterminate && 'bg-sec-600/50',
104
102
  className
105
103
  )}
106
104
  {...(isIndeterminate ? {} : { value })}
@@ -260,9 +260,9 @@ export function ProtectedRoute({
260
260
  // Use isLoading (combined loading state) for consistency with simpler implementations
261
261
  if (isLoading && !sessionRestoration.hasTimedOut) {
262
262
  return loadingFallback || (
263
- <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100vh' }}>
263
+ <main className="grid place-items-center size-full">
264
264
  <LoadingSpinner />
265
- </div>
265
+ </main>
266
266
  );
267
267
  }
268
268
 
@@ -294,9 +294,9 @@ export function ProtectedRoute({
294
294
  const isTabVisible = typeof document !== 'undefined' && !document.hidden;
295
295
  if (tabJustBecameVisibleRef.current || (isTabVisible && wasAuthenticatedRef.current && isLoading)) {
296
296
  return loadingFallback || (
297
- <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100vh' }}>
297
+ <main className="grid place-items-center size-full">
298
298
  <LoadingSpinner />
299
- </div>
299
+ </main>
300
300
  );
301
301
  }
302
302
 
@@ -309,9 +309,9 @@ export function ProtectedRoute({
309
309
  // Show loading state while we wait for session refresh (unless we're not loading)
310
310
  if (isLoading) {
311
311
  return loadingFallback || (
312
- <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100vh' }}>
312
+ <main className="grid place-items-center size-full">
313
313
  <LoadingSpinner />
314
- </div>
314
+ </main>
315
315
  );
316
316
  }
317
317
 
@@ -333,14 +333,14 @@ export function ProtectedRoute({
333
333
  // If no events are available, show error message
334
334
  if (!events || events.length === 0) {
335
335
  return noEventsFallback || (
336
- <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', minHeight: '100vh', padding: '2rem' }}>
336
+ <main className="grid place-items-center text-center min-h-screen p-8">
337
337
  <Alert variant="destructive" className="max-w-md">
338
338
  <AlertTitle>No Events Available</AlertTitle>
339
339
  <AlertDescription>
340
340
  You don't have access to any events. Please contact your administrator if you believe this is an error.
341
341
  </AlertDescription>
342
342
  </Alert>
343
- </div>
343
+ </main>
344
344
  );
345
345
  }
346
346