@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.
- package/CHANGELOG.md +29 -0
- package/README.md +7 -1
- package/cursor-rules/00-pace-core-compliance.mdc +372 -0
- package/cursor-rules/01-standards-compliance.mdc +275 -0
- package/cursor-rules/02-project-structure.mdc +200 -0
- package/cursor-rules/03-solid-principles.mdc +341 -0
- package/cursor-rules/04-testing-standards.mdc +315 -0
- package/cursor-rules/05-bug-reports-and-features.mdc +246 -0
- package/cursor-rules/06-code-quality.mdc +392 -0
- package/cursor-rules/07-tech-stack-compliance.mdc +309 -0
- package/cursor-rules/CHANGELOG.md +101 -0
- package/cursor-rules/README.md +191 -0
- package/dist/{DataTable-Be6dH_dR.d.ts → DataTable-CH1U5Tpy.d.ts} +1 -1
- package/dist/{DataTable-5FU7IESH.js → DataTable-DQ7RSOHE.js} +6 -6
- package/dist/{PublicPageProvider-C0Sm_e5k.d.ts → PublicPageProvider-ce4xlHYA.d.ts} +34 -155
- package/dist/{UnifiedAuthProvider-RGJTDE2C.js → UnifiedAuthProvider-ATAP5UTR.js} +2 -2
- package/dist/{chunk-6C4YBBJM 5.js → chunk-3QRJFVBR.js} +1 -1
- package/dist/chunk-3QRJFVBR.js.map +1 -0
- package/dist/{chunk-IIELH4DL.js → chunk-3XTALGJF.js} +2 -2
- package/dist/{chunk-IIELH4DL.js.map → chunk-3XTALGJF.js.map} +1 -1
- package/dist/{chunk-HWIIPPNI.js → chunk-4N5C5XZU.js} +20 -20
- package/dist/chunk-4N5C5XZU.js.map +1 -0
- package/dist/{chunk-7EQTDTTJ.js → chunk-4ZC4GX36.js} +5 -5
- package/dist/{chunk-7EQTDTTJ.js 2.map → chunk-4ZC4GX36.js.map} +1 -1
- package/dist/{chunk-7FLMSG37.js → chunk-BYFSK72L.js} +22 -22
- package/dist/chunk-BYFSK72L.js.map +1 -0
- package/dist/{chunk-LFNCN2SP.js → chunk-EXUD6RNJ.js} +46 -7
- package/dist/chunk-EXUD6RNJ.js.map +1 -0
- package/dist/{chunk-NOAYCWCX 5.js → chunk-GLK6VM3F.js} +167 -169
- package/dist/chunk-GLK6VM3F.js.map +1 -0
- package/dist/{chunk-HW3OVDUF.js → chunk-J36DSWQK.js} +1 -1
- package/dist/{chunk-HW3OVDUF.js.map → chunk-J36DSWQK.js.map} +1 -1
- package/dist/{chunk-BC4IJKSL.js → chunk-JBKQ3SAO.js} +2 -2
- package/dist/{chunk-QWWZ5CAQ.js → chunk-LXQLPRQ2.js} +2 -2
- package/dist/{chunk-E3SPN4VZ 5.js → chunk-T33XF5ZC.js} +119 -114
- package/dist/chunk-T33XF5ZC.js.map +1 -0
- package/dist/{chunk-XNXXZ43G.js → chunk-XM25TVIE.js} +27 -4
- package/dist/chunk-XM25TVIE.js.map +1 -0
- package/dist/components.d.ts +3 -3
- package/dist/components.js +8 -8
- package/dist/hooks.d.ts +6 -6
- package/dist/hooks.js +17 -22
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +7 -7
- package/dist/index.js +15 -16
- package/dist/index.js.map +1 -1
- package/dist/providers.js +1 -1
- package/dist/rbac/index.d.ts +1 -1
- package/dist/rbac/index.js +5 -5
- package/dist/{usePublicRouteParams-TZe0gy-4.d.ts → usePublicRouteParams-BJAlWfuJ.d.ts} +3 -3
- package/dist/{useToast-C8gR5ir4.d.ts → useToast-AyaT-x7p.d.ts} +2 -2
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +3 -3
- package/docs/getting-started/cursor-rules.md +262 -0
- package/docs/getting-started/installation-guide.md +6 -1
- package/docs/getting-started/quick-start.md +6 -1
- package/docs/migration/MIGRATION_GUIDE.md +4 -4
- package/docs/migration/REACT_19_MIGRATION.md +227 -0
- package/docs/standards/README.md +39 -0
- package/docs/troubleshooting/migration.md +4 -4
- package/examples/PublicPages/PublicEventPage.tsx +1 -1
- package/package.json +11 -6
- package/scripts/audit-consuming-app.cjs +961 -0
- package/scripts/check-pace-core-compliance.cjs +34 -15
- package/scripts/install-cursor-rules.cjs +236 -0
- package/src/__tests__/helpers/test-providers.tsx +1 -1
- package/src/__tests__/helpers/test-utils.tsx +1 -1
- package/src/components/Badge/Badge.tsx +2 -4
- package/src/components/Button/Button.tsx +5 -4
- package/src/components/Calendar/Calendar.tsx +1 -1
- package/src/components/DataTable/DataTable.test.tsx +57 -93
- package/src/components/DataTable/DataTable.tsx +2 -2
- package/src/components/DataTable/__tests__/pagination.modes.test.tsx +13 -5
- package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx +12 -12
- package/src/components/DataTable/components/AccessDeniedPage.tsx +1 -1
- package/src/components/DataTable/components/BulkOperationsDropdown.tsx +1 -1
- package/src/components/DataTable/components/DataTableCore.tsx +4 -7
- package/src/components/DataTable/components/DataTableModals.tsx +1 -1
- package/src/components/DataTable/components/EditableRow.tsx +1 -1
- package/src/components/DataTable/components/UnifiedTableBody.tsx +6 -8
- package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +23 -23
- package/src/components/DataTable/components/__tests__/EditableRow.test.tsx +11 -11
- package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +36 -36
- package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +27 -27
- package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +39 -39
- package/src/components/DataTable/components/__tests__/UnifiedTableBody.test.tsx +33 -33
- package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +29 -29
- package/src/components/DataTable/hooks/useColumnReordering.ts +2 -2
- package/src/components/DataTable/hooks/useKeyboardNavigation.ts +2 -2
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +8 -14
- package/src/components/Dialog/Dialog.tsx +6 -5
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +1 -1
- package/src/components/EventSelector/EventSelector.tsx +1 -1
- package/src/components/FileDisplay/FileDisplay.test.tsx +2 -2
- package/src/components/Footer/Footer.tsx +1 -1
- package/src/components/Form/Form.test.tsx +36 -15
- package/src/components/Form/Form.tsx +30 -26
- package/src/components/Header/Header.tsx +1 -1
- package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +40 -40
- package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +1 -1
- package/src/components/Input/Input.tsx +28 -30
- package/src/components/Label/Label.tsx +1 -1
- package/src/components/LoadingSpinner/LoadingSpinner.tsx +1 -1
- package/src/components/LoginForm/LoginForm.test.tsx +42 -42
- package/src/components/LoginForm/LoginForm.tsx +8 -8
- package/src/components/NavigationMenu/NavigationMenu.tsx +1 -1
- package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +1 -1
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +50 -50
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +1 -1
- package/src/components/PaceAppLayout/README.md +1 -1
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +1 -1
- package/src/components/PasswordChange/PasswordChangeForm.test.tsx +33 -33
- package/src/components/PasswordChange/PasswordChangeForm.tsx +1 -1
- package/src/components/Progress/Progress.tsx +1 -1
- package/src/components/PublicLayout/PublicPageLayout.tsx +1 -1
- package/src/components/Select/Select.tsx +33 -22
- package/src/components/SessionRestorationLoader/SessionRestorationLoader.tsx +1 -1
- package/src/components/Table/Table.tsx +1 -1
- package/src/components/Textarea/Textarea.tsx +27 -29
- package/src/components/Toast/Toast.tsx +1 -1
- package/src/components/Tooltip/Tooltip.tsx +1 -1
- package/src/components/UserMenu/UserMenu.tsx +1 -1
- package/src/hooks/__tests__/hooks.integration.test.tsx +80 -55
- package/src/hooks/__tests__/useStorage.unit.test.ts +36 -36
- package/src/hooks/public/usePublicEvent.ts +1 -1
- package/src/hooks/public/usePublicEventLogo.ts +1 -1
- package/src/hooks/public/usePublicRouteParams.ts +1 -1
- package/src/hooks/useDataTableState.ts +8 -18
- package/src/hooks/useFocusManagement.ts +2 -2
- package/src/hooks/useFocusTrap.ts +4 -4
- package/src/hooks/useFormDialog.ts +8 -7
- package/src/hooks/useInactivityTracker.ts +1 -1
- package/src/hooks/usePermissionCache.ts +1 -1
- package/src/hooks/useSecureDataAccess.ts +19 -4
- package/src/hooks/useToast.ts +2 -2
- package/src/providers/__tests__/OrganisationProvider.test.tsx +57 -13
- package/src/providers/__tests__/ProviderLifecycle.test.tsx +21 -6
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +10 -10
- package/src/providers/services/UnifiedAuthProvider.tsx +22 -22
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +13 -3
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +24 -24
- package/src/rbac/components/EnhancedNavigationMenu.tsx +1 -1
- package/src/rbac/components/NavigationGuard.tsx +1 -1
- package/src/rbac/components/NavigationProvider.tsx +1 -1
- package/src/rbac/components/PagePermissionGuard.tsx +1 -1
- package/src/rbac/components/PagePermissionProvider.tsx +1 -1
- package/src/rbac/components/PermissionEnforcer.tsx +1 -1
- package/src/rbac/components/RoleBasedRouter.tsx +1 -1
- package/src/rbac/components/SecureDataProvider.tsx +1 -1
- package/src/rbac/secureClient.ts +12 -0
- package/src/utils/security/secureDataAccess.test.ts +31 -20
- package/src/utils/security/secureDataAccess.ts +4 -3
- package/dist/chunk-6C4YBBJM.js +0 -628
- package/dist/chunk-6C4YBBJM.js.map +0 -1
- package/dist/chunk-7D4SUZUM.js 2.map +0 -1
- package/dist/chunk-7EQTDTTJ.js.map +0 -1
- package/dist/chunk-7FLMSG37.js 2.map +0 -1
- package/dist/chunk-7FLMSG37.js.map +0 -1
- package/dist/chunk-E3SPN4VZ.js +0 -12917
- package/dist/chunk-E3SPN4VZ.js.map +0 -1
- package/dist/chunk-E66EQZE6 5.js +0 -37
- package/dist/chunk-E66EQZE6.js 2.map +0 -1
- package/dist/chunk-HWIIPPNI.js.map +0 -1
- package/dist/chunk-I7PSE6JW 5.js +0 -191
- package/dist/chunk-I7PSE6JW.js 2.map +0 -1
- package/dist/chunk-KNC55RTG.js 5.map +0 -1
- package/dist/chunk-KQCRWDSA.js 5.map +0 -1
- package/dist/chunk-LFNCN2SP.js 2.map +0 -1
- package/dist/chunk-LFNCN2SP.js.map +0 -1
- package/dist/chunk-LMC26NLJ 2.js +0 -84
- package/dist/chunk-NOAYCWCX.js +0 -4993
- package/dist/chunk-NOAYCWCX.js.map +0 -1
- package/dist/chunk-QWWZ5CAQ.js.map +0 -1
- package/dist/chunk-QXHPKYJV 3.js +0 -113
- package/dist/chunk-R77UEZ4E 3.js +0 -68
- package/dist/chunk-VBXEHIUJ.js 6.map +0 -1
- package/dist/chunk-XNXXZ43G.js.map +0 -1
- package/dist/chunk-ZSAAAMVR 6.js +0 -25
- package/dist/components.js 5.map +0 -1
- package/dist/styles/index 2.js +0 -12
- package/dist/styles/index.js 5.map +0 -1
- package/dist/theming/runtime 5.js +0 -19
- package/dist/theming/runtime.js 5.map +0 -1
- /package/dist/{DataTable-5FU7IESH.js.map → DataTable-DQ7RSOHE.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-RGJTDE2C.js.map → UnifiedAuthProvider-ATAP5UTR.js.map} +0 -0
- /package/dist/{chunk-BC4IJKSL.js.map → chunk-JBKQ3SAO.js.map} +0 -0
- /package/dist/{chunk-QWWZ5CAQ.js 3.map → chunk-LXQLPRQ2.js.map} +0 -0
- /package/examples/{rbac → RBAC}/CompleteRBACExample.tsx +0 -0
- /package/examples/{rbac → RBAC}/EventBasedApp.tsx +0 -0
- /package/examples/{rbac → RBAC}/PermissionExample.tsx +0 -0
- /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
|
|
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 {...
|
|
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
|
-
{...
|
|
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 {...
|
|
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 {...
|
|
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 {...
|
|
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 {...
|
|
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 {...
|
|
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 {...
|
|
195
|
+
const { rerender } = render(<DataTable {...baseProps} features={features} />);
|
|
196
196
|
|
|
197
197
|
expect(getMockedDataTableCore()).toHaveBeenCalledTimes(1);
|
|
198
198
|
|
|
199
|
-
rerender(<DataTable {...
|
|
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 {...
|
|
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 {...
|
|
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 {...
|
|
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 {...
|
|
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 {...
|
|
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 {...
|
|
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 {...
|
|
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 {...
|
|
295
|
+
render(<DataTable {...baseProps} onEditRow={onEditRow} />);
|
|
296
296
|
|
|
297
|
-
|
|
298
|
-
|
|
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 {...
|
|
303
|
+
render(<DataTable {...baseProps} onDeleteRow={onDeleteRow} />);
|
|
308
304
|
|
|
309
|
-
|
|
310
|
-
|
|
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 {...
|
|
311
|
+
render(<DataTable {...baseProps} onCreateRow={onCreateRow} />);
|
|
320
312
|
|
|
321
|
-
|
|
322
|
-
|
|
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 {...
|
|
319
|
+
render(<DataTable {...baseProps} onImport={onImport} />);
|
|
332
320
|
|
|
333
|
-
|
|
334
|
-
|
|
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 {...
|
|
327
|
+
render(<DataTable {...baseProps} onRowSelectionChange={onRowSelectionChange} />);
|
|
344
328
|
|
|
345
|
-
|
|
346
|
-
|
|
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 {...
|
|
335
|
+
render(<DataTable {...baseProps} onDeleteSelected={onDeleteSelected} />);
|
|
356
336
|
|
|
357
|
-
|
|
358
|
-
|
|
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 {...
|
|
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 {...
|
|
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 {...
|
|
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 {...
|
|
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 {...
|
|
381
|
+
render(<DataTable {...baseProps} performance={performance} />);
|
|
406
382
|
|
|
407
|
-
|
|
408
|
-
|
|
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 {...
|
|
393
|
+
render(<DataTable {...baseProps} serverSide={serverSide} />);
|
|
422
394
|
|
|
423
|
-
|
|
424
|
-
|
|
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 {...
|
|
400
|
+
render(<DataTable {...baseProps} paginationMode="server" />);
|
|
433
401
|
|
|
434
|
-
|
|
435
|
-
|
|
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
|
-
...
|
|
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 {...
|
|
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 {...
|
|
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 {...
|
|
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 {...
|
|
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 {...
|
|
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 {...
|
|
494
|
+
const { rerender } = render(<DataTable {...baseProps} features={{ search: true }} />);
|
|
531
495
|
|
|
532
496
|
expect(getMockedDataTableCore()).toHaveBeenCalledTimes(1);
|
|
533
497
|
|
|
534
|
-
rerender(<DataTable {...
|
|
498
|
+
rerender(<DataTable {...baseProps} features={{ search: false }} />);
|
|
535
499
|
expect(getMockedDataTableCore()).toHaveBeenCalledTimes(2);
|
|
536
500
|
|
|
537
|
-
rerender(<DataTable {...
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
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 {...
|
|
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 {...
|
|
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 {...
|
|
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 {...
|
|
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 {...
|
|
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 {...
|
|
314
|
+
<SafeDataTable {...baseProps} />
|
|
315
315
|
</React.StrictMode>
|
|
316
316
|
);
|
|
317
317
|
|
|
@@ -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,
|
|
12
|
-
import { useReactTable
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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)
|