@jmruthers/pace-core 0.6.9 → 0.6.11

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 (1182) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/audit-tool/00-dependencies.cjs +46 -13
  3. package/audit-tool/audits/01-pace-core-compliance.cjs +96 -21
  4. package/audit-tool/audits/02-project-structure.cjs +74 -2
  5. package/audit-tool/audits/03-architecture.cjs +220 -20
  6. package/audit-tool/audits/04-code-quality.cjs +95 -3
  7. package/audit-tool/audits/05-styling.cjs +19 -7
  8. package/audit-tool/audits/06-security-rbac.cjs +214 -25
  9. package/audit-tool/audits/07-api-tech-stack.cjs +31 -15
  10. package/audit-tool/audits/08-testing-documentation.cjs +11 -3
  11. package/audit-tool/audits/09-operations.cjs +19 -7
  12. package/audit-tool/index.cjs +22 -11
  13. package/audit-tool/utils/report-utils.cjs +4 -0
  14. package/cursor-rules/01-pace-core-compliance.mdc +1 -0
  15. package/cursor-rules/02-project-structure.mdc +3 -26
  16. package/cursor-rules/03-architecture.mdc +3 -1
  17. package/cursor-rules/04-code-quality.mdc +1 -0
  18. package/cursor-rules/05-styling.mdc +120 -8
  19. package/cursor-rules/06-security-rbac.mdc +126 -2
  20. package/cursor-rules/07-api-tech-stack.mdc +1 -0
  21. package/cursor-rules/08-testing-documentation.mdc +1 -0
  22. package/cursor-rules/09-operations.mdc +1 -0
  23. package/dist/DataTable-EFYP2QLE.js +16 -0
  24. package/dist/InactivityServiceProvider-BbxwwDz1.d.ts +308 -0
  25. package/dist/UnifiedAuthProvider-Bkt_tzdS.d.ts +183 -0
  26. package/dist/api-BZR2CYXL.js +5 -0
  27. package/dist/api-result-USV1Czr-.d.ts +51 -0
  28. package/dist/assets/app-icons/admin_favicon.svg +462 -0
  29. package/dist/assets/app-icons/base_favicon.svg +85 -0
  30. package/dist/assets/app-icons/cake_favicon.svg +68 -0
  31. package/dist/assets/app-icons/core_favicon.svg +256 -0
  32. package/dist/assets/app-icons/gear_favicon.svg +91 -0
  33. package/dist/assets/app-icons/medi_favicon.svg +92 -0
  34. package/dist/assets/app-icons/mint_favicon.svg +83 -0
  35. package/dist/assets/app-icons/pace_favicon.svg +49 -0
  36. package/dist/assets/app-icons/pump_favicon.svg +68 -0
  37. package/dist/assets/app-icons/seed_favicon.svg +91 -0
  38. package/dist/assets/app-icons/team_favicon.svg +67 -0
  39. package/dist/assets/app-icons/trac_favicon.svg +112 -0
  40. package/dist/assets/app-icons/trip_favicon.svg +102 -0
  41. package/dist/audit-HI2DHUVU.js +4 -0
  42. package/dist/auth-JvdRVaud.d.ts +49 -0
  43. package/dist/chunk-2DL2WSOE.js +327 -0
  44. package/dist/chunk-2OEVOGGR.js +9598 -0
  45. package/dist/chunk-44CNXN4P.js +15 -0
  46. package/dist/chunk-4R3T5ENU.js +2943 -0
  47. package/dist/chunk-7A6IMHH2.js +2321 -0
  48. package/dist/chunk-BTHN5MKC.js +121 -0
  49. package/dist/chunk-CU2BU2MQ.js +2 -0
  50. package/dist/chunk-D6BMFMQZ.js +200 -0
  51. package/dist/chunk-DDMPHZ3D.js +58 -0
  52. package/dist/chunk-ENLXB7GP.js +721 -0
  53. package/dist/chunk-J2KQK6DG.js +2159 -0
  54. package/dist/chunk-KJXRL3XE.js +6434 -0
  55. package/dist/chunk-L5LFKKLJ.js +61 -0
  56. package/dist/chunk-PCSHBLPB.js +811 -0
  57. package/dist/chunk-QRYSEPHB.js +429 -0
  58. package/dist/chunk-RMLY6KB5.js +187 -0
  59. package/dist/chunk-SACF5YSM.js +31 -0
  60. package/dist/chunk-UZNAFKGW.js +125 -0
  61. package/dist/chunk-V7FTM2LU.js +1080 -0
  62. package/dist/chunk-WY6Y7KC3.js +264 -0
  63. package/dist/chunk-XOJME5T7.js +407 -0
  64. package/dist/chunk-XPFVT3GN.js +492 -0
  65. package/dist/chunk-YFTFFJIV.js +529 -0
  66. package/dist/chunk-YYTWKVHO.js +1334 -0
  67. package/dist/components.d.ts +12 -89
  68. package/dist/components.js +23 -55
  69. package/dist/database.generated-qkdoiVrJ.d.ts +9441 -0
  70. package/dist/eslint-rules/index.cjs +3 -0
  71. package/dist/eslint-rules/rules/03-architecture.cjs +74 -0
  72. package/dist/eslint-rules/rules/05-styling.cjs +507 -0
  73. package/dist/eslint-rules/rules/06-security-rbac.cjs +84 -0
  74. package/dist/event-BfCox3N2.d.ts +265 -0
  75. package/dist/file-reference-DU1hcawx.d.ts +164 -0
  76. package/dist/functions-DH45k8ec.d.ts +208 -0
  77. package/dist/hooks.d.ts +28 -14
  78. package/dist/hooks.js +90 -56
  79. package/dist/icons/index.d.ts +1 -0
  80. package/dist/icons/index.js +1 -0
  81. package/dist/index.d.ts +392 -155
  82. package/dist/index.js +337 -347
  83. package/dist/pagination-BW1mqywp.d.ts +201 -0
  84. package/dist/papaparseLoader-WG2UXQ22.js +7 -0
  85. package/dist/providers.d.ts +29 -14
  86. package/dist/providers.js +7 -5
  87. package/dist/rbac/eslint-rules.js +2 -2
  88. package/dist/rbac/index.d.ts +180 -351
  89. package/dist/rbac/index.js +13 -11
  90. package/dist/theming/runtime.d.ts +28 -5
  91. package/dist/theming/runtime.js +2 -2
  92. package/dist/timezone-BTWWXKVY.d.ts +696 -0
  93. package/dist/types-BE2sEHKd.d.ts +55 -0
  94. package/dist/types-CvOPXWWZ.d.ts +111 -0
  95. package/dist/types-Dr8sNhER.d.ts +50 -0
  96. package/dist/types.d.ts +20 -13
  97. package/dist/types.js +1 -0
  98. package/dist/usePublicPageContext-B91dGYW1.d.ts +4367 -0
  99. package/dist/usePublicRouteParams-BgV6VhMi.d.ts +946 -0
  100. package/dist/utils.d.ts +338 -156
  101. package/dist/utils.js +78 -60
  102. package/dist/validation-g5n0hDkh.d.ts +177 -0
  103. package/docs/api/modules.md +1226 -1094
  104. package/docs/api-reference/components.md +5 -5
  105. package/docs/api-reference/rpc-functions.md +12 -3
  106. package/docs/core-concepts/rbac-system.md +8 -0
  107. package/docs/getting-started/cursor-rules.md +17 -20
  108. package/docs/getting-started/dependencies.md +1 -1
  109. package/docs/getting-started/setup.md +235 -0
  110. package/docs/implementation-guides/authentication.md +27 -0
  111. package/docs/implementation-guides/data-tables.md +365 -10
  112. package/docs/migration/ApiResult-migration.md +25 -0
  113. package/docs/rbac/RBAC_CONTRACT.md +0 -12
  114. package/docs/rbac/api-reference.md +33 -31
  115. package/docs/standards/0-standards-overview.md +50 -15
  116. package/docs/standards/1-pace-core-compliance-standards.md +62 -57
  117. package/docs/standards/2-project-structure-standards.md +45 -90
  118. package/docs/standards/3-architecture-standards.md +41 -1
  119. package/docs/standards/4-code-quality-standards.md +26 -6
  120. package/docs/standards/5-styling-standards.md +35 -1
  121. package/docs/standards/6-security-rbac-standards.md +288 -7
  122. package/docs/standards/7-api-tech-stack-standards.md +116 -17
  123. package/docs/standards/8-testing-documentation-standards.md +31 -0
  124. package/docs/standards/9-operations-standards.md +19 -0
  125. package/docs/standards/README.md +20 -201
  126. package/docs/testing/README.md +10 -0
  127. package/docs/testing/test-setup-for-consumers.md +916 -0
  128. package/docs/troubleshooting/common-issues.md +17 -1
  129. package/docs/troubleshooting/organisation-context-setup.md +8 -0
  130. package/docs/troubleshooting/print-event-name-css-variable-analysis.md +217 -0
  131. package/eslint-config-pace-core.cjs +24 -0
  132. package/package.json +14 -20
  133. package/scripts/build-docs.js +180 -0
  134. package/scripts/setup.cjs +536 -0
  135. package/scripts/validate.cjs +480 -0
  136. package/src/__mocks__/lucide-react.ts +0 -2
  137. package/src/__tests__/helpers/component-test-utils.test.tsx +260 -0
  138. package/src/__tests__/helpers/optimized-test-setup.test.ts +224 -0
  139. package/src/__tests__/helpers/supabaseMock.test.ts +273 -0
  140. package/src/__tests__/helpers/test-providers.test.tsx +99 -0
  141. package/src/__tests__/helpers/test-providers.tsx +37 -39
  142. package/src/__tests__/helpers/test-utils.test.tsx +447 -0
  143. package/src/__tests__/helpers/timer-utils.test.ts +371 -0
  144. package/src/assets/app-icons/admin_favicon.svg +462 -0
  145. package/src/assets/app-icons/base_favicon.svg +85 -0
  146. package/src/assets/app-icons/cake_favicon.svg +68 -0
  147. package/src/assets/app-icons/core_favicon.svg +256 -0
  148. package/src/assets/app-icons/gear_favicon.svg +91 -0
  149. package/src/assets/app-icons/index.test.ts +304 -0
  150. package/src/assets/app-icons/index.ts +83 -0
  151. package/src/assets/app-icons/medi_favicon.svg +92 -0
  152. package/src/assets/app-icons/mint_favicon.svg +83 -0
  153. package/src/assets/app-icons/pace_favicon.svg +49 -0
  154. package/src/assets/app-icons/pump_favicon.svg +68 -0
  155. package/src/assets/app-icons/seed_favicon.svg +91 -0
  156. package/src/assets/app-icons/team_favicon.svg +67 -0
  157. package/src/assets/app-icons/trac_favicon.svg +112 -0
  158. package/src/assets/app-icons/trip_favicon.svg +102 -0
  159. package/src/components/AddressField/AddressField.test.tsx +379 -4
  160. package/src/components/AddressField/AddressField.tsx +239 -213
  161. package/src/components/AddressField/types.ts +2 -2
  162. package/src/components/Alert/Alert.test.tsx +35 -25
  163. package/src/components/Alert/Alert.tsx +8 -8
  164. package/src/components/AppSwitcher/AppSwitcher.test.tsx +1250 -0
  165. package/src/components/AppSwitcher/AppSwitcher.tsx +315 -0
  166. package/src/components/Avatar/Avatar.test.tsx +11 -1
  167. package/src/components/Avatar/Avatar.tsx +3 -2
  168. package/src/components/Badge/Badge.test.tsx +11 -1
  169. package/src/components/Button/Button.test.tsx +13 -3
  170. package/src/components/Button/Button.tsx +1 -1
  171. package/src/components/Calendar/Calendar.test.tsx +523 -131
  172. package/src/components/Calendar/Calendar.tsx +107 -488
  173. package/src/components/Card/Card.test.tsx +384 -258
  174. package/src/components/Card/Card.tsx +19 -10
  175. package/src/components/Checkbox/Checkbox.test.tsx +58 -174
  176. package/src/components/ContextSelector/ContextSelector.internals.tsx +204 -0
  177. package/src/components/ContextSelector/ContextSelector.test.tsx +360 -0
  178. package/src/components/ContextSelector/ContextSelector.tsx +66 -280
  179. package/src/components/ContextSelector/ContextSelector.types.ts +35 -0
  180. package/src/components/ContextSelector/useContextSelectorState.tsx +195 -0
  181. package/src/components/DataTable/AUDIT_REPORT.md +59 -44
  182. package/src/components/DataTable/DataTable.comprehensive.test.tsx +759 -0
  183. package/src/components/DataTable/DataTable.default-state.test.tsx +524 -0
  184. package/src/components/DataTable/DataTable.export.test.tsx +705 -0
  185. package/src/components/DataTable/DataTable.grouping-aggregation.test.tsx +658 -0
  186. package/src/components/DataTable/DataTable.hooks.test.tsx +192 -0
  187. package/src/components/DataTable/DataTable.select-label-display.test.tsx +485 -0
  188. package/src/components/DataTable/DataTable.test.tsx +787 -416
  189. package/src/components/DataTable/DataTable.tsx +14 -14
  190. package/src/components/DataTable/DataTableCore.integration.test.tsx +458 -0
  191. package/src/components/DataTable/DataTableCore.test-setup.ts +221 -0
  192. package/src/components/DataTable/DataTableCore.test.tsx +970 -0
  193. package/src/components/DataTable/README.md +155 -0
  194. package/src/components/DataTable/TESTING.md +101 -0
  195. package/src/components/DataTable/a11y.basic.test.tsx +788 -0
  196. package/src/components/DataTable/components/DataTableCore.tsx +126 -894
  197. package/src/components/DataTable/components/GroupingDropdown.test.tsx +621 -0
  198. package/src/components/DataTable/components/GroupingDropdown.tsx +2 -3
  199. package/src/components/DataTable/components/ImportModal.tsx +82 -408
  200. package/src/components/DataTable/components/ImportModalFileSection.tsx +148 -0
  201. package/src/components/DataTable/context/DataTableContext.test.tsx +328 -0
  202. package/src/components/DataTable/context/DataTableContext.tsx +13 -13
  203. package/src/components/DataTable/core/ColumnFactory.test.ts +403 -0
  204. package/src/components/DataTable/core/ColumnFactory.ts +3 -3
  205. package/src/components/DataTable/hooks/useColumnOrderPersistence.test.ts +516 -0
  206. package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +12 -9
  207. package/src/components/DataTable/hooks/useColumnVisibilityPersistence.test.ts +256 -0
  208. package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +12 -9
  209. package/src/components/DataTable/hooks/useDataTableConfiguration.test.ts +297 -0
  210. package/src/components/DataTable/hooks/useDataTableConfiguration.ts +15 -3
  211. package/src/components/DataTable/hooks/useDataTableDataPipeline.test.ts +270 -0
  212. package/src/components/DataTable/hooks/useDataTableDeletionBatching.test.ts +127 -0
  213. package/src/components/DataTable/hooks/useDataTableDeletionBatching.ts +106 -0
  214. package/src/components/DataTable/hooks/useDataTableEffectiveActions.test.ts +461 -0
  215. package/src/components/DataTable/hooks/useDataTableEffectiveActions.ts +238 -0
  216. package/src/components/DataTable/hooks/useDataTableLayoutHandlers.test.ts +296 -0
  217. package/src/components/DataTable/hooks/useDataTableLayoutHandlers.ts +175 -0
  218. package/src/components/DataTable/hooks/useDataTablePaginationSync.test.ts +203 -0
  219. package/src/components/DataTable/hooks/useDataTablePaginationSync.ts +109 -0
  220. package/src/components/DataTable/hooks/useDataTablePermissions.test.ts +280 -0
  221. package/src/components/DataTable/hooks/useDataTablePermissions.ts +81 -260
  222. package/src/components/DataTable/hooks/useDataTablePipeline.test.tsx +219 -0
  223. package/src/components/DataTable/hooks/useDataTablePipeline.tsx +239 -0
  224. package/src/components/DataTable/hooks/useDataTableRenderGuard.test.tsx +316 -0
  225. package/src/components/DataTable/hooks/useDataTableRenderGuard.tsx +195 -0
  226. package/src/components/DataTable/hooks/useDataTableScope.test.ts +110 -0
  227. package/src/components/DataTable/hooks/useDataTableScope.ts +123 -0
  228. package/src/components/DataTable/hooks/useDataTableState.test.ts +733 -0
  229. package/src/components/DataTable/hooks/useDataTableState.ts +161 -114
  230. package/src/components/DataTable/hooks/useDataTableStateAndPersistence.test.ts +277 -0
  231. package/src/components/DataTable/hooks/useDataTableStateAndPersistence.ts +222 -0
  232. package/src/components/DataTable/hooks/useDataTableSuperAdmin.test.ts +93 -0
  233. package/src/components/DataTable/hooks/useDataTableSuperAdmin.ts +86 -0
  234. package/src/components/DataTable/hooks/useDataTableTableInstance.test.ts +185 -0
  235. package/src/components/DataTable/hooks/useDataTableTableInstance.ts +178 -0
  236. package/src/components/DataTable/hooks/useEffectiveColumnOrder.test.ts +183 -0
  237. package/src/components/DataTable/hooks/useHierarchicalState.test.ts +294 -0
  238. package/src/components/DataTable/hooks/useImportModalFocus.test.ts +184 -0
  239. package/src/components/DataTable/hooks/useImportModalFocus.ts +53 -0
  240. package/src/components/DataTable/hooks/useImportModalState.test.ts +390 -0
  241. package/src/components/DataTable/hooks/useImportModalState.ts +345 -0
  242. package/src/components/DataTable/hooks/useKeyboardNavigation.test.ts +787 -0
  243. package/src/components/DataTable/hooks/useKeyboardNavigation.ts +311 -271
  244. package/src/components/DataTable/hooks/usePermissionTracking.test.ts +381 -0
  245. package/src/components/DataTable/hooks/usePermissionTracking.ts +122 -0
  246. package/src/components/DataTable/hooks/useServerSideDataEffect.test.ts +258 -0
  247. package/src/components/DataTable/hooks/useServerSideDataEffect.ts +27 -4
  248. package/src/components/DataTable/hooks/useTableColumns.test.ts +499 -0
  249. package/src/components/DataTable/hooks/useTableColumns.ts +15 -39
  250. package/src/components/DataTable/hooks/useTableHandlers.test.ts +461 -0
  251. package/src/components/DataTable/hooks/useTableHandlers.ts +13 -22
  252. package/src/components/DataTable/index.ts +28 -5
  253. package/src/components/DataTable/keyboard.test.tsx +734 -0
  254. package/src/components/DataTable/mocks/MockRBACProvider.tsx +66 -0
  255. package/src/components/DataTable/pagination.modes.test.tsx +728 -0
  256. package/src/components/DataTable/ssr.strict-mode.test.tsx +319 -0
  257. package/src/components/DataTable/styles.test.ts +379 -0
  258. package/src/components/DataTable/styles.ts +0 -1
  259. package/src/components/DataTable/test-utils/MockDataTableComponents.tsx +55 -0
  260. package/src/components/DataTable/test-utils/dataFactories.ts +103 -0
  261. package/src/components/DataTable/test-utils/featureConfig.ts +10 -0
  262. package/src/components/DataTable/test-utils/sharedTestUtils.ts +419 -0
  263. package/src/components/DataTable/test-utils.ts +94 -0
  264. package/src/components/DataTable/types/actions.ts +71 -0
  265. package/src/components/DataTable/types/base.ts +39 -0
  266. package/src/components/DataTable/types/columns.ts +125 -0
  267. package/src/components/DataTable/types/export.ts +32 -0
  268. package/src/components/DataTable/types/features.ts +81 -0
  269. package/src/components/DataTable/types/hierarchical.ts +44 -0
  270. package/src/components/DataTable/types/index.ts +43 -0
  271. package/src/components/DataTable/types/pagination.ts +85 -0
  272. package/src/components/DataTable/types/performance.ts +47 -0
  273. package/src/components/DataTable/types/props.ts +62 -0
  274. package/src/components/DataTable/types/rbac.ts +45 -0
  275. package/src/components/DataTable/ui/layout/DataTableCore.test.tsx +1194 -0
  276. package/src/components/DataTable/ui/layout/DataTableCore.tsx +345 -0
  277. package/src/components/DataTable/ui/layout/DataTableErrorBoundary.test.tsx +438 -0
  278. package/src/components/DataTable/ui/layout/DataTableErrorBoundary.tsx +225 -0
  279. package/src/components/DataTable/ui/layout/DataTableLayout.test.tsx +1352 -0
  280. package/src/components/DataTable/ui/layout/DataTableLayout.tsx +661 -0
  281. package/src/components/DataTable/ui/modals/BulkDeleteConfirmDialog.test.tsx +91 -0
  282. package/src/components/DataTable/ui/modals/BulkDeleteConfirmDialog.tsx +43 -0
  283. package/src/components/DataTable/ui/modals/DataTableModals.test.tsx +749 -0
  284. package/src/components/DataTable/ui/modals/DataTableModals.tsx +341 -0
  285. package/src/components/DataTable/ui/modals/ImportModal.test.tsx +1834 -0
  286. package/src/components/DataTable/ui/modals/ImportModal.tsx +197 -0
  287. package/src/components/DataTable/ui/modals/ImportModalFailedRowsSection.tsx +60 -0
  288. package/src/components/DataTable/ui/modals/ImportModalFileSection.tsx +148 -0
  289. package/src/components/DataTable/ui/modals/ImportModalPreviewSection.tsx +60 -0
  290. package/src/components/DataTable/ui/modals/ImportModalSummarySection.tsx +59 -0
  291. package/src/components/DataTable/ui/modals/importModalPersistence.ts +73 -0
  292. package/src/components/DataTable/ui/shared/AccessDeniedPage.test.tsx +245 -0
  293. package/src/components/DataTable/ui/shared/AccessDeniedPage.tsx +159 -0
  294. package/src/components/DataTable/ui/shared/ActionButtons.test.tsx +921 -0
  295. package/src/components/DataTable/ui/shared/ActionButtons.tsx +195 -0
  296. package/src/components/DataTable/ui/shared/ColumnFilter.test.tsx +497 -0
  297. package/src/components/DataTable/ui/shared/ColumnFilter.tsx +113 -0
  298. package/src/components/DataTable/ui/shared/PaginationControls.test.tsx +451 -0
  299. package/src/components/DataTable/ui/shared/PaginationControls.tsx +291 -0
  300. package/src/components/DataTable/ui/shared/SortIndicator.test.tsx +135 -0
  301. package/src/components/DataTable/ui/shared/SortIndicator.tsx +50 -0
  302. package/src/components/DataTable/ui/table/EditFields.test.tsx +526 -0
  303. package/src/components/DataTable/ui/table/EditFields.tsx +355 -0
  304. package/src/components/DataTable/ui/table/EditableRow.test.tsx +1003 -0
  305. package/src/components/DataTable/ui/table/EditableRow.tsx +444 -0
  306. package/src/components/DataTable/ui/table/EmptyState.test.tsx +360 -0
  307. package/src/components/DataTable/ui/table/EmptyState.tsx +74 -0
  308. package/src/components/DataTable/ui/table/FilterRow.test.tsx +416 -0
  309. package/src/components/DataTable/ui/table/FilterRow.tsx +148 -0
  310. package/src/components/DataTable/ui/table/LoadingState.test.tsx +77 -0
  311. package/src/components/DataTable/ui/table/LoadingState.tsx +17 -0
  312. package/src/components/DataTable/ui/table/RowComponent.test.tsx +1024 -0
  313. package/src/components/DataTable/ui/table/RowComponent.tsx +429 -0
  314. package/src/components/DataTable/ui/table/UnifiedTableBody.test.tsx +1273 -0
  315. package/src/components/DataTable/ui/table/UnifiedTableBody.tsx +440 -0
  316. package/src/components/DataTable/ui/table/cellValueUtils.test.ts +453 -0
  317. package/src/components/DataTable/ui/table/cellValueUtils.ts +40 -0
  318. package/src/components/DataTable/ui/toolbar/BulkOperationsDropdown.test.tsx +551 -0
  319. package/src/components/DataTable/ui/toolbar/BulkOperationsDropdown.tsx +160 -0
  320. package/src/components/DataTable/ui/toolbar/ColumnVisibilityDropdown.test.tsx +751 -0
  321. package/src/components/DataTable/ui/toolbar/ColumnVisibilityDropdown.tsx +114 -0
  322. package/src/components/DataTable/ui/toolbar/DataTableToolbar.test.tsx +629 -0
  323. package/src/components/DataTable/ui/toolbar/DataTableToolbar.tsx +271 -0
  324. package/src/components/DataTable/ui/toolbar/GroupingDropdown.test.tsx +621 -0
  325. package/src/components/DataTable/ui/toolbar/GroupingDropdown.tsx +107 -0
  326. package/src/components/DataTable/utils/a11yUtils.test.ts +548 -0
  327. package/src/components/DataTable/utils/a11yUtils.ts +1 -1
  328. package/src/components/DataTable/utils/aggregationUtils.test.ts +288 -0
  329. package/src/components/DataTable/utils/aggregationUtils.ts +5 -5
  330. package/src/components/DataTable/utils/columnUtils.test.ts +94 -0
  331. package/src/components/DataTable/utils/csvParse.test.ts +74 -0
  332. package/src/components/DataTable/utils/csvParse.ts +65 -0
  333. package/src/components/DataTable/utils/errorHandling.test.ts +209 -0
  334. package/src/components/DataTable/utils/errorHandling.ts +3 -1
  335. package/src/components/DataTable/utils/exportUtils.test.ts +954 -0
  336. package/src/components/DataTable/utils/exportUtils.ts +1 -1
  337. package/src/components/DataTable/utils/flexibleImport.test.ts +573 -0
  338. package/src/components/DataTable/utils/flexibleImport.ts +3 -186
  339. package/src/components/DataTable/utils/hierarchicalSorting.test.ts +235 -0
  340. package/src/components/DataTable/utils/hierarchicalSorting.ts +3 -3
  341. package/src/components/DataTable/utils/hierarchicalUtils.test.ts +586 -0
  342. package/src/components/DataTable/utils/importDateParser.test.ts +162 -0
  343. package/src/components/DataTable/utils/importDateParser.ts +114 -0
  344. package/src/components/DataTable/utils/importValueParser.test.ts +138 -0
  345. package/src/components/DataTable/utils/importValueParser.ts +91 -0
  346. package/src/components/DataTable/utils/paginationUtils.test.ts +593 -0
  347. package/src/components/DataTable/utils/paginationUtils.ts +7 -4
  348. package/src/components/DataTable/utils/performanceUtils.test.ts +470 -0
  349. package/src/components/DataTable/utils/performanceUtils.ts +1 -1
  350. package/src/components/DataTable/utils/rowUtils.test.ts +235 -0
  351. package/src/components/DataTable/utils/selectFieldUtils.test.ts +271 -0
  352. package/src/components/DataTable/utils/selectFieldUtils.ts +97 -67
  353. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +18 -25
  354. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +1 -1
  355. package/src/components/DateTimeField/DateTimeField.test.tsx +3 -16
  356. package/src/components/DateTimeField/DateTimeField.tsx +1 -1
  357. package/src/components/Dialog/Dialog.test-utils.ts +49 -0
  358. package/src/components/Dialog/Dialog.test.tsx +2865 -458
  359. package/src/components/Dialog/Dialog.tsx +183 -986
  360. package/src/components/Dialog/dialogLock.test.ts +238 -0
  361. package/src/components/Dialog/dialogLock.ts +98 -0
  362. package/src/components/Dialog/index.ts +2 -0
  363. package/src/components/Dialog/useDialogDimensions.test.ts +163 -0
  364. package/src/components/Dialog/useDialogDimensions.ts +140 -0
  365. package/src/components/Dialog/useDialogLifecycle.test.ts +358 -0
  366. package/src/components/Dialog/useDialogLifecycle.ts +135 -0
  367. package/src/components/Dialog/useDialogPersistence.test.ts +381 -0
  368. package/src/components/Dialog/useDialogPersistence.ts +357 -0
  369. package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +2 -62
  370. package/src/components/ErrorBoundary/ErrorBoundaryContext.context.ts +17 -0
  371. package/src/components/ErrorBoundary/ErrorBoundaryContext.tsx +2 -45
  372. package/src/components/ErrorBoundary/ErrorBoundaryContext.types.ts +41 -0
  373. package/src/components/ErrorBoundary/index.ts +3 -4
  374. package/src/components/ErrorBoundary/useErrorBoundaryContext.ts +20 -0
  375. package/src/components/FileDisplay/FileDisplay.test.tsx +479 -247
  376. package/src/components/FileDisplay/FileDisplay.tsx +29 -659
  377. package/src/components/FileDisplay/FileDisplayContent.test.tsx +395 -0
  378. package/src/components/FileDisplay/FileDisplayContent.tsx +242 -0
  379. package/src/components/FileDisplay/FileDisplayDeleteConfirmDialog.test.tsx +74 -0
  380. package/src/components/FileDisplay/FileDisplayDeleteConfirmDialog.tsx +38 -0
  381. package/src/components/FileDisplay/FileDisplayEmptyView.test.tsx +33 -0
  382. package/src/components/FileDisplay/FileDisplayEmptyView.tsx +33 -0
  383. package/src/components/FileDisplay/FileDisplayErrorView.test.tsx +71 -0
  384. package/src/components/FileDisplay/FileDisplayErrorView.tsx +50 -0
  385. package/src/components/FileDisplay/FileDisplayLoadingFallbackView.test.tsx +22 -0
  386. package/src/components/FileDisplay/FileDisplayLoadingFallbackView.tsx +22 -0
  387. package/src/components/FileDisplay/FileDisplayLoadingView.test.tsx +21 -0
  388. package/src/components/FileDisplay/FileDisplayLoadingView.tsx +23 -0
  389. package/src/components/FileDisplay/FileDisplayMultipleFilesView.test.tsx +101 -0
  390. package/src/components/FileDisplay/FileDisplayMultipleFilesView.tsx +109 -0
  391. package/src/components/FileDisplay/FileDisplaySingleDocumentLinkView.test.tsx +58 -0
  392. package/src/components/FileDisplay/FileDisplaySingleDocumentLinkView.tsx +48 -0
  393. package/src/components/FileDisplay/FileDisplaySingleFileWithActionsView.test.tsx +111 -0
  394. package/src/components/FileDisplay/FileDisplaySingleFileWithActionsView.tsx +270 -0
  395. package/src/components/FileDisplay/FileDisplaySingleImageView.test.tsx +78 -0
  396. package/src/components/FileDisplay/FileDisplaySingleImageView.tsx +67 -0
  397. package/src/components/FileDisplay/fallbackUtils.test.ts +50 -0
  398. package/src/components/FileDisplay/fallbackUtils.ts +44 -0
  399. package/src/components/FileDisplay/fetchFileDisplayData.ts +24 -0
  400. package/src/components/FileDisplay/fetchFileDisplayData.unit.test.ts +183 -0
  401. package/src/components/FileDisplay/fileDisplayUtils.test.ts +58 -0
  402. package/src/components/FileDisplay/fileDisplayUtils.ts +24 -0
  403. package/src/components/FileDisplay/index.tsx +1 -1
  404. package/src/components/FileDisplay/useFileDisplay.test.ts +538 -0
  405. package/src/components/FileDisplay/useFileDisplay.ts +515 -0
  406. package/src/components/FileDisplay/useFileDisplay.unit.test.ts +1438 -0
  407. package/src/components/FileDisplay/useFileDisplayData.ts +126 -0
  408. package/src/components/FileDisplay/usePublicFileDisplay.test.ts +729 -0
  409. package/src/components/FileDisplay/usePublicFileDisplay.ts +579 -0
  410. package/src/components/FileUpload/FileUpload.test.tsx +69 -27
  411. package/src/components/FileUpload/FileUpload.tsx +112 -527
  412. package/src/components/FileUpload/FileUploadDropZone.tsx +112 -0
  413. package/src/components/FileUpload/FileUploadProgressItem.tsx +86 -0
  414. package/src/components/FileUpload/FileUploadProgressList.tsx +40 -0
  415. package/src/components/FileUpload/index.tsx +1 -1
  416. package/src/components/FileUpload/useFileUploadManager.test.ts +308 -0
  417. package/src/components/FileUpload/useFileUploadManager.ts +454 -0
  418. package/src/components/FileUpload/useResolvedAppId.test.ts +102 -0
  419. package/src/components/FileUpload/useResolvedAppId.ts +77 -0
  420. package/src/components/Footer/Footer.test.tsx +15 -382
  421. package/src/components/Footer/Footer.tsx +8 -125
  422. package/src/components/Form/Form.test.tsx +425 -88
  423. package/src/components/Form/Form.tsx +91 -299
  424. package/src/components/Form/useFormPersistence.ts +257 -0
  425. package/src/components/Header/Header.test.tsx +653 -163
  426. package/src/components/Header/Header.tsx +62 -44
  427. package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +35 -76
  428. package/src/components/Input/Input.test.tsx +34 -120
  429. package/src/components/Input/Input.tsx +1 -1
  430. package/src/components/Label/Label.test.tsx +46 -45
  431. package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +8 -11
  432. package/src/components/LoginForm/LoginForm.test.tsx +0 -1
  433. package/src/components/NavigationMenu/HierarchicalNavItem.tsx +104 -0
  434. package/src/components/NavigationMenu/NavigationMenu.test.tsx +2422 -102
  435. package/src/components/NavigationMenu/NavigationMenu.tsx +62 -362
  436. package/src/components/NavigationMenu/index.ts +6 -1
  437. package/src/components/NavigationMenu/navigationPermissionHelper.ts +188 -0
  438. package/src/components/NavigationMenu/useNavigationFiltering.test.ts +1949 -0
  439. package/src/components/NavigationMenu/useNavigationFiltering.ts +199 -308
  440. package/src/components/NavigationMenu/useNavigationScope.ts +125 -0
  441. package/src/components/PaceAppLayout/PaceAppLayout.edge-cases.test.tsx +1322 -0
  442. package/src/components/PaceAppLayout/PaceAppLayout.integration.test.tsx +50 -49
  443. package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +81 -38
  444. package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +103 -85
  445. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +774 -44
  446. package/src/components/PaceAppLayout/PaceAppLayout.tsx +282 -764
  447. package/src/components/PaceAppLayout/README.md +0 -9
  448. package/src/components/PaceAppLayout/test-setup.tsx +15 -9
  449. package/src/components/PaceAppLayout/useFilteredNavItems.ts +304 -0
  450. package/src/components/PaceAppLayout/usePaceAppLayoutConfig.ts +142 -0
  451. package/src/components/PaceAppLayout/usePaceAppLayoutGate.tsx +150 -0
  452. package/src/components/PaceAppLayout/usePaceAppLayoutPermissions.ts +162 -0
  453. package/src/components/PaceAppLayout/usePaceAppLayoutScope.ts +79 -0
  454. package/src/components/PaceAppLayout/useRoleBasedRouteAccess.ts +157 -0
  455. package/src/components/PaceAppLayout/useSuperAdminFallback.ts +58 -0
  456. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +782 -20
  457. package/src/components/PaceLoginPage/PaceLoginPage.tsx +33 -125
  458. package/src/components/PaceLoginPage/useLoginAppAccess.ts +153 -0
  459. package/src/components/PasswordChange/PasswordChangeForm.test.tsx +1 -1
  460. package/src/components/Progress/Progress.test.tsx +127 -1
  461. package/src/components/Progress/Progress.tsx +1 -2
  462. package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +1196 -4
  463. package/src/components/ProtectedRoute/ProtectedRoute.tsx +29 -217
  464. package/src/components/ProtectedRoute/useProtectedRouteState.ts +128 -0
  465. package/src/components/ProtectedRoute/useVisibilityRedirectGrace.ts +89 -0
  466. package/src/components/PublicLayout/PublicLayout.test.tsx +1640 -38
  467. package/src/components/PublicLayout/PublicPageContext.ts +28 -0
  468. package/src/components/PublicLayout/PublicPageLayout.tsx +134 -75
  469. package/src/components/PublicLayout/PublicPageProvider.tsx +7 -42
  470. package/src/components/PublicLayout/usePublicPageContext.ts +36 -0
  471. package/src/components/Select/Select.test.tsx +45 -8
  472. package/src/components/Select/Select.tsx +57 -40
  473. package/src/components/Select/context.test.tsx +56 -0
  474. package/src/components/Select/text.test.tsx +104 -0
  475. package/src/components/Select/text.ts +26 -0
  476. package/src/components/Select/types.ts +3 -0
  477. package/src/components/Select/useSelectEvents.test.ts +279 -0
  478. package/src/components/Select/useSelectEvents.ts +87 -0
  479. package/src/components/Select/useSelectSearch.test.tsx +295 -0
  480. package/src/components/Select/useSelectSearch.ts +91 -0
  481. package/src/components/Select/useSelectState.test.ts +268 -0
  482. package/src/components/Select/useSelectState.ts +104 -0
  483. package/src/components/SessionRestorationLoader/SessionRestorationLoader.test.tsx +28 -112
  484. package/src/components/Switch/Switch.test.tsx +57 -153
  485. package/src/components/Table/Table.test.tsx +395 -317
  486. package/src/components/Tabs/Tabs.test.tsx +270 -0
  487. package/src/components/Tabs/Tabs.tsx +4 -4
  488. package/src/components/Textarea/Textarea.test.tsx +11 -38
  489. package/src/components/Toast/Toast.test.tsx +425 -496
  490. package/src/components/Tooltip/Tooltip.test.tsx +4 -21
  491. package/src/components/UserMenu/UserMenu.test.tsx +1 -21
  492. package/src/components/UserMenu/UserMenu.tsx +0 -1
  493. package/src/components/index.test.ts +346 -0
  494. package/src/components/index.ts +12 -1
  495. package/src/constants/performance.test.ts +91 -0
  496. package/src/hooks/ServiceHooks.test.tsx +725 -0
  497. package/src/hooks/hooks.integration.test.tsx +608 -0
  498. package/src/hooks/index.ts +18 -3
  499. package/src/hooks/index.unit.test.ts +220 -0
  500. package/src/hooks/public/usePublicEvent.test.ts +304 -0
  501. package/src/hooks/public/usePublicEvent.ts +11 -11
  502. package/src/hooks/public/usePublicEventLogo.test.ts +655 -120
  503. package/src/hooks/public/usePublicEventLogo.ts +2 -2
  504. package/src/hooks/public/usePublicRouteParams.test.ts +595 -0
  505. package/src/hooks/public/usePublicRouteParams.ts +2 -2
  506. package/src/hooks/services/useAuth.ts +9 -7
  507. package/src/hooks/services/useAuthService.ts +1 -1
  508. package/src/hooks/services/useEventService.ts +1 -1
  509. package/src/hooks/useAccessibleApps.test.ts +400 -0
  510. package/src/hooks/useAccessibleApps.ts +264 -0
  511. package/src/hooks/useAddressAutocomplete.test.ts +170 -47
  512. package/src/hooks/useAddressAutocomplete.ts +109 -81
  513. package/src/hooks/useApiFetch.unit.test.ts +111 -0
  514. package/src/hooks/useAppConfig.ts +13 -3
  515. package/src/hooks/useAppConfig.unit.test.ts +712 -0
  516. package/src/hooks/useComponentPerformance.unit.test.tsx +314 -0
  517. package/src/hooks/useDataTablePerformance.ts +111 -130
  518. package/src/hooks/useDataTablePerformance.unit.test.ts +720 -0
  519. package/src/hooks/useDataTableState.test.ts +170 -0
  520. package/src/hooks/useDataTableState.ts +5 -5
  521. package/src/hooks/useDebounce.unit.test.ts +157 -0
  522. package/src/hooks/useEventTheme.test.ts +70 -18
  523. package/src/hooks/useEventTheme.ts +50 -22
  524. package/src/hooks/useEvents.ts +49 -2
  525. package/src/hooks/useEvents.unit.test.ts +227 -0
  526. package/src/hooks/useFileReference.test.ts +388 -107
  527. package/src/hooks/useFileReference.ts +184 -179
  528. package/src/hooks/useFileUrl.ts +1 -1
  529. package/src/hooks/useFileUrl.unit.test.ts +686 -0
  530. package/src/hooks/useFileUrlCache.test.ts +319 -0
  531. package/src/hooks/useFileUrlCache.ts +5 -2
  532. package/src/hooks/useFocusManagement.unit.test.ts +604 -0
  533. package/src/hooks/useFocusTrap.unit.test.tsx +613 -0
  534. package/src/hooks/useFormDialog.test.ts +307 -0
  535. package/src/hooks/useFormDialog.ts +2 -2
  536. package/src/hooks/useInactivityTracker.ts +141 -134
  537. package/src/hooks/useInactivityTracker.unit.test.ts +446 -0
  538. package/src/hooks/useIsMobile.unit.test.ts +317 -0
  539. package/src/hooks/useIsPrint.ts +62 -0
  540. package/src/hooks/useIsPrint.unit.test.ts +545 -0
  541. package/src/hooks/useKeyboardShortcuts.unit.test.ts +907 -0
  542. package/src/hooks/useOrganisationPermissions.test.ts +1 -2
  543. package/src/hooks/useOrganisationPermissions.ts +1 -4
  544. package/src/hooks/useOrganisationPermissions.unit.test.tsx +293 -0
  545. package/src/hooks/useOrganisationSecurity.test.ts +4 -33
  546. package/src/hooks/useOrganisationSecurity.ts +192 -203
  547. package/src/hooks/useOrganisationSecurity.unit.test.tsx +959 -0
  548. package/src/hooks/useOrganisations.ts +1 -1
  549. package/src/hooks/useOrganisations.unit.test.ts +369 -0
  550. package/src/hooks/usePerformanceMonitor.ts +1 -1
  551. package/src/hooks/usePerformanceMonitor.unit.test.ts +693 -0
  552. package/src/hooks/usePermissionCache.test.ts +298 -329
  553. package/src/hooks/usePermissionCache.ts +277 -276
  554. package/src/hooks/usePreventTabReload.test.ts +307 -0
  555. package/src/hooks/usePublicEvent.simple.test.ts +794 -0
  556. package/src/hooks/usePublicEvent.test.ts +670 -0
  557. package/src/hooks/usePublicEvent.unit.test.ts +638 -0
  558. package/src/hooks/usePublicFileDisplay.test.ts +948 -0
  559. package/src/hooks/usePublicRouteParams.unit.test.ts +442 -0
  560. package/src/hooks/useQueryCache.test.ts +391 -0
  561. package/src/hooks/useQueryCache.ts +7 -9
  562. package/src/hooks/useRBAC.unit.test.ts +253 -0
  563. package/src/hooks/useSessionDraft.test.ts +556 -0
  564. package/src/hooks/useSessionDraft.ts +14 -11
  565. package/src/hooks/useSessionRestoration.ts +1 -1
  566. package/src/hooks/useSessionRestoration.unit.test.tsx +381 -0
  567. package/src/hooks/useStorage.ts +94 -54
  568. package/src/hooks/useStorage.unit.test.ts +684 -0
  569. package/src/hooks/useToast.test.ts +413 -0
  570. package/src/hooks/useToast.ts +2 -2
  571. package/src/hooks/useToast.unit.test.tsx +481 -0
  572. package/src/hooks/useZodForm.ts +3 -3
  573. package/src/hooks/useZodForm.unit.test.tsx +191 -0
  574. package/src/icons/index.test.ts +133 -0
  575. package/src/icons/index.ts +3 -1
  576. package/src/index.test.ts +528 -0
  577. package/src/index.ts +56 -9
  578. package/src/providers/AuthProvider.test.tsx +218 -0
  579. package/src/providers/EventProvider.test.tsx +487 -0
  580. package/src/providers/InactivityProvider.test-helper.tsx +40 -0
  581. package/src/providers/InactivityProvider.test.tsx +421 -0
  582. package/src/providers/ProviderLifecycle.test.tsx +308 -0
  583. package/src/providers/UnifiedAuthProvider.smoke.test.tsx +7 -12
  584. package/src/providers/UnifiedAuthProvider.test.tsx +503 -0
  585. package/src/providers/index.test.ts +138 -0
  586. package/src/providers/services/AuthServiceContext.ts +27 -0
  587. package/src/providers/services/AuthServiceProvider.integration.test.tsx +229 -0
  588. package/src/providers/services/AuthServiceProvider.test.tsx +638 -0
  589. package/src/providers/services/AuthServiceProvider.tsx +81 -20
  590. package/src/providers/services/EventServiceContext.ts +25 -0
  591. package/src/providers/services/EventServiceProvider.test.tsx +839 -0
  592. package/src/providers/services/EventServiceProvider.tsx +11 -20
  593. package/src/providers/services/InactivityServiceContext.ts +25 -0
  594. package/src/providers/services/InactivityServiceProvider.test.tsx +662 -0
  595. package/src/providers/services/InactivityServiceProvider.tsx +7 -17
  596. package/src/providers/services/OrganisationServiceContext.ts +25 -0
  597. package/src/providers/services/OrganisationServiceProvider.test.tsx +440 -0
  598. package/src/providers/services/OrganisationServiceProvider.tsx +7 -17
  599. package/src/providers/services/UnifiedAuthContext.ts +102 -0
  600. package/src/providers/services/UnifiedAuthProvider.advanced.test.tsx +434 -0
  601. package/src/providers/services/UnifiedAuthProvider.appId.test.tsx +408 -0
  602. package/src/providers/services/UnifiedAuthProvider.integration.test.tsx +304 -0
  603. package/src/providers/services/UnifiedAuthProvider.test.tsx +212 -0
  604. package/src/providers/services/UnifiedAuthProvider.tsx +147 -497
  605. package/src/providers/services/contexts.test.tsx +281 -0
  606. package/src/providers/services/useUnifiedAuth.test.tsx +251 -0
  607. package/src/providers/services/useUnifiedAuth.ts +29 -0
  608. package/src/providers/services/useUnifiedAuthContextValue.ts +279 -0
  609. package/src/providers/useInactivity.test-helper.ts +27 -0
  610. package/src/rbac/README.md +5 -5
  611. package/src/rbac/adapters.comprehensive.test.tsx +429 -0
  612. package/src/rbac/adapters.test.tsx +654 -0
  613. package/src/rbac/adapters.tsx +53 -38
  614. package/src/rbac/api.test.ts +986 -259
  615. package/src/rbac/api.ts +260 -216
  616. package/src/rbac/audit-batched.test.ts +550 -0
  617. package/src/rbac/audit-batched.ts +5 -4
  618. package/src/rbac/audit.test.ts +225 -28
  619. package/src/rbac/audit.ts +26 -18
  620. package/src/rbac/auth-rbac-security.integration.test.tsx +300 -0
  621. package/src/rbac/auth-rbac.e2e.test.tsx +510 -0
  622. package/src/rbac/cache-invalidation.test.ts +715 -0
  623. package/src/rbac/cache-invalidation.ts +18 -15
  624. package/src/rbac/cache.test.ts +123 -63
  625. package/src/rbac/cache.ts +3 -4
  626. package/src/rbac/components/AccessDenied.test.tsx +324 -0
  627. package/src/rbac/components/AccessDenied.tsx +20 -18
  628. package/src/rbac/components/NavigationGuard.test.tsx +1148 -0
  629. package/src/rbac/components/NavigationGuard.tsx +10 -8
  630. package/src/rbac/components/PagePermissionGuard.guard.test.tsx +236 -0
  631. package/src/rbac/components/PagePermissionGuard.performance.test.tsx +252 -0
  632. package/src/rbac/components/PagePermissionGuard.race-condition.test.tsx +243 -0
  633. package/src/rbac/components/PagePermissionGuard.test.tsx +1430 -0
  634. package/src/rbac/components/PagePermissionGuard.tsx +188 -381
  635. package/src/rbac/components/PagePermissionGuard.verification.test.tsx +185 -0
  636. package/src/rbac/config.test.ts +131 -48
  637. package/src/rbac/config.ts +69 -26
  638. package/src/rbac/docs/event-based-apps.md +26 -13
  639. package/src/rbac/engine.comprehensive.test.ts +808 -0
  640. package/src/rbac/engine.test.ts +974 -130
  641. package/src/rbac/engine.ts +53 -13
  642. package/src/rbac/errors.test.ts +99 -87
  643. package/src/rbac/errors.ts +89 -55
  644. package/src/rbac/eslint-rules.js +2 -2
  645. package/src/rbac/hooks/permissions/runPermissionCheck.ts +77 -0
  646. package/src/rbac/hooks/permissions/useAccessLevel.test.ts +622 -0
  647. package/src/rbac/hooks/permissions/useAccessLevel.ts +23 -14
  648. package/src/rbac/hooks/permissions/useCan.test.ts +798 -0
  649. package/src/rbac/hooks/permissions/useCan.ts +173 -253
  650. package/src/rbac/hooks/permissions/useMultiplePermissions.test.ts +843 -0
  651. package/src/rbac/hooks/permissions/useMultiplePermissions.ts +63 -10
  652. package/src/rbac/hooks/permissions/usePermissions.test.ts +543 -0
  653. package/src/rbac/hooks/permissions/usePermissions.ts +50 -78
  654. package/src/rbac/hooks/useCan.test.ts +348 -32
  655. package/src/rbac/hooks/usePageAccessLogging.ts +160 -0
  656. package/src/rbac/hooks/usePageGuardScope.ts +117 -0
  657. package/src/rbac/hooks/usePagePermissionCheck.ts +67 -0
  658. package/src/rbac/hooks/usePermissions.integration.test.ts +427 -0
  659. package/src/rbac/hooks/usePermissions.stability.test.ts +268 -0
  660. package/src/rbac/hooks/usePermissions.test.ts +459 -33
  661. package/src/rbac/hooks/usePermissions.ts +5 -7
  662. package/src/rbac/hooks/useRBAC.test.ts +1784 -21
  663. package/src/rbac/hooks/useRBAC.ts +148 -88
  664. package/src/rbac/hooks/useResolvedScope.test.ts +442 -5
  665. package/src/rbac/hooks/useResolvedScope.ts +4 -1
  666. package/src/rbac/hooks/useResourcePermissions.test.ts +561 -24
  667. package/src/rbac/hooks/useResourcePermissions.ts +76 -140
  668. package/src/rbac/hooks/useResourcePermissionsSuperAdmin.ts +67 -0
  669. package/src/rbac/hooks/useRoleManagement.test.ts +634 -61
  670. package/src/rbac/hooks/useRoleManagement.ts +158 -586
  671. package/src/rbac/hooks/useSecureSupabase.test.ts +1179 -0
  672. package/src/rbac/hooks/useSecureSupabase.ts +21 -14
  673. package/src/rbac/hooks/useSuperAdminCheck.ts +80 -0
  674. package/src/rbac/index.test.ts +107 -0
  675. package/src/rbac/index.ts +32 -32
  676. package/src/rbac/performance.test.ts +451 -0
  677. package/src/rbac/permissions.test.ts +149 -68
  678. package/src/rbac/permissions.ts +0 -3
  679. package/src/rbac/rbac-core.test.tsx +276 -0
  680. package/src/rbac/rbac-engine-core-logic.test.ts +387 -0
  681. package/src/rbac/rbac-engine-simplified.test.ts +252 -0
  682. package/src/rbac/rbac-functions.test.ts +703 -0
  683. package/src/rbac/rbac-integration.test.ts +523 -0
  684. package/src/rbac/rbac-role-isolation.test.ts +456 -0
  685. package/src/rbac/request-deduplication.test.ts +352 -0
  686. package/src/rbac/request-deduplication.ts +5 -4
  687. package/src/rbac/scenarios.user-role.test.tsx +271 -0
  688. package/src/rbac/secureClient.test.ts +499 -115
  689. package/src/rbac/secureClient.ts +54 -28
  690. package/src/rbac/security.test.ts +448 -44
  691. package/src/rbac/security.ts +7 -6
  692. package/src/rbac/types/roleManagement.ts +66 -0
  693. package/src/rbac/types.test.ts +236 -0
  694. package/src/rbac/types.ts +7 -5
  695. package/src/rbac/utils/clientSecurity.test.ts +192 -0
  696. package/src/rbac/utils/clientSecurity.ts +6 -4
  697. package/src/rbac/utils/contextValidator.test.ts +126 -0
  698. package/src/rbac/utils/contextValidator.ts +6 -3
  699. package/src/rbac/utils/deep-equal.test.ts +76 -0
  700. package/src/rbac/utils/eventContext.test.ts +401 -0
  701. package/src/rbac/utils/eventContext.ts +38 -34
  702. package/src/rbac/utils/fetchPermissionMap.ts +13 -0
  703. package/src/rbac/utils/permissionMapHelpers.ts +34 -0
  704. package/src/rbac/utils/roleManagementRpc.ts +303 -0
  705. package/src/services/AuthService.edge-cases.test.ts +746 -0
  706. package/src/services/AuthService.restoreSession.test.ts +59 -0
  707. package/src/services/AuthService.test.ts +1362 -0
  708. package/src/services/AuthService.ts +197 -216
  709. package/src/services/BaseService.edge-cases.test.ts +506 -0
  710. package/src/services/BaseService.test.ts +363 -0
  711. package/src/services/EventService.edge-cases.test.ts +636 -0
  712. package/src/services/EventService.eventColours.test.ts +64 -0
  713. package/src/services/EventService.test.ts +1250 -0
  714. package/src/services/EventService.ts +244 -315
  715. package/src/services/InactivityService.edge-cases.test.ts +492 -0
  716. package/src/services/InactivityService.lifecycle.test.ts +406 -0
  717. package/src/services/InactivityService.test.ts +829 -0
  718. package/src/services/InactivityService.ts +172 -213
  719. package/src/services/OrganisationService.edge-cases.test.ts +633 -0
  720. package/src/services/OrganisationService.pagination.test.ts +409 -0
  721. package/src/services/OrganisationService.test.ts +1579 -0
  722. package/src/services/OrganisationService.ts +186 -257
  723. package/src/services/base/BaseService.test.ts +214 -0
  724. package/src/services/interfaces/IAuthService.test.ts +184 -0
  725. package/src/services/interfaces/IAuthService.ts +10 -9
  726. package/src/services/interfaces/IEventService.test.ts +176 -0
  727. package/src/services/interfaces/IInactivityService.test.ts +183 -0
  728. package/src/services/interfaces/IOrganisationService.test.ts +207 -0
  729. package/src/services/interfaces/IOrganisationService.ts +0 -1
  730. package/src/styles/core.css +244 -12
  731. package/src/theming/parseEventColours.test.ts +321 -0
  732. package/src/theming/parseEventColours.ts +18 -9
  733. package/src/theming/runtime.test.ts +495 -0
  734. package/src/theming/runtime.ts +72 -7
  735. package/src/types/api-result.ts +53 -0
  736. package/src/types/auth.ts +0 -1
  737. package/src/types/core.test.ts +397 -0
  738. package/src/types/database-generated.test.ts +78 -0
  739. package/src/types/database.generated.ts +45 -10
  740. package/src/types/event.ts +39 -19
  741. package/src/types/file-reference.test.ts +351 -0
  742. package/src/types/file-reference.ts +37 -12
  743. package/src/types/guards.test.ts +246 -0
  744. package/src/types/index.test.ts +265 -0
  745. package/src/types/index.ts +3 -0
  746. package/src/types/organisation.roles.test.ts +55 -0
  747. package/src/types/organisation.test.ts +1105 -0
  748. package/src/types/organisation.ts +15 -15
  749. package/src/types/rpc-responses.ts +33 -0
  750. package/src/types/supabase.ts +14 -6
  751. package/src/types/theme.test.ts +830 -0
  752. package/src/types/type-validation.test.ts +526 -0
  753. package/src/types/validation.test.ts +729 -0
  754. package/src/types/vitest-globals.d.ts +1 -1
  755. package/src/utils/app/appConfig.test.ts +235 -0
  756. package/src/utils/app/appIdResolver.test.ts +252 -57
  757. package/src/utils/app/appIdResolver.ts +31 -20
  758. package/src/utils/app/appNameResolver.test.ts +18 -10
  759. package/src/utils/app/appNameResolver.ts +11 -9
  760. package/src/utils/app/appPortMap.test.ts +125 -0
  761. package/src/utils/app/appPortMap.ts +51 -0
  762. package/src/utils/app/buildAppUrl.test.ts +273 -0
  763. package/src/utils/app/buildAppUrl.ts +114 -0
  764. package/src/utils/appConfig.unit.test.ts +55 -0
  765. package/src/utils/audit/audit.test.ts +354 -39
  766. package/src/utils/audit.unit.test.ts +69 -0
  767. package/src/utils/auth-utils.unit.test.ts +69 -0
  768. package/src/utils/bundleAnalysis.unit.test.ts +326 -0
  769. package/src/utils/cn.unit.test.ts +34 -0
  770. package/src/utils/context/organisationContext.test.ts +115 -95
  771. package/src/utils/context/organisationContext.ts +32 -43
  772. package/src/utils/context/sessionTracking.test.ts +354 -0
  773. package/src/utils/core/cn.test.ts +66 -0
  774. package/src/utils/core/debugLogger.test.ts +113 -0
  775. package/src/utils/core/debugLogger.ts +15 -8
  776. package/src/utils/core/logger.test.ts +217 -0
  777. package/src/utils/core/logger.ts +20 -16
  778. package/src/utils/core/mergeRefs.ts +24 -0
  779. package/src/utils/debugLogger.test.ts +417 -0
  780. package/src/utils/device/deviceFingerprint.test.ts +8 -5
  781. package/src/utils/device/deviceFingerprint.ts +3 -3
  782. package/src/utils/deviceFingerprint.unit.test.ts +818 -0
  783. package/src/utils/dynamic/createLazyComponent.tsx +46 -0
  784. package/src/utils/dynamic/dynamicUtils.test.ts +185 -0
  785. package/src/utils/dynamic/dynamicUtils.ts +6 -6
  786. package/src/utils/dynamic/lazyLoad.test.tsx +156 -0
  787. package/src/utils/dynamic/lazyLoad.tsx +8 -36
  788. package/src/utils/dynamic/papaparseLoader.ts +7 -0
  789. package/src/utils/dynamicUtils.unit.test.ts +331 -0
  790. package/src/utils/file-reference/file-reference.test.ts +1238 -0
  791. package/src/utils/file-reference/index.ts +330 -348
  792. package/src/utils/formatDate.unit.test.ts +109 -0
  793. package/src/utils/formatting/formatDate.test.ts +22 -148
  794. package/src/utils/formatting/formatDateTime.test.ts +41 -119
  795. package/src/utils/formatting/formatDateTimeTimezone.test.ts +41 -85
  796. package/src/utils/formatting/formatNumber.test.ts +259 -0
  797. package/src/utils/formatting/formatTime.test.ts +36 -128
  798. package/src/utils/formatting/formatting.ts +1 -1
  799. package/src/utils/formatting.unit.test.ts +99 -0
  800. package/src/utils/google-places/googlePlacesUtils.test.ts +127 -36
  801. package/src/utils/google-places/googlePlacesUtils.ts +67 -86
  802. package/src/utils/google-places/loadGoogleMapsScript.test.ts +68 -8
  803. package/src/utils/google-places/loadGoogleMapsScript.ts +140 -118
  804. package/src/utils/index.ts +52 -11
  805. package/src/utils/index.unit.test.ts +251 -0
  806. package/src/utils/lazyLoad.unit.test.tsx +319 -0
  807. package/src/utils/location/location.test.ts +19 -116
  808. package/src/utils/logger.unit.test.ts +398 -0
  809. package/src/utils/organisationContext.unit.test.ts +180 -0
  810. package/src/utils/performance/bundleAnalysis.test.ts +148 -0
  811. package/src/utils/performance/bundleAnalysis.ts +16 -22
  812. package/src/utils/performance/performanceBenchmark.test.ts +251 -0
  813. package/src/utils/performance/performanceBenchmark.ts +12 -4
  814. package/src/utils/performance/performanceBudgets.test.ts +241 -0
  815. package/src/utils/performance/performanceBudgets.ts +9 -6
  816. package/src/utils/performanceBenchmark.test.ts +174 -0
  817. package/src/utils/performanceBudgets.unit.test.ts +288 -0
  818. package/src/utils/permissionTypes.unit.test.ts +250 -0
  819. package/src/utils/permissionUtils.unit.test.ts +362 -0
  820. package/src/utils/permissions/permissionTypes.test.ts +149 -0
  821. package/src/utils/permissions/permissionUtils.test.ts +20 -42
  822. package/src/utils/persistence/keyDerivation.test.ts +306 -0
  823. package/src/utils/persistence/sensitiveFieldDetection.test.ts +271 -0
  824. package/src/utils/persistence/sensitiveFieldDetection.ts +2 -2
  825. package/src/utils/request-deduplication.test.ts +349 -0
  826. package/src/utils/request-deduplication.ts +6 -4
  827. package/src/utils/sanitization.unit.test.ts +346 -0
  828. package/src/utils/schemaUtils.unit.test.ts +441 -0
  829. package/src/utils/secureDataAccess.unit.test.ts +334 -0
  830. package/src/utils/secureErrors.unit.test.ts +390 -0
  831. package/src/utils/secureStorage.unit.test.ts +289 -0
  832. package/src/utils/security/auth-utils.ts +38 -27
  833. package/src/utils/security/secureDataAccess.test.ts +22 -191
  834. package/src/utils/security/secureDataAccess.ts +241 -281
  835. package/src/utils/security/secureErrors.test.ts +163 -0
  836. package/src/utils/security/secureStorage.test.ts +156 -0
  837. package/src/utils/security/secureStorage.ts +1 -1
  838. package/src/utils/security/security.test.ts +212 -0
  839. package/src/utils/security/security.ts +15 -18
  840. package/src/utils/security/securityMonitor.test.ts +90 -0
  841. package/src/utils/security/securityMonitor.ts +1 -1
  842. package/src/utils/security.unit.test.ts +155 -0
  843. package/src/utils/securityMonitor.unit.test.ts +276 -0
  844. package/src/utils/sessionTracking.unit.test.ts +218 -0
  845. package/src/utils/storage/config.unit.test.ts +239 -0
  846. package/src/utils/storage/helpers.test.ts +769 -456
  847. package/src/utils/storage/helpers.ts +174 -253
  848. package/src/utils/storage/index.unit.test.ts +68 -0
  849. package/src/utils/storage/storageUtils.ts +32 -0
  850. package/src/utils/storage/types.ts +9 -2
  851. package/src/utils/supabase/createBaseClient.test.ts +201 -0
  852. package/src/utils/supabase/createBaseClient.ts +2 -1
  853. package/src/utils/timezone/timezone.test.ts +26 -44
  854. package/src/utils/timezone.test.ts +345 -0
  855. package/src/utils/validation/common.test.ts +115 -0
  856. package/src/utils/validation/csrf.test.ts +198 -0
  857. package/src/utils/validation/csrf.ts +42 -41
  858. package/src/utils/validation/htmlSanitization.ts +27 -31
  859. package/src/utils/validation/htmlSanitization.unit.test.ts +618 -0
  860. package/src/utils/validation/passwordSchema.test.ts +164 -0
  861. package/src/utils/validation/schema.test.ts +127 -0
  862. package/src/utils/validation/schema.ts +6 -3
  863. package/src/utils/validation/sqlInjectionProtection.test.ts +165 -0
  864. package/src/utils/validation/sqlInjectionProtection.ts +2 -2
  865. package/src/utils/validation/user.test.ts +173 -0
  866. package/src/utils/validation/validation.test.ts +197 -0
  867. package/src/utils/validation/validationUtils.test.ts +294 -0
  868. package/src/utils/validation.unit.test.ts +307 -0
  869. package/src/utils/validationUtils.unit.test.ts +558 -0
  870. package/src/vite-env.d.ts +6 -0
  871. package/dist/AuthService-DmfO5rGS.d.ts +0 -524
  872. package/dist/DataTable-DRUIgtUH.d.ts +0 -166
  873. package/dist/DataTable-SOAFXIWY.js +0 -15
  874. package/dist/PublicPageProvider-CIGSujI2.d.ts +0 -4147
  875. package/dist/UnifiedAuthProvider-7SNDOWYD.js +0 -7
  876. package/dist/UnifiedAuthProvider-CKvHP1MK.d.ts +0 -139
  877. package/dist/api-7P7DI652.js +0 -4
  878. package/dist/audit-MYQXYZFU.js +0 -3
  879. package/dist/auth-BZOJqrdd.d.ts +0 -49
  880. package/dist/chunk-4DDCYDQ3.js +0 -544
  881. package/dist/chunk-5HNSDQWH.js +0 -5046
  882. package/dist/chunk-5W2A3DRC.js +0 -164
  883. package/dist/chunk-6GLLNA6U.js +0 -31
  884. package/dist/chunk-7ILTDCL2.js +0 -80
  885. package/dist/chunk-A3W6LW53.js +0 -70
  886. package/dist/chunk-AHU7G2R5.js +0 -423
  887. package/dist/chunk-C7ZQ5O4C.js +0 -481
  888. package/dist/chunk-EF2UGZWY.js +0 -611
  889. package/dist/chunk-FEJLJNWA.js +0 -181
  890. package/dist/chunk-FYHN4DD5.js +0 -415
  891. package/dist/chunk-GS5672WG.js +0 -2003
  892. package/dist/chunk-HF6O3O37.js +0 -187
  893. package/dist/chunk-J2U36LHD.js +0 -8517
  894. package/dist/chunk-LX6U42O3.js +0 -2177
  895. package/dist/chunk-MPBLMWVR.js +0 -2161
  896. package/dist/chunk-OJ4SKRSV.js +0 -105
  897. package/dist/chunk-S6ZQKDY6.js +0 -62
  898. package/dist/chunk-S7DKJPLT.js +0 -699
  899. package/dist/chunk-T5CVK4R3.js +0 -2816
  900. package/dist/chunk-TTRFSOKR.js +0 -121
  901. package/dist/chunk-Z2FNRKF3.js +0 -994
  902. package/dist/database.generated-DT8JTZiP.d.ts +0 -9406
  903. package/dist/event-CW5YB_2p.d.ts +0 -239
  904. package/dist/file-reference-BavO2eQj.d.ts +0 -148
  905. package/dist/functions-lBy5L2ry.d.ts +0 -208
  906. package/dist/timezone-0AyangqX.d.ts +0 -697
  907. package/dist/types-BeoeWV5I.d.ts +0 -110
  908. package/dist/types-DXstZpNI.d.ts +0 -614
  909. package/dist/types-t9H8qKRw.d.ts +0 -55
  910. package/dist/usePublicRouteParams-DQLrDqDb.d.ts +0 -876
  911. package/dist/useToast-AyaT-x7p.d.ts +0 -68
  912. package/dist/validation-643vUDZW.d.ts +0 -177
  913. package/scripts/build-docs-incremental.js +0 -179
  914. package/scripts/eslint-audit.cjs +0 -123
  915. package/scripts/generate-docs.js +0 -157
  916. package/scripts/install-cursor-rules.cjs +0 -255
  917. package/scripts/install-eslint-config.cjs +0 -349
  918. package/scripts/setup-build-cache.js +0 -73
  919. package/scripts/validate-pre-publish.js +0 -145
  920. package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +0 -260
  921. package/src/__tests__/helpers/__tests__/optimized-test-setup.test.ts +0 -224
  922. package/src/__tests__/helpers/__tests__/supabaseMock.test.ts +0 -273
  923. package/src/__tests__/helpers/__tests__/test-providers.test.tsx +0 -99
  924. package/src/__tests__/helpers/__tests__/test-utils.test.tsx +0 -448
  925. package/src/__tests__/helpers/__tests__/timer-utils.test.ts +0 -371
  926. package/src/__tests__/hooks/usePermissions.test.ts +0 -268
  927. package/src/__tests__/integration/UserProfile.test.tsx +0 -124
  928. package/src/__tests__/public-recipe-view.test.ts +0 -228
  929. package/src/__tests__/rbac/PagePermissionGuard.test.tsx +0 -220
  930. package/src/__tests__/rls-policies.test.ts +0 -471
  931. package/src/components/DataTable/__tests__/DataTable.comprehensive.test.tsx +0 -759
  932. package/src/components/DataTable/__tests__/DataTable.default-state.test.tsx +0 -524
  933. package/src/components/DataTable/__tests__/DataTable.export.test.tsx +0 -705
  934. package/src/components/DataTable/__tests__/DataTable.grouping-aggregation.test.tsx +0 -658
  935. package/src/components/DataTable/__tests__/DataTable.hooks.test.tsx +0 -192
  936. package/src/components/DataTable/__tests__/DataTable.select-label-display.test.tsx +0 -483
  937. package/src/components/DataTable/__tests__/DataTable.test.tsx +0 -876
  938. package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +0 -220
  939. package/src/components/DataTable/__tests__/DataTableCore.test.tsx +0 -1474
  940. package/src/components/DataTable/__tests__/README.md +0 -145
  941. package/src/components/DataTable/__tests__/a11y.basic.test.tsx +0 -788
  942. package/src/components/DataTable/__tests__/keyboard.test.tsx +0 -756
  943. package/src/components/DataTable/__tests__/mocks/MockRBACProvider.tsx +0 -66
  944. package/src/components/DataTable/__tests__/pagination.modes.test.tsx +0 -730
  945. package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx +0 -325
  946. package/src/components/DataTable/__tests__/styles.test.ts +0 -382
  947. package/src/components/DataTable/__tests__/test-utils/dataFactories.ts +0 -103
  948. package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +0 -380
  949. package/src/components/DataTable/__tests__/test-utils.ts +0 -94
  950. package/src/components/DataTable/components/AccessDeniedPage.tsx +0 -159
  951. package/src/components/DataTable/components/ActionButtons.tsx +0 -190
  952. package/src/components/DataTable/components/BulkOperationsDropdown.tsx +0 -160
  953. package/src/components/DataTable/components/ColumnFilter.tsx +0 -118
  954. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +0 -114
  955. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +0 -225
  956. package/src/components/DataTable/components/DataTableLayout.tsx +0 -573
  957. package/src/components/DataTable/components/DataTableModals.tsx +0 -245
  958. package/src/components/DataTable/components/DataTableToolbar.tsx +0 -271
  959. package/src/components/DataTable/components/EditFields.tsx +0 -327
  960. package/src/components/DataTable/components/EditableRow.tsx +0 -462
  961. package/src/components/DataTable/components/EmptyState.tsx +0 -79
  962. package/src/components/DataTable/components/FilterRow.tsx +0 -141
  963. package/src/components/DataTable/components/LoadingState.tsx +0 -17
  964. package/src/components/DataTable/components/PaginationControls.tsx +0 -289
  965. package/src/components/DataTable/components/RowComponent.tsx +0 -403
  966. package/src/components/DataTable/components/SortIndicator.tsx +0 -50
  967. package/src/components/DataTable/components/UnifiedTableBody.tsx +0 -355
  968. package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +0 -657
  969. package/src/components/DataTable/components/__tests__/ActionButtons.test.tsx +0 -913
  970. package/src/components/DataTable/components/__tests__/BulkOperationsDropdown.test.tsx +0 -572
  971. package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +0 -612
  972. package/src/components/DataTable/components/__tests__/ColumnVisibilityDropdown.test.tsx +0 -708
  973. package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +0 -479
  974. package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +0 -475
  975. package/src/components/DataTable/components/__tests__/DataTableToolbar.test.tsx +0 -157
  976. package/src/components/DataTable/components/__tests__/EditableRow.test.tsx +0 -1061
  977. package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +0 -437
  978. package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +0 -474
  979. package/src/components/DataTable/components/__tests__/GroupingDropdown.test.tsx +0 -617
  980. package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +0 -1093
  981. package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +0 -139
  982. package/src/components/DataTable/components/__tests__/PaginationControls.test.tsx +0 -519
  983. package/src/components/DataTable/components/__tests__/UnifiedTableBody.test.tsx +0 -1004
  984. package/src/components/DataTable/components/cellValueUtils.ts +0 -40
  985. package/src/components/DataTable/components/hooks/useImportModalFocus.ts +0 -53
  986. package/src/components/DataTable/components/hooks/usePermissionTracking.ts +0 -122
  987. package/src/components/DataTable/components/index.ts +0 -16
  988. package/src/components/DataTable/context/__tests__/DataTableContext.test.tsx +0 -342
  989. package/src/components/DataTable/core/ActionManager.ts +0 -235
  990. package/src/components/DataTable/core/ColumnManager.ts +0 -205
  991. package/src/components/DataTable/core/DataManager.ts +0 -188
  992. package/src/components/DataTable/core/LocalDataAdapter.ts +0 -274
  993. package/src/components/DataTable/core/PluginRegistry.ts +0 -229
  994. package/src/components/DataTable/core/StateManager.ts +0 -312
  995. package/src/components/DataTable/core/__tests__/ActionManager.test.ts +0 -123
  996. package/src/components/DataTable/core/__tests__/ColumnFactory.test.ts +0 -305
  997. package/src/components/DataTable/core/__tests__/ColumnManager.test.ts +0 -84
  998. package/src/components/DataTable/core/__tests__/DataManager.test.ts +0 -115
  999. package/src/components/DataTable/core/__tests__/LocalDataAdapter.test.ts +0 -100
  1000. package/src/components/DataTable/core/__tests__/PluginRegistry.test.ts +0 -120
  1001. package/src/components/DataTable/core/__tests__/StateManager.test.ts +0 -104
  1002. package/src/components/DataTable/core/index.ts +0 -1
  1003. package/src/components/DataTable/core/interfaces.ts +0 -338
  1004. package/src/components/DataTable/hooks/__tests__/useColumnOrderPersistence.test.ts +0 -521
  1005. package/src/components/DataTable/hooks/__tests__/useColumnVisibilityPersistence.test.ts +0 -167
  1006. package/src/components/DataTable/hooks/__tests__/useDataTableConfiguration.test.ts +0 -124
  1007. package/src/components/DataTable/hooks/__tests__/useDataTableDataPipeline.test.ts +0 -117
  1008. package/src/components/DataTable/hooks/__tests__/useDataTablePermissions.test.ts +0 -102
  1009. package/src/components/DataTable/hooks/__tests__/useDataTableState.test.ts +0 -596
  1010. package/src/components/DataTable/hooks/__tests__/useEffectiveColumnOrder.test.ts +0 -53
  1011. package/src/components/DataTable/hooks/__tests__/useHierarchicalState.test.ts +0 -214
  1012. package/src/components/DataTable/hooks/__tests__/useTableColumns.test.ts +0 -448
  1013. package/src/components/DataTable/hooks/index.ts +0 -13
  1014. package/src/components/DataTable/types.ts +0 -761
  1015. package/src/components/DataTable/utils/__tests__/a11yUtils.test.ts +0 -612
  1016. package/src/components/DataTable/utils/__tests__/columnUtils.test.ts +0 -94
  1017. package/src/components/DataTable/utils/__tests__/errorHandling.test.ts +0 -266
  1018. package/src/components/DataTable/utils/__tests__/exportUtils.test.ts +0 -954
  1019. package/src/components/DataTable/utils/__tests__/flexibleImport.test.ts +0 -573
  1020. package/src/components/DataTable/utils/__tests__/hierarchicalSorting.test.ts +0 -247
  1021. package/src/components/DataTable/utils/__tests__/hierarchicalUtils.test.ts +0 -570
  1022. package/src/components/DataTable/utils/__tests__/performanceUtils.test.ts +0 -470
  1023. package/src/components/DataTable/utils/__tests__/rowUtils.test.ts +0 -251
  1024. package/src/components/DataTable/utils/__tests__/selectFieldUtils.test.ts +0 -207
  1025. package/src/components/DataTable/utils/index.ts +0 -10
  1026. package/src/components/PublicLayout/index.ts +0 -32
  1027. package/src/components/Select/hooks/useSelectEvents.ts +0 -87
  1028. package/src/components/Select/hooks/useSelectSearch.ts +0 -91
  1029. package/src/components/Select/hooks/useSelectState.ts +0 -104
  1030. package/src/components/Select/utils/text.ts +0 -26
  1031. package/src/hooks/__tests__/ServiceHooks.test.tsx +0 -615
  1032. package/src/hooks/__tests__/hooks.integration.test.tsx +0 -607
  1033. package/src/hooks/__tests__/index.unit.test.ts +0 -220
  1034. package/src/hooks/__tests__/useApiFetch.unit.test.ts +0 -111
  1035. package/src/hooks/__tests__/useAppConfig.unit.test.ts +0 -347
  1036. package/src/hooks/__tests__/useComponentPerformance.unit.test.tsx +0 -144
  1037. package/src/hooks/__tests__/useDataTablePerformance.unit.test.ts +0 -776
  1038. package/src/hooks/__tests__/useDataTableState.test.ts +0 -76
  1039. package/src/hooks/__tests__/useDebounce.unit.test.ts +0 -82
  1040. package/src/hooks/__tests__/useEvents.unit.test.ts +0 -252
  1041. package/src/hooks/__tests__/useFileDisplay.unit.test.ts +0 -1112
  1042. package/src/hooks/__tests__/useFileUrl.unit.test.ts +0 -916
  1043. package/src/hooks/__tests__/useFileUrlCache.test.ts +0 -129
  1044. package/src/hooks/__tests__/useFocusManagement.unit.test.ts +0 -230
  1045. package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +0 -828
  1046. package/src/hooks/__tests__/useFormDialog.test.ts +0 -478
  1047. package/src/hooks/__tests__/useInactivityTracker.unit.test.ts +0 -446
  1048. package/src/hooks/__tests__/useIsMobile.unit.test.ts +0 -317
  1049. package/src/hooks/__tests__/useKeyboardShortcuts.unit.test.ts +0 -910
  1050. package/src/hooks/__tests__/useOrganisationPermissions.unit.test.tsx +0 -294
  1051. package/src/hooks/__tests__/useOrganisationSecurity.unit.test.tsx +0 -961
  1052. package/src/hooks/__tests__/useOrganisations.unit.test.ts +0 -369
  1053. package/src/hooks/__tests__/usePerformanceMonitor.unit.test.ts +0 -694
  1054. package/src/hooks/__tests__/usePermissionCache.simple.test.ts +0 -192
  1055. package/src/hooks/__tests__/usePermissionCache.unit.test.ts +0 -741
  1056. package/src/hooks/__tests__/usePreventTabReload.test.ts +0 -88
  1057. package/src/hooks/__tests__/usePublicEvent.simple.test.ts +0 -785
  1058. package/src/hooks/__tests__/usePublicEvent.test.ts +0 -678
  1059. package/src/hooks/__tests__/usePublicEvent.unit.test.ts +0 -630
  1060. package/src/hooks/__tests__/usePublicFileDisplay.test.ts +0 -951
  1061. package/src/hooks/__tests__/usePublicRouteParams.unit.test.ts +0 -443
  1062. package/src/hooks/__tests__/useQueryCache.test.ts +0 -144
  1063. package/src/hooks/__tests__/useRBAC.unit.test.ts +0 -236
  1064. package/src/hooks/__tests__/useSessionDraft.test.ts +0 -163
  1065. package/src/hooks/__tests__/useSessionRestoration.unit.test.tsx +0 -390
  1066. package/src/hooks/__tests__/useStorage.unit.test.ts +0 -751
  1067. package/src/hooks/__tests__/useToast.unit.test.tsx +0 -481
  1068. package/src/hooks/__tests__/useZodForm.unit.test.tsx +0 -37
  1069. package/src/hooks/public/index.ts +0 -36
  1070. package/src/hooks/public/usePublicFileDisplay.ts +0 -504
  1071. package/src/hooks/useFileDisplay.ts +0 -715
  1072. package/src/providers/OrganisationProvider.tsx +0 -92
  1073. package/src/providers/__tests__/AuthProvider.test.tsx +0 -287
  1074. package/src/providers/__tests__/EventProvider.test.tsx +0 -551
  1075. package/src/providers/__tests__/InactivityProvider.test-helper.tsx +0 -65
  1076. package/src/providers/__tests__/InactivityProvider.test.tsx +0 -572
  1077. package/src/providers/__tests__/OrganisationProvider.test.tsx +0 -617
  1078. package/src/providers/__tests__/ProviderLifecycle.test.tsx +0 -424
  1079. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +0 -596
  1080. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +0 -263
  1081. package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +0 -294
  1082. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +0 -434
  1083. package/src/rbac/__tests__/auth-rbac-security.integration.test.tsx +0 -313
  1084. package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +0 -486
  1085. package/src/rbac/__tests__/cache-invalidation.test.ts +0 -399
  1086. package/src/rbac/__tests__/engine.comprehensive.test.ts +0 -813
  1087. package/src/rbac/__tests__/isSuperAdmin.real.test.ts +0 -82
  1088. package/src/rbac/__tests__/rbac-core.test.tsx +0 -276
  1089. package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +0 -392
  1090. package/src/rbac/__tests__/rbac-engine-simplified.test.ts +0 -258
  1091. package/src/rbac/__tests__/rbac-functions.test.ts +0 -647
  1092. package/src/rbac/__tests__/rbac-integration.test.ts +0 -524
  1093. package/src/rbac/__tests__/rbac-role-isolation.test.ts +0 -456
  1094. package/src/rbac/__tests__/scenarios.user-role.test.tsx +0 -282
  1095. package/src/rbac/audit-enhanced.ts +0 -384
  1096. package/src/rbac/compliance/database-validator.ts +0 -165
  1097. package/src/rbac/compliance/index.ts +0 -48
  1098. package/src/rbac/compliance/pattern-detector.ts +0 -553
  1099. package/src/rbac/compliance/quick-fix-suggestions.ts +0 -209
  1100. package/src/rbac/compliance/runtime-compliance.ts +0 -99
  1101. package/src/rbac/compliance/setup-validator.ts +0 -131
  1102. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +0 -975
  1103. package/src/rbac/components/__tests__/PagePermissionGuard.performance.test.tsx +0 -248
  1104. package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +0 -242
  1105. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +0 -1107
  1106. package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +0 -184
  1107. package/src/rbac/components/index.ts +0 -26
  1108. package/src/rbac/hooks/__tests__/usePermissions.integration.test.ts +0 -432
  1109. package/src/rbac/hooks/__tests__/useSecureSupabase.test.ts +0 -579
  1110. package/src/rbac/hooks/index.ts +0 -34
  1111. package/src/rbac/hooks/permissions/index.ts +0 -4
  1112. package/src/rbac/hooks/useRBAC.simple.test.ts +0 -95
  1113. package/src/rbac/utils/__tests__/contextValidator.test.ts +0 -128
  1114. package/src/rbac/utils/__tests__/deep-equal.test.ts +0 -53
  1115. package/src/rbac/utils/__tests__/eventContext.test.ts +0 -433
  1116. package/src/rbac/utils/__tests__/eventContext.unit.test.ts +0 -490
  1117. package/src/services/__tests__/AuthService.restoreSession.test.ts +0 -39
  1118. package/src/services/__tests__/AuthService.test.ts +0 -1332
  1119. package/src/services/__tests__/BaseService.test.ts +0 -314
  1120. package/src/services/__tests__/EventService.eventColours.test.ts +0 -76
  1121. package/src/services/__tests__/EventService.test.ts +0 -1025
  1122. package/src/services/__tests__/InactivityService.lifecycle.test.ts +0 -411
  1123. package/src/services/__tests__/InactivityService.test.ts +0 -654
  1124. package/src/services/__tests__/OrganisationService.pagination.test.ts +0 -409
  1125. package/src/services/__tests__/OrganisationService.test.ts +0 -1176
  1126. package/src/theming/__tests__/parseEventColours.test.ts +0 -321
  1127. package/src/theming/__tests__/runtime.test.ts +0 -569
  1128. package/src/types/__tests__/file-reference.test.ts +0 -447
  1129. package/src/types/__tests__/guards.test.ts +0 -246
  1130. package/src/types/__tests__/organisation.roles.test.ts +0 -55
  1131. package/src/types/__tests__/organisation.test.ts +0 -1133
  1132. package/src/types/__tests__/theme.test.ts +0 -830
  1133. package/src/types/__tests__/type-validation.test.ts +0 -526
  1134. package/src/types/__tests__/validation.test.ts +0 -731
  1135. package/src/utils/__tests__/appConfig.unit.test.ts +0 -55
  1136. package/src/utils/__tests__/audit.unit.test.ts +0 -69
  1137. package/src/utils/__tests__/auth-utils.unit.test.ts +0 -70
  1138. package/src/utils/__tests__/bundleAnalysis.unit.test.ts +0 -339
  1139. package/src/utils/__tests__/cn.unit.test.ts +0 -34
  1140. package/src/utils/__tests__/debugLogger.test.ts +0 -417
  1141. package/src/utils/__tests__/deviceFingerprint.unit.test.ts +0 -818
  1142. package/src/utils/__tests__/dynamicUtils.unit.test.ts +0 -318
  1143. package/src/utils/__tests__/formatDate.unit.test.ts +0 -109
  1144. package/src/utils/__tests__/formatting.unit.test.ts +0 -99
  1145. package/src/utils/__tests__/index.unit.test.ts +0 -251
  1146. package/src/utils/__tests__/lazyLoad.unit.test.tsx +0 -321
  1147. package/src/utils/__tests__/logger.unit.test.ts +0 -398
  1148. package/src/utils/__tests__/organisationContext.unit.test.ts +0 -191
  1149. package/src/utils/__tests__/performanceBenchmark.test.ts +0 -175
  1150. package/src/utils/__tests__/performanceBudgets.unit.test.ts +0 -253
  1151. package/src/utils/__tests__/permissionTypes.unit.test.ts +0 -250
  1152. package/src/utils/__tests__/permissionUtils.unit.test.ts +0 -362
  1153. package/src/utils/__tests__/sanitization.unit.test.ts +0 -346
  1154. package/src/utils/__tests__/schemaUtils.unit.test.ts +0 -441
  1155. package/src/utils/__tests__/secureDataAccess.unit.test.ts +0 -335
  1156. package/src/utils/__tests__/secureErrors.unit.test.ts +0 -390
  1157. package/src/utils/__tests__/secureStorage.unit.test.ts +0 -289
  1158. package/src/utils/__tests__/security.unit.test.ts +0 -149
  1159. package/src/utils/__tests__/securityMonitor.unit.test.ts +0 -276
  1160. package/src/utils/__tests__/sessionTracking.unit.test.ts +0 -218
  1161. package/src/utils/__tests__/timezone.test.ts +0 -345
  1162. package/src/utils/__tests__/validation.unit.test.ts +0 -308
  1163. package/src/utils/__tests__/validationUtils.unit.test.ts +0 -555
  1164. package/src/utils/app/appNameResolver.simple.test.ts +0 -212
  1165. package/src/utils/file-reference/__tests__/file-reference.test.ts +0 -875
  1166. package/src/utils/google-places/index.ts +0 -26
  1167. package/src/utils/location/index.ts +0 -16
  1168. package/src/utils/persistence/__tests__/keyDerivation.test.ts +0 -135
  1169. package/src/utils/persistence/__tests__/sensitiveFieldDetection.test.ts +0 -123
  1170. package/src/utils/storage/__tests__/helpers.unit.test.ts +0 -332
  1171. package/src/utils/storage/__tests__/index.unit.test.ts +0 -16
  1172. package/src/utils/storage/index.ts +0 -67
  1173. package/src/utils/timezone/index.ts +0 -17
  1174. package/src/utils/validation/__tests__/csrf.test.ts +0 -105
  1175. package/src/utils/validation/__tests__/htmlSanitization.unit.test.ts +0 -598
  1176. package/src/utils/validation/__tests__/sqlInjectionProtection.test.ts +0 -92
  1177. package/src/utils/validation/__tests__/validationUtils.test.ts +0 -72
  1178. package/src/utils/validation/index.ts +0 -73
  1179. /package/src/components/DataTable/{components/__tests__ → ui}/COVERAGE_NOTE.md +0 -0
  1180. /package/src/components/DataTable/utils/{__tests__/COVERAGE_NOTE.md → COVERAGE_NOTE.md} +0 -0
  1181. /package/src/providers/{__tests__/README.md → README.md} +0 -0
  1182. /package/src/types/{__tests__/README.md → README.md} +0 -0
@@ -1,1093 +0,0 @@
1
- /**
2
- * @file Import Modal Component Tests
3
- * @package @jmruthers/pace-core
4
- * @module Components/DataTable/Components/__tests__
5
- * @since 0.4.0
6
- *
7
- * Comprehensive test suite for ImportModal component following testing guidelines.
8
- * Tests cover all major functionality, edge cases, and user interactions.
9
- */
10
-
11
- import React from 'react';
12
- import { render, screen, waitFor } from '@testing-library/react';
13
- import userEvent from '@testing-library/user-event';
14
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
15
- import { ImportModal } from '../ImportModal';
16
-
17
- // Helper function to wait for dialog to be accessible
18
- // Native dialog elements are only accessible after showModal() completes
19
- // In test environments, we use querySelector as fallback since getByRole may not work
20
- // Note: In test environments (jsdom), dialog.open may not be set even when dialog is rendered
21
- // Also note: Dialog uses requestAnimationFrame before showModal(), so we need to wait for content
22
- const waitForDialog = async (): Promise<HTMLElement> => {
23
- return await waitFor(
24
- () => {
25
- // Try getByRole first (works in browsers with full dialog support)
26
- try {
27
- const dialog = screen.getByRole('dialog');
28
- expect(dialog).toBeInTheDocument();
29
- return dialog;
30
- } catch (e) {
31
- // Fallback: use querySelector for test environments that don't fully support dialog accessibility
32
- const dialog = document.querySelector('dialog[role="dialog"]') as HTMLDialogElement;
33
- if (!dialog) {
34
- throw new Error('Dialog not found in DOM');
35
- }
36
- // In test environments, dialog.open may not be set even when dialog is rendered
37
- // Just check that dialog exists in DOM - that's sufficient for testing
38
- return dialog;
39
- }
40
- },
41
- { timeout: 5000 }
42
- );
43
- };
44
-
45
- // Helper function to find buttons in dialogs (more reliable than getByRole in test environments)
46
- const findButtonByText = (text: string | RegExp): HTMLButtonElement | null => {
47
- // Try getByRole first
48
- try {
49
- const button = screen.getByRole('button', { name: text });
50
- return button as HTMLButtonElement;
51
- } catch (e) {
52
- // Fallback: search all buttons by text content
53
- const buttons = Array.from(document.querySelectorAll('button'));
54
- const regex = typeof text === 'string' ? new RegExp(text, 'i') : text;
55
- return buttons.find(btn => regex.test(btn.textContent || '')) as HTMLButtonElement || null;
56
- }
57
- };
58
-
59
- // Helper function to upload a file and wait for preview to appear
60
- const uploadFileAndWaitForPreview = async (user: ReturnType<typeof userEvent.setup>, file: File) => {
61
- // Wait for dialog and file input
62
- await waitForDialog();
63
- await waitFor(() => {
64
- const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
65
- expect(fileInput).toBeInTheDocument();
66
- }, { timeout: 5000 });
67
-
68
- const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
69
-
70
- // Upload the file
71
- await user.upload(fileInput, file);
72
-
73
- // Wait for preview table to appear (file processing is async)
74
- // Try multiple ways to find the table since queryByRole may not work in all test environments
75
- await waitFor(() => {
76
- const table = screen.queryByRole('table') ||
77
- document.querySelector('table') ||
78
- document.querySelector('table.min-w-full');
79
- expect(table).toBeInTheDocument();
80
- }, { timeout: 10000 });
81
- };
82
-
83
- // Mock Button component
84
- vi.mock('../../Button/Button', () => ({
85
- Button: ({ children, onClick, variant, size, disabled, className }: any) => (
86
- <button
87
- onClick={onClick}
88
- disabled={disabled}
89
- data-variant={variant}
90
- data-size={size}
91
- className={className}
92
- >
93
- {children}
94
- </button>
95
- ),
96
- }));
97
-
98
- // Mock Input component
99
- vi.mock('../../Input/Input', () => ({
100
- Input: React.forwardRef(({ type, accept, onChange, className, ...props }: any, ref: any) => (
101
- <input
102
- ref={ref}
103
- type={type}
104
- accept={accept}
105
- onChange={onChange}
106
- className={className}
107
- {...props}
108
- />
109
- )),
110
- }));
111
-
112
- // Mock lucide-react icons
113
- vi.mock('lucide-react', () => ({
114
- Upload: ({ className }: { className?: string }) => (
115
- <span data-testid="upload-icon" className={className}>Upload</span>
116
- ),
117
- FileText: ({ className }: { className?: string }) => (
118
- <span data-testid="file-text-icon" className={className}>File</span>
119
- ),
120
- AlertCircle: ({ className }: { className?: string }) => (
121
- <span data-testid="alert-circle-icon" className={className}>Alert</span>
122
- ),
123
- X: ({ className }: { className?: string }) => (
124
- <span data-testid="x-icon" className={className}>X</span>
125
- ),
126
- }));
127
-
128
- // Mock logger
129
- vi.mock('../../../utils/core/logger', () => ({
130
- createLogger: () => ({
131
- debug: vi.fn(),
132
- info: vi.fn(),
133
- warn: vi.fn(),
134
- error: vi.fn(),
135
- }),
136
- }));
137
-
138
- describe('[component] ImportModal', () => {
139
- const baseProps = {
140
- isOpen: true,
141
- onClose: vi.fn(),
142
- onImport: vi.fn(),
143
- };
144
-
145
- const createCSVFile = (content: string, filename = 'test.csv'): File => {
146
- const blob = new Blob([content], { type: 'text/csv' });
147
- const file = new File([blob], filename, { type: 'text/csv' });
148
- // Store content for File.text() mock to access
149
- (file as any)._content = content;
150
- return file;
151
- };
152
-
153
- beforeEach(() => {
154
- vi.clearAllMocks();
155
-
156
- // Mock showModal for dialog elements (needed for test environments)
157
- HTMLDialogElement.prototype.showModal = vi.fn(function(this: HTMLDialogElement) {
158
- this.setAttribute('open', '');
159
- this.dispatchEvent(new Event('show', { bubbles: true }));
160
- });
161
- HTMLDialogElement.prototype.close = vi.fn(function(this: HTMLDialogElement) {
162
- this.removeAttribute('open');
163
- this.dispatchEvent(new Event('close', { bubbles: true }));
164
- });
165
-
166
- // Mock File.text() method for jsdom compatibility
167
- // File.text() reads the file content asynchronously
168
- // In tests, we read directly from the Blob content synchronously
169
- if (!File.prototype.text) {
170
- Object.defineProperty(File.prototype, 'text', {
171
- writable: true,
172
- configurable: true,
173
- value: async function(this: File) {
174
- // For test files, read from the stored content or from the Blob
175
- const file = this as any;
176
- if (file._content) {
177
- // Use stored content if available
178
- return Promise.resolve(file._content);
179
- }
180
- // Otherwise, try to read from Blob using FileReader
181
- return new Promise((resolve, reject) => {
182
- const reader = new FileReader();
183
- reader.onload = (e) => {
184
- resolve(e.target?.result as string);
185
- };
186
- reader.onerror = () => {
187
- reject(new Error('Failed to read file'));
188
- };
189
- // Read the file as text
190
- reader.readAsText(this);
191
- });
192
- },
193
- });
194
- }
195
- });
196
-
197
- afterEach(() => {
198
- vi.clearAllMocks();
199
- });
200
-
201
- describe('Rendering', () => {
202
- it('returns null when modal is closed', () => {
203
- const { container } = render(
204
- <ImportModal {...baseProps} isOpen={false} />
205
- );
206
- expect(container.firstChild).toBeNull();
207
- });
208
-
209
- it('renders modal when open', async () => {
210
- render(<ImportModal {...baseProps} />);
211
-
212
- // Wait for dialog to be accessible
213
- await waitForDialog();
214
- // Check for content instead of testids
215
- expect(screen.getByText('Import Data')).toBeInTheDocument();
216
- });
217
-
218
- it('renders default title', async () => {
219
- render(<ImportModal {...baseProps} />);
220
-
221
- // Wait for dialog content to be rendered (showModal is async via requestAnimationFrame)
222
- await waitFor(() => {
223
- // Try by role first, fallback to text content
224
- try {
225
- expect(screen.getByRole('heading', { name: 'Import Data' })).toBeInTheDocument();
226
- } catch (e) {
227
- expect(screen.getByText('Import Data')).toBeInTheDocument();
228
- }
229
- }, { timeout: 5000 });
230
- });
231
-
232
- it('renders custom title from config', async () => {
233
- render(
234
- <ImportModal
235
- {...baseProps}
236
- config={{ title: 'Custom Import Title' }}
237
- />
238
- );
239
-
240
- // Wait for dialog content to be rendered (showModal is async via requestAnimationFrame)
241
- await waitFor(() => {
242
- // Try by role first, fallback to text content
243
- try {
244
- expect(screen.getByRole('heading', { name: 'Custom Import Title' })).toBeInTheDocument();
245
- } catch (e) {
246
- expect(screen.getByText('Custom Import Title')).toBeInTheDocument();
247
- }
248
- }, { timeout: 5000 });
249
- });
250
-
251
- it('renders default description', async () => {
252
- render(<ImportModal {...baseProps} />);
253
-
254
- // Wait for dialog to be accessible
255
- await waitForDialog();
256
- // Description is rendered as p in DialogHeader
257
- expect(screen.getByText('Upload a CSV file to import multiple records at once.')).toBeInTheDocument();
258
- });
259
-
260
- it('renders custom description from config', async () => {
261
- render(
262
- <ImportModal
263
- {...baseProps}
264
- config={{ description: 'Custom description' }}
265
- />
266
- );
267
-
268
- // Wait for dialog to be accessible
269
- await waitForDialog();
270
- // Description is rendered as p in DialogHeader
271
- expect(screen.getByText('Custom description')).toBeInTheDocument();
272
- });
273
-
274
- it('renders file upload area', async () => {
275
- render(<ImportModal {...baseProps} />);
276
-
277
- // Wait for dialog title first (most reliable indicator dialog is rendered)
278
- // Use getByRole for heading to avoid multiple matches (h2 and button both have "Import Data")
279
- await waitFor(() => {
280
- try {
281
- expect(screen.getByRole('heading', { name: 'Import Data' })).toBeInTheDocument();
282
- } catch (e) {
283
- // Fallback: check if any element with "Import Data" exists
284
- const elements = screen.getAllByText('Import Data');
285
- expect(elements.length).toBeGreaterThan(0);
286
- }
287
- }, { timeout: 5000 });
288
-
289
- // Then check for upload area text and button (use querySelector as fallback for buttons in dialogs)
290
- await waitFor(() => {
291
- expect(screen.getByText(/choose a csv file/i)).toBeInTheDocument();
292
- const selectFileButton = findButtonByText(/select file/i);
293
- expect(selectFileButton).toBeInTheDocument();
294
- }, { timeout: 5000 });
295
- });
296
-
297
- it('renders cancel button', async () => {
298
- render(<ImportModal {...baseProps} />);
299
-
300
- // Wait for dialog title first (most reliable indicator dialog is rendered)
301
- // Use getByRole for heading to avoid multiple matches (h2 and button both have "Import Data")
302
- await waitFor(() => {
303
- try {
304
- expect(screen.getByRole('heading', { name: 'Import Data' })).toBeInTheDocument();
305
- } catch (e) {
306
- // Fallback: check if any element with "Import Data" exists
307
- const elements = screen.getAllByText('Import Data');
308
- expect(elements.length).toBeGreaterThan(0);
309
- }
310
- }, { timeout: 5000 });
311
-
312
- // Then check for cancel button (use querySelector as fallback for buttons in dialogs)
313
- await waitFor(() => {
314
- const cancelButton = screen.queryByRole('button', { name: /cancel/i })
315
- || Array.from(document.querySelectorAll('button')).find(btn => btn.textContent?.match(/cancel/i));
316
- expect(cancelButton).toBeInTheDocument();
317
- }, { timeout: 5000 });
318
- });
319
-
320
- it('renders import button', async () => {
321
- render(<ImportModal {...baseProps} />);
322
-
323
- // Wait for dialog title first (most reliable indicator dialog is rendered)
324
- // Use getByRole for heading to avoid multiple matches (h2 and button both have "Import Data")
325
- await waitFor(() => {
326
- try {
327
- expect(screen.getByRole('heading', { name: 'Import Data' })).toBeInTheDocument();
328
- } catch (e) {
329
- // Fallback: check if any element with "Import Data" exists
330
- const elements = screen.getAllByText('Import Data');
331
- expect(elements.length).toBeGreaterThan(0);
332
- }
333
- }, { timeout: 5000 });
334
-
335
- // Then check for import button (use querySelector as fallback for buttons in dialogs)
336
- await waitFor(() => {
337
- const importButton = screen.queryByRole('button', { name: /import/i })
338
- || Array.from(document.querySelectorAll('button')).find(btn => btn.textContent?.match(/^import$/i));
339
- expect(importButton).toBeInTheDocument();
340
- }, { timeout: 5000 });
341
- });
342
- });
343
-
344
- describe('File Selection', () => {
345
- it('displays selected file name', async () => {
346
- const user = userEvent.setup();
347
- const csvContent = 'name,email\nJohn,john@example.com';
348
- const file = createCSVFile(csvContent);
349
-
350
- render(<ImportModal {...baseProps} />);
351
-
352
- const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
353
- await user.upload(fileInput, file);
354
-
355
- await waitFor(() => {
356
- expect(screen.getByText(`Selected: ${file.name}`)).toBeInTheDocument();
357
- });
358
- });
359
-
360
- it('resets file when modal closes and reopens', async () => {
361
- const user = userEvent.setup();
362
- const csvContent = 'name,email\nJohn,john@example.com';
363
- const file = createCSVFile(csvContent);
364
-
365
- const { rerender } = render(<ImportModal {...baseProps} />);
366
-
367
- // Wait for dialog to be accessible first, then wait for file input
368
- await waitForDialog();
369
- await waitFor(() => {
370
- const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
371
- expect(fileInput).toBeInTheDocument();
372
- }, { timeout: 5000 });
373
-
374
- const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
375
- await user.upload(fileInput, file);
376
-
377
- await waitFor(() => {
378
- expect(screen.getByText(`Selected: ${file.name}`)).toBeInTheDocument();
379
- });
380
-
381
- rerender(<ImportModal {...baseProps} isOpen={false} />);
382
-
383
- // Wait for dialog to close
384
- await waitFor(() => {
385
- const dialog = document.querySelector('dialog[role="dialog"]');
386
- expect(dialog).not.toBeInTheDocument();
387
- });
388
-
389
- rerender(<ImportModal {...baseProps} isOpen={true} />);
390
-
391
- // Wait for dialog to reopen
392
- await waitForDialog();
393
-
394
- // File should be reset
395
- expect(screen.queryByText(`Selected: ${file.name}`)).not.toBeInTheDocument();
396
- });
397
- });
398
-
399
- describe('CSV Parsing', () => {
400
- it('parses valid CSV file correctly', async () => {
401
- const user = userEvent.setup();
402
- const csvContent = 'name,email\nJohn,john@example.com\nJane,jane@example.com';
403
- const file = createCSVFile(csvContent);
404
-
405
- render(<ImportModal {...baseProps} />);
406
-
407
- // Upload file and wait for preview
408
- await uploadFileAndWaitForPreview(user, file);
409
-
410
- // Once preview table is visible, check for table headers
411
- await waitFor(() => {
412
- expect(screen.getByText(/name/i)).toBeInTheDocument();
413
- expect(screen.getByText(/email/i)).toBeInTheDocument();
414
- }, { timeout: 5000 });
415
- });
416
-
417
- it('shows preview table with parsed data', async () => {
418
- const user = userEvent.setup();
419
- const csvContent = 'name,email\nJohn,john@example.com\nJane,jane@example.com';
420
- const file = createCSVFile(csvContent);
421
-
422
- render(<ImportModal {...baseProps} />);
423
-
424
- // Wait for dialog to be accessible first, then wait for file input
425
- await waitForDialog();
426
- await waitFor(() => {
427
- const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
428
- expect(fileInput).toBeInTheDocument();
429
- }, { timeout: 5000 });
430
-
431
- const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
432
- await user.upload(fileInput, file);
433
-
434
- // Wait for preview table to appear
435
- await waitFor(() => {
436
- const table = screen.queryByRole('table') ||
437
- document.querySelector('table') ||
438
- document.querySelector('table.min-w-full');
439
- expect(table).toBeInTheDocument();
440
- }, { timeout: 10000 });
441
-
442
- // Then check for data
443
- await waitFor(() => {
444
- expect(screen.getByText('John')).toBeInTheDocument();
445
- expect(screen.getByText('john@example.com')).toBeInTheDocument();
446
- }, { timeout: 2000 });
447
- });
448
-
449
- it('displays total row count', async () => {
450
- const user = userEvent.setup();
451
- const csvContent = 'name,email\nJohn,john@example.com\nJane,jane@example.com\nBob,bob@example.com';
452
- const file = createCSVFile(csvContent);
453
-
454
- render(<ImportModal {...baseProps} />);
455
-
456
- // Wait for dialog to be accessible first, then wait for file input
457
- await waitForDialog();
458
- await waitFor(() => {
459
- const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
460
- expect(fileInput).toBeInTheDocument();
461
- }, { timeout: 5000 });
462
-
463
- const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
464
- await user.upload(fileInput, file);
465
-
466
- // Wait for preview to appear first
467
- await waitFor(() => {
468
- const table = screen.queryByRole('table') ||
469
- document.querySelector('table') ||
470
- document.querySelector('table.min-w-full');
471
- expect(table).toBeInTheDocument();
472
- }, { timeout: 10000 });
473
-
474
- // Then check for total row count
475
- await waitFor(() => {
476
- expect(screen.getByText(/total rows to import: 3/i)).toBeInTheDocument();
477
- }, { timeout: 2000 });
478
- });
479
-
480
- it('handles CSV with quoted values', async () => {
481
- const user = userEvent.setup();
482
- const csvContent = 'name,email\n"John Doe","john@example.com"\n"Jane Smith","jane@example.com"';
483
- const file = createCSVFile(csvContent);
484
-
485
- render(<ImportModal {...baseProps} />);
486
-
487
- // Wait for dialog to be accessible first, then wait for file input
488
- await waitForDialog();
489
- await waitFor(() => {
490
- const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
491
- expect(fileInput).toBeInTheDocument();
492
- }, { timeout: 5000 });
493
-
494
- const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
495
- await user.upload(fileInput, file);
496
-
497
- // Wait for preview table first
498
- await waitFor(() => {
499
- const table = screen.queryByRole('table') ||
500
- document.querySelector('table') ||
501
- document.querySelector('table.min-w-full');
502
- expect(table).toBeInTheDocument();
503
- }, { timeout: 10000 });
504
-
505
- // Then check for data
506
- await waitFor(() => {
507
- expect(screen.getByText('John Doe')).toBeInTheDocument();
508
- }, { timeout: 2000 });
509
- });
510
-
511
- it('handles CSV with commas in quoted values', async () => {
512
- const user = userEvent.setup();
513
- const csvContent = 'name,description\nJohn,"Description, with comma"\nJane,"Another, description"';
514
- const file = createCSVFile(csvContent);
515
-
516
- render(<ImportModal {...baseProps} />);
517
-
518
- // Wait for dialog to be accessible first, then wait for file input
519
- await waitForDialog();
520
- await waitFor(() => {
521
- const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
522
- expect(fileInput).toBeInTheDocument();
523
- }, { timeout: 5000 });
524
-
525
- const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
526
- await user.upload(fileInput, file);
527
-
528
- // Wait for preview table first
529
- await waitFor(() => {
530
- const table = screen.queryByRole('table') ||
531
- document.querySelector('table') ||
532
- document.querySelector('table.min-w-full');
533
- expect(table).toBeInTheDocument();
534
- }, { timeout: 10000 });
535
-
536
- // Then check for data
537
- await waitFor(() => {
538
- expect(screen.getByText(/description, with comma/i)).toBeInTheDocument();
539
- }, { timeout: 2000 });
540
- });
541
- });
542
-
543
- describe('Error Handling', () => {
544
- it('displays error for invalid CSV file', async () => {
545
- const user = userEvent.setup();
546
- // Create a file that will cause parsing to fail
547
- // CSV with only header row (no data rows) will trigger the error
548
- const invalidContent = 'name,email';
549
- const file = createCSVFile(invalidContent);
550
-
551
- render(<ImportModal {...baseProps} />);
552
-
553
- // Wait for dialog to be accessible first, then wait for file input
554
- await waitForDialog();
555
- await waitFor(() => {
556
- const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
557
- expect(fileInput).toBeInTheDocument();
558
- }, { timeout: 5000 });
559
-
560
- const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
561
- await user.upload(fileInput, file);
562
-
563
- // Wait for error to appear - CSV with only header should throw error
564
- await waitFor(() => {
565
- // Error should be displayed with AlertCircle icon or error message
566
- const errorIcon = screen.queryByTestId('alert-circle-icon');
567
- const errorText = screen.queryByText(/must have at least|error|failed/i);
568
- expect(errorIcon || errorText).toBeInTheDocument();
569
- }, { timeout: 5000 });
570
- });
571
-
572
- it('displays error for CSV with only header row', async () => {
573
- const user = userEvent.setup();
574
- const csvContent = 'name,email';
575
- const file = createCSVFile(csvContent);
576
-
577
- render(<ImportModal {...baseProps} />);
578
-
579
- // Wait for dialog to be accessible first, then wait for file input
580
- await waitForDialog();
581
- await waitFor(() => {
582
- const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
583
- expect(fileInput).toBeInTheDocument();
584
- }, { timeout: 5000 });
585
-
586
- const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
587
- await user.upload(fileInput, file);
588
-
589
- await waitFor(() => {
590
- expect(screen.getByTestId('alert-circle-icon')).toBeInTheDocument();
591
- });
592
- });
593
-
594
- it('clears error when new file is selected', async () => {
595
- const user = userEvent.setup();
596
- const invalidFile = createCSVFile('invalid');
597
- const validFile = createCSVFile('name,email\nJohn,john@example.com');
598
-
599
- render(<ImportModal {...baseProps} />);
600
-
601
- const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
602
- await user.upload(fileInput, invalidFile);
603
-
604
- await waitFor(() => {
605
- expect(screen.getByTestId('alert-circle-icon')).toBeInTheDocument();
606
- }, { timeout: 3000 });
607
-
608
- // Clear the file input value first
609
- fileInput.value = '';
610
- await user.upload(fileInput, validFile);
611
-
612
- await waitFor(() => {
613
- expect(screen.queryByTestId('alert-circle-icon')).not.toBeInTheDocument();
614
- }, { timeout: 3000 });
615
- });
616
- });
617
-
618
- describe('Import Action', () => {
619
- it('calls onImport with parsed data when import button is clicked', async () => {
620
- const user = userEvent.setup();
621
- const onImport = vi.fn();
622
- const csvContent = 'name,email\nJohn,john@example.com';
623
- const file = createCSVFile(csvContent);
624
-
625
- render(<ImportModal {...baseProps} onImport={onImport} />);
626
-
627
- // Wait for dialog to be accessible first, then wait for file input
628
- await waitForDialog();
629
- await waitFor(() => {
630
- const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
631
- expect(fileInput).toBeInTheDocument();
632
- }, { timeout: 5000 });
633
-
634
- const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
635
- await user.upload(fileInput, file);
636
-
637
- // Wait for preview to appear
638
- await waitFor(() => {
639
- const table = screen.queryByRole('table') ||
640
- document.querySelector('table') ||
641
- document.querySelector('table.min-w-full');
642
- expect(table).toBeInTheDocument();
643
- }, { timeout: 10000 });
644
-
645
- await waitFor(() => {
646
- expect(screen.getByText('John')).toBeInTheDocument();
647
- }, { timeout: 2000 });
648
-
649
- const importButton = findButtonByText(/^import$/i);
650
- expect(importButton).toBeInTheDocument();
651
- if (importButton) {
652
- await user.click(importButton);
653
- }
654
-
655
- await waitFor(() => {
656
- expect(onImport).toHaveBeenCalledTimes(1);
657
- expect(onImport).toHaveBeenCalledWith(
658
- expect.arrayContaining([
659
- expect.objectContaining({
660
- name: 'John',
661
- email: 'john@example.com',
662
- }),
663
- ])
664
- );
665
- }, { timeout: 3000 });
666
- });
667
-
668
- it('disables import button when no file is selected', async () => {
669
- render(<ImportModal {...baseProps} />);
670
-
671
- // Wait for dialog title first (most reliable indicator dialog is rendered)
672
- // Use getByRole for heading to avoid multiple matches (h2 and button both have "Import Data")
673
- await waitFor(() => {
674
- try {
675
- expect(screen.getByRole('heading', { name: 'Import Data' })).toBeInTheDocument();
676
- } catch (e) {
677
- // Fallback: check if any element with "Import Data" exists
678
- const elements = screen.getAllByText('Import Data');
679
- expect(elements.length).toBeGreaterThan(0);
680
- }
681
- }, { timeout: 5000 });
682
-
683
- // Then check for import button and its disabled state
684
- await waitFor(() => {
685
- const importButton = findButtonByText(/^import$/i);
686
- expect(importButton).toBeInTheDocument();
687
- expect(importButton).toBeDisabled();
688
- }, { timeout: 5000 });
689
- });
690
-
691
- it('disables import button while processing', async () => {
692
- const user = userEvent.setup();
693
- const onImport = vi.fn(async () => {
694
- await new Promise<void>(resolve => setTimeout(resolve, 100));
695
- });
696
- const csvContent = 'name,email\nJohn,john@example.com';
697
- const file = createCSVFile(csvContent);
698
-
699
- render(<ImportModal {...baseProps} onImport={onImport} />);
700
-
701
- // Wait for dialog to be accessible first, then wait for file input
702
- await waitForDialog();
703
- await waitFor(() => {
704
- const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
705
- expect(fileInput).toBeInTheDocument();
706
- }, { timeout: 5000 });
707
-
708
- const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
709
- await user.upload(fileInput, file);
710
-
711
- // Wait for preview to appear
712
- await waitFor(() => {
713
- const table = screen.queryByRole('table') ||
714
- document.querySelector('table') ||
715
- document.querySelector('table.min-w-full');
716
- expect(table).toBeInTheDocument();
717
- }, { timeout: 10000 });
718
-
719
- await waitFor(() => {
720
- expect(screen.getByText('John')).toBeInTheDocument();
721
- }, { timeout: 2000 });
722
-
723
- const importButton = findButtonByText(/^import$/i);
724
- expect(importButton).toBeInTheDocument();
725
- if (importButton) {
726
- await user.click(importButton);
727
- }
728
-
729
- await waitFor(() => {
730
- expect(importButton).toBeDisabled();
731
- }, { timeout: 1000 });
732
- });
733
-
734
- it('shows processing text while importing', async () => {
735
- const user = userEvent.setup();
736
- const onImport = vi.fn(async () => {
737
- await new Promise<void>(resolve => setTimeout(resolve, 100));
738
- });
739
- const csvContent = 'name,email\nJohn,john@example.com';
740
- const file = createCSVFile(csvContent);
741
-
742
- render(<ImportModal {...baseProps} onImport={onImport} />);
743
-
744
- // Wait for dialog to be accessible first, then wait for file input
745
- await waitForDialog();
746
- await waitFor(() => {
747
- const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
748
- expect(fileInput).toBeInTheDocument();
749
- }, { timeout: 5000 });
750
-
751
- const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
752
- await user.upload(fileInput, file);
753
-
754
- // Wait for preview to appear
755
- await waitFor(() => {
756
- const table = screen.queryByRole('table') ||
757
- document.querySelector('table') ||
758
- document.querySelector('table.min-w-full');
759
- expect(table).toBeInTheDocument();
760
- }, { timeout: 10000 });
761
-
762
- await waitFor(() => {
763
- expect(screen.getByText('John')).toBeInTheDocument();
764
- }, { timeout: 2000 });
765
-
766
- const importButton = findButtonByText(/^import$/i);
767
- expect(importButton).toBeInTheDocument();
768
- if (importButton) {
769
- await user.click(importButton);
770
- }
771
-
772
- // Button text changes to "Processing..." when isProcessing is true
773
- await waitFor(() => {
774
- const processingButton = findButtonByText(/processing/i);
775
- expect(processingButton).toBeInTheDocument();
776
- }, { timeout: 1000 });
777
- });
778
-
779
- it('calls onClose after successful import', async () => {
780
- const user = userEvent.setup();
781
- const onClose = vi.fn();
782
- const csvContent = 'name,email\nJohn,john@example.com';
783
- const file = createCSVFile(csvContent);
784
-
785
- render(<ImportModal {...baseProps} onClose={onClose} />);
786
-
787
- // Wait for dialog to be accessible first, then wait for file input
788
- await waitForDialog();
789
- await waitFor(() => {
790
- const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
791
- expect(fileInput).toBeInTheDocument();
792
- }, { timeout: 5000 });
793
-
794
- const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
795
- await user.upload(fileInput, file);
796
-
797
- // Wait for preview to appear
798
- await waitFor(() => {
799
- const table = screen.queryByRole('table') ||
800
- document.querySelector('table') ||
801
- document.querySelector('table.min-w-full');
802
- expect(table).toBeInTheDocument();
803
- }, { timeout: 10000 });
804
-
805
- await waitFor(() => {
806
- expect(screen.getByText('John')).toBeInTheDocument();
807
- }, { timeout: 2000 });
808
-
809
- const importButton = findButtonByText(/^import$/i);
810
- expect(importButton).toBeInTheDocument();
811
- if (importButton) {
812
- await user.click(importButton);
813
- }
814
-
815
- await waitFor(() => {
816
- expect(onClose).toHaveBeenCalled();
817
- }, { timeout: 3000 });
818
- });
819
- });
820
-
821
- describe('Close Action', () => {
822
- it('calls onClose when cancel button is clicked', async () => {
823
- const user = userEvent.setup();
824
- const onClose = vi.fn();
825
-
826
- render(<ImportModal {...baseProps} onClose={onClose} />);
827
-
828
- // Wait for dialog title first (most reliable indicator dialog is rendered)
829
- // Use getByRole for heading to avoid multiple matches (h2 and button both have "Import Data")
830
- await waitFor(() => {
831
- try {
832
- expect(screen.getByRole('heading', { name: 'Import Data' })).toBeInTheDocument();
833
- } catch (e) {
834
- // Fallback: check if any element with "Import Data" exists
835
- const elements = screen.getAllByText('Import Data');
836
- expect(elements.length).toBeGreaterThan(0);
837
- }
838
- }, { timeout: 5000 });
839
-
840
- // Then wait for cancel button
841
- await waitFor(() => {
842
- const cancelButton = findButtonByText(/cancel/i);
843
- expect(cancelButton).toBeInTheDocument();
844
- }, { timeout: 5000 });
845
-
846
- const cancelButton = findButtonByText(/cancel/i);
847
- expect(cancelButton).toBeInTheDocument();
848
- if (cancelButton) {
849
- await user.click(cancelButton);
850
- }
851
-
852
- expect(onClose).toHaveBeenCalledTimes(1);
853
- });
854
-
855
- it('resets state when modal closes', async () => {
856
- const user = userEvent.setup();
857
- const csvContent = 'name,email\nJohn,john@example.com';
858
- const file = createCSVFile(csvContent);
859
-
860
- const { rerender } = render(<ImportModal {...baseProps} />);
861
-
862
- // Wait for dialog to be accessible first, then wait for file input
863
- await waitForDialog();
864
- await waitFor(() => {
865
- const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
866
- expect(fileInput).toBeInTheDocument();
867
- }, { timeout: 5000 });
868
-
869
- const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
870
- await user.upload(fileInput, file);
871
-
872
- // Wait for preview to appear
873
- await waitFor(() => {
874
- const table = screen.queryByRole('table') ||
875
- document.querySelector('table') ||
876
- document.querySelector('table.min-w-full');
877
- expect(table).toBeInTheDocument();
878
- }, { timeout: 10000 });
879
-
880
- await waitFor(() => {
881
- expect(screen.getByText('John')).toBeInTheDocument();
882
- }, { timeout: 2000 });
883
-
884
- rerender(<ImportModal {...baseProps} isOpen={false} />);
885
- rerender(<ImportModal {...baseProps} isOpen={true} />);
886
-
887
- // State should be reset - wait for modal to reopen
888
- await waitFor(() => {
889
- expect(screen.queryByText('John')).not.toBeInTheDocument();
890
- }, { timeout: 1000 });
891
- });
892
- });
893
-
894
- describe('Custom Configuration', () => {
895
- it('uses custom button texts from config', async () => {
896
- render(
897
- <ImportModal
898
- {...baseProps}
899
- config={{
900
- selectFileButtonText: 'Browse Files',
901
- importButtonText: 'Import Data',
902
- cancelButtonText: 'Close',
903
- }}
904
- />
905
- );
906
-
907
- // Wait for dialog title first (most reliable indicator dialog is rendered)
908
- // Use getByRole for heading to avoid multiple matches (h2 and button both have "Import Data")
909
- await waitFor(() => {
910
- try {
911
- expect(screen.getByRole('heading', { name: 'Import Data' })).toBeInTheDocument();
912
- } catch (e) {
913
- // Fallback: check if any element with "Import Data" exists
914
- const elements = screen.getAllByText('Import Data');
915
- expect(elements.length).toBeGreaterThan(0);
916
- }
917
- }, { timeout: 5000 });
918
-
919
- // Then wait for buttons
920
- await waitFor(() => {
921
- const browseButton = findButtonByText(/browse files/i);
922
- const importDataButton = findButtonByText(/import data/i);
923
- expect(browseButton).toBeInTheDocument();
924
- expect(importDataButton).toBeInTheDocument();
925
- }, { timeout: 5000 });
926
- // Dialog has a close button too, so find the Cancel one by text
927
- const cancelButton = findButtonByText(/^close$/i);
928
- expect(cancelButton).toBeInTheDocument();
929
- });
930
-
931
- it('uses custom preview header text from config', async () => {
932
- const user = userEvent.setup();
933
- const csvContent = 'name,email\nJohn,john@example.com';
934
- const file = createCSVFile(csvContent);
935
-
936
- render(
937
- <ImportModal
938
- {...baseProps}
939
- config={{ previewHeaderText: 'Data Preview' }}
940
- />
941
- );
942
-
943
- const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
944
- await user.upload(fileInput, file);
945
-
946
- // Wait for preview to appear
947
- await waitFor(() => {
948
- expect(screen.getByText('Data Preview')).toBeInTheDocument();
949
- }, { timeout: 5000 });
950
- });
951
-
952
- it('uses custom total rows text from config', async () => {
953
- const user = userEvent.setup();
954
- const csvContent = 'name,email\nJohn,john@example.com\nJane,jane@example.com';
955
- const file = createCSVFile(csvContent);
956
-
957
- render(
958
- <ImportModal
959
- {...baseProps}
960
- config={{ totalRowsText: 'Found {count} records' }}
961
- />
962
- );
963
-
964
- // Wait for dialog to be accessible first, then wait for file input
965
- await waitForDialog();
966
- await waitFor(() => {
967
- const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
968
- expect(fileInput).toBeInTheDocument();
969
- }, { timeout: 5000 });
970
-
971
- const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
972
- await user.upload(fileInput, file);
973
-
974
- // Wait for preview to appear
975
- await waitFor(() => {
976
- const table = screen.queryByRole('table') ||
977
- document.querySelector('table') ||
978
- document.querySelector('table.min-w-full');
979
- expect(table).toBeInTheDocument();
980
- }, { timeout: 10000 });
981
-
982
- await waitFor(() => {
983
- expect(screen.getByText(/found 2 records/i)).toBeInTheDocument();
984
- }, { timeout: 2000 });
985
- });
986
- });
987
-
988
- describe('Edge Cases', () => {
989
- it('handles empty CSV file', async () => {
990
- const user = userEvent.setup();
991
- const file = createCSVFile('');
992
-
993
- render(<ImportModal {...baseProps} />);
994
-
995
- // Wait for dialog to be accessible first, then wait for file input
996
- await waitForDialog();
997
- await waitFor(() => {
998
- const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
999
- expect(fileInput).toBeInTheDocument();
1000
- }, { timeout: 5000 });
1001
-
1002
- const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
1003
- await user.upload(fileInput, file);
1004
-
1005
- await waitFor(() => {
1006
- expect(screen.getByTestId('alert-circle-icon')).toBeInTheDocument();
1007
- }, { timeout: 3000 });
1008
- });
1009
-
1010
- it('handles CSV with only whitespace', async () => {
1011
- const user = userEvent.setup();
1012
- const file = createCSVFile(' \n \n ');
1013
-
1014
- render(<ImportModal {...baseProps} />);
1015
-
1016
- // Wait for dialog to be accessible first, then wait for file input
1017
- await waitForDialog();
1018
- await waitFor(() => {
1019
- const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
1020
- expect(fileInput).toBeInTheDocument();
1021
- }, { timeout: 5000 });
1022
-
1023
- const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
1024
- await user.upload(fileInput, file);
1025
-
1026
- await waitFor(() => {
1027
- expect(screen.getByTestId('alert-circle-icon')).toBeInTheDocument();
1028
- }, { timeout: 3000 });
1029
- });
1030
-
1031
- it('handles very large CSV files', async () => {
1032
- const user = userEvent.setup();
1033
- const headers = 'name,email\n';
1034
- const rows = Array.from({ length: 1000 }, (_, i) => `User${i},user${i}@example.com`).join('\n');
1035
- const csvContent = headers + rows;
1036
- const file = createCSVFile(csvContent);
1037
-
1038
- render(<ImportModal {...baseProps} />);
1039
-
1040
- // Wait for dialog to be accessible first, then wait for file input
1041
- await waitForDialog();
1042
- await waitFor(() => {
1043
- const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
1044
- expect(fileInput).toBeInTheDocument();
1045
- }, { timeout: 5000 });
1046
-
1047
- const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
1048
- await user.upload(fileInput, file);
1049
-
1050
- await waitFor(() => {
1051
- expect(screen.getByText(/total rows to import: 1000/i)).toBeInTheDocument();
1052
- }, { timeout: 5000 });
1053
- });
1054
- });
1055
-
1056
- describe('Accessibility', () => {
1057
- it('provides accessible file input', () => {
1058
- render(<ImportModal {...baseProps} />);
1059
-
1060
- const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
1061
- expect(fileInput).toBeInTheDocument();
1062
- expect(fileInput).toHaveAttribute('type', 'file');
1063
- expect(fileInput).toHaveAttribute('accept', '.csv');
1064
- });
1065
-
1066
- it('provides accessible button labels', async () => {
1067
- render(<ImportModal {...baseProps} />);
1068
-
1069
- // Wait for dialog title first (most reliable indicator dialog is rendered)
1070
- // Use getByRole for heading to avoid multiple matches (h2 and button both have "Import Data")
1071
- await waitFor(() => {
1072
- try {
1073
- expect(screen.getByRole('heading', { name: 'Import Data' })).toBeInTheDocument();
1074
- } catch (e) {
1075
- // Fallback: check if any element with "Import Data" exists
1076
- const elements = screen.getAllByText('Import Data');
1077
- expect(elements.length).toBeGreaterThan(0);
1078
- }
1079
- }, { timeout: 5000 });
1080
-
1081
- // Then wait for buttons
1082
- await waitFor(() => {
1083
- const selectFileButton = findButtonByText(/select file/i);
1084
- const importButton = findButtonByText(/^import$/i);
1085
- const cancelButton = findButtonByText(/cancel/i);
1086
- expect(selectFileButton).toBeInTheDocument();
1087
- expect(importButton).toBeInTheDocument();
1088
- expect(cancelButton).toBeInTheDocument();
1089
- }, { timeout: 5000 });
1090
- });
1091
- });
1092
- });
1093
-