@jmruthers/pace-core 0.5.193 → 0.6.1

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 (191) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +7 -1
  3. package/cursor-rules/00-pace-core-compliance.mdc +372 -0
  4. package/cursor-rules/01-standards-compliance.mdc +275 -0
  5. package/cursor-rules/02-project-structure.mdc +200 -0
  6. package/cursor-rules/03-solid-principles.mdc +341 -0
  7. package/cursor-rules/04-testing-standards.mdc +315 -0
  8. package/cursor-rules/05-bug-reports-and-features.mdc +246 -0
  9. package/cursor-rules/06-code-quality.mdc +392 -0
  10. package/cursor-rules/07-tech-stack-compliance.mdc +309 -0
  11. package/cursor-rules/CHANGELOG.md +101 -0
  12. package/cursor-rules/README.md +191 -0
  13. package/dist/{DataTable-Be6dH_dR.d.ts → DataTable-CH1U5Tpy.d.ts} +1 -1
  14. package/dist/{DataTable-5FU7IESH.js → DataTable-DQ7RSOHE.js} +6 -6
  15. package/dist/{PublicPageProvider-C0Sm_e5k.d.ts → PublicPageProvider-ce4xlHYA.d.ts} +34 -155
  16. package/dist/{UnifiedAuthProvider-RGJTDE2C.js → UnifiedAuthProvider-ATAP5UTR.js} +2 -2
  17. package/dist/{chunk-6C4YBBJM 5.js → chunk-3QRJFVBR.js} +1 -1
  18. package/dist/chunk-3QRJFVBR.js.map +1 -0
  19. package/dist/{chunk-IIELH4DL.js → chunk-3XTALGJF.js} +2 -2
  20. package/dist/{chunk-IIELH4DL.js.map → chunk-3XTALGJF.js.map} +1 -1
  21. package/dist/{chunk-HWIIPPNI.js → chunk-4N5C5XZU.js} +20 -20
  22. package/dist/chunk-4N5C5XZU.js.map +1 -0
  23. package/dist/{chunk-7EQTDTTJ.js → chunk-4ZC4GX36.js} +5 -5
  24. package/dist/{chunk-7EQTDTTJ.js 2.map → chunk-4ZC4GX36.js.map} +1 -1
  25. package/dist/{chunk-7FLMSG37.js → chunk-BYFSK72L.js} +22 -22
  26. package/dist/chunk-BYFSK72L.js.map +1 -0
  27. package/dist/{chunk-LFNCN2SP.js → chunk-EXUD6RNJ.js} +46 -7
  28. package/dist/chunk-EXUD6RNJ.js.map +1 -0
  29. package/dist/{chunk-NOAYCWCX 5.js → chunk-GLK6VM3F.js} +167 -169
  30. package/dist/chunk-GLK6VM3F.js.map +1 -0
  31. package/dist/{chunk-HW3OVDUF.js → chunk-J36DSWQK.js} +1 -1
  32. package/dist/{chunk-HW3OVDUF.js.map → chunk-J36DSWQK.js.map} +1 -1
  33. package/dist/{chunk-BC4IJKSL.js → chunk-JBKQ3SAO.js} +2 -2
  34. package/dist/{chunk-QWWZ5CAQ.js → chunk-LXQLPRQ2.js} +2 -2
  35. package/dist/{chunk-E3SPN4VZ 5.js → chunk-T33XF5ZC.js} +119 -114
  36. package/dist/chunk-T33XF5ZC.js.map +1 -0
  37. package/dist/{chunk-XNXXZ43G.js → chunk-XM25TVIE.js} +27 -4
  38. package/dist/chunk-XM25TVIE.js.map +1 -0
  39. package/dist/components.d.ts +3 -3
  40. package/dist/components.js +8 -8
  41. package/dist/hooks.d.ts +6 -6
  42. package/dist/hooks.js +17 -22
  43. package/dist/hooks.js.map +1 -1
  44. package/dist/index.d.ts +7 -7
  45. package/dist/index.js +15 -16
  46. package/dist/index.js.map +1 -1
  47. package/dist/providers.js +1 -1
  48. package/dist/rbac/index.d.ts +1 -1
  49. package/dist/rbac/index.js +5 -5
  50. package/dist/{usePublicRouteParams-TZe0gy-4.d.ts → usePublicRouteParams-BJAlWfuJ.d.ts} +3 -3
  51. package/dist/{useToast-C8gR5ir4.d.ts → useToast-AyaT-x7p.d.ts} +2 -2
  52. package/dist/utils.d.ts +1 -1
  53. package/dist/utils.js +3 -3
  54. package/docs/getting-started/cursor-rules.md +262 -0
  55. package/docs/getting-started/installation-guide.md +6 -1
  56. package/docs/getting-started/quick-start.md +6 -1
  57. package/docs/migration/MIGRATION_GUIDE.md +4 -4
  58. package/docs/migration/REACT_19_MIGRATION.md +227 -0
  59. package/docs/standards/README.md +39 -0
  60. package/docs/troubleshooting/migration.md +4 -4
  61. package/examples/PublicPages/PublicEventPage.tsx +1 -1
  62. package/package.json +11 -6
  63. package/scripts/audit-consuming-app.cjs +961 -0
  64. package/scripts/check-pace-core-compliance.cjs +34 -15
  65. package/scripts/install-cursor-rules.cjs +236 -0
  66. package/src/__tests__/helpers/test-providers.tsx +1 -1
  67. package/src/__tests__/helpers/test-utils.tsx +1 -1
  68. package/src/components/Badge/Badge.tsx +2 -4
  69. package/src/components/Button/Button.tsx +5 -4
  70. package/src/components/Calendar/Calendar.tsx +1 -1
  71. package/src/components/DataTable/DataTable.test.tsx +57 -93
  72. package/src/components/DataTable/DataTable.tsx +2 -2
  73. package/src/components/DataTable/__tests__/pagination.modes.test.tsx +13 -5
  74. package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx +12 -12
  75. package/src/components/DataTable/components/AccessDeniedPage.tsx +1 -1
  76. package/src/components/DataTable/components/BulkOperationsDropdown.tsx +1 -1
  77. package/src/components/DataTable/components/DataTableCore.tsx +4 -7
  78. package/src/components/DataTable/components/DataTableModals.tsx +1 -1
  79. package/src/components/DataTable/components/EditableRow.tsx +1 -1
  80. package/src/components/DataTable/components/UnifiedTableBody.tsx +6 -8
  81. package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +23 -23
  82. package/src/components/DataTable/components/__tests__/EditableRow.test.tsx +11 -11
  83. package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +36 -36
  84. package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +27 -27
  85. package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +39 -39
  86. package/src/components/DataTable/components/__tests__/UnifiedTableBody.test.tsx +33 -33
  87. package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +29 -29
  88. package/src/components/DataTable/hooks/useColumnReordering.ts +2 -2
  89. package/src/components/DataTable/hooks/useKeyboardNavigation.ts +2 -2
  90. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +8 -14
  91. package/src/components/Dialog/Dialog.tsx +6 -5
  92. package/src/components/ErrorBoundary/ErrorBoundary.tsx +1 -1
  93. package/src/components/EventSelector/EventSelector.tsx +1 -1
  94. package/src/components/FileDisplay/FileDisplay.test.tsx +2 -2
  95. package/src/components/Footer/Footer.tsx +1 -1
  96. package/src/components/Form/Form.test.tsx +36 -15
  97. package/src/components/Form/Form.tsx +30 -26
  98. package/src/components/Header/Header.tsx +1 -1
  99. package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +40 -40
  100. package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +1 -1
  101. package/src/components/Input/Input.tsx +28 -30
  102. package/src/components/Label/Label.tsx +1 -1
  103. package/src/components/LoadingSpinner/LoadingSpinner.tsx +1 -1
  104. package/src/components/LoginForm/LoginForm.test.tsx +42 -42
  105. package/src/components/LoginForm/LoginForm.tsx +8 -8
  106. package/src/components/NavigationMenu/NavigationMenu.tsx +1 -1
  107. package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +1 -1
  108. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +50 -50
  109. package/src/components/PaceAppLayout/PaceAppLayout.tsx +1 -1
  110. package/src/components/PaceAppLayout/README.md +1 -1
  111. package/src/components/PaceLoginPage/PaceLoginPage.tsx +1 -1
  112. package/src/components/PasswordChange/PasswordChangeForm.test.tsx +33 -33
  113. package/src/components/PasswordChange/PasswordChangeForm.tsx +1 -1
  114. package/src/components/Progress/Progress.tsx +1 -1
  115. package/src/components/PublicLayout/PublicPageLayout.tsx +1 -1
  116. package/src/components/Select/Select.tsx +33 -22
  117. package/src/components/SessionRestorationLoader/SessionRestorationLoader.tsx +1 -1
  118. package/src/components/Table/Table.tsx +1 -1
  119. package/src/components/Textarea/Textarea.tsx +27 -29
  120. package/src/components/Toast/Toast.tsx +1 -1
  121. package/src/components/Tooltip/Tooltip.tsx +1 -1
  122. package/src/components/UserMenu/UserMenu.tsx +1 -1
  123. package/src/hooks/__tests__/hooks.integration.test.tsx +80 -55
  124. package/src/hooks/__tests__/useStorage.unit.test.ts +36 -36
  125. package/src/hooks/public/usePublicEvent.ts +1 -1
  126. package/src/hooks/public/usePublicEventLogo.ts +1 -1
  127. package/src/hooks/public/usePublicRouteParams.ts +1 -1
  128. package/src/hooks/useDataTableState.ts +8 -18
  129. package/src/hooks/useFocusManagement.ts +2 -2
  130. package/src/hooks/useFocusTrap.ts +4 -4
  131. package/src/hooks/useFormDialog.ts +8 -7
  132. package/src/hooks/useInactivityTracker.ts +1 -1
  133. package/src/hooks/usePermissionCache.ts +1 -1
  134. package/src/hooks/useSecureDataAccess.ts +19 -4
  135. package/src/hooks/useToast.ts +2 -2
  136. package/src/providers/__tests__/OrganisationProvider.test.tsx +57 -13
  137. package/src/providers/__tests__/ProviderLifecycle.test.tsx +21 -6
  138. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +10 -10
  139. package/src/providers/services/UnifiedAuthProvider.tsx +22 -22
  140. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +13 -3
  141. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +24 -24
  142. package/src/rbac/components/EnhancedNavigationMenu.tsx +1 -1
  143. package/src/rbac/components/NavigationGuard.tsx +1 -1
  144. package/src/rbac/components/NavigationProvider.tsx +1 -1
  145. package/src/rbac/components/PagePermissionGuard.tsx +1 -1
  146. package/src/rbac/components/PagePermissionProvider.tsx +1 -1
  147. package/src/rbac/components/PermissionEnforcer.tsx +1 -1
  148. package/src/rbac/components/RoleBasedRouter.tsx +1 -1
  149. package/src/rbac/components/SecureDataProvider.tsx +1 -1
  150. package/src/rbac/secureClient.ts +12 -0
  151. package/src/utils/security/secureDataAccess.test.ts +31 -20
  152. package/src/utils/security/secureDataAccess.ts +4 -3
  153. package/dist/chunk-6C4YBBJM.js +0 -628
  154. package/dist/chunk-6C4YBBJM.js.map +0 -1
  155. package/dist/chunk-7D4SUZUM.js 2.map +0 -1
  156. package/dist/chunk-7EQTDTTJ.js.map +0 -1
  157. package/dist/chunk-7FLMSG37.js 2.map +0 -1
  158. package/dist/chunk-7FLMSG37.js.map +0 -1
  159. package/dist/chunk-E3SPN4VZ.js +0 -12917
  160. package/dist/chunk-E3SPN4VZ.js.map +0 -1
  161. package/dist/chunk-E66EQZE6 5.js +0 -37
  162. package/dist/chunk-E66EQZE6.js 2.map +0 -1
  163. package/dist/chunk-HWIIPPNI.js.map +0 -1
  164. package/dist/chunk-I7PSE6JW 5.js +0 -191
  165. package/dist/chunk-I7PSE6JW.js 2.map +0 -1
  166. package/dist/chunk-KNC55RTG.js 5.map +0 -1
  167. package/dist/chunk-KQCRWDSA.js 5.map +0 -1
  168. package/dist/chunk-LFNCN2SP.js 2.map +0 -1
  169. package/dist/chunk-LFNCN2SP.js.map +0 -1
  170. package/dist/chunk-LMC26NLJ 2.js +0 -84
  171. package/dist/chunk-NOAYCWCX.js +0 -4993
  172. package/dist/chunk-NOAYCWCX.js.map +0 -1
  173. package/dist/chunk-QWWZ5CAQ.js.map +0 -1
  174. package/dist/chunk-QXHPKYJV 3.js +0 -113
  175. package/dist/chunk-R77UEZ4E 3.js +0 -68
  176. package/dist/chunk-VBXEHIUJ.js 6.map +0 -1
  177. package/dist/chunk-XNXXZ43G.js.map +0 -1
  178. package/dist/chunk-ZSAAAMVR 6.js +0 -25
  179. package/dist/components.js 5.map +0 -1
  180. package/dist/styles/index 2.js +0 -12
  181. package/dist/styles/index.js 5.map +0 -1
  182. package/dist/theming/runtime 5.js +0 -19
  183. package/dist/theming/runtime.js 5.map +0 -1
  184. /package/dist/{DataTable-5FU7IESH.js.map → DataTable-DQ7RSOHE.js.map} +0 -0
  185. /package/dist/{UnifiedAuthProvider-RGJTDE2C.js.map → UnifiedAuthProvider-ATAP5UTR.js.map} +0 -0
  186. /package/dist/{chunk-BC4IJKSL.js.map → chunk-JBKQ3SAO.js.map} +0 -0
  187. /package/dist/{chunk-QWWZ5CAQ.js 3.map → chunk-LXQLPRQ2.js.map} +0 -0
  188. /package/examples/{rbac → RBAC}/CompleteRBACExample.tsx +0 -0
  189. /package/examples/{rbac → RBAC}/EventBasedApp.tsx +0 -0
  190. /package/examples/{rbac → RBAC}/PermissionExample.tsx +0 -0
  191. /package/examples/{rbac → RBAC}/index.ts +0 -0
@@ -70,7 +70,7 @@ describe('[component] DataTable', () => {
70
70
  const testData = createTestData(3);
71
71
  const testColumns = createTestColumns();
72
72
  const defaultRBAC = { pageId: 'test-page-id' };
73
- const defaultProps = {
73
+ const baseProps = {
74
74
  data: testData,
75
75
  columns: testColumns,
76
76
  rbac: defaultRBAC,
@@ -90,7 +90,7 @@ describe('[component] DataTable', () => {
90
90
 
91
91
  describe('Rendering', () => {
92
92
  it('renders DataTableCore with required props', () => {
93
- render(<DataTable {...defaultProps} />);
93
+ render(<DataTable {...baseProps} />);
94
94
 
95
95
  expect(screen.getByTestId('data-table-core')).toBeInTheDocument();
96
96
  expect(getMockedDataTableCore()).toHaveBeenCalledTimes(1);
@@ -99,7 +99,7 @@ describe('[component] DataTable', () => {
99
99
  it('renders with title and description', () => {
100
100
  render(
101
101
  <DataTable
102
- {...defaultProps}
102
+ {...baseProps}
103
103
  title="Test Table"
104
104
  description="Test description"
105
105
  />
@@ -111,14 +111,14 @@ describe('[component] DataTable', () => {
111
111
  });
112
112
 
113
113
  it('renders with custom variant', () => {
114
- render(<DataTable {...defaultProps} variant="compact" />);
114
+ render(<DataTable {...baseProps} variant="compact" />);
115
115
 
116
116
  const core = screen.getByTestId('data-table-core');
117
117
  expect(core).toHaveAttribute('data-variant', 'compact');
118
118
  });
119
119
 
120
120
  it('renders with custom className', () => {
121
- render(<DataTable {...defaultProps} className="custom-table-class" />);
121
+ render(<DataTable {...baseProps} className="custom-table-class" />);
122
122
 
123
123
  const core = screen.getByTestId('data-table-core');
124
124
  expect(core).toHaveAttribute('data-class-name', 'custom-table-class');
@@ -127,7 +127,7 @@ describe('[component] DataTable', () => {
127
127
 
128
128
  describe('Feature Configuration', () => {
129
129
  it('normalizes undefined features to default disabled features', () => {
130
- render(<DataTable {...defaultProps} />);
130
+ render(<DataTable {...baseProps} />);
131
131
 
132
132
  const core = screen.getByTestId('data-table-core');
133
133
  const features = JSON.parse(core.getAttribute('data-features') || '{}');
@@ -157,7 +157,7 @@ describe('[component] DataTable', () => {
157
157
  pagination: true,
158
158
  };
159
159
 
160
- render(<DataTable {...defaultProps} features={partialFeatures} />);
160
+ render(<DataTable {...baseProps} features={partialFeatures} />);
161
161
 
162
162
  const core = screen.getByTestId('data-table-core');
163
163
  const features = JSON.parse(core.getAttribute('data-features') || '{}');
@@ -170,7 +170,7 @@ describe('[component] DataTable', () => {
170
170
 
171
171
  it('passes fully normalized features to DataTableCore', () => {
172
172
  const features = createFullFeatures();
173
- render(<DataTable {...defaultProps} features={features} />);
173
+ render(<DataTable {...baseProps} features={features} />);
174
174
 
175
175
  const core = screen.getByTestId('data-table-core');
176
176
  const normalizedFeatures = JSON.parse(core.getAttribute('data-features') || '{}');
@@ -192,11 +192,11 @@ describe('[component] DataTable', () => {
192
192
 
193
193
  it('memoizes normalized features when features prop does not change', () => {
194
194
  const features = createDefaultFeatures();
195
- const { rerender } = render(<DataTable {...defaultProps} features={features} />);
195
+ const { rerender } = render(<DataTable {...baseProps} features={features} />);
196
196
 
197
197
  expect(getMockedDataTableCore()).toHaveBeenCalledTimes(1);
198
198
 
199
- rerender(<DataTable {...defaultProps} features={features} />);
199
+ rerender(<DataTable {...baseProps} features={features} />);
200
200
 
201
201
  // Should not re-normalize if features object reference is the same
202
202
  expect(getMockedDataTableCore()).toHaveBeenCalledTimes(2);
@@ -205,14 +205,14 @@ describe('[component] DataTable', () => {
205
205
 
206
206
  describe('RBAC Configuration', () => {
207
207
  it('passes rbac with pageId to DataTableCore', () => {
208
- render(<DataTable {...defaultProps} rbac={{ pageId: 'custom-page-id' }} />);
208
+ render(<DataTable {...baseProps} rbac={{ pageId: 'custom-page-id' }} />);
209
209
 
210
210
  const core = screen.getByTestId('data-table-core');
211
211
  expect(core).toHaveAttribute('data-page-id', 'custom-page-id');
212
212
  });
213
213
 
214
214
  it('passes rbac with pageName to DataTableCore', () => {
215
- render(<DataTable {...defaultProps} rbac={{ pageName: 'custom-page-name' }} />);
215
+ render(<DataTable {...baseProps} rbac={{ pageName: 'custom-page-name' }} />);
216
216
 
217
217
  const core = screen.getByTestId('data-table-core');
218
218
  expect(core).toHaveAttribute('data-page-name', 'custom-page-name');
@@ -223,7 +223,7 @@ describe('[component] DataTable', () => {
223
223
  it('logs debug information in development mode', () => {
224
224
  // Note: Debug logging has been removed from DataTable component
225
225
  // The component now uses a logger utility instead of console.log
226
- render(<DataTable {...defaultProps} />);
226
+ render(<DataTable {...baseProps} />);
227
227
 
228
228
  // Verify component renders correctly
229
229
  expect(screen.getByTestId('data-table-core')).toBeInTheDocument();
@@ -241,7 +241,7 @@ describe('[component] DataTable', () => {
241
241
  mockLogger.info.mockClear();
242
242
 
243
243
  // Render without features prop - should trigger the warning in development mode
244
- render(<DataTable {...defaultProps} />);
244
+ render(<DataTable {...baseProps} />);
245
245
 
246
246
  // Wait for useEffect to run - it runs after render
247
247
  // The effect checks: if (!features && import.meta.env?.MODE === 'development')
@@ -259,7 +259,7 @@ describe('[component] DataTable', () => {
259
259
  });
260
260
 
261
261
  it('does not log info message when features are provided', () => {
262
- render(<DataTable {...defaultProps} features={createDefaultFeatures()} />);
262
+ render(<DataTable {...baseProps} features={createDefaultFeatures()} />);
263
263
 
264
264
  expect(mockLogger.info).not.toHaveBeenCalled();
265
265
  });
@@ -270,7 +270,7 @@ describe('[component] DataTable', () => {
270
270
  deletion: false,
271
271
  });
272
272
 
273
- render(<DataTable {...defaultProps} features={invalidFeatures} />);
273
+ render(<DataTable {...baseProps} features={invalidFeatures} />);
274
274
 
275
275
  expect(mockLogger.warn).toHaveBeenCalledWith(
276
276
  'deleteSelected requires deletion to be enabled'
@@ -283,7 +283,7 @@ describe('[component] DataTable', () => {
283
283
  deletion: true,
284
284
  });
285
285
 
286
- render(<DataTable {...defaultProps} features={validFeatures} />);
286
+ render(<DataTable {...baseProps} features={validFeatures} />);
287
287
 
288
288
  expect(mockLogger.warn).not.toHaveBeenCalled();
289
289
  });
@@ -292,81 +292,57 @@ describe('[component] DataTable', () => {
292
292
  describe('Event Handlers', () => {
293
293
  it('passes onEditRow handler to DataTableCore', () => {
294
294
  const onEditRow = vi.fn();
295
- render(<DataTable {...defaultProps} onEditRow={onEditRow} />);
295
+ render(<DataTable {...baseProps} onEditRow={onEditRow} />);
296
296
 
297
- expect(getMockedDataTableCore()).toHaveBeenCalledWith(
298
- expect.objectContaining({
299
- onEditRow,
300
- }),
301
- expect.anything()
302
- );
297
+ const mockCall = getMockedDataTableCore().mock.calls[0];
298
+ expect(mockCall[0]).toHaveProperty('onEditRow', onEditRow);
303
299
  });
304
300
 
305
301
  it('passes onDeleteRow handler to DataTableCore', () => {
306
302
  const onDeleteRow = vi.fn();
307
- render(<DataTable {...defaultProps} onDeleteRow={onDeleteRow} />);
303
+ render(<DataTable {...baseProps} onDeleteRow={onDeleteRow} />);
308
304
 
309
- expect(getMockedDataTableCore()).toHaveBeenCalledWith(
310
- expect.objectContaining({
311
- onDeleteRow,
312
- }),
313
- expect.anything()
314
- );
305
+ const mockCall = getMockedDataTableCore().mock.calls[0];
306
+ expect(mockCall[0]).toHaveProperty('onDeleteRow', onDeleteRow);
315
307
  });
316
308
 
317
309
  it('passes onCreateRow handler to DataTableCore', () => {
318
310
  const onCreateRow = vi.fn();
319
- render(<DataTable {...defaultProps} onCreateRow={onCreateRow} />);
311
+ render(<DataTable {...baseProps} onCreateRow={onCreateRow} />);
320
312
 
321
- expect(getMockedDataTableCore()).toHaveBeenCalledWith(
322
- expect.objectContaining({
323
- onCreateRow,
324
- }),
325
- expect.anything()
326
- );
313
+ const mockCall = getMockedDataTableCore().mock.calls[0];
314
+ expect(mockCall[0]).toHaveProperty('onCreateRow', onCreateRow);
327
315
  });
328
316
 
329
317
  it('passes onImport handler to DataTableCore', () => {
330
318
  const onImport = vi.fn();
331
- render(<DataTable {...defaultProps} onImport={onImport} />);
319
+ render(<DataTable {...baseProps} onImport={onImport} />);
332
320
 
333
- expect(getMockedDataTableCore()).toHaveBeenCalledWith(
334
- expect.objectContaining({
335
- onImport,
336
- }),
337
- expect.anything()
338
- );
321
+ const mockCall = getMockedDataTableCore().mock.calls[0];
322
+ expect(mockCall[0]).toHaveProperty('onImport', onImport);
339
323
  });
340
324
 
341
325
  it('passes onRowSelectionChange handler to DataTableCore', () => {
342
326
  const onRowSelectionChange = vi.fn();
343
- render(<DataTable {...defaultProps} onRowSelectionChange={onRowSelectionChange} />);
327
+ render(<DataTable {...baseProps} onRowSelectionChange={onRowSelectionChange} />);
344
328
 
345
- expect(getMockedDataTableCore()).toHaveBeenCalledWith(
346
- expect.objectContaining({
347
- onRowSelectionChange,
348
- }),
349
- expect.anything()
350
- );
329
+ const mockCall = getMockedDataTableCore().mock.calls[0];
330
+ expect(mockCall[0]).toHaveProperty('onRowSelectionChange', onRowSelectionChange);
351
331
  });
352
332
 
353
333
  it('passes onDeleteSelected handler to DataTableCore', () => {
354
334
  const onDeleteSelected = vi.fn();
355
- render(<DataTable {...defaultProps} onDeleteSelected={onDeleteSelected} />);
335
+ render(<DataTable {...baseProps} onDeleteSelected={onDeleteSelected} />);
356
336
 
357
- expect(getMockedDataTableCore()).toHaveBeenCalledWith(
358
- expect.objectContaining({
359
- onDeleteSelected,
360
- }),
361
- expect.anything()
362
- );
337
+ const mockCall = getMockedDataTableCore().mock.calls[0];
338
+ expect(mockCall[0]).toHaveProperty('onDeleteSelected', onDeleteSelected);
363
339
  });
364
340
  });
365
341
 
366
342
  describe('Data Handling', () => {
367
343
  it('handles empty data array', () => {
368
344
  // Note: Debug logging has been removed from DataTable component
369
- render(<DataTable {...defaultProps} data={[]} />);
345
+ render(<DataTable {...baseProps} data={[]} />);
370
346
 
371
347
  expect(screen.getByTestId('data-table-core')).toBeInTheDocument();
372
348
  });
@@ -374,7 +350,7 @@ describe('[component] DataTable', () => {
374
350
  it('handles single data item', () => {
375
351
  // Note: Debug logging has been removed from DataTable component
376
352
  const singleData = testDataScenarios.single;
377
- render(<DataTable {...defaultProps} data={singleData} />);
353
+ render(<DataTable {...baseProps} data={singleData} />);
378
354
 
379
355
  expect(screen.getByTestId('data-table-core')).toBeInTheDocument();
380
356
  });
@@ -382,14 +358,14 @@ describe('[component] DataTable', () => {
382
358
  it('handles large dataset', () => {
383
359
  // Note: Debug logging has been removed from DataTable component
384
360
  const largeData = testDataScenarios.large;
385
- render(<DataTable {...defaultProps} data={largeData} />);
361
+ render(<DataTable {...baseProps} data={largeData} />);
386
362
 
387
363
  expect(screen.getByTestId('data-table-core')).toBeInTheDocument();
388
364
  });
389
365
 
390
366
  it('handles data with null values', () => {
391
367
  const dataWithNulls = testDataScenarios.withNulls;
392
- render(<DataTable {...defaultProps} data={dataWithNulls} />);
368
+ render(<DataTable {...baseProps} data={dataWithNulls} />);
393
369
 
394
370
  expect(screen.getByTestId('data-table-core')).toBeInTheDocument();
395
371
  });
@@ -402,14 +378,10 @@ describe('[component] DataTable', () => {
402
378
  enableChunking: true,
403
379
  };
404
380
 
405
- render(<DataTable {...defaultProps} performance={performance} />);
381
+ render(<DataTable {...baseProps} performance={performance} />);
406
382
 
407
- expect(getMockedDataTableCore()).toHaveBeenCalledWith(
408
- expect.objectContaining({
409
- performance,
410
- }),
411
- expect.anything()
412
- );
383
+ const mockCall = getMockedDataTableCore().mock.calls[0];
384
+ expect(mockCall[0]).toHaveProperty('performance', performance);
413
385
  });
414
386
 
415
387
  it('passes serverSide config to DataTableCore', () => {
@@ -418,32 +390,24 @@ describe('[component] DataTable', () => {
418
390
  enableServerSorting: true,
419
391
  };
420
392
 
421
- render(<DataTable {...defaultProps} serverSide={serverSide} />);
393
+ render(<DataTable {...baseProps} serverSide={serverSide} />);
422
394
 
423
- expect(getMockedDataTableCore()).toHaveBeenCalledWith(
424
- expect.objectContaining({
425
- serverSide,
426
- }),
427
- expect.anything()
428
- );
395
+ const mockCall = getMockedDataTableCore().mock.calls[0];
396
+ expect(mockCall[0]).toHaveProperty('serverSide', serverSide);
429
397
  });
430
398
 
431
399
  it('passes paginationMode to DataTableCore', () => {
432
- render(<DataTable {...defaultProps} paginationMode="server" />);
400
+ render(<DataTable {...baseProps} paginationMode="server" />);
433
401
 
434
- expect(getMockedDataTableCore()).toHaveBeenCalledWith(
435
- expect.objectContaining({
436
- paginationMode: 'server',
437
- }),
438
- expect.anything()
439
- );
402
+ const mockCall = getMockedDataTableCore().mock.calls[0];
403
+ expect(mockCall[0]).toHaveProperty('paginationMode', 'server');
440
404
  });
441
405
  });
442
406
 
443
407
  describe('Component Integration', () => {
444
408
  it('passes all props to DataTableCore except features', () => {
445
409
  const allProps = {
446
- ...defaultProps,
410
+ ...baseProps,
447
411
  title: 'Test Table',
448
412
  description: 'Test description',
449
413
  variant: 'compact' as const,
@@ -490,7 +454,7 @@ describe('[component] DataTable', () => {
490
454
 
491
455
  describe('Memory Management', () => {
492
456
  it('cleans up resources on unmount', () => {
493
- const { unmount } = render(<DataTable {...defaultProps} />);
457
+ const { unmount } = render(<DataTable {...baseProps} />);
494
458
 
495
459
  expect(screen.getByTestId('data-table-core')).toBeInTheDocument();
496
460
 
@@ -499,11 +463,11 @@ describe('[component] DataTable', () => {
499
463
  });
500
464
 
501
465
  it('handles rapid mount/unmount cycles', () => {
502
- const { unmount: unmount1 } = render(<DataTable {...defaultProps} />);
466
+ const { unmount: unmount1 } = render(<DataTable {...baseProps} />);
503
467
  expect(screen.getByTestId('data-table-core')).toBeInTheDocument();
504
468
  unmount1();
505
469
 
506
- const { unmount: unmount2 } = render(<DataTable {...defaultProps} />);
470
+ const { unmount: unmount2 } = render(<DataTable {...baseProps} />);
507
471
  expect(screen.getByTestId('data-table-core')).toBeInTheDocument();
508
472
  unmount2();
509
473
 
@@ -514,27 +478,27 @@ describe('[component] DataTable', () => {
514
478
  describe('Edge Cases', () => {
515
479
  it('handles missing columns gracefully', () => {
516
480
  // Note: Debug logging has been removed from DataTable component
517
- render(<DataTable {...defaultProps} columns={[]} />);
481
+ render(<DataTable {...baseProps} columns={[]} />);
518
482
 
519
483
  expect(screen.getByTestId('data-table-core')).toBeInTheDocument();
520
484
  });
521
485
 
522
486
  it('handles undefined rbac pageId and pageName', () => {
523
487
  // Note: Debug logging has been removed from DataTable component
524
- render(<DataTable {...defaultProps} rbac={{}} />);
488
+ render(<DataTable {...baseProps} rbac={{}} />);
525
489
 
526
490
  expect(screen.getByTestId('data-table-core')).toBeInTheDocument();
527
491
  });
528
492
 
529
493
  it('handles rapid feature prop changes', () => {
530
- const { rerender } = render(<DataTable {...defaultProps} features={{ search: true }} />);
494
+ const { rerender } = render(<DataTable {...baseProps} features={{ search: true }} />);
531
495
 
532
496
  expect(getMockedDataTableCore()).toHaveBeenCalledTimes(1);
533
497
 
534
- rerender(<DataTable {...defaultProps} features={{ search: false }} />);
498
+ rerender(<DataTable {...baseProps} features={{ search: false }} />);
535
499
  expect(getMockedDataTableCore()).toHaveBeenCalledTimes(2);
536
500
 
537
- rerender(<DataTable {...defaultProps} features={{ search: true, pagination: true }} />);
501
+ rerender(<DataTable {...baseProps} features={{ search: true, pagination: true }} />);
538
502
  expect(getMockedDataTableCore()).toHaveBeenCalledTimes(3);
539
503
  });
540
504
  });
@@ -424,7 +424,7 @@ export interface DataTableProps<TData extends DataRecord> {
424
424
  /** Whether the table is in a loading state */
425
425
  isLoading?: boolean;
426
426
  /** Custom component to display when table is empty */
427
- emptyState?: EmptyStateConfig | React.ReactElement;
427
+ emptyState?: EmptyStateConfig | React.ReactElement<any>;
428
428
  /** Array of aggregation functions for grouped data */
429
429
  aggregates?: AggregateConfig[];
430
430
 
@@ -463,7 +463,7 @@ export interface DataTableProps<TData extends DataRecord> {
463
463
  * Features are configured through the unified `features` prop for maximum flexibility.
464
464
  */
465
465
  export function DataTable<TData extends DataRecord>(props: DataTableProps<TData>) {
466
- const logger = React.useMemo(() => createLogger('DataTable'), []);
466
+ const logger = createLogger('DataTable');
467
467
  const { features, ...rest } = props;
468
468
 
469
469
  const normalizedFeatures = React.useMemo(
@@ -9,7 +9,8 @@
9
9
  */
10
10
 
11
11
  import React from 'react';
12
- import { render, screen, fireEvent, waitFor } from '@testing-library/react';
12
+ import { render, screen, fireEvent, waitFor, act } from '@testing-library/react';
13
+ import userEvent from '@testing-library/user-event';
13
14
  import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
14
15
  import { DataTable } from '../DataTable';
15
16
  import { getPaginationBinding, getPageSizeOptions, calculateOptimalPageSize } from '../utils/paginationUtils';
@@ -377,6 +378,7 @@ describe('DataTable Pagination Integration', () => {
377
378
  });
378
379
 
379
380
  it('should navigate pages correctly in client mode', async () => {
381
+ const user = userEvent.setup();
380
382
  render(
381
383
  <DataTable
382
384
  data={smallDataset}
@@ -412,13 +414,19 @@ describe('DataTable Pagination Integration', () => {
412
414
  // Should start on page 1
413
415
  expect(await screen.findByText('Page 1 of 3')).toBeInTheDocument();
414
416
 
415
- // Click next page
417
+ // Click next page using userEvent
416
418
  const nextButton = screen.getByLabelText('Go to next page');
417
- fireEvent.click(nextButton);
418
-
419
+
420
+ // Click the button - this triggers table.nextPage() which calls onPaginationChange
421
+ // The onPaginationChange handler updates React state, which should trigger a re-render
422
+ await user.click(nextButton);
423
+
424
+ // Wait for the page text to update - TanStack Table updates state synchronously,
425
+ // but React needs to process the state update and re-render
426
+ // We wait for the text to change, which indicates the component has re-rendered
419
427
  await waitFor(() => {
420
428
  expect(screen.getByText('Page 2 of 3')).toBeInTheDocument();
421
- });
429
+ }, { timeout: 10000, interval: 100 });
422
430
  });
423
431
 
424
432
  // Note: Page size change test removed due to test environment issues with Select component
@@ -4,7 +4,7 @@
4
4
  * @module Components/DataTable/__tests__
5
5
  * @since 2.0.0
6
6
  *
7
- * Tests for SSR safety and React 18 strict mode compatibility.
7
+ * Tests for SSR safety and React 19 strict mode compatibility.
8
8
  */
9
9
 
10
10
  import React from 'react';
@@ -39,7 +39,7 @@ const testColumns: DataTableColumn<TestData>[] = [
39
39
  },
40
40
  ];
41
41
 
42
- const defaultProps = {
42
+ const baseProps = {
43
43
  data: testData,
44
44
  columns: testColumns,
45
45
  rbac: { pageId: 'test' },
@@ -92,7 +92,7 @@ describe('DataTable SSR and Strict Mode Compatibility', () => {
92
92
  (global as any).window = {} as any;
93
93
 
94
94
  expect(() => {
95
- React.createElement(SafeDataTable, defaultProps);
95
+ React.createElement(SafeDataTable, baseProps);
96
96
  }).not.toThrow();
97
97
 
98
98
  global.window = originalWindow;
@@ -104,7 +104,7 @@ describe('DataTable SSR and Strict Mode Compatibility', () => {
104
104
  (global as any).localStorage = undefined as any;
105
105
 
106
106
  expect(() => {
107
- React.createElement(SafeDataTable, defaultProps);
107
+ React.createElement(SafeDataTable, baseProps);
108
108
  }).not.toThrow();
109
109
 
110
110
  global.localStorage = originalLocalStorage;
@@ -116,20 +116,20 @@ describe('DataTable SSR and Strict Mode Compatibility', () => {
116
116
  (global as any).document = {} as any;
117
117
 
118
118
  expect(() => {
119
- React.createElement(SafeDataTable, defaultProps);
119
+ React.createElement(SafeDataTable, baseProps);
120
120
  }).not.toThrow();
121
121
 
122
122
  global.document = originalDocument;
123
123
  });
124
124
  });
125
125
 
126
- describe('React 18 Strict Mode Compatibility', () => {
126
+ describe('React 19 Strict Mode Compatibility', () => {
127
127
  it('should handle strict mode double-invoke without errors', () => {
128
128
  let renderCount = 0;
129
129
 
130
130
  const TestComponent = () => {
131
131
  renderCount++;
132
- return <SafeDataTable {...defaultProps} />;
132
+ return <SafeDataTable {...baseProps} />;
133
133
  };
134
134
 
135
135
  expect(() => {
@@ -153,7 +153,7 @@ describe('DataTable SSR and Strict Mode Compatibility', () => {
153
153
  };
154
154
  }, []);
155
155
 
156
- return <SafeDataTable {...defaultProps} />;
156
+ return <SafeDataTable {...baseProps} />;
157
157
  };
158
158
 
159
159
  expect(() => {
@@ -176,7 +176,7 @@ describe('DataTable SSR and Strict Mode Compatibility', () => {
176
176
  return (
177
177
  <div>
178
178
  <span data-testid="count">{count}</span>
179
- <SafeDataTable {...defaultProps} />
179
+ <SafeDataTable {...baseProps} />
180
180
  </div>
181
181
  );
182
182
  };
@@ -270,7 +270,7 @@ describe('DataTable SSR and Strict Mode Compatibility', () => {
270
270
 
271
271
  return (
272
272
  <div>
273
- <SafeDataTable {...defaultProps} />
273
+ <SafeDataTable {...baseProps} />
274
274
  {mounted && <div data-testid="client-only">Client Only</div>}
275
275
  </div>
276
276
  );
@@ -291,7 +291,7 @@ describe('DataTable SSR and Strict Mode Compatibility', () => {
291
291
 
292
292
  return (
293
293
  <div>
294
- <SafeDataTable {...defaultProps} />
294
+ <SafeDataTable {...baseProps} />
295
295
  <div data-testid="client-state">
296
296
  {isClient ? 'Client' : 'Server'}
297
297
  </div>
@@ -311,7 +311,7 @@ describe('DataTable SSR and Strict Mode Compatibility', () => {
311
311
 
312
312
  render(
313
313
  <React.StrictMode>
314
- <SafeDataTable {...defaultProps} />
314
+ <SafeDataTable {...baseProps} />
315
315
  </React.StrictMode>
316
316
  );
317
317
 
@@ -33,7 +33,7 @@
33
33
  * - Card component for layout
34
34
  * - Button component for actions
35
35
  * - Lucide React icons
36
- * - React 18+ hooks
36
+ * - React 19+ hooks
37
37
  */
38
38
 
39
39
  import React from 'react';
@@ -38,7 +38,7 @@
38
38
  * - Select components (formerly DropdownMenu)
39
39
  * - Button component
40
40
  * - Lucide React icons
41
- * - React 18+ hooks
41
+ * - React 19+ hooks
42
42
  */
43
43
 
44
44
  import React from 'react';
@@ -8,12 +8,10 @@
8
8
  * This is the main component that consumers will use.
9
9
  */
10
10
 
11
- import React, { useMemo, useCallback, useEffect, useState, useRef } from 'react';
12
- import { useReactTable, flexRender } from '@tanstack/react-table';
11
+ import React, { useMemo, useCallback, useEffect, useRef } from 'react';
12
+ import { useReactTable } from '@tanstack/react-table';
13
13
  import type {
14
14
  SortingState,
15
- ColumnFiltersState,
16
- VisibilityState,
17
15
  GroupingState,
18
16
  ExpandedState,
19
17
  PaginationState,
@@ -159,7 +157,7 @@ export interface DataTableCoreProps<TData extends DataRecord> {
159
157
  // Utilities
160
158
  getRowId?: GetRowId<TData>;
161
159
  isLoading?: boolean;
162
- emptyState?: EmptyStateConfig | React.ReactElement;
160
+ emptyState?: EmptyStateConfig | React.ReactElement<any>;
163
161
  aggregates?: AggregateConfig[];
164
162
  importModalConfig?: ImportModalConfig;
165
163
  actions?: DataTableAction<TData>[];
@@ -220,7 +218,7 @@ function DataTableInternal<TData extends DataRecord>(props: DataTableCoreProps<T
220
218
  onLayoutChange,
221
219
  } = props;
222
220
 
223
- const logger = React.useMemo(() => createLogger('DataTableCore'), []);
221
+ const logger = createLogger('DataTableCore');
224
222
 
225
223
  // ============================================================================
226
224
  // ALL HOOKS MUST BE CALLED IN THE SAME ORDER EVERY RENDER
@@ -1234,7 +1232,6 @@ function DataTableInternal<TData extends DataRecord>(props: DataTableCoreProps<T
1234
1232
  )}
1235
1233
 
1236
1234
  </table>
1237
-
1238
1235
  {/* Modal Dialogs */}
1239
1236
  <DataTableModals
1240
1237
  showImportModal={state.showImportModal}
@@ -184,7 +184,7 @@ export function DataTableModals<TData extends Record<string, unknown> = Record<s
184
184
  onStoreFocus,
185
185
  onRestoreFocus,
186
186
  }: DataTableModalsProps<TData>) {
187
- const logger = React.useMemo(() => createLogger('DataTableModals'), []);
187
+ const logger = createLogger('DataTableModals');
188
188
  // Handle focus management for import modal
189
189
  useEffect(() => {
190
190
  if (showImportModal) {
@@ -45,7 +45,7 @@ function SelectEditField<TData extends DataRecord>({
45
45
  onChange: (value: CellValue) => void;
46
46
  className?: string;
47
47
  }) {
48
- const logger = React.useMemo(() => createLogger('SelectEditField'), []);
48
+ const logger = createLogger('SelectEditField');
49
49
  // Determine if searchable - explicitly check for true to ensure visible search input appears
50
50
  // When selectSearchable is true or undefined, show the visible search input box
51
51
  // When selectSearchable is false, hide the search input (type-to-search still works via SelectContent internals)