@jmruthers/pace-core 0.6.6 → 0.6.7

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 (246) hide show
  1. package/{scripts/audit/audit-dependencies.cjs → audit-tool/00-dependencies.cjs} +12 -13
  2. package/audit-tool/audits/01-pace-core-compliance.cjs +556 -0
  3. package/audit-tool/audits/02-project-structure.cjs +255 -0
  4. package/audit-tool/audits/03-architecture.cjs +196 -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 +544 -0
  8. package/audit-tool/audits/07-api-tech-stack.cjs +301 -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 +291 -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 +241 -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-7PMH7XN7.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-DlsCaR5v.d.ts} +26 -16
  28. package/dist/{chunk-FENMYN2U.js → chunk-5X4QLXRG.js} +1 -3
  29. package/dist/{chunk-4T7OBVTU.js → chunk-6F3IILHI.js} +1 -1
  30. package/dist/{chunk-SD6WQY43.js → chunk-7ILTDCL2.js} +9 -1
  31. package/dist/{chunk-3QC3KRHK.js → chunk-A3W6LW53.js} +16 -1
  32. package/dist/{chunk-7TYHROIV.js → chunk-BM4CQ5P3.js} +50 -8
  33. package/dist/{chunk-2HGJFNAH.js → chunk-FEJLJNWA.js} +1 -15
  34. package/dist/{chunk-OHIK3MIO.js → chunk-GHYHJTYV.js} +2 -2
  35. package/dist/{chunk-UIYSCEV7.js → chunk-IUBRCBSY.js} +1 -1
  36. package/dist/{chunk-LAZMKTTF.js → chunk-JGWDVX64.js} +281 -347
  37. package/dist/{chunk-MAGBIDNS.js → chunk-L4XMVJKY.js} +2 -2
  38. package/dist/{chunk-A55DK444.js → chunk-OJ4SKRSV.js} +1 -7
  39. package/dist/{chunk-ZS5VO5JB.js → chunk-Q7Q7V5NV.js} +406 -451
  40. package/dist/{chunk-3O3WHILE.js → chunk-VBCS3DUA.js} +236 -60
  41. package/dist/{chunk-BVP2BCJF.js → chunk-ZKAWKYT4.js} +8 -8
  42. package/dist/components.d.ts +5 -4
  43. package/dist/components.js +27 -32
  44. package/dist/eslint-rules/index.cjs +22 -9
  45. package/{src/eslint-rules/rules/compliance.cjs → dist/eslint-rules/rules/01-pace-core-compliance.cjs} +184 -23
  46. package/dist/eslint-rules/rules/04-code-quality.cjs +290 -0
  47. package/dist/eslint-rules/rules/05-styling.cjs +61 -0
  48. package/dist/eslint-rules/rules/{rbac.cjs → 06-security-rbac.cjs} +26 -10
  49. package/dist/eslint-rules/rules/07-api-tech-stack.cjs +263 -0
  50. package/dist/eslint-rules/rules/08-testing.cjs +94 -0
  51. package/dist/hooks.d.ts +5 -5
  52. package/dist/hooks.js +6 -6
  53. package/dist/index.d.ts +6 -6
  54. package/dist/index.js +18 -17
  55. package/dist/rbac/index.js +6 -6
  56. package/dist/theming/runtime.d.ts +14 -1
  57. package/dist/theming/runtime.js +1 -1
  58. package/dist/{types-B-K_5VnO.d.ts → types-DXstZpNI.d.ts} +0 -17
  59. package/dist/{usePublicRouteParams-COZ28Mvq.d.ts → usePublicRouteParams-MamNgwqe.d.ts} +19 -19
  60. package/dist/utils.d.ts +2 -2
  61. package/dist/utils.js +8 -8
  62. package/docs/README.md +1 -1
  63. package/docs/api/modules.md +47 -31
  64. package/docs/api-reference/components.md +18 -20
  65. package/docs/api-reference/hooks.md +80 -80
  66. package/docs/api-reference/types.md +1 -1
  67. package/docs/api-reference/utilities.md +1 -1
  68. package/docs/architecture/README.md +1 -1
  69. package/docs/core-concepts/events.md +3 -3
  70. package/docs/core-concepts/organisations.md +6 -6
  71. package/docs/core-concepts/permissions.md +6 -6
  72. package/docs/documentation-index.md +12 -18
  73. package/docs/getting-started/documentation-index.md +1 -1
  74. package/docs/getting-started/examples/README.md +4 -4
  75. package/docs/getting-started/examples/full-featured-app.md +1 -1
  76. package/docs/getting-started/faq.md +2 -2
  77. package/docs/getting-started/quick-reference.md +4 -4
  78. package/docs/implementation-guides/authentication.md +15 -15
  79. package/docs/implementation-guides/component-styling.md +1 -1
  80. package/docs/implementation-guides/data-tables.md +126 -33
  81. package/docs/implementation-guides/datatable-rbac-usage.md +1 -1
  82. package/docs/implementation-guides/dynamic-colors.md +3 -3
  83. package/docs/implementation-guides/file-upload-storage.md +2 -2
  84. package/docs/implementation-guides/hierarchical-datatable.md +40 -60
  85. package/docs/implementation-guides/inactivity-tracking.md +3 -3
  86. package/docs/implementation-guides/large-datasets.md +3 -2
  87. package/docs/implementation-guides/organisation-security.md +2 -2
  88. package/docs/implementation-guides/performance.md +2 -2
  89. package/docs/implementation-guides/permission-enforcement.md +1 -1
  90. package/docs/migration/V0.3.44_organisation-context-timing-fix.md +1 -1
  91. package/docs/migration/V0.4.0_rbac-migration.md +6 -6
  92. package/docs/rbac/README.md +5 -5
  93. package/docs/rbac/advanced-patterns.md +6 -6
  94. package/docs/rbac/api-reference.md +20 -20
  95. package/docs/rbac/event-based-apps.md +3 -3
  96. package/docs/rbac/examples.md +41 -41
  97. package/docs/rbac/getting-started.md +37 -37
  98. package/docs/rbac/performance.md +1 -1
  99. package/docs/rbac/quick-start.md +52 -52
  100. package/docs/rbac/secure-client-protection.md +1 -1
  101. package/docs/rbac/troubleshooting.md +1 -1
  102. package/docs/security/README.md +5 -5
  103. package/docs/standards/0-standards-overview.md +220 -0
  104. package/docs/standards/{00-pace-core-compliance.md → 1-pace-core-compliance-standards.md} +204 -185
  105. package/docs/standards/{02-project-structure.md → 2-project-structure-standards.md} +11 -47
  106. package/docs/standards/3-architecture-standards.md +606 -0
  107. package/docs/standards/4-code-quality-standards.md +728 -0
  108. package/docs/standards/{08-markup-quality.md → 5-styling-standards.md} +12 -9
  109. package/docs/standards/{09-rbac-compliance.md → 6-security-rbac-standards.md} +126 -18
  110. package/docs/standards/7-api-tech-stack-standards.md +662 -0
  111. package/docs/standards/8-testing-documentation-standards.md +401 -0
  112. package/docs/standards/9-operations-standards.md +1102 -0
  113. package/docs/standards/README.md +203 -104
  114. package/docs/troubleshooting/README.md +4 -4
  115. package/docs/troubleshooting/common-issues.md +2 -2
  116. package/docs/troubleshooting/debugging.md +9 -9
  117. package/docs/troubleshooting/migration.md +4 -4
  118. package/eslint-config-pace-core.cjs +21 -10
  119. package/package.json +6 -5
  120. package/scripts/install-cursor-rules.cjs +11 -243
  121. package/scripts/install-eslint-config.cjs +284 -0
  122. package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +2 -2
  123. package/src/__tests__/helpers/__tests__/test-providers.test.tsx +2 -2
  124. package/src/__tests__/helpers/__tests__/test-utils.test.tsx +10 -10
  125. package/src/__tests__/integration/UserProfile.test.tsx +14 -14
  126. package/src/__tests__/rbac/PagePermissionGuard.test.tsx +6 -6
  127. package/src/__tests__/templates/accessibility.test.template.tsx +9 -9
  128. package/src/__tests__/templates/component.test.template.tsx +18 -15
  129. package/src/components/Calendar/Calendar.tsx +201 -47
  130. package/src/components/ContextSelector/ContextSelector.tsx +137 -153
  131. package/src/components/DataTable/AUDIT_REPORT.md +293 -0
  132. package/src/components/DataTable/__tests__/DataTableCore.test.tsx +10 -2
  133. package/src/components/DataTable/__tests__/a11y.basic.test.tsx +10 -4
  134. package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +9 -9
  135. package/src/components/DataTable/components/ColumnFilter.tsx +63 -74
  136. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +43 -41
  137. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +9 -11
  138. package/src/components/DataTable/components/DataTableLayout.tsx +5 -16
  139. package/src/components/DataTable/components/EditableRow.tsx +5 -7
  140. package/src/components/DataTable/components/EmptyState.tsx +10 -9
  141. package/src/components/DataTable/components/FilterRow.tsx +2 -4
  142. package/src/components/DataTable/components/ImportModal.tsx +124 -126
  143. package/src/components/DataTable/components/LoadingState.tsx +5 -6
  144. package/src/components/DataTable/components/SortIndicator.tsx +50 -0
  145. package/src/components/DataTable/components/__tests__/COVERAGE_NOTE.md +4 -4
  146. package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +23 -82
  147. package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +37 -9
  148. package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +7 -4
  149. package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +12 -4
  150. package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +41 -27
  151. package/src/components/DataTable/components/index.ts +2 -1
  152. package/src/components/DataTable/types.ts +0 -18
  153. package/src/components/DataTable/utils/a11yUtils.ts +17 -0
  154. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +2 -1
  155. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +11 -15
  156. package/src/components/DateTimeField/DateTimeField.tsx +7 -8
  157. package/src/components/Dialog/Dialog.test.tsx +1 -0
  158. package/src/components/Dialog/Dialog.tsx +25 -8
  159. package/src/components/ErrorBoundary/ErrorBoundary.tsx +77 -79
  160. package/src/components/FileUpload/FileUpload.test.tsx +52 -14
  161. package/src/components/FileUpload/FileUpload.tsx +112 -130
  162. package/src/components/Progress/Progress.tsx +2 -4
  163. package/src/components/ProtectedRoute/ProtectedRoute.tsx +8 -8
  164. package/src/components/Select/Select.tsx +86 -77
  165. package/src/components/Select/types.ts +3 -0
  166. package/src/hooks/__tests__/ServiceHooks.test.tsx +16 -16
  167. package/src/hooks/__tests__/hooks.integration.test.tsx +49 -49
  168. package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +97 -97
  169. package/src/hooks/public/usePublicEvent.ts +5 -5
  170. package/src/hooks/public/usePublicEventLogo.ts +5 -5
  171. package/src/hooks/public/usePublicFileDisplay.ts +2 -2
  172. package/src/hooks/public/usePublicRouteParams.ts +5 -5
  173. package/src/hooks/useAppConfig.ts +2 -2
  174. package/src/hooks/useEventTheme.test.ts +7 -7
  175. package/src/hooks/useEventTheme.ts +1 -4
  176. package/src/hooks/useFileDisplay.ts +2 -2
  177. package/src/providers/UnifiedAuthProvider.smoke.test.tsx +21 -21
  178. package/src/providers/__tests__/AuthProvider.test.tsx +21 -21
  179. package/src/providers/__tests__/EventProvider.test.tsx +61 -61
  180. package/src/providers/__tests__/InactivityProvider.test.tsx +56 -56
  181. package/src/providers/__tests__/OrganisationProvider.test.tsx +75 -75
  182. package/src/providers/__tests__/ProviderLifecycle.test.tsx +37 -37
  183. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +103 -103
  184. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +7 -7
  185. package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +10 -10
  186. package/src/styles/core.css +7 -0
  187. package/src/theming/__tests__/parseEventColours.test.ts +9 -3
  188. package/src/theming/parseEventColours.ts +22 -10
  189. package/src/utils/__tests__/lazyLoad.unit.test.tsx +42 -39
  190. package/src/utils/storage/README.md +1 -1
  191. package/cursor-rules/01-standards-compliance.mdc +0 -285
  192. package/cursor-rules/04-testing-standards.mdc +0 -270
  193. package/cursor-rules/05-bug-reports-and-features.mdc +0 -248
  194. package/cursor-rules/06-code-quality.mdc +0 -311
  195. package/cursor-rules/07-tech-stack-compliance.mdc +0 -216
  196. package/cursor-rules/10-error-handling-patterns.mdc +0 -179
  197. package/cursor-rules/11-performance-optimization.mdc +0 -169
  198. package/cursor-rules/12-ci-cd-integration.mdc +0 -150
  199. package/dist/DataTable-LRJL4IRV.js +0 -15
  200. package/dist/eslint-rules/rules/compliance.cjs +0 -348
  201. package/dist/eslint-rules/rules/components.cjs +0 -113
  202. package/dist/eslint-rules/rules/imports.cjs +0 -102
  203. package/docs/best-practices/README.md +0 -472
  204. package/docs/best-practices/accessibility.md +0 -604
  205. package/docs/best-practices/common-patterns.md +0 -516
  206. package/docs/best-practices/deployment.md +0 -1103
  207. package/docs/best-practices/performance.md +0 -1328
  208. package/docs/best-practices/security.md +0 -940
  209. package/docs/best-practices/testing.md +0 -1034
  210. package/docs/rbac/compliance/compliance-guide.md +0 -544
  211. package/docs/standards/01-standards-compliance.md +0 -188
  212. package/docs/standards/03-solid-principles.md +0 -39
  213. package/docs/standards/04-testing-standards.md +0 -36
  214. package/docs/standards/05-bug-reports-and-features.md +0 -27
  215. package/docs/standards/06-code-quality.md +0 -34
  216. package/docs/standards/07-tech-stack-compliance.md +0 -30
  217. package/docs/standards/10-error-handling-patterns.md +0 -401
  218. package/docs/standards/11-performance-optimization.md +0 -348
  219. package/docs/standards/12-ci-cd-integration.md +0 -370
  220. package/docs/standards/ALIGNMENT_REVIEW_SUMMARY.md +0 -192
  221. package/scripts/audit/audit-compliance.cjs +0 -1295
  222. package/scripts/audit/audit-components.cjs +0 -260
  223. package/scripts/audit/audit-rbac.cjs +0 -954
  224. package/scripts/audit/audit-standards.cjs +0 -1268
  225. package/scripts/audit/index.cjs +0 -1927
  226. package/src/components/DataTable/components/DataTableBody.tsx +0 -478
  227. package/src/components/DataTable/components/DraggableColumnHeader.tsx +0 -156
  228. package/src/components/DataTable/components/ExpandButton.tsx +0 -113
  229. package/src/components/DataTable/components/GroupHeader.tsx +0 -54
  230. package/src/components/DataTable/components/ViewRowModal.tsx +0 -68
  231. package/src/components/DataTable/components/VirtualizedDataTable.tsx +0 -525
  232. package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +0 -462
  233. package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +0 -393
  234. package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +0 -476
  235. package/src/components/DataTable/components/__tests__/VirtualizedDataTable.test.tsx +0 -128
  236. package/src/components/DataTable/core/DataTableContext.tsx +0 -216
  237. package/src/components/DataTable/core/__tests__/DataTableContext.test.tsx +0 -136
  238. package/src/components/DataTable/hooks/__tests__/useColumnReordering.test.ts +0 -570
  239. package/src/components/DataTable/hooks/useColumnReordering.ts +0 -123
  240. package/src/components/DataTable/utils/debugTools.ts +0 -514
  241. package/src/eslint-rules/index.cjs +0 -22
  242. package/src/eslint-rules/rules/components.cjs +0 -113
  243. package/src/eslint-rules/rules/imports.cjs +0 -102
  244. package/src/eslint-rules/rules/rbac.cjs +0 -790
  245. package/src/eslint-rules/utils/helpers.cjs +0 -42
  246. 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
- }