@jmruthers/pace-core 0.6.10 → 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 (726) 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 +13 -3
  5. package/audit-tool/audits/03-architecture.cjs +78 -4
  6. package/audit-tool/audits/04-code-quality.cjs +9 -2
  7. package/audit-tool/audits/05-styling.cjs +19 -7
  8. package/audit-tool/audits/06-security-rbac.cjs +105 -14
  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 +1 -0
  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 +41 -7
  19. package/cursor-rules/06-security-rbac.mdc +2 -1
  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-SAXFG4XI.js → DataTable-EFYP2QLE.js} +10 -7
  24. package/dist/{InactivityServiceProvider-DHryoh6K.d.ts → InactivityServiceProvider-BbxwwDz1.d.ts} +10 -1
  25. package/dist/{UnifiedAuthProvider-CiBAl9-s.d.ts → UnifiedAuthProvider-Bkt_tzdS.d.ts} +56 -24
  26. package/dist/{api-F47QJ7FX.js → api-BZR2CYXL.js} +3 -2
  27. package/dist/api-result-USV1Czr-.d.ts +51 -0
  28. package/dist/{audit-Z6ZZBWLU.js → audit-HI2DHUVU.js} +2 -1
  29. package/dist/{auth-BZOJqrdd.d.ts → auth-JvdRVaud.d.ts} +1 -1
  30. package/dist/{chunk-KSNLMI7N.js → chunk-2DL2WSOE.js} +1 -155
  31. package/dist/{chunk-MPY44PWB.js → chunk-2OEVOGGR.js} +4648 -3560
  32. package/dist/chunk-44CNXN4P.js +15 -0
  33. package/dist/{chunk-Y4PF6HIM.js → chunk-4R3T5ENU.js} +867 -786
  34. package/dist/{chunk-LNHFAF4X.js → chunk-7A6IMHH2.js} +289 -247
  35. package/dist/chunk-CU2BU2MQ.js +2 -0
  36. package/dist/{chunk-JJEYZ3DX.js → chunk-D6BMFMQZ.js} +37 -2
  37. package/dist/{chunk-BCTXBU6U.js → chunk-ENLXB7GP.js} +88 -71
  38. package/dist/{chunk-FBZ7U3ID.js → chunk-J2KQK6DG.js} +937 -987
  39. package/dist/{chunk-TFIPNIPE.js → chunk-KJXRL3XE.js} +3300 -2245
  40. package/dist/{chunk-3GWSPISD.js → chunk-L5LFKKLJ.js} +1 -1
  41. package/dist/{chunk-X5EAU5G7.js → chunk-PCSHBLPB.js} +132 -114
  42. package/dist/{chunk-NIU6DPQV.js → chunk-QRYSEPHB.js} +2 -0
  43. package/dist/{chunk-KYURMOQM.js → chunk-V7FTM2LU.js} +423 -320
  44. package/dist/chunk-WY6Y7KC3.js +264 -0
  45. package/dist/{chunk-FN52B75D.js → chunk-XOJME5T7.js} +176 -15
  46. package/dist/{chunk-7YDC7LMU.js → chunk-XPFVT3GN.js} +71 -66
  47. package/dist/{chunk-66R6RLUZ.js → chunk-YFTFFJIV.js} +3 -3
  48. package/dist/{chunk-W46INAVW.js → chunk-YYTWKVHO.js} +688 -570
  49. package/dist/components.d.ts +8 -7
  50. package/dist/components.js +17 -15
  51. package/dist/{database.generated-DT8JTZiP.d.ts → database.generated-qkdoiVrJ.d.ts} +45 -10
  52. package/dist/eslint-rules/index.cjs +3 -0
  53. package/dist/eslint-rules/rules/03-architecture.cjs +74 -0
  54. package/dist/eslint-rules/rules/06-security-rbac.cjs +74 -0
  55. package/dist/{event-WTAQuGcq.d.ts → event-BfCox3N2.d.ts} +36 -10
  56. package/dist/{file-reference-BavO2eQj.d.ts → file-reference-DU1hcawx.d.ts} +29 -13
  57. package/dist/hooks.d.ts +22 -9
  58. package/dist/hooks.js +34 -25
  59. package/dist/icons/index.d.ts +1 -0
  60. package/dist/icons/index.js +1 -0
  61. package/dist/index.d.ts +66 -177
  62. package/dist/index.js +316 -340
  63. package/dist/pagination-BW1mqywp.d.ts +201 -0
  64. package/dist/providers.d.ts +6 -5
  65. package/dist/providers.js +5 -3
  66. package/dist/rbac/index.d.ts +123 -138
  67. package/dist/rbac/index.js +10 -8
  68. package/dist/theming/runtime.d.ts +19 -2
  69. package/dist/theming/runtime.js +1 -1
  70. package/dist/{timezone-K-ptz3HO.d.ts → timezone-BTWWXKVY.d.ts} +1 -1
  71. package/dist/types.d.ts +17 -10
  72. package/dist/types.js +1 -0
  73. package/dist/{usePublicPageContext-vxBlEHO9.d.ts → usePublicPageContext-B91dGYW1.d.ts} +433 -356
  74. package/dist/{usePublicRouteParams-G3Ks53mk.d.ts → usePublicRouteParams-BgV6VhMi.d.ts} +73 -4
  75. package/dist/utils.d.ts +163 -145
  76. package/dist/utils.js +42 -25
  77. package/docs/api/modules.md +782 -643
  78. package/docs/api-reference/rpc-functions.md +12 -3
  79. package/docs/core-concepts/rbac-system.md +8 -0
  80. package/docs/getting-started/cursor-rules.md +17 -20
  81. package/docs/getting-started/dependencies.md +1 -1
  82. package/docs/getting-started/setup.md +235 -0
  83. package/docs/implementation-guides/authentication.md +27 -0
  84. package/docs/implementation-guides/data-tables.md +176 -3
  85. package/docs/migration/ApiResult-migration.md +25 -0
  86. package/docs/rbac/api-reference.md +33 -31
  87. package/docs/standards/0-standards-overview.md +50 -15
  88. package/docs/standards/1-pace-core-compliance-standards.md +62 -57
  89. package/docs/standards/2-project-structure-standards.md +33 -16
  90. package/docs/standards/3-architecture-standards.md +41 -1
  91. package/docs/standards/4-code-quality-standards.md +26 -6
  92. package/docs/standards/5-styling-standards.md +35 -1
  93. package/docs/standards/6-security-rbac-standards.md +66 -0
  94. package/docs/standards/7-api-tech-stack-standards.md +25 -14
  95. package/docs/standards/8-testing-documentation-standards.md +31 -0
  96. package/docs/standards/9-operations-standards.md +19 -0
  97. package/docs/standards/README.md +20 -201
  98. package/docs/testing/test-setup-for-consumers.md +2 -0
  99. package/docs/troubleshooting/common-issues.md +17 -1
  100. package/docs/troubleshooting/organisation-context-setup.md +8 -0
  101. package/docs/troubleshooting/print-event-name-css-variable-analysis.md +217 -0
  102. package/eslint-config-pace-core.cjs +20 -0
  103. package/package.json +14 -20
  104. package/scripts/{build-docs-incremental.js → build-docs.js} +3 -2
  105. package/scripts/setup.cjs +536 -0
  106. package/scripts/validate.cjs +480 -0
  107. package/src/__tests__/helpers/{__tests__/component-test-utils.test.tsx → component-test-utils.test.tsx} +3 -3
  108. package/src/__tests__/helpers/{__tests__/optimized-test-setup.test.ts → optimized-test-setup.test.ts} +2 -2
  109. package/src/__tests__/helpers/{__tests__/supabaseMock.test.ts → supabaseMock.test.ts} +2 -2
  110. package/src/__tests__/helpers/{__tests__/test-providers.test.tsx → test-providers.test.tsx} +1 -1
  111. package/src/__tests__/helpers/test-providers.tsx +37 -39
  112. package/src/__tests__/helpers/{__tests__/test-utils.test.tsx → test-utils.test.tsx} +4 -3
  113. package/src/__tests__/helpers/{__tests__/timer-utils.test.ts → timer-utils.test.ts} +2 -2
  114. package/src/assets/app-icons/index.test.ts +304 -0
  115. package/src/components/AddressField/AddressField.test.tsx +1 -1
  116. package/src/components/AddressField/AddressField.tsx +238 -212
  117. package/src/components/Button/Button.tsx +1 -1
  118. package/src/components/Card/Card.test.tsx +172 -17
  119. package/src/components/Card/Card.tsx +19 -10
  120. package/src/components/ContextSelector/ContextSelector.internals.tsx +204 -0
  121. package/src/components/ContextSelector/{__tests__/ContextSelector.test.tsx → ContextSelector.test.tsx} +6 -6
  122. package/src/components/ContextSelector/ContextSelector.tsx +66 -280
  123. package/src/components/ContextSelector/ContextSelector.types.ts +35 -0
  124. package/src/components/ContextSelector/useContextSelectorState.tsx +195 -0
  125. package/src/components/DataTable/AUDIT_REPORT.md +59 -44
  126. package/src/components/DataTable/{__tests__/DataTable.comprehensive.test.tsx → DataTable.comprehensive.test.tsx} +6 -6
  127. package/src/components/DataTable/{__tests__/DataTable.default-state.test.tsx → DataTable.default-state.test.tsx} +5 -5
  128. package/src/components/DataTable/{__tests__/DataTable.export.test.tsx → DataTable.export.test.tsx} +10 -10
  129. package/src/components/DataTable/{__tests__/DataTable.grouping-aggregation.test.tsx → DataTable.grouping-aggregation.test.tsx} +6 -6
  130. package/src/components/DataTable/{__tests__/DataTable.hooks.test.tsx → DataTable.hooks.test.tsx} +6 -6
  131. package/src/components/DataTable/{__tests__/DataTable.select-label-display.test.tsx → DataTable.select-label-display.test.tsx} +6 -6
  132. package/src/components/DataTable/DataTable.test.tsx +787 -416
  133. package/src/components/DataTable/DataTable.tsx +12 -12
  134. package/src/components/DataTable/DataTableCore.integration.test.tsx +458 -0
  135. package/src/components/DataTable/{__tests__/DataTableCore.test-setup.ts → DataTableCore.test-setup.ts} +10 -9
  136. package/src/components/DataTable/{__tests__/DataTableCore.test.tsx → DataTableCore.test.tsx} +8 -8
  137. package/src/components/DataTable/{__tests__/README.md → README.md} +17 -7
  138. package/src/components/DataTable/TESTING.md +101 -0
  139. package/src/components/DataTable/{__tests__/a11y.basic.test.tsx → a11y.basic.test.tsx} +34 -34
  140. package/src/components/DataTable/components/DataTableCore.tsx +104 -864
  141. package/src/components/DataTable/components/{__tests__/GroupingDropdown.test.tsx → GroupingDropdown.test.tsx} +17 -8
  142. package/src/components/DataTable/components/GroupingDropdown.tsx +2 -2
  143. package/src/components/DataTable/components/ImportModal.tsx +61 -559
  144. package/src/components/DataTable/components/ImportModalFileSection.tsx +148 -0
  145. package/src/components/DataTable/context/{__tests__/DataTableContext.test.tsx → DataTableContext.test.tsx} +2 -2
  146. package/src/components/DataTable/context/DataTableContext.tsx +7 -6
  147. package/src/components/DataTable/core/{__tests__/ColumnFactory.test.ts → ColumnFactory.test.ts} +2 -2
  148. package/src/components/DataTable/hooks/{__tests__/useColumnOrderPersistence.test.ts → useColumnOrderPersistence.test.ts} +2 -2
  149. package/src/components/DataTable/hooks/{__tests__/useColumnVisibilityPersistence.test.ts → useColumnVisibilityPersistence.test.ts} +2 -2
  150. package/src/components/DataTable/hooks/{__tests__/useDataTableConfiguration.test.ts → useDataTableConfiguration.test.ts} +3 -3
  151. package/src/components/DataTable/hooks/useDataTableConfiguration.ts +14 -2
  152. package/src/components/DataTable/hooks/{__tests__/useDataTableDataPipeline.test.ts → useDataTableDataPipeline.test.ts} +6 -6
  153. package/src/components/DataTable/hooks/useDataTableDeletionBatching.test.ts +127 -0
  154. package/src/components/DataTable/hooks/useDataTableDeletionBatching.ts +106 -0
  155. package/src/components/DataTable/hooks/useDataTableEffectiveActions.test.ts +461 -0
  156. package/src/components/DataTable/hooks/useDataTableEffectiveActions.ts +238 -0
  157. package/src/components/DataTable/hooks/useDataTableLayoutHandlers.test.ts +296 -0
  158. package/src/components/DataTable/hooks/useDataTableLayoutHandlers.ts +175 -0
  159. package/src/components/DataTable/hooks/useDataTablePaginationSync.test.ts +203 -0
  160. package/src/components/DataTable/hooks/useDataTablePaginationSync.ts +109 -0
  161. package/src/components/DataTable/hooks/{__tests__/useDataTablePermissions.test.ts → useDataTablePermissions.test.ts} +11 -11
  162. package/src/components/DataTable/hooks/useDataTablePermissions.ts +79 -247
  163. package/src/components/DataTable/hooks/useDataTablePipeline.test.tsx +219 -0
  164. package/src/components/DataTable/hooks/useDataTablePipeline.tsx +239 -0
  165. package/src/components/DataTable/hooks/useDataTableRenderGuard.test.tsx +316 -0
  166. package/src/components/DataTable/hooks/useDataTableRenderGuard.tsx +195 -0
  167. package/src/components/DataTable/hooks/useDataTableScope.test.ts +110 -0
  168. package/src/components/DataTable/hooks/useDataTableScope.ts +123 -0
  169. package/src/components/DataTable/hooks/{__tests__/useDataTableState.test.ts → useDataTableState.test.ts} +47 -5
  170. package/src/components/DataTable/hooks/useDataTableState.ts +145 -94
  171. package/src/components/DataTable/hooks/useDataTableStateAndPersistence.test.ts +277 -0
  172. package/src/components/DataTable/hooks/useDataTableStateAndPersistence.ts +222 -0
  173. package/src/components/DataTable/hooks/useDataTableSuperAdmin.test.ts +93 -0
  174. package/src/components/DataTable/hooks/useDataTableSuperAdmin.ts +86 -0
  175. package/src/components/DataTable/hooks/useDataTableTableInstance.test.ts +185 -0
  176. package/src/components/DataTable/hooks/useDataTableTableInstance.ts +178 -0
  177. package/src/components/DataTable/hooks/{__tests__/useEffectiveColumnOrder.test.ts → useEffectiveColumnOrder.test.ts} +2 -2
  178. package/src/components/DataTable/hooks/{__tests__/useHierarchicalState.test.ts → useHierarchicalState.test.ts} +2 -2
  179. package/src/components/DataTable/{components/hooks → hooks}/useImportModalFocus.test.ts +3 -3
  180. package/src/components/DataTable/{components/hooks → hooks}/useImportModalFocus.ts +2 -2
  181. package/src/components/DataTable/hooks/useImportModalState.test.ts +390 -0
  182. package/src/components/DataTable/hooks/useImportModalState.ts +345 -0
  183. package/src/components/DataTable/hooks/{__tests__/useKeyboardNavigation.test.ts → useKeyboardNavigation.test.ts} +3 -3
  184. package/src/components/DataTable/hooks/useKeyboardNavigation.ts +309 -269
  185. package/src/components/DataTable/{components/hooks → hooks}/usePermissionTracking.test.ts +3 -3
  186. package/src/components/DataTable/{components/hooks → hooks}/usePermissionTracking.ts +3 -3
  187. package/src/components/DataTable/hooks/{__tests__/useServerSideDataEffect.test.ts → useServerSideDataEffect.test.ts} +2 -2
  188. package/src/components/DataTable/hooks/useServerSideDataEffect.ts +14 -3
  189. package/src/components/DataTable/hooks/{__tests__/useTableColumns.test.ts → useTableColumns.test.ts} +2 -2
  190. package/src/components/DataTable/hooks/{__tests__/useTableHandlers.test.ts → useTableHandlers.test.ts} +25 -4
  191. package/src/components/DataTable/hooks/useTableHandlers.ts +5 -2
  192. package/src/components/DataTable/index.ts +18 -17
  193. package/src/components/DataTable/{__tests__/keyboard.test.tsx → keyboard.test.tsx} +41 -63
  194. package/src/components/DataTable/{__tests__/mocks → mocks}/MockRBACProvider.tsx +1 -1
  195. package/src/components/DataTable/{__tests__/pagination.modes.test.tsx → pagination.modes.test.tsx} +6 -6
  196. package/src/components/DataTable/{__tests__/ssr.strict-mode.test.tsx → ssr.strict-mode.test.tsx} +2 -2
  197. package/src/components/DataTable/{__tests__/styles.test.ts → styles.test.ts} +1 -4
  198. package/src/components/DataTable/styles.ts +0 -1
  199. package/src/components/DataTable/test-utils/MockDataTableComponents.tsx +55 -0
  200. package/src/components/DataTable/{__tests__/test-utils → test-utils}/dataFactories.ts +2 -2
  201. package/src/components/DataTable/test-utils/featureConfig.ts +10 -0
  202. package/src/components/DataTable/{__tests__/test-utils/sharedTestUtils.tsx → test-utils/sharedTestUtils.ts} +97 -66
  203. package/src/components/DataTable/{__tests__/test-utils.ts → test-utils.ts} +1 -1
  204. package/src/components/DataTable/types/actions.ts +71 -0
  205. package/src/components/DataTable/types/base.ts +39 -0
  206. package/src/components/DataTable/types/columns.ts +125 -0
  207. package/src/components/DataTable/types/export.ts +32 -0
  208. package/src/components/DataTable/types/features.ts +81 -0
  209. package/src/components/DataTable/types/hierarchical.ts +44 -0
  210. package/src/components/DataTable/types/index.ts +43 -0
  211. package/src/components/DataTable/types/pagination.ts +85 -0
  212. package/src/components/DataTable/types/performance.ts +47 -0
  213. package/src/components/DataTable/types/props.ts +62 -0
  214. package/src/components/DataTable/types/rbac.ts +45 -0
  215. package/src/components/DataTable/{components/__tests__ → ui/layout}/DataTableCore.test.tsx +430 -28
  216. package/src/components/DataTable/ui/layout/DataTableCore.tsx +345 -0
  217. package/src/components/DataTable/{components/__tests__ → ui/layout}/DataTableErrorBoundary.test.tsx +4 -4
  218. package/src/components/DataTable/{components → ui/layout}/DataTableErrorBoundary.tsx +7 -7
  219. package/src/components/DataTable/ui/layout/DataTableLayout.test.tsx +1352 -0
  220. package/src/components/DataTable/ui/layout/DataTableLayout.tsx +661 -0
  221. package/src/components/DataTable/ui/modals/BulkDeleteConfirmDialog.test.tsx +91 -0
  222. package/src/components/DataTable/ui/modals/BulkDeleteConfirmDialog.tsx +43 -0
  223. package/src/components/DataTable/ui/modals/DataTableModals.test.tsx +749 -0
  224. package/src/components/DataTable/{components → ui/modals}/DataTableModals.tsx +36 -28
  225. package/src/components/DataTable/ui/modals/ImportModal.test.tsx +1834 -0
  226. package/src/components/DataTable/ui/modals/ImportModal.tsx +197 -0
  227. package/src/components/DataTable/ui/modals/ImportModalFailedRowsSection.tsx +60 -0
  228. package/src/components/DataTable/ui/modals/ImportModalFileSection.tsx +148 -0
  229. package/src/components/DataTable/ui/modals/ImportModalPreviewSection.tsx +60 -0
  230. package/src/components/DataTable/ui/modals/ImportModalSummarySection.tsx +59 -0
  231. package/src/components/DataTable/ui/modals/importModalPersistence.ts +73 -0
  232. package/src/components/DataTable/{components/__tests__ → ui/shared}/AccessDeniedPage.test.tsx +2 -2
  233. package/src/components/DataTable/{components → ui/shared}/AccessDeniedPage.tsx +2 -2
  234. package/src/components/DataTable/{components/__tests__ → ui/shared}/ActionButtons.test.tsx +6 -4
  235. package/src/components/DataTable/{components → ui/shared}/ActionButtons.tsx +4 -4
  236. package/src/components/DataTable/{components/__tests__ → ui/shared}/ColumnFilter.test.tsx +29 -16
  237. package/src/components/DataTable/{components → ui/shared}/ColumnFilter.tsx +4 -4
  238. package/src/components/DataTable/{components/__tests__ → ui/shared}/PaginationControls.test.tsx +38 -16
  239. package/src/components/DataTable/{components → ui/shared}/PaginationControls.tsx +21 -15
  240. package/src/components/DataTable/{components/__tests__ → ui/shared}/SortIndicator.test.tsx +2 -2
  241. package/src/components/DataTable/{components → ui/shared}/SortIndicator.tsx +1 -1
  242. package/src/components/DataTable/{components/__tests__ → ui/table}/EditFields.test.tsx +3 -3
  243. package/src/components/DataTable/{components → ui/table}/EditFields.tsx +138 -69
  244. package/src/components/DataTable/{components/__tests__ → ui/table}/EditableRow.test.tsx +36 -27
  245. package/src/components/DataTable/{components → ui/table}/EditableRow.tsx +86 -104
  246. package/src/components/DataTable/{components/__tests__ → ui/table}/EmptyState.test.tsx +2 -62
  247. package/src/components/DataTable/{components → ui/table}/EmptyState.tsx +7 -15
  248. package/src/components/DataTable/{components/__tests__ → ui/table}/FilterRow.test.tsx +5 -4
  249. package/src/components/DataTable/{components → ui/table}/FilterRow.tsx +3 -3
  250. package/src/components/DataTable/{components/__tests__ → ui/table}/LoadingState.test.tsx +6 -10
  251. package/src/components/DataTable/{components → ui/table}/LoadingState.tsx +4 -4
  252. package/src/components/DataTable/{components/__tests__ → ui/table}/RowComponent.test.tsx +412 -17
  253. package/src/components/DataTable/{components → ui/table}/RowComponent.tsx +183 -177
  254. package/src/components/DataTable/{components/__tests__ → ui/table}/UnifiedTableBody.test.tsx +425 -16
  255. package/src/components/DataTable/ui/table/UnifiedTableBody.tsx +440 -0
  256. package/src/components/DataTable/{components/__tests__ → ui/table}/cellValueUtils.test.ts +2 -2
  257. package/src/components/DataTable/{components → ui/table}/cellValueUtils.ts +1 -1
  258. package/src/components/DataTable/{components/__tests__ → ui/toolbar}/BulkOperationsDropdown.test.tsx +12 -5
  259. package/src/components/DataTable/{components → ui/toolbar}/BulkOperationsDropdown.tsx +3 -3
  260. package/src/components/DataTable/{components/__tests__ → ui/toolbar}/ColumnVisibilityDropdown.test.tsx +7 -4
  261. package/src/components/DataTable/{components → ui/toolbar}/ColumnVisibilityDropdown.tsx +7 -7
  262. package/src/components/DataTable/{components/__tests__ → ui/toolbar}/DataTableToolbar.test.tsx +4 -4
  263. package/src/components/DataTable/{components → ui/toolbar}/DataTableToolbar.tsx +4 -4
  264. package/src/components/DataTable/ui/toolbar/GroupingDropdown.test.tsx +621 -0
  265. package/src/components/DataTable/ui/toolbar/GroupingDropdown.tsx +107 -0
  266. package/src/components/DataTable/utils/{__tests__/a11yUtils.test.ts → a11yUtils.test.ts} +2 -2
  267. package/src/components/DataTable/utils/{__tests__/aggregationUtils.test.ts → aggregationUtils.test.ts} +3 -3
  268. package/src/components/DataTable/utils/{__tests__/columnUtils.test.ts → columnUtils.test.ts} +2 -2
  269. package/src/components/DataTable/utils/csvParse.test.ts +74 -0
  270. package/src/components/DataTable/utils/csvParse.ts +65 -0
  271. package/src/components/DataTable/utils/{__tests__/errorHandling.test.ts → errorHandling.test.ts} +2 -2
  272. package/src/components/DataTable/utils/{__tests__/exportUtils.test.ts → exportUtils.test.ts} +3 -3
  273. package/src/components/DataTable/utils/{__tests__/flexibleImport.test.ts → flexibleImport.test.ts} +2 -2
  274. package/src/components/DataTable/utils/flexibleImport.ts +3 -186
  275. package/src/components/DataTable/utils/{__tests__/hierarchicalSorting.test.ts → hierarchicalSorting.test.ts} +3 -3
  276. package/src/components/DataTable/utils/{__tests__/hierarchicalUtils.test.ts → hierarchicalUtils.test.ts} +3 -3
  277. package/src/components/DataTable/utils/importDateParser.test.ts +162 -0
  278. package/src/components/DataTable/utils/importDateParser.ts +114 -0
  279. package/src/components/DataTable/utils/importValueParser.test.ts +138 -0
  280. package/src/components/DataTable/utils/importValueParser.ts +91 -0
  281. package/src/components/DataTable/utils/{__tests__/paginationUtils.test.ts → paginationUtils.test.ts} +2 -2
  282. package/src/components/DataTable/utils/paginationUtils.ts +6 -3
  283. package/src/components/DataTable/utils/{__tests__/performanceUtils.test.ts → performanceUtils.test.ts} +3 -3
  284. package/src/components/DataTable/utils/{__tests__/rowUtils.test.ts → rowUtils.test.ts} +3 -3
  285. package/src/components/DataTable/utils/{__tests__/selectFieldUtils.test.ts → selectFieldUtils.test.ts} +66 -3
  286. package/src/components/DataTable/utils/selectFieldUtils.ts +97 -60
  287. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +1 -1
  288. package/src/components/DateTimeField/DateTimeField.test.tsx +1 -1
  289. package/src/components/Dialog/Dialog.test-utils.ts +49 -0
  290. package/src/components/Dialog/Dialog.test.tsx +896 -89
  291. package/src/components/Dialog/Dialog.tsx +174 -882
  292. package/src/components/Dialog/dialogLock.test.ts +238 -0
  293. package/src/components/Dialog/dialogLock.ts +98 -0
  294. package/src/components/Dialog/index.ts +2 -0
  295. package/src/components/Dialog/useDialogDimensions.test.ts +163 -0
  296. package/src/components/Dialog/useDialogDimensions.ts +140 -0
  297. package/src/components/Dialog/useDialogLifecycle.test.ts +358 -0
  298. package/src/components/Dialog/useDialogLifecycle.ts +135 -0
  299. package/src/components/Dialog/useDialogPersistence.test.ts +381 -0
  300. package/src/components/Dialog/useDialogPersistence.ts +357 -0
  301. package/src/components/FileDisplay/FileDisplay.test.tsx +40 -40
  302. package/src/components/FileDisplay/FileDisplay.tsx +24 -656
  303. package/src/components/FileDisplay/FileDisplayContent.test.tsx +395 -0
  304. package/src/components/FileDisplay/FileDisplayContent.tsx +242 -0
  305. package/src/components/FileDisplay/FileDisplayDeleteConfirmDialog.test.tsx +74 -0
  306. package/src/components/FileDisplay/FileDisplayDeleteConfirmDialog.tsx +38 -0
  307. package/src/components/FileDisplay/FileDisplayEmptyView.test.tsx +33 -0
  308. package/src/components/FileDisplay/FileDisplayEmptyView.tsx +33 -0
  309. package/src/components/FileDisplay/FileDisplayErrorView.test.tsx +71 -0
  310. package/src/components/FileDisplay/FileDisplayErrorView.tsx +50 -0
  311. package/src/components/FileDisplay/FileDisplayLoadingFallbackView.test.tsx +22 -0
  312. package/src/components/FileDisplay/FileDisplayLoadingFallbackView.tsx +22 -0
  313. package/src/components/FileDisplay/FileDisplayLoadingView.test.tsx +21 -0
  314. package/src/components/FileDisplay/FileDisplayLoadingView.tsx +23 -0
  315. package/src/components/FileDisplay/FileDisplayMultipleFilesView.test.tsx +101 -0
  316. package/src/components/FileDisplay/FileDisplayMultipleFilesView.tsx +109 -0
  317. package/src/components/FileDisplay/FileDisplaySingleDocumentLinkView.test.tsx +58 -0
  318. package/src/components/FileDisplay/FileDisplaySingleDocumentLinkView.tsx +48 -0
  319. package/src/components/FileDisplay/FileDisplaySingleFileWithActionsView.test.tsx +111 -0
  320. package/src/components/FileDisplay/FileDisplaySingleFileWithActionsView.tsx +270 -0
  321. package/src/components/FileDisplay/FileDisplaySingleImageView.test.tsx +78 -0
  322. package/src/components/FileDisplay/FileDisplaySingleImageView.tsx +67 -0
  323. package/src/components/FileDisplay/fallbackUtils.test.ts +50 -0
  324. package/src/components/FileDisplay/fallbackUtils.ts +44 -0
  325. package/src/components/FileDisplay/fetchFileDisplayData.ts +24 -0
  326. package/src/components/FileDisplay/fetchFileDisplayData.unit.test.ts +183 -0
  327. package/src/components/FileDisplay/fileDisplayUtils.test.ts +58 -0
  328. package/src/components/FileDisplay/fileDisplayUtils.ts +24 -0
  329. package/src/{hooks/__tests__ → components/FileDisplay}/useFileDisplay.test.ts +40 -42
  330. package/src/components/FileDisplay/useFileDisplay.ts +515 -0
  331. package/src/{hooks/__tests__ → components/FileDisplay}/useFileDisplay.unit.test.ts +406 -77
  332. package/src/components/FileDisplay/useFileDisplayData.ts +126 -0
  333. package/src/{hooks/public → components/FileDisplay}/usePublicFileDisplay.test.ts +94 -88
  334. package/src/components/FileDisplay/usePublicFileDisplay.ts +579 -0
  335. package/src/components/FileUpload/FileUpload.test.tsx +16 -10
  336. package/src/components/FileUpload/FileUpload.tsx +107 -525
  337. package/src/components/FileUpload/FileUploadDropZone.tsx +112 -0
  338. package/src/components/FileUpload/FileUploadProgressItem.tsx +86 -0
  339. package/src/components/FileUpload/FileUploadProgressList.tsx +40 -0
  340. package/src/components/FileUpload/useFileUploadManager.test.ts +308 -0
  341. package/src/components/FileUpload/useFileUploadManager.ts +454 -0
  342. package/src/components/FileUpload/useResolvedAppId.test.ts +102 -0
  343. package/src/components/FileUpload/useResolvedAppId.ts +77 -0
  344. package/src/components/Footer/Footer.test.tsx +6 -292
  345. package/src/components/Footer/Footer.tsx +8 -125
  346. package/src/components/Form/Form.test.tsx +44 -27
  347. package/src/components/Form/Form.tsx +64 -287
  348. package/src/components/Form/useFormPersistence.ts +257 -0
  349. package/src/components/Header/Header.test.tsx +17 -18
  350. package/src/components/Header/Header.tsx +10 -1
  351. package/src/components/Input/Input.tsx +1 -1
  352. package/src/components/Label/Label.test.tsx +1 -1
  353. package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +1 -1
  354. package/src/components/NavigationMenu/HierarchicalNavItem.tsx +104 -0
  355. package/src/components/NavigationMenu/NavigationMenu.test.tsx +1029 -26
  356. package/src/components/NavigationMenu/NavigationMenu.tsx +61 -361
  357. package/src/components/NavigationMenu/index.ts +6 -1
  358. package/src/components/NavigationMenu/navigationPermissionHelper.ts +188 -0
  359. package/src/components/NavigationMenu/{__tests__/useNavigationFiltering.test.ts → useNavigationFiltering.test.ts} +68 -53
  360. package/src/components/NavigationMenu/useNavigationFiltering.ts +197 -296
  361. package/src/components/NavigationMenu/useNavigationScope.ts +125 -0
  362. package/src/components/PaceAppLayout/PaceAppLayout.edge-cases.test.tsx +77 -62
  363. package/src/components/PaceAppLayout/PaceAppLayout.integration.test.tsx +3 -3
  364. package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +16 -19
  365. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +529 -5
  366. package/src/components/PaceAppLayout/PaceAppLayout.tsx +280 -756
  367. package/src/components/PaceAppLayout/useFilteredNavItems.ts +304 -0
  368. package/src/components/PaceAppLayout/usePaceAppLayoutConfig.ts +142 -0
  369. package/src/components/PaceAppLayout/usePaceAppLayoutGate.tsx +150 -0
  370. package/src/components/PaceAppLayout/usePaceAppLayoutPermissions.ts +162 -0
  371. package/src/components/PaceAppLayout/usePaceAppLayoutScope.ts +79 -0
  372. package/src/components/PaceAppLayout/useRoleBasedRouteAccess.ts +157 -0
  373. package/src/components/PaceAppLayout/useSuperAdminFallback.ts +58 -0
  374. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +31 -25
  375. package/src/components/PaceLoginPage/PaceLoginPage.tsx +31 -122
  376. package/src/components/PaceLoginPage/useLoginAppAccess.ts +153 -0
  377. package/src/components/Progress/Progress.tsx +1 -2
  378. package/src/components/ProtectedRoute/ProtectedRoute.tsx +29 -235
  379. package/src/components/ProtectedRoute/useProtectedRouteState.ts +128 -0
  380. package/src/components/ProtectedRoute/useVisibilityRedirectGrace.ts +89 -0
  381. package/src/components/PublicLayout/PublicLayout.test.tsx +217 -36
  382. package/src/components/PublicLayout/PublicPageLayout.tsx +132 -73
  383. package/src/components/PublicLayout/PublicPageProvider.tsx +5 -1
  384. package/src/components/Select/Select.test.tsx +1 -1
  385. package/src/components/Select/Select.tsx +28 -18
  386. package/src/components/Select/{__tests__/context.test.tsx → context.test.tsx} +3 -3
  387. package/src/components/Select/{utils/__tests__/text.test.tsx → text.test.tsx} +2 -2
  388. package/src/components/Select/{utils/text.ts → text.ts} +1 -1
  389. package/src/components/Select/{hooks/__tests__/useSelectEvents.test.ts → useSelectEvents.test.ts} +5 -5
  390. package/src/components/Select/{hooks/useSelectEvents.ts → useSelectEvents.ts} +2 -2
  391. package/src/components/Select/{hooks/__tests__/useSelectSearch.test.tsx → useSelectSearch.test.tsx} +7 -7
  392. package/src/components/Select/{hooks/useSelectSearch.ts → useSelectSearch.ts} +2 -2
  393. package/src/components/Select/{hooks/__tests__/useSelectState.test.ts → useSelectState.test.ts} +16 -2
  394. package/src/components/Select/{hooks/useSelectState.ts → useSelectState.ts} +3 -3
  395. package/src/components/Table/Table.test.tsx +348 -0
  396. package/src/components/Tabs/Tabs.test.tsx +270 -0
  397. package/src/components/Tabs/Tabs.tsx +1 -1
  398. package/src/components/Toast/Toast.test.tsx +420 -0
  399. package/src/components/{__tests__/index.test.ts → index.test.ts} +2 -2
  400. package/src/constants/{__tests__/performance.test.ts → performance.test.ts} +2 -2
  401. package/src/hooks/{__tests__/ServiceHooks.test.tsx → ServiceHooks.test.tsx} +8 -8
  402. package/src/hooks/{__tests__/hooks.integration.test.tsx → hooks.integration.test.tsx} +11 -11
  403. package/src/hooks/index.ts +7 -4
  404. package/src/hooks/{__tests__/index.unit.test.ts → index.unit.test.ts} +2 -2
  405. package/src/hooks/public/usePublicEvent.test.ts +1 -1
  406. package/src/hooks/public/usePublicEventLogo.test.ts +1 -1
  407. package/src/hooks/public/usePublicRouteParams.test.ts +1 -1
  408. package/src/hooks/services/useAuth.ts +9 -7
  409. package/src/hooks/useAddressAutocomplete.test.ts +22 -22
  410. package/src/hooks/useAddressAutocomplete.ts +90 -75
  411. package/src/hooks/{__tests__/useAppConfig.unit.test.ts → useAppConfig.unit.test.ts} +328 -22
  412. package/src/hooks/{__tests__/useComponentPerformance.unit.test.tsx → useComponentPerformance.unit.test.tsx} +27 -41
  413. package/src/hooks/useDataTablePerformance.ts +100 -120
  414. package/src/hooks/{__tests__/useDataTablePerformance.unit.test.ts → useDataTablePerformance.unit.test.ts} +5 -5
  415. package/src/hooks/{__tests__/useDataTableState.test.ts → useDataTableState.test.ts} +2 -2
  416. package/src/hooks/{__tests__/useDebounce.unit.test.ts → useDebounce.unit.test.ts} +2 -2
  417. package/src/hooks/useEventTheme.test.ts +4 -1
  418. package/src/hooks/useEventTheme.ts +49 -21
  419. package/src/hooks/useEvents.ts +41 -1
  420. package/src/hooks/{__tests__/useEvents.unit.test.ts → useEvents.unit.test.ts} +5 -5
  421. package/src/hooks/useFileReference.test.ts +44 -41
  422. package/src/hooks/useFileReference.ts +182 -173
  423. package/src/hooks/useFileUrl.ts +1 -1
  424. package/src/hooks/{__tests__/useFileUrl.unit.test.ts → useFileUrl.unit.test.ts} +26 -36
  425. package/src/hooks/{__tests__/useFileUrlCache.test.ts → useFileUrlCache.test.ts} +8 -8
  426. package/src/hooks/useFileUrlCache.ts +1 -1
  427. package/src/hooks/{__tests__/useFocusManagement.unit.test.ts → useFocusManagement.unit.test.ts} +2 -2
  428. package/src/hooks/{__tests__/useFocusTrap.unit.test.tsx → useFocusTrap.unit.test.tsx} +2 -2
  429. package/src/hooks/{__tests__/useFormDialog.test.ts → useFormDialog.test.ts} +2 -2
  430. package/src/hooks/useInactivityTracker.ts +138 -131
  431. package/src/hooks/{__tests__/useInactivityTracker.unit.test.ts → useInactivityTracker.unit.test.ts} +3 -3
  432. package/src/hooks/{__tests__/useIsMobile.unit.test.ts → useIsMobile.unit.test.ts} +2 -2
  433. package/src/hooks/useIsPrint.ts +62 -0
  434. package/src/hooks/useIsPrint.unit.test.ts +545 -0
  435. package/src/hooks/{__tests__/useKeyboardShortcuts.unit.test.ts → useKeyboardShortcuts.unit.test.ts} +2 -2
  436. package/src/hooks/{__tests__/useOrganisationPermissions.unit.test.tsx → useOrganisationPermissions.unit.test.tsx} +4 -4
  437. package/src/hooks/useOrganisationSecurity.test.ts +3 -3
  438. package/src/hooks/useOrganisationSecurity.ts +190 -201
  439. package/src/hooks/{__tests__/useOrganisationSecurity.unit.test.tsx → useOrganisationSecurity.unit.test.tsx} +61 -63
  440. package/src/hooks/{__tests__/useOrganisations.unit.test.ts → useOrganisations.unit.test.ts} +5 -5
  441. package/src/hooks/{__tests__/usePerformanceMonitor.unit.test.ts → usePerformanceMonitor.unit.test.ts} +13 -14
  442. package/src/hooks/{__tests__/usePermissionCache.test.ts → usePermissionCache.test.ts} +26 -27
  443. package/src/hooks/usePermissionCache.ts +276 -271
  444. package/src/hooks/{__tests__/usePreventTabReload.test.ts → usePreventTabReload.test.ts} +2 -2
  445. package/src/hooks/{__tests__/usePublicEvent.simple.test.ts → usePublicEvent.simple.test.ts} +4 -4
  446. package/src/hooks/{__tests__/usePublicEvent.test.ts → usePublicEvent.test.ts} +4 -4
  447. package/src/hooks/{__tests__/usePublicEvent.unit.test.ts → usePublicEvent.unit.test.ts} +4 -4
  448. package/src/hooks/{__tests__/usePublicFileDisplay.test.ts → usePublicFileDisplay.test.ts} +12 -12
  449. package/src/hooks/{__tests__/usePublicRouteParams.unit.test.ts → usePublicRouteParams.unit.test.ts} +3 -3
  450. package/src/hooks/{__tests__/useQueryCache.test.ts → useQueryCache.test.ts} +2 -2
  451. package/src/hooks/useQueryCache.ts +0 -2
  452. package/src/hooks/{__tests__/useRBAC.unit.test.ts → useRBAC.unit.test.ts} +55 -38
  453. package/src/hooks/{__tests__/useSessionDraft.test.ts → useSessionDraft.test.ts} +2 -2
  454. package/src/hooks/{__tests__/useSessionRestoration.unit.test.tsx → useSessionRestoration.unit.test.tsx} +10 -19
  455. package/src/hooks/useStorage.ts +21 -16
  456. package/src/hooks/{__tests__/useStorage.unit.test.ts → useStorage.unit.test.ts} +38 -75
  457. package/src/hooks/{__tests__/useToast.test.ts → useToast.test.ts} +2 -2
  458. package/src/hooks/{__tests__/useToast.unit.test.tsx → useToast.unit.test.tsx} +2 -2
  459. package/src/hooks/{__tests__/useZodForm.unit.test.tsx → useZodForm.unit.test.tsx} +2 -2
  460. package/src/icons/{__tests__/index.test.ts → index.test.ts} +2 -2
  461. package/src/icons/index.ts +2 -0
  462. package/src/{__tests__/index.test.ts → index.test.ts} +3 -7
  463. package/src/index.ts +15 -7
  464. package/src/providers/{__tests__/AuthProvider.test.tsx → AuthProvider.test.tsx} +3 -3
  465. package/src/providers/{__tests__/EventProvider.test.tsx → EventProvider.test.tsx} +3 -3
  466. package/src/providers/InactivityProvider.test-helper.tsx +40 -0
  467. package/src/providers/{__tests__/InactivityProvider.test.tsx → InactivityProvider.test.tsx} +14 -21
  468. package/src/providers/{__tests__/ProviderLifecycle.test.tsx → ProviderLifecycle.test.tsx} +4 -4
  469. package/src/providers/{__tests__/UnifiedAuthProvider.test.tsx → UnifiedAuthProvider.test.tsx} +1 -1
  470. package/src/providers/{__tests__/index.test.ts → index.test.ts} +2 -2
  471. package/src/providers/services/{__tests__/AuthServiceProvider.integration.test.tsx → AuthServiceProvider.integration.test.tsx} +4 -4
  472. package/src/providers/services/{__tests__/AuthServiceProvider.test.tsx → AuthServiceProvider.test.tsx} +7 -7
  473. package/src/providers/services/{__tests__/EventServiceProvider.test.tsx → EventServiceProvider.test.tsx} +7 -7
  474. package/src/providers/services/{__tests__/InactivityServiceProvider.test.tsx → InactivityServiceProvider.test.tsx} +5 -5
  475. package/src/providers/services/{__tests__/OrganisationServiceProvider.test.tsx → OrganisationServiceProvider.test.tsx} +6 -6
  476. package/src/providers/services/UnifiedAuthContext.ts +30 -27
  477. package/src/providers/services/{__tests__/UnifiedAuthProvider.advanced.test.tsx → UnifiedAuthProvider.advanced.test.tsx} +8 -9
  478. package/src/providers/services/{__tests__/UnifiedAuthProvider.appId.test.tsx → UnifiedAuthProvider.appId.test.tsx} +25 -25
  479. package/src/providers/services/{__tests__/UnifiedAuthProvider.integration.test.tsx → UnifiedAuthProvider.integration.test.tsx} +14 -11
  480. package/src/providers/services/UnifiedAuthProvider.tsx +115 -360
  481. package/src/providers/services/{__tests__/contexts.test.tsx → contexts.test.tsx} +6 -6
  482. package/src/providers/services/{__tests__/useUnifiedAuth.test.tsx → useUnifiedAuth.test.tsx} +6 -6
  483. package/src/providers/services/useUnifiedAuthContextValue.ts +279 -0
  484. package/src/providers/useInactivity.test-helper.ts +27 -0
  485. package/src/rbac/{__tests__/adapters.comprehensive.test.tsx → adapters.comprehensive.test.tsx} +24 -24
  486. package/src/rbac/adapters.test.tsx +22 -22
  487. package/src/rbac/adapters.tsx +29 -29
  488. package/src/rbac/api.test.ts +973 -42
  489. package/src/rbac/api.ts +228 -253
  490. package/src/rbac/{__tests__/audit-batched.test.ts → audit-batched.test.ts} +6 -6
  491. package/src/rbac/audit.ts +4 -1
  492. package/src/rbac/{__tests__/auth-rbac-security.integration.test.tsx → auth-rbac-security.integration.test.tsx} +1 -1
  493. package/src/rbac/{__tests__/auth-rbac.e2e.test.tsx → auth-rbac.e2e.test.tsx} +27 -34
  494. package/src/rbac/cache-invalidation.test.ts +715 -0
  495. package/src/rbac/components/{__tests__/AccessDenied.test.tsx → AccessDenied.test.tsx} +3 -3
  496. package/src/rbac/components/{__tests__/NavigationGuard.test.tsx → NavigationGuard.test.tsx} +13 -11
  497. package/src/{__tests__/rbac/PagePermissionGuard.test.tsx → rbac/components/PagePermissionGuard.guard.test.tsx} +33 -19
  498. package/src/rbac/components/{__tests__/PagePermissionGuard.performance.test.tsx → PagePermissionGuard.performance.test.tsx} +30 -9
  499. package/src/rbac/components/{__tests__/PagePermissionGuard.race-condition.test.tsx → PagePermissionGuard.race-condition.test.tsx} +7 -7
  500. package/src/rbac/components/{__tests__/PagePermissionGuard.test.tsx → PagePermissionGuard.test.tsx} +10 -10
  501. package/src/rbac/components/PagePermissionGuard.tsx +177 -372
  502. package/src/rbac/components/{__tests__/PagePermissionGuard.verification.test.tsx → PagePermissionGuard.verification.test.tsx} +7 -7
  503. package/src/rbac/config.ts +58 -18
  504. package/src/rbac/{__tests__/engine.comprehensive.test.ts → engine.comprehensive.test.ts} +3 -3
  505. package/src/rbac/engine.test.ts +494 -0
  506. package/src/rbac/errors.ts +89 -55
  507. package/src/rbac/hooks/permissions/runPermissionCheck.ts +77 -0
  508. package/src/rbac/hooks/permissions/{__tests__/useAccessLevel.test.ts → useAccessLevel.test.ts} +40 -40
  509. package/src/rbac/hooks/permissions/useAccessLevel.ts +16 -6
  510. package/src/rbac/hooks/permissions/{__tests__/useCan.test.ts → useCan.test.ts} +41 -41
  511. package/src/rbac/hooks/permissions/useCan.ts +170 -252
  512. package/src/rbac/hooks/permissions/{__tests__/useMultiplePermissions.test.ts → useMultiplePermissions.test.ts} +49 -49
  513. package/src/rbac/hooks/permissions/useMultiplePermissions.ts +6 -2
  514. package/src/rbac/hooks/permissions/{__tests__/usePermissions.test.ts → usePermissions.test.ts} +10 -12
  515. package/src/rbac/hooks/permissions/usePermissions.ts +36 -65
  516. package/src/rbac/hooks/useCan.test.ts +42 -42
  517. package/src/rbac/hooks/usePageAccessLogging.ts +160 -0
  518. package/src/rbac/hooks/usePageGuardScope.ts +117 -0
  519. package/src/rbac/hooks/usePagePermissionCheck.ts +67 -0
  520. package/src/rbac/hooks/{__tests__/usePermissions.integration.test.ts → usePermissions.integration.test.ts} +9 -9
  521. package/src/{__tests__/hooks/usePermissions.test.ts → rbac/hooks/usePermissions.stability.test.ts} +18 -18
  522. package/src/rbac/hooks/usePermissions.test.ts +54 -54
  523. package/src/rbac/hooks/useRBAC.test.ts +313 -217
  524. package/src/rbac/hooks/useRBAC.ts +145 -81
  525. package/src/rbac/hooks/useResourcePermissions.test.ts +25 -25
  526. package/src/rbac/hooks/useResourcePermissions.ts +68 -134
  527. package/src/rbac/hooks/useResourcePermissionsSuperAdmin.ts +67 -0
  528. package/src/rbac/hooks/useRoleManagement.test.ts +27 -112
  529. package/src/rbac/hooks/useRoleManagement.ts +153 -585
  530. package/src/rbac/hooks/{__tests__/useSecureSupabase.test.ts → useSecureSupabase.test.ts} +17 -17
  531. package/src/rbac/hooks/useSecureSupabase.ts +10 -2
  532. package/src/rbac/hooks/useSuperAdminCheck.ts +80 -0
  533. package/src/rbac/{__tests__/performance.test.ts → performance.test.ts} +1 -1
  534. package/src/rbac/{__tests__/rbac-core.test.tsx → rbac-core.test.tsx} +3 -3
  535. package/src/rbac/{__tests__/rbac-engine-core-logic.test.ts → rbac-engine-core-logic.test.ts} +2 -2
  536. package/src/rbac/{__tests__/rbac-engine-simplified.test.ts → rbac-engine-simplified.test.ts} +3 -3
  537. package/src/rbac/{__tests__/rbac-functions.test.ts → rbac-functions.test.ts} +57 -0
  538. package/src/rbac/{__tests__/rbac-role-isolation.test.ts → rbac-role-isolation.test.ts} +2 -2
  539. package/src/rbac/request-deduplication.test.ts +14 -9
  540. package/src/rbac/request-deduplication.ts +5 -4
  541. package/src/rbac/{__tests__/scenarios.user-role.test.tsx → scenarios.user-role.test.tsx} +23 -23
  542. package/src/rbac/secureClient.test.ts +514 -83
  543. package/src/rbac/secureClient.ts +8 -2
  544. package/src/rbac/security.test.ts +323 -0
  545. package/src/rbac/types/roleManagement.ts +66 -0
  546. package/src/rbac/utils/{__tests__/clientSecurity.test.ts → clientSecurity.test.ts} +4 -4
  547. package/src/rbac/utils/{__tests__/contextValidator.test.ts → contextValidator.test.ts} +4 -4
  548. package/src/rbac/utils/contextValidator.ts +5 -1
  549. package/src/rbac/utils/{__tests__/deep-equal.test.ts → deep-equal.test.ts} +1 -1
  550. package/src/rbac/utils/{__tests__/eventContext.test.ts → eventContext.test.ts} +36 -21
  551. package/src/rbac/utils/eventContext.ts +37 -33
  552. package/src/rbac/utils/fetchPermissionMap.ts +13 -0
  553. package/src/rbac/utils/permissionMapHelpers.ts +34 -0
  554. package/src/rbac/utils/roleManagementRpc.ts +303 -0
  555. package/src/services/{__tests__/AuthService.edge-cases.test.ts → AuthService.edge-cases.test.ts} +19 -19
  556. package/src/services/{__tests__/AuthService.restoreSession.test.ts → AuthService.restoreSession.test.ts} +2 -2
  557. package/src/services/{__tests__/AuthService.test.ts → AuthService.test.ts} +89 -55
  558. package/src/services/AuthService.ts +184 -205
  559. package/src/services/{__tests__/BaseService.edge-cases.test.ts → BaseService.edge-cases.test.ts} +3 -3
  560. package/src/services/{__tests__/BaseService.test.ts → BaseService.test.ts} +2 -2
  561. package/src/services/{__tests__/EventService.edge-cases.test.ts → EventService.edge-cases.test.ts} +27 -24
  562. package/src/services/{__tests__/EventService.eventColours.test.ts → EventService.eventColours.test.ts} +1 -1
  563. package/src/services/{__tests__/EventService.test.ts → EventService.test.ts} +256 -24
  564. package/src/services/EventService.ts +242 -312
  565. package/src/services/{__tests__/InactivityService.edge-cases.test.ts → InactivityService.edge-cases.test.ts} +3 -3
  566. package/src/services/{__tests__/InactivityService.lifecycle.test.ts → InactivityService.lifecycle.test.ts} +2 -2
  567. package/src/services/{__tests__/InactivityService.test.ts → InactivityService.test.ts} +179 -4
  568. package/src/services/InactivityService.ts +172 -213
  569. package/src/services/{__tests__/OrganisationService.edge-cases.test.ts → OrganisationService.edge-cases.test.ts} +5 -5
  570. package/src/services/{__tests__/OrganisationService.pagination.test.ts → OrganisationService.pagination.test.ts} +4 -4
  571. package/src/services/{__tests__/OrganisationService.test.ts → OrganisationService.test.ts} +410 -7
  572. package/src/services/OrganisationService.ts +184 -238
  573. package/src/services/base/BaseService.test.ts +1 -1
  574. package/src/services/interfaces/{__tests__/IAuthService.test.ts → IAuthService.test.ts} +21 -27
  575. package/src/services/interfaces/IAuthService.ts +10 -9
  576. package/src/services/interfaces/{__tests__/IEventService.test.ts → IEventService.test.ts} +4 -4
  577. package/src/services/interfaces/{__tests__/IInactivityService.test.ts → IInactivityService.test.ts} +3 -3
  578. package/src/services/interfaces/{__tests__/IOrganisationService.test.ts → IOrganisationService.test.ts} +3 -3
  579. package/src/styles/core.css +243 -12
  580. package/src/theming/{__tests__/parseEventColours.test.ts → parseEventColours.test.ts} +1 -1
  581. package/src/theming/{__tests__/runtime.test.ts → runtime.test.ts} +8 -17
  582. package/src/theming/runtime.ts +71 -2
  583. package/src/types/api-result.ts +53 -0
  584. package/src/types/{__tests__/core.test.ts → core.test.ts} +2 -2
  585. package/src/types/{__tests__/database-generated.test.ts → database-generated.test.ts} +3 -3
  586. package/src/types/database.generated.ts +45 -10
  587. package/src/types/event.ts +38 -18
  588. package/src/types/{__tests__/file-reference.test.ts → file-reference.test.ts} +13 -13
  589. package/src/types/file-reference.ts +37 -12
  590. package/src/types/{__tests__/guards.test.ts → guards.test.ts} +2 -2
  591. package/src/types/{__tests__/index.test.ts → index.test.ts} +2 -2
  592. package/src/types/index.ts +3 -0
  593. package/src/types/{__tests__/organisation.roles.test.ts → organisation.roles.test.ts} +1 -1
  594. package/src/types/{__tests__/organisation.test.ts → organisation.test.ts} +3 -31
  595. package/src/types/organisation.ts +15 -15
  596. package/src/types/supabase.ts +13 -4
  597. package/src/types/{__tests__/theme.test.ts → theme.test.ts} +1 -1
  598. package/src/types/{__tests__/type-validation.test.ts → type-validation.test.ts} +1 -1
  599. package/src/types/{__tests__/validation.test.ts → validation.test.ts} +2 -2
  600. package/src/utils/app/appIdResolver.test.ts +98 -71
  601. package/src/utils/app/appIdResolver.ts +31 -20
  602. package/src/utils/{__tests__/appConfig.unit.test.ts → appConfig.unit.test.ts} +1 -1
  603. package/src/utils/{__tests__/audit.unit.test.ts → audit.unit.test.ts} +1 -1
  604. package/src/utils/{__tests__/auth-utils.unit.test.ts → auth-utils.unit.test.ts} +16 -17
  605. package/src/utils/{__tests__/bundleAnalysis.unit.test.ts → bundleAnalysis.unit.test.ts} +35 -35
  606. package/src/utils/{__tests__/cn.unit.test.ts → cn.unit.test.ts} +1 -1
  607. package/src/utils/context/organisationContext.test.ts +105 -91
  608. package/src/utils/context/organisationContext.ts +29 -40
  609. package/src/utils/core/{__tests__/cn.test.ts → cn.test.ts} +3 -3
  610. package/src/utils/core/{__tests__/debugLogger.test.ts → debugLogger.test.ts} +2 -2
  611. package/src/utils/core/{__tests__/logger.test.ts → logger.test.ts} +2 -2
  612. package/src/utils/core/mergeRefs.ts +24 -0
  613. package/src/utils/{__tests__/debugLogger.test.ts → debugLogger.test.ts} +1 -1
  614. package/src/utils/{__tests__/deviceFingerprint.unit.test.ts → deviceFingerprint.unit.test.ts} +1 -1
  615. package/src/utils/dynamic/createLazyComponent.tsx +9 -1
  616. package/src/utils/dynamic/{__tests__/dynamicUtils.test.ts → dynamicUtils.test.ts} +2 -2
  617. package/src/utils/dynamic/{__tests__/lazyLoad.test.tsx → lazyLoad.test.tsx} +2 -2
  618. package/src/utils/{__tests__/dynamicUtils.unit.test.ts → dynamicUtils.unit.test.ts} +1 -1
  619. package/src/utils/file-reference/{__tests__/file-reference.test.ts → file-reference.test.ts} +214 -289
  620. package/src/utils/file-reference/index.ts +330 -347
  621. package/src/utils/{__tests__/formatDate.unit.test.ts → formatDate.unit.test.ts} +2 -2
  622. package/src/utils/formatting/formatDateTimeTimezone.test.ts +1 -1
  623. package/src/utils/formatting/formatNumber.test.ts +1 -1
  624. package/src/utils/{__tests__/formatting.unit.test.ts → formatting.unit.test.ts} +1 -1
  625. package/src/utils/google-places/googlePlacesUtils.test.ts +70 -48
  626. package/src/utils/google-places/googlePlacesUtils.ts +67 -99
  627. package/src/utils/google-places/loadGoogleMapsScript.test.ts +25 -22
  628. package/src/utils/google-places/loadGoogleMapsScript.ts +138 -117
  629. package/src/utils/{__tests__/index.unit.test.ts → index.unit.test.ts} +1 -1
  630. package/src/utils/{__tests__/lazyLoad.unit.test.tsx → lazyLoad.unit.test.tsx} +13 -14
  631. package/src/utils/location/location.test.ts +1 -1
  632. package/src/utils/{__tests__/logger.unit.test.ts → logger.unit.test.ts} +1 -1
  633. package/src/utils/{__tests__/organisationContext.unit.test.ts → organisationContext.unit.test.ts} +37 -48
  634. package/src/utils/performance/{__tests__/bundleAnalysis.test.ts → bundleAnalysis.test.ts} +2 -2
  635. package/src/utils/performance/{__tests__/performanceBenchmark.test.ts → performanceBenchmark.test.ts} +2 -2
  636. package/src/utils/performance/{__tests__/performanceBudgets.test.ts → performanceBudgets.test.ts} +2 -2
  637. package/src/utils/{__tests__/performanceBenchmark.test.ts → performanceBenchmark.test.ts} +2 -2
  638. package/src/utils/{__tests__/performanceBudgets.unit.test.ts → performanceBudgets.unit.test.ts} +2 -2
  639. package/src/utils/{__tests__/permissionTypes.unit.test.ts → permissionTypes.unit.test.ts} +1 -1
  640. package/src/utils/{__tests__/permissionUtils.unit.test.ts → permissionUtils.unit.test.ts} +1 -1
  641. package/src/utils/permissions/{__tests__/permissionTypes.test.ts → permissionTypes.test.ts} +2 -2
  642. package/src/utils/persistence/{__tests__/keyDerivation.test.ts → keyDerivation.test.ts} +2 -2
  643. package/src/utils/persistence/{__tests__/sensitiveFieldDetection.test.ts → sensitiveFieldDetection.test.ts} +2 -2
  644. package/src/utils/{__tests__/request-deduplication.test.ts → request-deduplication.test.ts} +2 -2
  645. package/src/utils/{__tests__/sanitization.unit.test.ts → sanitization.unit.test.ts} +1 -1
  646. package/src/utils/{__tests__/schemaUtils.unit.test.ts → schemaUtils.unit.test.ts} +1 -1
  647. package/src/utils/{__tests__/secureDataAccess.unit.test.ts → secureDataAccess.unit.test.ts} +2 -2
  648. package/src/utils/{__tests__/secureErrors.unit.test.ts → secureErrors.unit.test.ts} +4 -4
  649. package/src/utils/{__tests__/secureStorage.unit.test.ts → secureStorage.unit.test.ts} +1 -1
  650. package/src/utils/security/auth-utils.ts +34 -23
  651. package/src/utils/security/secureDataAccess.ts +241 -281
  652. package/src/utils/security/secureErrors.test.ts +1 -1
  653. package/src/utils/security/secureStorage.test.ts +1 -1
  654. package/src/utils/security/security.test.ts +25 -17
  655. package/src/utils/security/security.ts +15 -18
  656. package/src/utils/security/securityMonitor.test.ts +1 -1
  657. package/src/utils/{__tests__/security.unit.test.ts → security.unit.test.ts} +21 -15
  658. package/src/utils/{__tests__/securityMonitor.unit.test.ts → securityMonitor.unit.test.ts} +1 -1
  659. package/src/utils/{__tests__/sessionTracking.unit.test.ts → sessionTracking.unit.test.ts} +12 -12
  660. package/src/utils/storage/{__tests__/config.unit.test.ts → config.unit.test.ts} +2 -2
  661. package/src/utils/storage/helpers.test.ts +88 -102
  662. package/src/utils/storage/helpers.ts +173 -251
  663. package/src/utils/storage/{__tests__/index.unit.test.ts → index.unit.test.ts} +3 -3
  664. package/src/utils/storage/types.ts +7 -0
  665. package/src/utils/supabase/createBaseClient.test.ts +1 -1
  666. package/src/utils/timezone/timezone.test.ts +1 -1
  667. package/src/utils/{__tests__/timezone.test.ts → timezone.test.ts} +2 -2
  668. package/src/utils/validation/{__tests__/common.test.ts → common.test.ts} +2 -2
  669. package/src/utils/validation/{__tests__/csrf.test.ts → csrf.test.ts} +56 -28
  670. package/src/utils/validation/csrf.ts +42 -41
  671. package/src/utils/validation/{__tests__/htmlSanitization.unit.test.ts → htmlSanitization.unit.test.ts} +2 -2
  672. package/src/utils/validation/{__tests__/passwordSchema.test.ts → passwordSchema.test.ts} +2 -2
  673. package/src/utils/validation/{__tests__/schema.test.ts → schema.test.ts} +2 -2
  674. package/src/utils/validation/{__tests__/sqlInjectionProtection.test.ts → sqlInjectionProtection.test.ts} +2 -2
  675. package/src/utils/validation/{__tests__/user.test.ts → user.test.ts} +2 -2
  676. package/src/utils/validation/{__tests__/validation.test.ts → validation.test.ts} +2 -2
  677. package/src/utils/validation/{__tests__/validationUtils.test.ts → validationUtils.test.ts} +2 -2
  678. package/src/utils/{__tests__/validation.unit.test.ts → validation.unit.test.ts} +1 -1
  679. package/src/utils/{__tests__/validationUtils.unit.test.ts → validationUtils.unit.test.ts} +5 -2
  680. package/dist/UnifiedAuthProvider-BBD2PS3Q.js +0 -7
  681. package/dist/chunk-KPYQWGFQ.js +0 -183
  682. package/dist/types-D05dCGma.d.ts +0 -521
  683. package/scripts/eslint-audit.cjs +0 -222
  684. package/scripts/generate-docs.js +0 -157
  685. package/scripts/install-cursor-rules.cjs +0 -255
  686. package/scripts/install-eslint-config.cjs +0 -349
  687. package/scripts/setup-build-cache.js +0 -73
  688. package/scripts/validate-pre-publish.js +0 -145
  689. package/src/__tests__/integration/UserProfile.test.tsx +0 -124
  690. package/src/__tests__/public-recipe-view.test.ts +0 -228
  691. package/src/__tests__/rls-policies.test.ts +0 -472
  692. package/src/components/DataTable/__tests__/DataTable.test.tsx +0 -876
  693. package/src/components/DataTable/components/DataTableLayout.tsx +0 -584
  694. package/src/components/DataTable/components/UnifiedTableBody.tsx +0 -395
  695. package/src/components/DataTable/components/__tests__/DataTableLayout.test.tsx +0 -467
  696. package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +0 -358
  697. package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +0 -957
  698. package/src/components/DataTable/core/ActionManager.ts +0 -235
  699. package/src/components/DataTable/core/ColumnManager.ts +0 -204
  700. package/src/components/DataTable/core/DataManager.ts +0 -190
  701. package/src/components/DataTable/core/LocalDataAdapter.ts +0 -274
  702. package/src/components/DataTable/core/PluginRegistry.ts +0 -229
  703. package/src/components/DataTable/core/StateManager.ts +0 -312
  704. package/src/components/DataTable/core/__tests__/ActionManager.test.ts +0 -235
  705. package/src/components/DataTable/core/__tests__/ColumnManager.test.ts +0 -141
  706. package/src/components/DataTable/core/__tests__/DataManager.test.ts +0 -178
  707. package/src/components/DataTable/core/__tests__/LocalDataAdapter.test.ts +0 -133
  708. package/src/components/DataTable/core/__tests__/PluginRegistry.test.ts +0 -142
  709. package/src/components/DataTable/core/__tests__/StateManager.test.ts +0 -158
  710. package/src/components/DataTable/core/interfaces.ts +0 -338
  711. package/src/components/DataTable/types.ts +0 -764
  712. package/src/hooks/public/usePublicFileDisplay.ts +0 -534
  713. package/src/hooks/useFileDisplay.ts +0 -748
  714. package/src/providers/OrganisationProvider.test.tsx +0 -40
  715. package/src/providers/OrganisationProvider.tsx +0 -92
  716. package/src/providers/__tests__/InactivityProvider.test-helper.tsx +0 -65
  717. package/src/providers/__tests__/OrganisationProvider.test.tsx +0 -616
  718. package/src/providers/__tests__/OrganisationProvider.wrapper.test.tsx +0 -591
  719. package/src/rbac/__tests__/cache-invalidation.test.ts +0 -393
  720. /package/src/components/DataTable/{components/__tests__ → ui}/COVERAGE_NOTE.md +0 -0
  721. /package/src/components/DataTable/utils/{__tests__/COVERAGE_NOTE.md → COVERAGE_NOTE.md} +0 -0
  722. /package/src/hooks/{__tests__/useApiFetch.unit.test.ts → useApiFetch.unit.test.ts} +0 -0
  723. /package/src/providers/{__tests__/README.md → README.md} +0 -0
  724. /package/src/rbac/{__tests__/index.test.ts → index.test.ts} +0 -0
  725. /package/src/rbac/{__tests__/rbac-integration.test.ts → rbac-integration.test.ts} +0 -0
  726. /package/src/types/{__tests__/README.md → README.md} +0 -0
@@ -14,20 +14,20 @@ import { screen, waitFor, within } from '@testing-library/react';
14
14
  import userEvent from '@testing-library/user-event';
15
15
  import { describe, it, expect, vi, beforeEach } from 'vitest';
16
16
  import '@testing-library/jest-dom/vitest';
17
- import {
18
- Dialog,
19
- DialogTrigger,
20
- DialogContent,
21
- DialogHeader,
22
- DialogBody,
23
- DialogFooter,
17
+ import {
18
+ Dialog,
19
+ DialogTrigger,
20
+ DialogContent,
21
+ DialogHeader,
22
+ DialogBody,
23
+ DialogFooter,
24
24
  DialogClose,
25
25
  DialogTitle,
26
- DialogDescription
26
+ DialogDescription,
27
27
  } from './Dialog';
28
28
  import { renderWithProviders } from '../../__tests__/helpers/test-utils';
29
+ import { waitForDialog, setupDialogEnv } from './Dialog.test-utils';
29
30
 
30
- // Mock logger - must define before other mocks
31
31
  const mockLogger = {
32
32
  debug: vi.fn(),
33
33
  info: vi.fn(),
@@ -39,33 +39,6 @@ vi.mock('../../utils/core/logger', () => ({
39
39
  createLogger: () => mockLogger,
40
40
  }));
41
41
 
42
- // Helper function to wait for dialog to be accessible
43
- // Native dialog elements are only accessible after showModal() completes
44
- // In test environments, we use querySelector as fallback since getByRole may not work
45
- // Note: In test environments (jsdom), dialog.open may not be set even when dialog is rendered
46
- const waitForDialog = async (): Promise<HTMLElement> => {
47
- return await waitFor(
48
- () => {
49
- // Try getByRole first (works in browsers with full dialog support)
50
- try {
51
- const dialog = screen.getByRole('dialog');
52
- expect(dialog).toBeInTheDocument();
53
- return dialog;
54
- } catch (_e) {
55
- // Fallback: use querySelector for test environments that don't fully support dialog accessibility
56
- const dialog = document.querySelector('dialog[role="dialog"]') as HTMLDialogElement;
57
- if (!dialog) {
58
- throw new Error('Dialog not found in DOM');
59
- }
60
- // In test environments, dialog.open may not be set even when dialog is rendered
61
- // Just check that dialog exists in DOM - that's sufficient for testing
62
- return dialog;
63
- }
64
- },
65
- { timeout: 3000 }
66
- );
67
- };
68
-
69
42
  // Mock lodash debounce to avoid timing issues in tests
70
43
  vi.mock('lodash', () => ({
71
44
  debounce: (fn: Function) => fn
@@ -106,24 +79,7 @@ vi.mock('../../hooks/useFocusTrap', () => ({
106
79
  describe('Dialog Component System', () => {
107
80
  beforeEach(() => {
108
81
  vi.clearAllMocks();
109
- // Mock console methods to avoid noise in tests
110
- vi.spyOn(console, 'log').mockImplementation(() => {});
111
- vi.spyOn(console, 'warn').mockImplementation(() => {});
112
-
113
- // Mock showModal for dialog elements (needed for test environments)
114
- // Override the prototype methods to ensure they're always available
115
- if (typeof HTMLDialogElement !== 'undefined') {
116
- HTMLDialogElement.prototype.showModal = vi.fn(function(this: HTMLDialogElement) {
117
- this.setAttribute('open', '');
118
- this.open = true;
119
- this.dispatchEvent(new Event('show', { bubbles: true }));
120
- });
121
- HTMLDialogElement.prototype.close = vi.fn(function(this: HTMLDialogElement) {
122
- this.removeAttribute('open');
123
- this.open = false;
124
- this.dispatchEvent(new Event('close', { bubbles: true }));
125
- });
126
- }
82
+ setupDialogEnv();
127
83
  });
128
84
 
129
85
  describe('Dialog Root Component', () => {
@@ -1048,7 +1004,7 @@ describe('Dialog Component System', () => {
1048
1004
 
1049
1005
  it('renders multiple dialog triggers independently', () => {
1050
1006
  renderWithProviders(
1051
- <div>
1007
+ <>
1052
1008
  <Dialog>
1053
1009
  <DialogTrigger asChild>
1054
1010
  <button>Open Dialog 1</button>
@@ -1069,7 +1025,7 @@ describe('Dialog Component System', () => {
1069
1025
  </DialogHeader>
1070
1026
  </DialogContent>
1071
1027
  </Dialog>
1072
- </div>
1028
+ </>
1073
1029
  );
1074
1030
 
1075
1031
  expect(screen.getByRole('button', { name: 'Open Dialog 1' })).toBeInTheDocument();
@@ -1750,36 +1706,6 @@ describe('Dialog Component System', () => {
1750
1706
  });
1751
1707
  });
1752
1708
 
1753
- describe('DialogOverlay Component', () => {
1754
- it('does not render (native dialog provides backdrop automatically)', async () => {
1755
- // DialogOverlay component returns null because native <dialog> element
1756
- // provides ::backdrop automatically via CSS
1757
- // This test verifies that dialogs work correctly without DialogOverlay
1758
- const user = userEvent.setup();
1759
-
1760
- renderWithProviders(
1761
- <Dialog>
1762
- <DialogTrigger asChild>
1763
- <button>Open Dialog</button>
1764
- </DialogTrigger>
1765
- <DialogContent title="Test Dialog">
1766
- <DialogHeader>
1767
- <h2>Test Dialog</h2>
1768
- </DialogHeader>
1769
- </DialogContent>
1770
- </Dialog>
1771
- );
1772
-
1773
- await user.click(screen.getByRole('button', { name: 'Open Dialog' }));
1774
- await waitForDialog();
1775
-
1776
- // Dialog should render and function correctly without DialogOverlay
1777
- // (backdrop is provided by native dialog element)
1778
- const dialog = document.querySelector('dialog[role="dialog"]');
1779
- expect(dialog).toBeInTheDocument();
1780
- });
1781
- });
1782
-
1783
1709
  describe('Context Error Handling', () => {
1784
1710
  it('throws error when DialogTrigger is used outside Dialog', () => {
1785
1711
  // Suppress console.error for this test
@@ -2585,11 +2511,11 @@ describe('Dialog Component System', () => {
2585
2511
  });
2586
2512
 
2587
2513
  describe('DialogTrigger Edge Cases', () => {
2588
- it('handles asChild with non-React element gracefully', () => {
2514
+ it('handles asChild with non-button element gracefully', () => {
2589
2515
  renderWithProviders(
2590
2516
  <Dialog>
2591
2517
  <DialogTrigger asChild>
2592
- <div>Not a button</div>
2518
+ <span>Not a button</span>
2593
2519
  </DialogTrigger>
2594
2520
  <DialogContent title="Test Dialog">
2595
2521
  <DialogHeader>
@@ -2599,7 +2525,6 @@ describe('Dialog Component System', () => {
2599
2525
  </Dialog>
2600
2526
  );
2601
2527
 
2602
- // Should render the div
2603
2528
  expect(screen.getByText('Not a button')).toBeInTheDocument();
2604
2529
  });
2605
2530
 
@@ -2813,4 +2738,886 @@ describe('Dialog Component System', () => {
2813
2738
  unmountFirst();
2814
2739
  });
2815
2740
  });
2741
+
2742
+ describe('Ref Forwarding', () => {
2743
+ it('forwards ref correctly to dialog element', async () => {
2744
+ const user = userEvent.setup();
2745
+ const ref = React.createRef<HTMLDialogElement>();
2746
+
2747
+ renderWithProviders(
2748
+ <Dialog>
2749
+ <DialogTrigger asChild>
2750
+ <button>Open Dialog</button>
2751
+ </DialogTrigger>
2752
+ <DialogContent ref={ref} title="Test Dialog">
2753
+ <DialogHeader>
2754
+ <h2>Test Dialog</h2>
2755
+ </DialogHeader>
2756
+ </DialogContent>
2757
+ </Dialog>
2758
+ );
2759
+
2760
+ await user.click(screen.getByRole('button', { name: 'Open Dialog' }));
2761
+ const dialog = await waitForDialog();
2762
+
2763
+ expect(ref.current).toBeInstanceOf(HTMLDialogElement);
2764
+ expect(ref.current).toBe(dialog);
2765
+ });
2766
+
2767
+ it('ref persists when dialog is closed', async () => {
2768
+ const user = userEvent.setup();
2769
+ const ref = React.createRef<HTMLDialogElement>();
2770
+
2771
+ const { rerender } = renderWithProviders(
2772
+ <Dialog open={true}>
2773
+ <DialogContent ref={ref} title="Test Dialog">
2774
+ <DialogHeader>
2775
+ <h2>Test Dialog</h2>
2776
+ </DialogHeader>
2777
+ </DialogContent>
2778
+ </Dialog>
2779
+ );
2780
+
2781
+ await waitForDialog();
2782
+ expect(ref.current).toBeInstanceOf(HTMLDialogElement);
2783
+
2784
+ // Close dialog
2785
+ rerender(
2786
+ <Dialog open={false}>
2787
+ <DialogContent ref={ref} title="Test Dialog">
2788
+ <DialogHeader>
2789
+ <h2>Test Dialog</h2>
2790
+ </DialogHeader>
2791
+ </DialogContent>
2792
+ </Dialog>
2793
+ );
2794
+
2795
+ await waitFor(() => {
2796
+ expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
2797
+ });
2798
+
2799
+ // React refs persist even when element is removed from DOM
2800
+ // The ref.current will still point to the element until component unmounts
2801
+ // This is expected React behavior
2802
+ if (ref.current) {
2803
+ expect(ref.current).toBeInstanceOf(HTMLDialogElement);
2804
+ }
2805
+ });
2806
+
2807
+ it('handles ref callback function', async () => {
2808
+ const user = userEvent.setup();
2809
+ let refElement: HTMLDialogElement | null = null;
2810
+ const refCallback = (node: HTMLDialogElement | null) => {
2811
+ refElement = node;
2812
+ };
2813
+
2814
+ renderWithProviders(
2815
+ <Dialog>
2816
+ <DialogTrigger asChild>
2817
+ <button>Open Dialog</button>
2818
+ </DialogTrigger>
2819
+ <DialogContent ref={refCallback} title="Test Dialog">
2820
+ <DialogHeader>
2821
+ <h2>Test Dialog</h2>
2822
+ </DialogHeader>
2823
+ </DialogContent>
2824
+ </Dialog>
2825
+ );
2826
+
2827
+ await user.click(screen.getByRole('button', { name: 'Open Dialog' }));
2828
+ await waitForDialog();
2829
+
2830
+ expect(refElement).toBeInstanceOf(HTMLDialogElement);
2831
+ });
2832
+ });
2833
+
2834
+ describe('DialogContent Props Spreading', () => {
2835
+ it('spreads additional props to dialog element', async () => {
2836
+ const user = userEvent.setup();
2837
+
2838
+ renderWithProviders(
2839
+ <Dialog>
2840
+ <DialogTrigger asChild>
2841
+ <button>Open Dialog</button>
2842
+ </DialogTrigger>
2843
+ <DialogContent
2844
+ title="Test Dialog"
2845
+ data-testid="custom-dialog"
2846
+ data-custom-attr="custom-value"
2847
+ >
2848
+ <DialogHeader>
2849
+ <h2>Test Dialog</h2>
2850
+ </DialogHeader>
2851
+ </DialogContent>
2852
+ </Dialog>
2853
+ );
2854
+
2855
+ await user.click(screen.getByRole('button', { name: 'Open Dialog' }));
2856
+ const dialog = await waitForDialog();
2857
+
2858
+ expect(dialog).toHaveAttribute('data-testid', 'custom-dialog');
2859
+ expect(dialog).toHaveAttribute('data-custom-attr', 'custom-value');
2860
+ });
2861
+
2862
+ it('merges custom style with smart dimensions', async () => {
2863
+ const user = userEvent.setup();
2864
+
2865
+ renderWithProviders(
2866
+ <Dialog>
2867
+ <DialogTrigger asChild>
2868
+ <button>Open Dialog</button>
2869
+ </DialogTrigger>
2870
+ <DialogContent
2871
+ title="Test Dialog"
2872
+ maxHeightPercent={80}
2873
+ >
2874
+ <DialogHeader>
2875
+ <h2>Test Dialog</h2>
2876
+ </DialogHeader>
2877
+ </DialogContent>
2878
+ </Dialog>
2879
+ );
2880
+
2881
+ await user.click(screen.getByRole('button', { name: 'Open Dialog' }));
2882
+ const dialog = await waitForDialog();
2883
+
2884
+ expect(dialog).toHaveStyle({ maxHeight: '80vh' });
2885
+ const style = dialog.getAttribute('style');
2886
+ expect(style).toBeTruthy();
2887
+ expect(style).toContain('max-height');
2888
+ });
2889
+ });
2890
+
2891
+ describe('DialogBody Flex Container Detection Edge Cases', () => {
2892
+ it('handles flex container detection when dialog is closed', () => {
2893
+ renderWithProviders(
2894
+ <Dialog open={false}>
2895
+ <DialogContent title="Test Dialog" maxHeightPercent={80}>
2896
+ <DialogHeader>
2897
+ <h2>Test Dialog</h2>
2898
+ </DialogHeader>
2899
+ <DialogBody>
2900
+ <p>Content</p>
2901
+ </DialogBody>
2902
+ </DialogContent>
2903
+ </Dialog>
2904
+ );
2905
+
2906
+ // Should not crash when dialog is closed
2907
+ expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
2908
+ });
2909
+
2910
+ it('handles flex container detection timeout cleanup', async () => {
2911
+ const user = userEvent.setup();
2912
+ const { unmount } = renderWithProviders(
2913
+ <Dialog>
2914
+ <DialogTrigger asChild>
2915
+ <button>Open Dialog</button>
2916
+ </DialogTrigger>
2917
+ <DialogContent title="Test Dialog" maxHeightPercent={80}>
2918
+ <DialogHeader>
2919
+ <h2>Test Dialog</h2>
2920
+ </DialogHeader>
2921
+ <DialogBody>
2922
+ <p>Content</p>
2923
+ </DialogBody>
2924
+ </DialogContent>
2925
+ </Dialog>
2926
+ );
2927
+
2928
+ await user.click(screen.getByRole('button', { name: 'Open Dialog' }));
2929
+ await waitForDialog();
2930
+
2931
+ // Unmount should clean up timeouts
2932
+ unmount();
2933
+
2934
+ // Should not crash
2935
+ expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
2936
+ });
2937
+ });
2938
+
2939
+ describe('DialogTitle and DialogDescription Ref Forwarding', () => {
2940
+ it('forwards ref correctly to DialogTitle', async () => {
2941
+ const user = userEvent.setup();
2942
+ const ref = React.createRef<HTMLHeadingElement>();
2943
+
2944
+ renderWithProviders(
2945
+ <Dialog>
2946
+ <DialogTrigger asChild>
2947
+ <button>Open Dialog</button>
2948
+ </DialogTrigger>
2949
+ <DialogContent title="Test Dialog">
2950
+ <DialogHeader>
2951
+ <DialogTitle ref={ref}>Custom Title</DialogTitle>
2952
+ </DialogHeader>
2953
+ </DialogContent>
2954
+ </Dialog>
2955
+ );
2956
+
2957
+ await user.click(screen.getByRole('button', { name: 'Open Dialog' }));
2958
+ await waitForDialog();
2959
+
2960
+ expect(ref.current).toBeInstanceOf(HTMLHeadingElement);
2961
+ expect(ref.current?.tagName).toBe('H2');
2962
+ });
2963
+
2964
+ it('forwards ref correctly to DialogDescription', async () => {
2965
+ const user = userEvent.setup();
2966
+ const ref = React.createRef<HTMLParagraphElement>();
2967
+
2968
+ renderWithProviders(
2969
+ <Dialog>
2970
+ <DialogTrigger asChild>
2971
+ <button>Open Dialog</button>
2972
+ </DialogTrigger>
2973
+ <DialogContent title="Test Dialog">
2974
+ <DialogHeader>
2975
+ <DialogTitle>Test Dialog</DialogTitle>
2976
+ <DialogDescription ref={ref}>Custom Description</DialogDescription>
2977
+ </DialogHeader>
2978
+ </DialogContent>
2979
+ </Dialog>
2980
+ );
2981
+
2982
+ await user.click(screen.getByRole('button', { name: 'Open Dialog' }));
2983
+ await waitForDialog();
2984
+
2985
+ expect(ref.current).toBeInstanceOf(HTMLParagraphElement);
2986
+ expect(ref.current?.tagName).toBe('P');
2987
+ });
2988
+ });
2989
+
2990
+ describe('DialogHeader and DialogFooter Sticky Behavior', () => {
2991
+ it('applies sticky styles correctly when enableScrolling is true', async () => {
2992
+ const user = userEvent.setup();
2993
+
2994
+ renderWithProviders(
2995
+ <Dialog>
2996
+ <DialogTrigger asChild>
2997
+ <button>Open Dialog</button>
2998
+ </DialogTrigger>
2999
+ <DialogContent title="Test Dialog" enableScrolling>
3000
+ <DialogHeader sticky>
3001
+ <h2>Sticky Header</h2>
3002
+ </DialogHeader>
3003
+ <DialogBody>
3004
+ <p>Content</p>
3005
+ </DialogBody>
3006
+ <DialogFooter sticky>
3007
+ <button>Save</button>
3008
+ </DialogFooter>
3009
+ </DialogContent>
3010
+ </Dialog>
3011
+ );
3012
+
3013
+ await user.click(screen.getByRole('button', { name: 'Open Dialog' }));
3014
+ await waitForDialog();
3015
+
3016
+ const header = document.querySelector('dialog header');
3017
+ const footer = document.querySelector('dialog footer');
3018
+
3019
+ expect(header).toHaveClass('sticky', 'top-0');
3020
+ expect(footer).toHaveClass('sticky', 'bottom-0');
3021
+ });
3022
+
3023
+ it('does not apply sticky styles when sticky prop is false', async () => {
3024
+ const user = userEvent.setup();
3025
+
3026
+ renderWithProviders(
3027
+ <Dialog>
3028
+ <DialogTrigger asChild>
3029
+ <button>Open Dialog</button>
3030
+ </DialogTrigger>
3031
+ <DialogContent title="Test Dialog" enableScrolling>
3032
+ <DialogHeader sticky={false}>
3033
+ <h2>Non-Sticky Header</h2>
3034
+ </DialogHeader>
3035
+ <DialogBody>
3036
+ <p>Content</p>
3037
+ </DialogBody>
3038
+ <DialogFooter sticky={false}>
3039
+ <button>Save</button>
3040
+ </DialogFooter>
3041
+ </DialogContent>
3042
+ </Dialog>
3043
+ );
3044
+
3045
+ await user.click(screen.getByRole('button', { name: 'Open Dialog' }));
3046
+ await waitForDialog();
3047
+
3048
+ const header = document.querySelector('dialog header');
3049
+ const footer = document.querySelector('dialog footer');
3050
+
3051
+ expect(header).not.toHaveClass('sticky');
3052
+ expect(footer).not.toHaveClass('sticky');
3053
+ });
3054
+ });
3055
+
3056
+ describe('DialogContent Size Variants - Extended', () => {
3057
+ it('applies correct classes for full size variant', async () => {
3058
+ const user = userEvent.setup();
3059
+
3060
+ renderWithProviders(
3061
+ <Dialog>
3062
+ <DialogTrigger asChild>
3063
+ <button>Open Dialog</button>
3064
+ </DialogTrigger>
3065
+ <DialogContent size="full" title="Full Dialog">
3066
+ <DialogHeader>
3067
+ <h2>Full Dialog</h2>
3068
+ </DialogHeader>
3069
+ </DialogContent>
3070
+ </Dialog>
3071
+ );
3072
+
3073
+ await user.click(screen.getByRole('button', { name: 'Open Dialog' }));
3074
+ const dialog = await waitForDialog();
3075
+
3076
+ expect(dialog).toHaveClass('max-w-full', 'size-full');
3077
+ });
3078
+
3079
+ it('applies correct classes for auto size variant', async () => {
3080
+ const user = userEvent.setup();
3081
+
3082
+ renderWithProviders(
3083
+ <Dialog>
3084
+ <DialogTrigger asChild>
3085
+ <button>Open Dialog</button>
3086
+ </DialogTrigger>
3087
+ <DialogContent size="auto" title="Auto Dialog">
3088
+ <DialogHeader>
3089
+ <h2>Auto Dialog</h2>
3090
+ </DialogHeader>
3091
+ </DialogContent>
3092
+ </Dialog>
3093
+ );
3094
+
3095
+ await user.click(screen.getByRole('button', { name: 'Open Dialog' }));
3096
+ const dialog = await waitForDialog();
3097
+
3098
+ // Auto size applies w-fit and max-w-[90vw] sm:max-w-[80vw] classes
3099
+ expect(dialog).toHaveClass('w-fit');
3100
+ expect(dialog).toHaveClass('min-w-0');
3101
+ });
3102
+ });
3103
+
3104
+ describe('Dialog Persistence - Extended Scenarios', () => {
3105
+ beforeEach(() => {
3106
+ sessionStorage.clear();
3107
+ });
3108
+
3109
+ it('handles persistence when title changes', async () => {
3110
+ const user = userEvent.setup();
3111
+ const { useSessionDraft } = await import('../../hooks/useSessionDraft');
3112
+ const mockSetState = vi.fn();
3113
+
3114
+ vi.mocked(useSessionDraft).mockReturnValue({
3115
+ state: false,
3116
+ setState: mockSetState,
3117
+ clearDraft: vi.fn(),
3118
+ wasRestored: false,
3119
+ saveImmediately: vi.fn(),
3120
+ });
3121
+
3122
+ const { rerender } = renderWithProviders(
3123
+ <Dialog>
3124
+ <DialogTrigger asChild>
3125
+ <button>Open Dialog</button>
3126
+ </DialogTrigger>
3127
+ <DialogContent title="Original Title" persistOpenState={true}>
3128
+ <DialogHeader>
3129
+ <h2>Original Title</h2>
3130
+ </DialogHeader>
3131
+ </DialogContent>
3132
+ </Dialog>
3133
+ );
3134
+
3135
+ await user.click(screen.getByRole('button', { name: 'Open Dialog' }));
3136
+ await waitForDialog();
3137
+
3138
+ // Change title
3139
+ rerender(
3140
+ <Dialog>
3141
+ <DialogTrigger asChild>
3142
+ <button>Open Dialog</button>
3143
+ </DialogTrigger>
3144
+ <DialogContent title="New Title" persistOpenState={true}>
3145
+ <DialogHeader>
3146
+ <h2>New Title</h2>
3147
+ </DialogHeader>
3148
+ </DialogContent>
3149
+ </Dialog>
3150
+ );
3151
+
3152
+ // Dialog should handle title change gracefully
3153
+ await waitFor(() => {
3154
+ const dialog = document.querySelector('dialog[role="dialog"]');
3155
+ expect(dialog).toHaveAttribute('title', 'New Title');
3156
+ });
3157
+ });
3158
+
3159
+ it('handles persistence when description changes', async () => {
3160
+ const user = userEvent.setup();
3161
+ const { useSessionDraft } = await import('../../hooks/useSessionDraft');
3162
+
3163
+ vi.mocked(useSessionDraft).mockReturnValue({
3164
+ state: false,
3165
+ setState: vi.fn(),
3166
+ clearDraft: vi.fn(),
3167
+ wasRestored: false,
3168
+ saveImmediately: vi.fn(),
3169
+ });
3170
+
3171
+ const { rerender } = renderWithProviders(
3172
+ <Dialog>
3173
+ <DialogTrigger asChild>
3174
+ <button>Open Dialog</button>
3175
+ </DialogTrigger>
3176
+ <DialogContent title="Test Dialog" description="Original Description" persistOpenState={true}>
3177
+ <DialogHeader>
3178
+ <h2>Test Dialog</h2>
3179
+ </DialogHeader>
3180
+ </DialogContent>
3181
+ </Dialog>
3182
+ );
3183
+
3184
+ await user.click(screen.getByRole('button', { name: 'Open Dialog' }));
3185
+ await waitForDialog();
3186
+
3187
+ // Change description
3188
+ rerender(
3189
+ <Dialog>
3190
+ <DialogTrigger asChild>
3191
+ <button>Open Dialog</button>
3192
+ </DialogTrigger>
3193
+ <DialogContent title="Test Dialog" description="New Description" persistOpenState={true}>
3194
+ <DialogHeader>
3195
+ <h2>Test Dialog</h2>
3196
+ </DialogHeader>
3197
+ </DialogContent>
3198
+ </Dialog>
3199
+ );
3200
+
3201
+ // Dialog should handle description change gracefully
3202
+ await waitFor(() => {
3203
+ const dialog = document.querySelector('dialog[role="dialog"]');
3204
+ expect(dialog).toHaveAttribute('aria-description', 'New Description');
3205
+ });
3206
+ });
3207
+ });
3208
+
3209
+ describe('Dialog Lock Mechanism - Extended', () => {
3210
+ beforeEach(() => {
3211
+ sessionStorage.clear();
3212
+ });
3213
+
3214
+ it('handles lock acquisition when another dialog is closing', async () => {
3215
+ const user = userEvent.setup();
3216
+
3217
+ // First dialog
3218
+ const { unmount: unmountFirst } = renderWithProviders(
3219
+ <Dialog>
3220
+ <DialogTrigger asChild>
3221
+ <button>Open Dialog 1</button>
3222
+ </DialogTrigger>
3223
+ <DialogContent title="Dialog 1" persistOpenState={true}>
3224
+ <DialogHeader>
3225
+ <h2>Dialog 1</h2>
3226
+ </DialogHeader>
3227
+ </DialogContent>
3228
+ </Dialog>
3229
+ );
3230
+
3231
+ await user.click(screen.getByRole('button', { name: 'Open Dialog 1' }));
3232
+ await waitForDialog();
3233
+
3234
+ // Close first dialog
3235
+ const dialog1 = document.querySelector('dialog[role="dialog"]');
3236
+ const closeIcon1 = dialog1?.querySelector('[data-testid="lucide-x"]');
3237
+ const closeButton1 = closeIcon1?.closest('button') as HTMLButtonElement;
3238
+ await user.click(closeButton1);
3239
+
3240
+ await waitFor(() => {
3241
+ expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
3242
+ });
3243
+
3244
+ unmountFirst();
3245
+
3246
+ // Second dialog should be able to acquire lock
3247
+ renderWithProviders(
3248
+ <Dialog>
3249
+ <DialogTrigger asChild>
3250
+ <button>Open Dialog 2</button>
3251
+ </DialogTrigger>
3252
+ <DialogContent title="Dialog 2" persistOpenState={true}>
3253
+ <DialogHeader>
3254
+ <h2>Dialog 2</h2>
3255
+ </DialogHeader>
3256
+ </DialogContent>
3257
+ </Dialog>
3258
+ );
3259
+
3260
+ await user.click(screen.getByRole('button', { name: 'Open Dialog 2' }));
3261
+ await waitForDialog();
3262
+
3263
+ // Second dialog should open successfully
3264
+ const dialog2 = document.querySelector('dialog[role="dialog"]');
3265
+ expect(dialog2).toBeInTheDocument();
3266
+ });
3267
+ });
3268
+
3269
+ describe('DialogContent Conditional Rendering', () => {
3270
+ it('does not render when open is false', () => {
3271
+ renderWithProviders(
3272
+ <Dialog open={false}>
3273
+ <DialogContent title="Test Dialog">
3274
+ <DialogHeader>
3275
+ <h2>Test Dialog</h2>
3276
+ </DialogHeader>
3277
+ </DialogContent>
3278
+ </Dialog>
3279
+ );
3280
+
3281
+ expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
3282
+ });
3283
+
3284
+ it('does not render when lock is not acquired', async () => {
3285
+ const user = userEvent.setup();
3286
+
3287
+ // Set a lock for another dialog
3288
+ sessionStorage.setItem('pace-core:dialog:lock', JSON.stringify({
3289
+ key: 'other-dialog-key',
3290
+ timestamp: Date.now(),
3291
+ }));
3292
+
3293
+ // Create a fake dialog in DOM to simulate another dialog being open
3294
+ const fakeDialog = document.createElement('dialog');
3295
+ fakeDialog.setAttribute('data-persistence-key', 'other-dialog-key');
3296
+ fakeDialog.setAttribute('open', '');
3297
+ document.body.appendChild(fakeDialog);
3298
+
3299
+ renderWithProviders(
3300
+ <Dialog>
3301
+ <DialogTrigger asChild>
3302
+ <button>Open Dialog</button>
3303
+ </DialogTrigger>
3304
+ <DialogContent title="Test Dialog" persistOpenState={true}>
3305
+ <DialogHeader>
3306
+ <h2>Test Dialog</h2>
3307
+ </DialogHeader>
3308
+ </DialogContent>
3309
+ </Dialog>
3310
+ );
3311
+
3312
+ await user.click(screen.getByRole('button', { name: 'Open Dialog' }));
3313
+
3314
+ // Dialog should not render (lock not acquired)
3315
+ await waitFor(() => {
3316
+ const dialogs = document.querySelectorAll('dialog[role="dialog"]');
3317
+ // Should only have the fake dialog, not the new one
3318
+ expect(dialogs.length).toBeLessThanOrEqual(1);
3319
+ }, { timeout: 1000 });
3320
+
3321
+ // Cleanup
3322
+ document.body.removeChild(fakeDialog);
3323
+ });
3324
+ });
3325
+
3326
+ describe('DialogClose Context Integration', () => {
3327
+ it('uses markClosedByUser from DialogContext when available', async () => {
3328
+ const user = userEvent.setup();
3329
+ const { useSessionDraft } = await import('../../hooks/useSessionDraft');
3330
+ const mockClearDraft = vi.fn();
3331
+
3332
+ vi.mocked(useSessionDraft).mockReturnValue({
3333
+ state: false,
3334
+ setState: vi.fn(),
3335
+ clearDraft: mockClearDraft,
3336
+ wasRestored: false,
3337
+ saveImmediately: vi.fn(),
3338
+ });
3339
+
3340
+ renderWithProviders(
3341
+ <Dialog defaultOpen={true}>
3342
+ <DialogContent title="Test Dialog" persistOpenState={true}>
3343
+ <DialogHeader>
3344
+ <h2>Test Dialog</h2>
3345
+ </DialogHeader>
3346
+ <DialogFooter>
3347
+ <DialogClose />
3348
+ </DialogFooter>
3349
+ </DialogContent>
3350
+ </Dialog>
3351
+ );
3352
+
3353
+ await waitForDialog();
3354
+
3355
+ // Close via DialogClose button
3356
+ const dialog = document.querySelector('dialog[role="dialog"]');
3357
+ const closeIcon = dialog?.querySelector('[data-testid="lucide-x"]');
3358
+ const closeButton = closeIcon?.closest('button') as HTMLButtonElement;
3359
+ await user.click(closeButton);
3360
+
3361
+ await waitFor(() => {
3362
+ expect(mockClearDraft).toHaveBeenCalled();
3363
+ });
3364
+ });
3365
+ });
3366
+
3367
+ describe('DialogBody HTML Content Edge Cases', () => {
3368
+ it('handles HTML content that becomes empty after sanitization', async () => {
3369
+ const user = userEvent.setup();
3370
+
3371
+ renderWithProviders(
3372
+ <Dialog>
3373
+ <DialogTrigger asChild>
3374
+ <button>Open Dialog</button>
3375
+ </DialogTrigger>
3376
+ <DialogContent title="Test Dialog">
3377
+ <DialogHeader>
3378
+ <h2>Test Dialog</h2>
3379
+ </DialogHeader>
3380
+ <DialogBody
3381
+ htmlContent="<script>alert('xss')</script>"
3382
+ allowHtml={true}
3383
+ >
3384
+ <p>Fallback Content</p>
3385
+ </DialogBody>
3386
+ </DialogContent>
3387
+ </Dialog>
3388
+ );
3389
+
3390
+ await user.click(screen.getByRole('button', { name: 'Open Dialog' }));
3391
+ await waitForDialog();
3392
+
3393
+ // Should show fallback message or children
3394
+ const body = document.querySelector('dialog main');
3395
+ expect(body).toBeInTheDocument();
3396
+ });
3397
+
3398
+ it('handles HTML content with only unsafe elements', async () => {
3399
+ const user = userEvent.setup();
3400
+
3401
+ renderWithProviders(
3402
+ <Dialog>
3403
+ <DialogTrigger asChild>
3404
+ <button>Open Dialog</button>
3405
+ </DialogTrigger>
3406
+ <DialogContent title="Test Dialog">
3407
+ <DialogHeader>
3408
+ <h2>Test Dialog</h2>
3409
+ </DialogHeader>
3410
+ <DialogBody
3411
+ htmlContent="<script>alert('xss')</script><iframe src='evil.com'></iframe>"
3412
+ allowHtml={true}
3413
+ >
3414
+ <p>Fallback Content</p>
3415
+ </DialogBody>
3416
+ </DialogContent>
3417
+ </Dialog>
3418
+ );
3419
+
3420
+ await user.click(screen.getByRole('button', { name: 'Open Dialog' }));
3421
+ await waitForDialog();
3422
+
3423
+ // Should show fallback message
3424
+ const body = document.querySelector('dialog main');
3425
+ expect(body).toBeInTheDocument();
3426
+ });
3427
+ });
3428
+
3429
+ describe('DialogPortal Mount State', () => {
3430
+ it('handles portal mount state correctly', async () => {
3431
+ const user = userEvent.setup();
3432
+
3433
+ renderWithProviders(
3434
+ <Dialog>
3435
+ <DialogTrigger asChild>
3436
+ <button>Open Dialog</button>
3437
+ </DialogTrigger>
3438
+ <DialogContent title="Test Dialog">
3439
+ <DialogHeader>
3440
+ <h2>Test Dialog</h2>
3441
+ </DialogHeader>
3442
+ </DialogContent>
3443
+ </Dialog>
3444
+ );
3445
+
3446
+ // Portal should mount after initial render
3447
+ await user.click(screen.getByRole('button', { name: 'Open Dialog' }));
3448
+ await waitForDialog();
3449
+
3450
+ // Dialog should be portaled to document.body
3451
+ const dialog = document.querySelector('dialog[role="dialog"]');
3452
+ expect(dialog?.parentElement).toBe(document.body);
3453
+ });
3454
+ });
3455
+
3456
+ describe('DialogContent Title and Description Updates', () => {
3457
+ it('updates title attribute when title prop changes', async () => {
3458
+ const user = userEvent.setup();
3459
+ const { rerender } = renderWithProviders(
3460
+ <Dialog open={true}>
3461
+ <DialogContent title="Original Title">
3462
+ <DialogHeader>
3463
+ <h2>Original Title</h2>
3464
+ </DialogHeader>
3465
+ </DialogContent>
3466
+ </Dialog>
3467
+ );
3468
+
3469
+ await waitForDialog();
3470
+ let dialog = document.querySelector('dialog[role="dialog"]');
3471
+ expect(dialog).toHaveAttribute('title', 'Original Title');
3472
+
3473
+ // Update title
3474
+ rerender(
3475
+ <Dialog open={true}>
3476
+ <DialogContent title="Updated Title">
3477
+ <DialogHeader>
3478
+ <h2>Updated Title</h2>
3479
+ </DialogHeader>
3480
+ </DialogContent>
3481
+ </Dialog>
3482
+ );
3483
+
3484
+ await waitFor(() => {
3485
+ dialog = document.querySelector('dialog[role="dialog"]');
3486
+ expect(dialog).toHaveAttribute('title', 'Updated Title');
3487
+ });
3488
+ });
3489
+
3490
+ it('updates aria-description when description prop changes', async () => {
3491
+ const user = userEvent.setup();
3492
+ const { rerender } = renderWithProviders(
3493
+ <Dialog open={true}>
3494
+ <DialogContent title="Test Dialog" description="Original Description">
3495
+ <DialogHeader>
3496
+ <h2>Test Dialog</h2>
3497
+ </DialogHeader>
3498
+ </DialogContent>
3499
+ </Dialog>
3500
+ );
3501
+
3502
+ await waitForDialog();
3503
+ let dialog = document.querySelector('dialog[role="dialog"]');
3504
+ expect(dialog).toHaveAttribute('aria-description', 'Original Description');
3505
+
3506
+ // Update description
3507
+ rerender(
3508
+ <Dialog open={true}>
3509
+ <DialogContent title="Test Dialog" description="Updated Description">
3510
+ <DialogHeader>
3511
+ <h2>Test Dialog</h2>
3512
+ </DialogHeader>
3513
+ </DialogContent>
3514
+ </Dialog>
3515
+ );
3516
+
3517
+ await waitFor(() => {
3518
+ dialog = document.querySelector('dialog[role="dialog"]');
3519
+ expect(dialog).toHaveAttribute('aria-description', 'Updated Description');
3520
+ });
3521
+ });
3522
+ });
3523
+
3524
+ describe('DialogContent Size and Dimension Combinations', () => {
3525
+ it('handles size with custom maxWidth override', async () => {
3526
+ const user = userEvent.setup();
3527
+
3528
+ renderWithProviders(
3529
+ <Dialog>
3530
+ <DialogTrigger asChild>
3531
+ <button>Open Dialog</button>
3532
+ </DialogTrigger>
3533
+ <DialogContent size="md" maxWidth="1200px" title="Test Dialog">
3534
+ <DialogHeader>
3535
+ <h2>Test Dialog</h2>
3536
+ </DialogHeader>
3537
+ </DialogContent>
3538
+ </Dialog>
3539
+ );
3540
+
3541
+ await user.click(screen.getByRole('button', { name: 'Open Dialog' }));
3542
+ const dialog = await waitForDialog();
3543
+
3544
+ // Custom maxWidth should override size class
3545
+ expect(dialog).toHaveStyle({ maxWidth: '1200px' });
3546
+ });
3547
+
3548
+ it('handles size with custom maxHeight override', async () => {
3549
+ const user = userEvent.setup();
3550
+
3551
+ renderWithProviders(
3552
+ <Dialog>
3553
+ <DialogTrigger asChild>
3554
+ <button>Open Dialog</button>
3555
+ </DialogTrigger>
3556
+ <DialogContent size="lg" maxHeight="600px" title="Test Dialog">
3557
+ <DialogHeader>
3558
+ <h2>Test Dialog</h2>
3559
+ </DialogHeader>
3560
+ </DialogContent>
3561
+ </Dialog>
3562
+ );
3563
+
3564
+ await user.click(screen.getByRole('button', { name: 'Open Dialog' }));
3565
+ const dialog = await waitForDialog();
3566
+
3567
+ expect(dialog).toHaveStyle({ maxHeight: '600px' });
3568
+ });
3569
+ });
3570
+
3571
+ describe('DialogContent enableScrolling Behavior', () => {
3572
+ it('applies flex layout when enableScrolling is true', async () => {
3573
+ const user = userEvent.setup();
3574
+
3575
+ renderWithProviders(
3576
+ <Dialog>
3577
+ <DialogTrigger asChild>
3578
+ <button>Open Dialog</button>
3579
+ </DialogTrigger>
3580
+ <DialogContent title="Test Dialog" enableScrolling>
3581
+ <DialogHeader>
3582
+ <h2>Test Dialog</h2>
3583
+ </DialogHeader>
3584
+ <DialogBody>
3585
+ <p>Content</p>
3586
+ </DialogBody>
3587
+ </DialogContent>
3588
+ </Dialog>
3589
+ );
3590
+
3591
+ await user.click(screen.getByRole('button', { name: 'Open Dialog' }));
3592
+ const dialog = await waitForDialog();
3593
+
3594
+ expect(dialog).toHaveClass('flex', 'flex-col');
3595
+ });
3596
+
3597
+ it('applies default maxHeightPercent when enableScrolling is true', async () => {
3598
+ const user = userEvent.setup();
3599
+
3600
+ renderWithProviders(
3601
+ <Dialog>
3602
+ <DialogTrigger asChild>
3603
+ <button>Open Dialog</button>
3604
+ </DialogTrigger>
3605
+ <DialogContent title="Test Dialog" enableScrolling>
3606
+ <DialogHeader>
3607
+ <h2>Test Dialog</h2>
3608
+ </DialogHeader>
3609
+ <DialogBody>
3610
+ <p>Content</p>
3611
+ </DialogBody>
3612
+ </DialogContent>
3613
+ </Dialog>
3614
+ );
3615
+
3616
+ await user.click(screen.getByRole('button', { name: 'Open Dialog' }));
3617
+ const dialog = await waitForDialog();
3618
+
3619
+ // Should have default 80vh maxHeight when enableScrolling is true
3620
+ expect(dialog).toHaveStyle({ maxHeight: '80vh' });
3621
+ });
3622
+ });
2816
3623
  });