@jmruthers/pace-core 0.6.6 → 0.6.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (292) hide show
  1. package/{scripts/audit/audit-dependencies.cjs → audit-tool/00-dependencies.cjs} +227 -22
  2. package/audit-tool/audits/01-pace-core-compliance.cjs +556 -0
  3. package/audit-tool/audits/02-project-structure.cjs +240 -0
  4. package/audit-tool/audits/03-architecture.cjs +224 -0
  5. package/audit-tool/audits/04-code-quality.cjs +149 -0
  6. package/audit-tool/audits/05-styling.cjs +224 -0
  7. package/audit-tool/audits/06-security-rbac.cjs +554 -0
  8. package/audit-tool/audits/07-api-tech-stack.cjs +355 -0
  9. package/audit-tool/audits/08-testing-documentation.cjs +202 -0
  10. package/audit-tool/audits/09-operations.cjs +208 -0
  11. package/audit-tool/index.cjs +295 -0
  12. package/audit-tool/utils/code-utils.cjs +218 -0
  13. package/audit-tool/utils/file-utils.cjs +230 -0
  14. package/audit-tool/utils/report-utils.cjs +380 -0
  15. package/cursor-rules/00-standards-overview.mdc +156 -0
  16. package/cursor-rules/{00-pace-core-compliance.mdc → 01-pace-core-compliance.mdc} +187 -34
  17. package/cursor-rules/02-project-structure.mdc +37 -5
  18. package/cursor-rules/{03-solid-principles.mdc → 03-architecture.mdc} +125 -11
  19. package/cursor-rules/04-code-quality.mdc +419 -0
  20. package/cursor-rules/{08-markup-quality.mdc → 05-styling.mdc} +55 -10
  21. package/cursor-rules/{09-rbac-compliance.mdc → 06-security-rbac.mdc} +62 -6
  22. package/cursor-rules/07-api-tech-stack.mdc +377 -0
  23. package/cursor-rules/08-testing-documentation.mdc +324 -0
  24. package/cursor-rules/09-operations.mdc +365 -0
  25. package/dist/DataTable-6RMSCQJ6.js +15 -0
  26. package/dist/{DataTable-2N_tqbfq.d.ts → DataTable-DRUIgtUH.d.ts} +1 -1
  27. package/dist/{PublicPageProvider-BBH6Vqg7.d.ts → PublicPageProvider-CIGSujI2.d.ts} +40 -24
  28. package/dist/{UnifiedAuthProvider-ZT6TIGM7.js → UnifiedAuthProvider-7SNDOWYD.js} +2 -2
  29. package/dist/{api-Y4MQWOFW.js → api-7P7DI652.js} +1 -1
  30. package/dist/{chunk-MAGBIDNS.js → chunk-4DDCYDQ3.js} +8 -7
  31. package/dist/{chunk-BVP2BCJF.js → chunk-5W2A3DRC.js} +10 -9
  32. package/dist/{chunk-SD6WQY43.js → chunk-7ILTDCL2.js} +9 -1
  33. package/dist/{chunk-3QC3KRHK.js → chunk-A3W6LW53.js} +16 -1
  34. package/dist/{chunk-3O3WHILE.js → chunk-EF2UGZWY.js} +239 -63
  35. package/dist/{chunk-LAZMKTTF.js → chunk-EURB7QFZ.js} +341 -337
  36. package/dist/{chunk-2HGJFNAH.js → chunk-FEJLJNWA.js} +1 -15
  37. package/dist/{chunk-7TYHROIV.js → chunk-GS5672WG.js} +55 -13
  38. package/dist/{chunk-UIYSCEV7.js → chunk-IUBRCBSY.js} +1 -1
  39. package/dist/{chunk-ZFYPMX46.js → chunk-LX6U42O3.js} +1 -1
  40. package/dist/{chunk-FENMYN2U.js → chunk-MPBLMWVR.js} +3 -3
  41. package/dist/{chunk-ZS5VO5JB.js → chunk-NKHKXPI4.js} +408 -453
  42. package/dist/{chunk-A55DK444.js → chunk-OJ4SKRSV.js} +1 -7
  43. package/dist/{chunk-4T7OBVTU.js → chunk-S6ZQKDY6.js} +1 -1
  44. package/dist/{chunk-FTCRZOG2.js → chunk-T5CVK4R3.js} +5 -5
  45. package/dist/{chunk-OHIK3MIO.js → chunk-Z2FNRKF3.js} +13 -13
  46. package/dist/components.d.ts +5 -4
  47. package/dist/components.js +29 -34
  48. package/dist/eslint-rules/index.cjs +22 -9
  49. package/{src/eslint-rules/rules/compliance.cjs → dist/eslint-rules/rules/01-pace-core-compliance.cjs} +184 -23
  50. package/dist/eslint-rules/rules/04-code-quality.cjs +346 -0
  51. package/dist/eslint-rules/rules/05-styling.cjs +61 -0
  52. package/dist/eslint-rules/rules/{rbac.cjs → 06-security-rbac.cjs} +34 -13
  53. package/dist/eslint-rules/rules/07-api-tech-stack.cjs +385 -0
  54. package/dist/eslint-rules/rules/08-testing.cjs +94 -0
  55. package/dist/{functions-DHebl8-F.d.ts → functions-lBy5L2ry.d.ts} +1 -1
  56. package/dist/hooks.d.ts +5 -5
  57. package/dist/hooks.js +8 -8
  58. package/dist/index.d.ts +7 -7
  59. package/dist/index.js +21 -20
  60. package/dist/providers.js +2 -2
  61. package/dist/rbac/index.d.ts +1 -1
  62. package/dist/rbac/index.js +8 -8
  63. package/dist/theming/runtime.d.ts +61 -1
  64. package/dist/theming/runtime.js +1 -1
  65. package/dist/{types-B-K_5VnO.d.ts → types-DXstZpNI.d.ts} +0 -17
  66. package/dist/types.d.ts +2 -2
  67. package/dist/{usePublicRouteParams-COZ28Mvq.d.ts → usePublicRouteParams-MamNgwqe.d.ts} +19 -19
  68. package/dist/utils.d.ts +2 -2
  69. package/dist/utils.js +8 -8
  70. package/docs/README.md +1 -1
  71. package/docs/api/modules.md +106 -41
  72. package/docs/api-reference/components.md +18 -20
  73. package/docs/api-reference/hooks.md +80 -80
  74. package/docs/api-reference/types.md +1 -1
  75. package/docs/api-reference/utilities.md +1 -1
  76. package/docs/architecture/README.md +1 -1
  77. package/docs/core-concepts/events.md +3 -3
  78. package/docs/core-concepts/organisations.md +6 -6
  79. package/docs/core-concepts/permissions.md +6 -6
  80. package/docs/documentation-index.md +12 -18
  81. package/docs/getting-started/dependencies.md +23 -0
  82. package/docs/getting-started/documentation-index.md +1 -1
  83. package/docs/getting-started/examples/README.md +4 -4
  84. package/docs/getting-started/examples/full-featured-app.md +1 -1
  85. package/docs/getting-started/faq.md +2 -2
  86. package/docs/getting-started/quick-reference.md +4 -4
  87. package/docs/implementation-guides/app-layout.md +1 -1
  88. package/docs/implementation-guides/authentication.md +15 -15
  89. package/docs/implementation-guides/component-styling.md +1 -1
  90. package/docs/implementation-guides/data-tables.md +127 -34
  91. package/docs/implementation-guides/datatable-rbac-usage.md +1 -1
  92. package/docs/implementation-guides/dynamic-colors.md +3 -3
  93. package/docs/implementation-guides/file-upload-storage.md +2 -2
  94. package/docs/implementation-guides/hierarchical-datatable.md +40 -60
  95. package/docs/implementation-guides/inactivity-tracking.md +3 -3
  96. package/docs/implementation-guides/large-datasets.md +3 -2
  97. package/docs/implementation-guides/organisation-security.md +2 -2
  98. package/docs/implementation-guides/performance.md +2 -2
  99. package/docs/implementation-guides/permission-enforcement.md +1 -1
  100. package/docs/migration/V0.3.44_organisation-context-timing-fix.md +1 -1
  101. package/docs/migration/V0.4.0_rbac-migration.md +6 -6
  102. package/docs/rbac/README.md +5 -5
  103. package/docs/rbac/advanced-patterns.md +6 -6
  104. package/docs/rbac/api-reference.md +20 -20
  105. package/docs/rbac/event-based-apps.md +3 -3
  106. package/docs/rbac/examples.md +41 -41
  107. package/docs/rbac/getting-started.md +37 -37
  108. package/docs/rbac/performance.md +1 -1
  109. package/docs/rbac/quick-start.md +52 -52
  110. package/docs/rbac/secure-client-protection.md +1 -1
  111. package/docs/rbac/troubleshooting.md +1 -1
  112. package/docs/security/README.md +5 -5
  113. package/docs/standards/0-standards-overview.md +220 -0
  114. package/docs/standards/{00-pace-core-compliance.md → 1-pace-core-compliance-standards.md} +241 -185
  115. package/docs/standards/{02-project-structure.md → 2-project-structure-standards.md} +11 -47
  116. package/docs/standards/3-architecture-standards.md +606 -0
  117. package/docs/standards/4-code-quality-standards.md +728 -0
  118. package/docs/standards/{08-markup-quality.md → 5-styling-standards.md} +12 -9
  119. package/docs/standards/{09-rbac-compliance.md → 6-security-rbac-standards.md} +126 -18
  120. package/docs/standards/7-api-tech-stack-standards.md +662 -0
  121. package/docs/standards/8-testing-documentation-standards.md +401 -0
  122. package/docs/standards/9-operations-standards.md +1102 -0
  123. package/docs/standards/README.md +203 -104
  124. package/docs/troubleshooting/README.md +4 -4
  125. package/docs/troubleshooting/common-issues.md +2 -2
  126. package/docs/troubleshooting/debugging.md +9 -9
  127. package/docs/troubleshooting/migration.md +4 -4
  128. package/eslint-config-pace-core.cjs +50 -20
  129. package/package.json +50 -19
  130. package/scripts/eslint-audit.cjs +123 -0
  131. package/scripts/install-cursor-rules.cjs +11 -243
  132. package/scripts/install-eslint-config.cjs +349 -0
  133. package/scripts/validate-dependencies.cjs +248 -0
  134. package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +2 -2
  135. package/src/__tests__/helpers/__tests__/test-providers.test.tsx +2 -2
  136. package/src/__tests__/helpers/__tests__/test-utils.test.tsx +30 -18
  137. package/src/__tests__/integration/UserProfile.test.tsx +14 -14
  138. package/src/__tests__/rbac/PagePermissionGuard.test.tsx +6 -6
  139. package/src/__tests__/templates/accessibility.test.template.tsx +10 -9
  140. package/src/__tests__/templates/component.test.template.tsx +18 -15
  141. package/src/components/AddressField/AddressField.tsx +26 -1
  142. package/src/components/Alert/Alert.test.tsx +86 -22
  143. package/src/components/Alert/Alert.tsx +19 -11
  144. package/src/components/Badge/Badge.tsx +1 -1
  145. package/src/components/Calendar/Calendar.tsx +201 -47
  146. package/src/components/Checkbox/Checkbox.test.tsx +2 -1
  147. package/src/components/ContextSelector/ContextSelector.tsx +108 -126
  148. package/src/components/DataTable/AUDIT_REPORT.md +293 -0
  149. package/src/components/DataTable/DataTable.tsx +1 -19
  150. package/src/components/DataTable/__tests__/DataTableCore.test.tsx +6 -2
  151. package/src/components/DataTable/__tests__/a11y.basic.test.tsx +21 -6
  152. package/src/components/DataTable/__tests__/pagination.modes.test.tsx +3 -2
  153. package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +9 -9
  154. package/src/components/DataTable/components/ColumnFilter.tsx +63 -74
  155. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +43 -41
  156. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +9 -11
  157. package/src/components/DataTable/components/DataTableLayout.tsx +5 -16
  158. package/src/components/DataTable/components/EditableRow.tsx +5 -7
  159. package/src/components/DataTable/components/EmptyState.tsx +11 -10
  160. package/src/components/DataTable/components/FilterRow.tsx +2 -4
  161. package/src/components/DataTable/components/ImportModal.tsx +124 -126
  162. package/src/components/DataTable/components/LoadingState.tsx +5 -6
  163. package/src/components/DataTable/components/SortIndicator.tsx +50 -0
  164. package/src/components/DataTable/components/__tests__/COVERAGE_NOTE.md +4 -4
  165. package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +23 -82
  166. package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +37 -9
  167. package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +7 -4
  168. package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +12 -4
  169. package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +45 -27
  170. package/src/components/DataTable/components/index.ts +2 -1
  171. package/src/components/DataTable/types.ts +0 -18
  172. package/src/components/DataTable/utils/a11yUtils.ts +17 -0
  173. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +1 -1
  174. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +11 -15
  175. package/src/components/DateTimeField/DateTimeField.tsx +7 -8
  176. package/src/components/Dialog/Dialog.test.tsx +1 -0
  177. package/src/components/Dialog/Dialog.tsx +25 -8
  178. package/src/components/ErrorBoundary/ErrorBoundary.tsx +77 -79
  179. package/src/components/FileUpload/FileUpload.test.tsx +45 -16
  180. package/src/components/FileUpload/FileUpload.tsx +141 -130
  181. package/src/components/NavigationMenu/NavigationMenu.test.tsx +48 -12
  182. package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +9 -9
  183. package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +30 -30
  184. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +4 -4
  185. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +7 -1
  186. package/src/components/Progress/Progress.tsx +2 -4
  187. package/src/components/ProtectedRoute/ProtectedRoute.tsx +8 -8
  188. package/src/components/Select/Select.tsx +86 -77
  189. package/src/components/Select/types.ts +3 -0
  190. package/src/hooks/__tests__/ServiceHooks.test.tsx +16 -16
  191. package/src/hooks/__tests__/hooks.integration.test.tsx +49 -49
  192. package/src/hooks/__tests__/useDataTablePerformance.unit.test.ts +8 -5
  193. package/src/hooks/__tests__/useFileUrl.unit.test.ts +4 -0
  194. package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +99 -99
  195. package/src/hooks/__tests__/useInactivityTracker.unit.test.ts +45 -8
  196. package/src/hooks/__tests__/usePerformanceMonitor.unit.test.ts +22 -2
  197. package/src/hooks/public/usePublicEvent.ts +5 -5
  198. package/src/hooks/public/usePublicEventLogo.ts +5 -5
  199. package/src/hooks/public/usePublicFileDisplay.ts +2 -2
  200. package/src/hooks/public/usePublicRouteParams.ts +13 -9
  201. package/src/hooks/useAddressAutocomplete.test.ts +18 -18
  202. package/src/hooks/useAppConfig.ts +2 -2
  203. package/src/hooks/useEventTheme.test.ts +7 -7
  204. package/src/hooks/useEventTheme.ts +2 -1
  205. package/src/hooks/useFileDisplay.ts +2 -2
  206. package/src/hooks/useFileUrl.ts +52 -8
  207. package/src/hooks/useOrganisationSecurity.test.ts +2 -1
  208. package/src/providers/UnifiedAuthProvider.smoke.test.tsx +21 -21
  209. package/src/providers/__tests__/AuthProvider.test.tsx +21 -21
  210. package/src/providers/__tests__/EventProvider.test.tsx +61 -61
  211. package/src/providers/__tests__/InactivityProvider.test.tsx +56 -56
  212. package/src/providers/__tests__/OrganisationProvider.test.tsx +75 -75
  213. package/src/providers/__tests__/ProviderLifecycle.test.tsx +38 -38
  214. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +103 -103
  215. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +7 -7
  216. package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +10 -10
  217. package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +15 -6
  218. package/src/rbac/__tests__/rbac-functions.test.ts +3 -3
  219. package/src/rbac/api.test.ts +104 -0
  220. package/src/rbac/engine.ts +1 -1
  221. package/src/rbac/hooks/useCan.test.ts +2 -2
  222. package/src/rbac/secureClient.ts +1 -1
  223. package/src/rbac/types/functions.ts +1 -1
  224. package/src/styles/core.css +7 -0
  225. package/src/theming/__tests__/parseEventColours.test.ts +118 -3
  226. package/src/theming/parseEventColours.ts +77 -11
  227. package/src/types/supabase.ts +2 -3
  228. package/src/utils/__tests__/bundleAnalysis.unit.test.ts +9 -9
  229. package/src/utils/__tests__/lazyLoad.unit.test.tsx +42 -39
  230. package/src/utils/file-reference/__tests__/file-reference.test.ts +4 -0
  231. package/src/utils/formatting/formatDate.test.ts +3 -2
  232. package/src/utils/formatting/formatDateTime.test.ts +2 -2
  233. package/src/utils/google-places/googlePlacesUtils.test.ts +36 -24
  234. package/src/utils/storage/README.md +1 -1
  235. package/src/utils/storage/__tests__/helpers.unit.test.ts +19 -12
  236. package/src/utils/storage/helpers.test.ts +69 -3
  237. package/cursor-rules/01-standards-compliance.mdc +0 -285
  238. package/cursor-rules/04-testing-standards.mdc +0 -270
  239. package/cursor-rules/05-bug-reports-and-features.mdc +0 -248
  240. package/cursor-rules/06-code-quality.mdc +0 -311
  241. package/cursor-rules/07-tech-stack-compliance.mdc +0 -216
  242. package/cursor-rules/10-error-handling-patterns.mdc +0 -179
  243. package/cursor-rules/11-performance-optimization.mdc +0 -169
  244. package/cursor-rules/12-ci-cd-integration.mdc +0 -150
  245. package/dist/DataTable-LRJL4IRV.js +0 -15
  246. package/dist/eslint-rules/rules/compliance.cjs +0 -348
  247. package/dist/eslint-rules/rules/components.cjs +0 -113
  248. package/dist/eslint-rules/rules/imports.cjs +0 -102
  249. package/docs/best-practices/README.md +0 -472
  250. package/docs/best-practices/accessibility.md +0 -604
  251. package/docs/best-practices/common-patterns.md +0 -516
  252. package/docs/best-practices/deployment.md +0 -1103
  253. package/docs/best-practices/performance.md +0 -1328
  254. package/docs/best-practices/security.md +0 -940
  255. package/docs/best-practices/testing.md +0 -1034
  256. package/docs/rbac/compliance/compliance-guide.md +0 -544
  257. package/docs/standards/01-standards-compliance.md +0 -188
  258. package/docs/standards/03-solid-principles.md +0 -39
  259. package/docs/standards/04-testing-standards.md +0 -36
  260. package/docs/standards/05-bug-reports-and-features.md +0 -27
  261. package/docs/standards/06-code-quality.md +0 -34
  262. package/docs/standards/07-tech-stack-compliance.md +0 -30
  263. package/docs/standards/10-error-handling-patterns.md +0 -401
  264. package/docs/standards/11-performance-optimization.md +0 -348
  265. package/docs/standards/12-ci-cd-integration.md +0 -370
  266. package/docs/standards/ALIGNMENT_REVIEW_SUMMARY.md +0 -192
  267. package/scripts/audit/audit-compliance.cjs +0 -1295
  268. package/scripts/audit/audit-components.cjs +0 -260
  269. package/scripts/audit/audit-rbac.cjs +0 -954
  270. package/scripts/audit/audit-standards.cjs +0 -1268
  271. package/scripts/audit/index.cjs +0 -1927
  272. package/src/components/DataTable/components/DataTableBody.tsx +0 -478
  273. package/src/components/DataTable/components/DraggableColumnHeader.tsx +0 -156
  274. package/src/components/DataTable/components/ExpandButton.tsx +0 -113
  275. package/src/components/DataTable/components/GroupHeader.tsx +0 -54
  276. package/src/components/DataTable/components/ViewRowModal.tsx +0 -68
  277. package/src/components/DataTable/components/VirtualizedDataTable.tsx +0 -525
  278. package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +0 -462
  279. package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +0 -393
  280. package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +0 -476
  281. package/src/components/DataTable/components/__tests__/VirtualizedDataTable.test.tsx +0 -128
  282. package/src/components/DataTable/core/DataTableContext.tsx +0 -216
  283. package/src/components/DataTable/core/__tests__/DataTableContext.test.tsx +0 -136
  284. package/src/components/DataTable/hooks/__tests__/useColumnReordering.test.ts +0 -570
  285. package/src/components/DataTable/hooks/useColumnReordering.ts +0 -123
  286. package/src/components/DataTable/utils/debugTools.ts +0 -514
  287. package/src/eslint-rules/index.cjs +0 -22
  288. package/src/eslint-rules/rules/components.cjs +0 -113
  289. package/src/eslint-rules/rules/imports.cjs +0 -102
  290. package/src/eslint-rules/rules/rbac.cjs +0 -790
  291. package/src/eslint-rules/utils/helpers.cjs +0 -42
  292. package/src/eslint-rules/utils/manifest-loader.cjs +0 -75
@@ -1,570 +0,0 @@
1
- /**
2
- * @file Unit Tests for useColumnReordering Hook
3
- * @package @jmruthers/pace-core
4
- * @module Components/DataTable/Hooks/__tests__
5
- */
6
-
7
- import { describe, it, expect, beforeEach, vi } from 'vitest';
8
- import { renderHook, act } from '@testing-library/react';
9
- import { useColumnReordering } from '../useColumnReordering';
10
- import type { Column } from '@tanstack/react-table';
11
-
12
- // Mock column factory
13
- function createMockColumn(id: string): Column<any, unknown> {
14
- return {
15
- id,
16
- accessorKey: id,
17
- size: 100,
18
- enableSorting: true,
19
- enableColumnFilter: true,
20
- } as Column<any, unknown>;
21
- }
22
-
23
- describe('[unit] useColumnReordering', () => {
24
- const mockColumns = [
25
- createMockColumn('col1'),
26
- createMockColumn('col2'),
27
- createMockColumn('col3'),
28
- createMockColumn('col4'),
29
- ];
30
-
31
- const defaultColumnOrder = mockColumns.map(col => col.id);
32
-
33
- beforeEach(() => {
34
- vi.clearAllMocks();
35
- });
36
-
37
- describe('Initialization', () => {
38
- it('initializes with column IDs from columns array', () => {
39
- const { result } = renderHook(() => useColumnReordering({
40
- columns: mockColumns,
41
- enableReordering: true
42
- }));
43
-
44
- expect(result.current.columnOrder).toEqual(defaultColumnOrder);
45
- });
46
-
47
- it('initializes with saved column order when persistence and saved order are provided', () => {
48
- const savedOrder = ['col3', 'col1', 'col2', 'col4'];
49
-
50
- const { result } = renderHook(() => useColumnReordering({
51
- columns: mockColumns,
52
- enableReordering: false, // Disable to prevent auto-update
53
- enablePersistence: true,
54
- tableId: 'test',
55
- savedColumnOrder: savedOrder
56
- }));
57
-
58
- expect(result.current.columnOrder).toEqual(savedOrder);
59
- });
60
-
61
- it('uses default column order when persistence enabled but no saved order', () => {
62
- const { result } = renderHook(() => useColumnReordering({
63
- columns: mockColumns,
64
- enableReordering: false,
65
- enablePersistence: true,
66
- tableId: 'test',
67
- savedColumnOrder: undefined
68
- }));
69
-
70
- expect(result.current.columnOrder).toEqual(defaultColumnOrder);
71
- });
72
-
73
- it('ignores empty saved order', () => {
74
- const { result } = renderHook(() => useColumnReordering({
75
- columns: mockColumns,
76
- enableReordering: true,
77
- savedColumnOrder: []
78
- }));
79
-
80
- expect(result.current.columnOrder).toEqual(defaultColumnOrder);
81
- });
82
- });
83
-
84
- describe('Move Column', () => {
85
- it('moves column from one index to another', () => {
86
- const { result } = renderHook(() => useColumnReordering({
87
- columns: mockColumns,
88
- enableReordering: true
89
- }));
90
-
91
- act(() => {
92
- result.current.moveColumn(0, 2); // Move col1 to position 2
93
- });
94
-
95
- expect(result.current.columnOrder).toEqual(['col2', 'col3', 'col1', 'col4']);
96
- });
97
-
98
- it('does nothing when enableReordering is false', () => {
99
- const { result } = renderHook(() => useColumnReordering({
100
- columns: mockColumns,
101
- enableReordering: false
102
- }));
103
-
104
- const initialOrder = [...result.current.columnOrder];
105
-
106
- act(() => {
107
- result.current.moveColumn(0, 2);
108
- });
109
-
110
- expect(result.current.columnOrder).toEqual(initialOrder);
111
- });
112
-
113
- it('handles moving column to same position', () => {
114
- const { result } = renderHook(() => useColumnReordering({
115
- columns: mockColumns,
116
- enableReordering: true
117
- }));
118
-
119
- const initialOrder = [...result.current.columnOrder];
120
-
121
- act(() => {
122
- result.current.moveColumn(1, 1);
123
- });
124
-
125
- expect(result.current.columnOrder).toEqual(initialOrder);
126
- });
127
- });
128
-
129
- describe('Move Left/Right', () => {
130
- it('moves column left when not at the beginning', () => {
131
- const { result } = renderHook(() => useColumnReordering({
132
- columns: mockColumns,
133
- enableReordering: true
134
- }));
135
-
136
- act(() => {
137
- result.current.moveColumnLeft('col3');
138
- });
139
-
140
- expect(result.current.columnOrder).toEqual(['col1', 'col3', 'col2', 'col4']);
141
- });
142
-
143
- it('moves column right when not at the end', () => {
144
- const { result } = renderHook(() => useColumnReordering({
145
- columns: mockColumns,
146
- enableReordering: true
147
- }));
148
-
149
- act(() => {
150
- result.current.moveColumnRight('col2');
151
- });
152
-
153
- expect(result.current.columnOrder).toEqual(['col1', 'col3', 'col2', 'col4']);
154
- });
155
-
156
- it('does not move column left when at the beginning', () => {
157
- const { result } = renderHook(() => useColumnReordering({
158
- columns: mockColumns,
159
- enableReordering: true
160
- }));
161
-
162
- const initialOrder = [...result.current.columnOrder];
163
-
164
- act(() => {
165
- result.current.moveColumnLeft('col1');
166
- });
167
-
168
- expect(result.current.columnOrder).toEqual(initialOrder);
169
- });
170
-
171
- it('does not move column right when at the end', () => {
172
- const { result } = renderHook(() => useColumnReordering({
173
- columns: mockColumns,
174
- enableReordering: true
175
- }));
176
-
177
- const initialOrder = [...result.current.columnOrder];
178
-
179
- act(() => {
180
- result.current.moveColumnRight('col4');
181
- });
182
-
183
- expect(result.current.columnOrder).toEqual(initialOrder);
184
- });
185
-
186
- it('handles non-existent column ID gracefully', () => {
187
- const { result } = renderHook(() => useColumnReordering({
188
- columns: mockColumns,
189
- enableReordering: true
190
- }));
191
-
192
- const initialOrder = [...result.current.columnOrder];
193
-
194
- act(() => {
195
- result.current.moveColumnLeft('non-existent-col');
196
- });
197
-
198
- expect(result.current.columnOrder).toEqual(initialOrder);
199
- });
200
- });
201
-
202
- describe('Can Move Checks', () => {
203
- it('returns true when column can move left', () => {
204
- const { result } = renderHook(() => useColumnReordering({
205
- columns: mockColumns,
206
- enableReordering: true
207
- }));
208
-
209
- expect(result.current.canMoveLeft('col2')).toBe(true);
210
- expect(result.current.canMoveLeft('col3')).toBe(true);
211
- expect(result.current.canMoveLeft('col4')).toBe(true);
212
- });
213
-
214
- it('returns false when column cannot move left', () => {
215
- const { result } = renderHook(() => useColumnReordering({
216
- columns: mockColumns,
217
- enableReordering: true
218
- }));
219
-
220
- expect(result.current.canMoveLeft('col1')).toBe(false);
221
- });
222
-
223
- it('returns true when column can move right', () => {
224
- const { result } = renderHook(() => useColumnReordering({
225
- columns: mockColumns,
226
- enableReordering: true
227
- }));
228
-
229
- expect(result.current.canMoveRight('col1')).toBe(true);
230
- expect(result.current.canMoveRight('col2')).toBe(true);
231
- expect(result.current.canMoveRight('col3')).toBe(true);
232
- });
233
-
234
- it('returns false when column cannot move right', () => {
235
- const { result } = renderHook(() => useColumnReordering({
236
- columns: mockColumns,
237
- enableReordering: true
238
- }));
239
-
240
- expect(result.current.canMoveRight('col4')).toBe(false);
241
- });
242
-
243
- it('returns false for all checks when enableReordering is false', () => {
244
- const { result } = renderHook(() => useColumnReordering({
245
- columns: mockColumns,
246
- enableReordering: false
247
- }));
248
-
249
- expect(result.current.canMoveLeft('col2')).toBe(false);
250
- expect(result.current.canMoveRight('col1')).toBe(false);
251
- });
252
- });
253
-
254
- describe('Reorder Columns', () => {
255
- it('reorders columns with custom order array', () => {
256
- const { result } = renderHook(() => useColumnReordering({
257
- columns: mockColumns,
258
- enableReordering: true
259
- }));
260
-
261
- const newOrder = ['col4', 'col2', 'col1', 'col3'];
262
-
263
- act(() => {
264
- result.current.reorderColumns(newOrder);
265
- });
266
-
267
- expect(result.current.columnOrder).toEqual(newOrder);
268
- });
269
-
270
- it('does nothing when enableReordering is false', () => {
271
- const { result } = renderHook(() => useColumnReordering({
272
- columns: mockColumns,
273
- enableReordering: false
274
- }));
275
-
276
- const initialOrder = [...result.current.columnOrder];
277
- const newOrder = ['col4', 'col2', 'col1', 'col3'];
278
-
279
- act(() => {
280
- result.current.reorderColumns(newOrder);
281
- });
282
-
283
- expect(result.current.columnOrder).toEqual(initialOrder);
284
- });
285
-
286
- it('handles empty order array', () => {
287
- const { result } = renderHook(() => useColumnReordering({
288
- columns: mockColumns,
289
- enableReordering: true
290
- }));
291
-
292
- act(() => {
293
- result.current.reorderColumns([]);
294
- });
295
-
296
- expect(result.current.columnOrder).toEqual([]);
297
- });
298
-
299
- it('handles order with missing column IDs', () => {
300
- const { result } = renderHook(() => useColumnReordering({
301
- columns: mockColumns,
302
- enableReordering: true
303
- }));
304
-
305
- act(() => {
306
- result.current.reorderColumns(['col1', 'col3']); // Missing col2 and col4
307
- });
308
-
309
- expect(result.current.columnOrder).toEqual(['col1', 'col3']);
310
- });
311
- });
312
-
313
- describe('Ordered Columns', () => {
314
- it('returns columns in correct order', () => {
315
- const { result } = renderHook(() => useColumnReordering({
316
- columns: mockColumns,
317
- enableReordering: true
318
- }));
319
-
320
- act(() => {
321
- result.current.reorderColumns(['col3', 'col1', 'col2', 'col4']);
322
- });
323
-
324
- expect(result.current.orderedColumns.map(col => col.id)).toEqual(['col3', 'col1', 'col2', 'col4']);
325
- });
326
-
327
- it('returns columns in default order when enableReordering is false', () => {
328
- const { result } = renderHook(() => useColumnReordering({
329
- columns: mockColumns,
330
- enableReordering: false
331
- }));
332
-
333
- expect(result.current.orderedColumns).toEqual(mockColumns);
334
- });
335
-
336
- it('filters out missing columns from order', () => {
337
- const { result } = renderHook(() => useColumnReordering({
338
- columns: mockColumns,
339
- enableReordering: true
340
- }));
341
-
342
- act(() => {
343
- result.current.reorderColumns(['col2', 'col4', 'non-existent']);
344
- });
345
-
346
- const orderedIds = result.current.orderedColumns.map(col => col.id);
347
- expect(orderedIds).toEqual(['col2', 'col4']);
348
- expect(orderedIds).not.toContain('non-existent');
349
- });
350
-
351
- it('handles empty columns array', () => {
352
- const { result } = renderHook(() => useColumnReordering({
353
- columns: [],
354
- enableReordering: false // Disable reordering to avoid infinite loop
355
- }));
356
-
357
- expect(result.current.orderedColumns).toEqual([]);
358
- });
359
- });
360
-
361
- describe('Callbacks', () => {
362
- it('calls onColumnOrderChange when order is updated', () => {
363
- const onColumnOrderChange = vi.fn();
364
-
365
- const { result } = renderHook(() => useColumnReordering({
366
- columns: mockColumns,
367
- enableReordering: true,
368
- onColumnOrderChange
369
- }));
370
-
371
- const newOrder = ['col3', 'col1', 'col2', 'col4'];
372
-
373
- act(() => {
374
- result.current.reorderColumns(newOrder);
375
- });
376
-
377
- expect(onColumnOrderChange).toHaveBeenCalledWith(newOrder);
378
- });
379
-
380
- it('calls onColumnOrderUpdate when order is updated', () => {
381
- const onColumnOrderUpdate = vi.fn();
382
-
383
- const { result } = renderHook(() => useColumnReordering({
384
- columns: mockColumns,
385
- enableReordering: true,
386
- onColumnOrderUpdate
387
- }));
388
-
389
- const newOrder = ['col3', 'col1', 'col2', 'col4'];
390
-
391
- act(() => {
392
- result.current.reorderColumns(newOrder);
393
- });
394
-
395
- expect(onColumnOrderUpdate).toHaveBeenCalledWith(newOrder);
396
- });
397
-
398
- it('calls both callbacks when provided', () => {
399
- const onColumnOrderChange = vi.fn();
400
- const onColumnOrderUpdate = vi.fn();
401
-
402
- const { result } = renderHook(() => useColumnReordering({
403
- columns: mockColumns,
404
- enableReordering: true,
405
- onColumnOrderChange,
406
- onColumnOrderUpdate
407
- }));
408
-
409
- const newOrder = ['col3', 'col1', 'col2', 'col4'];
410
-
411
- act(() => {
412
- result.current.reorderColumns(newOrder);
413
- });
414
-
415
- expect(onColumnOrderChange).toHaveBeenCalledWith(newOrder);
416
- expect(onColumnOrderUpdate).toHaveBeenCalledWith(newOrder);
417
- });
418
-
419
- it('calls callbacks when moving columns', () => {
420
- const onColumnOrderChange = vi.fn();
421
-
422
- const { result } = renderHook(() => useColumnReordering({
423
- columns: mockColumns,
424
- enableReordering: true,
425
- onColumnOrderChange
426
- }));
427
-
428
- act(() => {
429
- result.current.moveColumn(0, 2);
430
- });
431
-
432
- expect(onColumnOrderChange).toHaveBeenCalledTimes(1);
433
- expect(onColumnOrderChange).toHaveBeenCalledWith(['col2', 'col3', 'col1', 'col4']);
434
- });
435
-
436
- it('does not call callbacks when enableReordering is false', () => {
437
- const onColumnOrderChange = vi.fn();
438
- const onColumnOrderUpdate = vi.fn();
439
-
440
- const { result } = renderHook(() => useColumnReordering({
441
- columns: mockColumns,
442
- enableReordering: false,
443
- onColumnOrderChange,
444
- onColumnOrderUpdate
445
- }));
446
-
447
- act(() => {
448
- result.current.reorderColumns(['col3', 'col1', 'col2', 'col4']);
449
- });
450
-
451
- expect(onColumnOrderChange).not.toHaveBeenCalled();
452
- expect(onColumnOrderUpdate).not.toHaveBeenCalled();
453
- });
454
- });
455
-
456
- describe('Column Updates', () => {
457
- it('updates order when columns change', () => {
458
- const { result, rerender } = renderHook(
459
- ({ columns }) => useColumnReordering({
460
- columns,
461
- enableReordering: true
462
- }),
463
- {
464
- initialProps: { columns: mockColumns }
465
- }
466
- );
467
-
468
- const newColumns = [
469
- createMockColumn('col5'),
470
- createMockColumn('col6'),
471
- ];
472
-
473
- rerender({ columns: newColumns });
474
-
475
- expect(result.current.columnOrder).toEqual(['col5', 'col6']);
476
- });
477
-
478
- it('preserves existing order when columns do not change', () => {
479
- const { result, rerender } = renderHook(
480
- ({ columns }) => useColumnReordering({
481
- columns,
482
- enableReordering: true
483
- }),
484
- {
485
- initialProps: { columns: mockColumns }
486
- }
487
- );
488
-
489
- act(() => {
490
- result.current.reorderColumns(['col3', 'col1', 'col2', 'col4']);
491
- });
492
-
493
- rerender({ columns: mockColumns });
494
-
495
- expect(result.current.columnOrder).toEqual(['col3', 'col1', 'col2', 'col4']);
496
- });
497
-
498
- it('handles column ID changes gracefully', () => {
499
- const { result, rerender } = renderHook(
500
- ({ columns }) => useColumnReordering({
501
- columns,
502
- enableReordering: true
503
- }),
504
- {
505
- initialProps: { columns: mockColumns }
506
- }
507
- );
508
-
509
- act(() => {
510
- result.current.reorderColumns(['col3', 'col1']);
511
- });
512
-
513
- const modifiedColumns = [
514
- createMockColumn('col3'),
515
- createMockColumn('col5'), // Changed from col2
516
- ];
517
-
518
- rerender({ columns: modifiedColumns });
519
-
520
- // After columns change, hook updates to new column order
521
- // col3 and col5 both exist now, but only col3 is in the modified list
522
- const orderedIds = result.current.orderedColumns.map(col => col.id);
523
- expect(orderedIds).toContain('col3');
524
- });
525
- });
526
-
527
- describe('Edge Cases', () => {
528
- it('handles single column', () => {
529
- const singleColumn = [createMockColumn('col1')];
530
-
531
- const { result } = renderHook(() => useColumnReordering({
532
- columns: singleColumn,
533
- enableReordering: true
534
- }));
535
-
536
- expect(result.current.columnOrder).toEqual(['col1']);
537
- expect(result.current.canMoveLeft('col1')).toBe(false);
538
- expect(result.current.canMoveRight('col1')).toBe(false);
539
- });
540
-
541
- it('handles columns without IDs', () => {
542
- const columnsWithoutIds = mockColumns.map(col => ({
543
- ...col,
544
- id: undefined
545
- }));
546
-
547
- const { result } = renderHook(() => useColumnReordering({
548
- columns: columnsWithoutIds,
549
- enableReordering: true
550
- }));
551
-
552
- expect(result.current.columnOrder).toEqual([undefined, undefined, undefined, undefined]);
553
- });
554
-
555
- it('handles null/undefined columns gracefully', () => {
556
- const columnsWithNulls = [
557
- createMockColumn('col1'),
558
- createMockColumn('col2'),
559
- ];
560
-
561
- const { result } = renderHook(() => useColumnReordering({
562
- columns: columnsWithNulls,
563
- enableReordering: true
564
- }));
565
-
566
- expect(result.current.columnOrder.length).toBeGreaterThan(0);
567
- });
568
- });
569
- });
570
-
@@ -1,123 +0,0 @@
1
- import { useState, useCallback, useMemo, useEffect } from 'react';
2
- import type { Column } from '@tanstack/react-table';
3
-
4
- /**
5
- * Props for the useColumnReordering hook.
6
- * @template TData - The type of data records in the table
7
- */
8
- interface UseColumnReorderingProps<TData> {
9
- columns: Column<TData, unknown>[];
10
- onColumnOrderChange?: (columnOrder: string[]) => void;
11
- enableReordering?: boolean;
12
- enablePersistence?: boolean;
13
- tableId?: string;
14
- savedColumnOrder?: string[];
15
- onColumnOrderUpdate?: (columnOrder: string[]) => void;
16
- }
17
-
18
- /**
19
- * Hook for managing column reordering in DataTable.
20
- * Provides functions to move columns and persist order changes.
21
- *
22
- * @template TData - The type of data records in the table
23
- * @param props - Column reordering configuration
24
- * @returns Column order state and reordering functions
25
- */
26
- export function useColumnReordering<TData>({
27
- columns,
28
- onColumnOrderChange,
29
- enableReordering = false,
30
- enablePersistence = false,
31
- tableId,
32
- savedColumnOrder,
33
- onColumnOrderUpdate,
34
- }: UseColumnReorderingProps<TData>) {
35
- const [columnOrder, setColumnOrder] = useState<string[]>(() => {
36
- // Use saved order if available, otherwise use default column order
37
- if (enablePersistence && savedColumnOrder && savedColumnOrder.length > 0) {
38
- return savedColumnOrder;
39
- }
40
- return columns.map(col => col.id);
41
- });
42
-
43
- // Update column order when columns change
44
- // Using useEffect for side effects (state updates)
45
- useEffect(() => {
46
- if (enableReordering) {
47
- const newOrder = columns.map(col => col.id);
48
- setColumnOrder(prevOrder => {
49
- // Only update if the order has actually changed
50
- if (JSON.stringify(prevOrder) !== JSON.stringify(newOrder)) {
51
- return newOrder;
52
- }
53
- return prevOrder;
54
- });
55
- }
56
- }, [columns, enableReordering]);
57
-
58
- const moveColumn = useCallback((fromIndex: number, toIndex: number) => {
59
- if (!enableReordering) return;
60
-
61
- const newOrder = [...columnOrder];
62
- const [movedColumn] = newOrder.splice(fromIndex, 1);
63
- newOrder.splice(toIndex, 0, movedColumn);
64
-
65
- setColumnOrder(newOrder);
66
- onColumnOrderChange?.(newOrder);
67
- onColumnOrderUpdate?.(newOrder);
68
- }, [columnOrder, onColumnOrderChange, onColumnOrderUpdate, enableReordering]);
69
-
70
- const moveColumnLeft = useCallback((columnId: string) => {
71
- if (!enableReordering) return;
72
-
73
- const currentIndex = columnOrder.indexOf(columnId);
74
- if (currentIndex > 0) {
75
- moveColumn(currentIndex, currentIndex - 1);
76
- }
77
- }, [columnOrder, moveColumn, enableReordering]);
78
-
79
- const moveColumnRight = useCallback((columnId: string) => {
80
- if (!enableReordering) return;
81
-
82
- const currentIndex = columnOrder.indexOf(columnId);
83
- if (currentIndex < columnOrder.length - 1) {
84
- moveColumn(currentIndex, currentIndex + 1);
85
- }
86
- }, [columnOrder, moveColumn, enableReordering]);
87
-
88
- const canMoveLeft = useCallback((columnId: string) => {
89
- return enableReordering && columnOrder.indexOf(columnId) > 0;
90
- }, [columnOrder, enableReordering]);
91
-
92
- const canMoveRight = useCallback((columnId: string) => {
93
- return enableReordering && columnOrder.indexOf(columnId) < columnOrder.length - 1;
94
- }, [columnOrder, enableReordering]);
95
-
96
- const reorderColumns = useCallback((newOrder: string[]) => {
97
- if (!enableReordering) return;
98
-
99
- setColumnOrder(newOrder);
100
- onColumnOrderChange?.(newOrder);
101
- onColumnOrderUpdate?.(newOrder);
102
- }, [onColumnOrderChange, onColumnOrderUpdate, enableReordering]);
103
-
104
- // Get columns in the current order
105
- const orderedColumns = useMemo(() => {
106
- if (!enableReordering) return columns;
107
-
108
- return columnOrder
109
- .map(id => columns.find(col => col.id === id))
110
- .filter(Boolean) as Column<TData, unknown>[];
111
- }, [columns, columnOrder, enableReordering]);
112
-
113
- return {
114
- columnOrder,
115
- orderedColumns,
116
- moveColumn,
117
- moveColumnLeft,
118
- moveColumnRight,
119
- canMoveLeft,
120
- canMoveRight,
121
- reorderColumns,
122
- };
123
- }