@jmruthers/pace-core 0.5.120 → 0.5.123
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/dist/{AuthService-D4646R4b.d.ts → AuthService-DYuQPJj6.d.ts} +0 -9
- package/dist/{DataTable-DGZDJUYM.js → DataTable-WTS4IRF2.js} +7 -8
- package/dist/{PublicLoadingSpinner-DgDWTFqn.d.ts → PublicLoadingSpinner-CaoRbHvJ.d.ts} +30 -4
- package/dist/{UnifiedAuthProvider-UACKFATV.js → UnifiedAuthProvider-6C47WIML.js} +3 -4
- package/dist/{chunk-D6BOFXYR.js → chunk-35ZDPMBM.js} +3 -3
- package/dist/{chunk-CGURJ27Z.js → chunk-4MXVZVNS.js} +2 -2
- package/dist/{chunk-ZYJ6O5CA.js → chunk-C43QIDN3.js} +2 -2
- package/dist/{chunk-VKOCWWVY.js → chunk-CX5M4ZAG.js} +1 -6
- package/dist/{chunk-VKOCWWVY.js.map → chunk-CX5M4ZAG.js.map} +1 -1
- package/dist/{chunk-HFBOFZ3Z.js → chunk-DHMFMXFV.js} +258 -243
- package/dist/chunk-DHMFMXFV.js.map +1 -0
- package/dist/{chunk-RIEJGKD3.js → chunk-ESJTIADP.js} +15 -6
- package/dist/{chunk-RIEJGKD3.js.map → chunk-ESJTIADP.js.map} +1 -1
- package/dist/{chunk-SMJZMKYN.js → chunk-GEVIB2UB.js} +43 -10
- package/dist/chunk-GEVIB2UB.js.map +1 -0
- package/dist/{chunk-TDNI6ZWL.js → chunk-IJOZZOGT.js} +7 -7
- package/dist/chunk-IJOZZOGT.js.map +1 -0
- package/dist/{chunk-GZRXOUBE.js → chunk-M6DDYFUD.js} +2 -2
- package/dist/chunk-M6DDYFUD.js.map +1 -0
- package/dist/{chunk-B4GZ2BXO.js → chunk-NZGLXZGP.js} +3 -3
- package/dist/{chunk-NZ32EONV.js → chunk-QWNJCQXZ.js} +2 -2
- package/dist/{chunk-FKFHZUGF.js → chunk-XN6GWKMV.js} +43 -56
- package/dist/chunk-XN6GWKMV.js.map +1 -0
- package/dist/{chunk-BHWIUEYH.js → chunk-ZBLK676C.js} +1 -61
- package/dist/chunk-ZBLK676C.js.map +1 -0
- package/dist/{chunk-QPI2CCBA.js → chunk-ZPJMYGEP.js} +149 -96
- package/dist/chunk-ZPJMYGEP.js.map +1 -0
- package/dist/components.d.ts +1 -1
- package/dist/components.js +11 -11
- package/dist/{formatting-B1jSqgl-.d.ts → formatting-DFcCxUEk.d.ts} +1 -1
- package/dist/hooks.d.ts +1 -1
- package/dist/hooks.js +9 -8
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.js +19 -17
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +2 -2
- package/dist/providers.js +2 -3
- package/dist/rbac/index.js +7 -8
- package/dist/styles/index.d.ts +1 -1
- package/dist/styles/index.js +5 -3
- package/dist/theming/runtime.d.ts +73 -1
- package/dist/theming/runtime.js +5 -5
- package/dist/{usePublicRouteParams-BdF8bZgs.d.ts → usePublicRouteParams-Dyt1tzI9.d.ts} +60 -8
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +5 -5
- package/docs/api/classes/ColumnFactory.md +1 -1
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +1 -1
- package/docs/api/classes/MissingUserContextError.md +1 -1
- package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
- package/docs/api/classes/PermissionDeniedError.md +1 -1
- package/docs/api/classes/PublicErrorBoundary.md +6 -6
- package/docs/api/classes/RBACAuditManager.md +1 -1
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +1 -1
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +1 -1
- package/docs/api/classes/SecureSupabaseClient.md +6 -6
- package/docs/api/classes/StorageUtils.md +1 -1
- package/docs/api/enums/FileCategory.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/ButtonProps.md +1 -1
- package/docs/api/interfaces/CardProps.md +1 -1
- package/docs/api/interfaces/ColorPalette.md +1 -1
- package/docs/api/interfaces/ColorShade.md +1 -1
- package/docs/api/interfaces/DataAccessRecord.md +1 -1
- package/docs/api/interfaces/DataRecord.md +1 -1
- package/docs/api/interfaces/DataTableAction.md +1 -1
- package/docs/api/interfaces/DataTableColumn.md +1 -1
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
- package/docs/api/interfaces/EmptyStateConfig.md +1 -1
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventAppRoleData.md +1 -1
- package/docs/api/interfaces/FileDisplayProps.md +1 -1
- package/docs/api/interfaces/FileMetadata.md +1 -1
- package/docs/api/interfaces/FileReference.md +1 -1
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadOptions.md +1 -1
- package/docs/api/interfaces/FileUploadProps.md +1 -1
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
- package/docs/api/interfaces/InputProps.md +1 -1
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +1 -1
- package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
- package/docs/api/interfaces/NavigationContextType.md +1 -1
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +1 -1
- package/docs/api/interfaces/NavigationMenuProps.md +1 -1
- package/docs/api/interfaces/NavigationProviderProps.md +1 -1
- package/docs/api/interfaces/Organisation.md +1 -1
- package/docs/api/interfaces/OrganisationContextType.md +1 -1
- package/docs/api/interfaces/OrganisationMembership.md +1 -1
- package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
- package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
- package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
- package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
- package/docs/api/interfaces/PageAccessRecord.md +1 -1
- package/docs/api/interfaces/PagePermissionContextType.md +1 -1
- package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
- package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryProps.md +7 -7
- package/docs/api/interfaces/PublicErrorBoundaryState.md +5 -5
- package/docs/api/interfaces/PublicLoadingSpinnerProps.md +7 -7
- package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
- package/docs/api/interfaces/PublicPageHeaderProps.md +51 -12
- package/docs/api/interfaces/PublicPageLayoutProps.md +72 -12
- package/docs/api/interfaces/RBACConfig.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RoleManagementResult.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +1 -1
- package/docs/api/interfaces/RouteConfig.md +1 -1
- package/docs/api/interfaces/SecureDataContextType.md +1 -1
- package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
- package/docs/api/interfaces/StorageConfig.md +1 -1
- package/docs/api/interfaces/StorageFileInfo.md +1 -1
- package/docs/api/interfaces/StorageFileMetadata.md +1 -1
- package/docs/api/interfaces/StorageListOptions.md +1 -1
- package/docs/api/interfaces/StorageListResult.md +1 -1
- package/docs/api/interfaces/StorageUploadOptions.md +1 -1
- package/docs/api/interfaces/StorageUploadResult.md +1 -1
- package/docs/api/interfaces/StorageUrlOptions.md +1 -1
- package/docs/api/interfaces/StyleImport.md +1 -1
- package/docs/api/interfaces/SwitchProps.md +1 -1
- package/docs/api/interfaces/ToastActionElement.md +1 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
- package/docs/api/interfaces/UsePublicFileDisplayOptions.md +1 -1
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +1 -1
- package/docs/api/interfaces/UserMenuProps.md +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +140 -30
- package/docs/best-practices/README.md +1 -1
- package/docs/implementation-guides/datatable-filtering.md +313 -0
- package/docs/implementation-guides/datatable-rbac-usage.md +317 -0
- package/docs/implementation-guides/hierarchical-datatable.md +850 -0
- package/docs/implementation-guides/large-datasets.md +281 -0
- package/docs/implementation-guides/performance.md +403 -0
- package/docs/implementation-guides/public-pages.md +4 -4
- package/docs/migration/quick-migration-guide.md +320 -0
- package/docs/rbac/quick-start.md +16 -16
- package/docs/troubleshooting/README.md +4 -4
- package/docs/troubleshooting/cake-page-permission-guard-issue-summary.md +1 -1
- package/docs/troubleshooting/debugging.md +1117 -0
- package/docs/troubleshooting/migration.md +918 -0
- package/examples/public-pages/CorrectPublicPageImplementation.tsx +30 -30
- package/examples/public-pages/PublicEventPage.tsx +41 -41
- package/examples/public-pages/PublicPageApp.tsx +33 -33
- package/examples/public-pages/PublicPageUsageExample.tsx +30 -30
- package/package.json +4 -4
- package/src/__tests__/hooks/usePermissions.test.ts +265 -0
- package/src/components/DataTable/DataTable.test.tsx +9 -38
- package/src/components/DataTable/DataTable.tsx +0 -7
- package/src/components/DataTable/components/DataTableCore.tsx +66 -136
- package/src/components/DataTable/components/DataTableModals.tsx +25 -22
- package/src/components/DataTable/components/EditableRow.tsx +118 -42
- package/src/components/DataTable/components/UnifiedTableBody.tsx +129 -76
- package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +33 -14
- package/src/components/DataTable/utils/__tests__/exportUtils.test.ts +17 -5
- package/src/components/DataTable/utils/exportUtils.ts +3 -2
- package/src/components/DataTable/utils/flexibleImport.ts +27 -6
- package/src/components/Dialog/Dialog.tsx +1 -1
- package/src/components/Dialog/README.md +24 -24
- package/src/components/Dialog/examples/BasicHtmlTest.tsx +2 -2
- package/src/components/Dialog/examples/DebugHtmlExample.tsx +6 -6
- package/src/components/Dialog/examples/HtmlDialogExample.tsx +2 -2
- package/src/components/Dialog/examples/SimpleHtmlTest.tsx +3 -3
- package/src/components/Dialog/examples/__tests__/SimpleHtmlTest.test.tsx +4 -4
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +12 -1
- package/src/components/PublicLayout/EventLogo.tsx +175 -0
- package/src/components/PublicLayout/PublicErrorBoundary.tsx +22 -18
- package/src/components/PublicLayout/PublicLoadingSpinner.tsx +22 -14
- package/src/components/PublicLayout/PublicPageHeader.tsx +133 -40
- package/src/components/PublicLayout/PublicPageLayout.tsx +75 -72
- package/src/components/PublicLayout/__tests__/PublicErrorBoundary.test.tsx +1 -1
- package/src/components/PublicLayout/__tests__/PublicLoadingSpinner.test.tsx +8 -8
- package/src/components/PublicLayout/__tests__/PublicPageHeader.test.tsx +23 -16
- package/src/components/PublicLayout/__tests__/PublicPageLayout.test.tsx +86 -14
- package/src/examples/CorrectPublicPageImplementation.tsx +30 -30
- package/src/examples/PublicEventPage.tsx +41 -41
- package/src/examples/PublicPageApp.tsx +33 -33
- package/src/examples/PublicPageUsageExample.tsx +30 -30
- package/src/hooks/__tests__/usePublicEvent.unit.test.ts +583 -0
- package/src/hooks/__tests__/usePublicRouteParams.unit.test.ts +10 -3
- package/src/hooks/index.ts +1 -1
- package/src/hooks/public/usePublicEventLogo.ts +285 -0
- package/src/hooks/public/usePublicRouteParams.ts +21 -4
- package/src/hooks/useEventTheme.test.ts +119 -43
- package/src/hooks/useEventTheme.ts +84 -55
- package/src/index.ts +3 -1
- package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +630 -0
- package/src/rbac/components/__tests__/NavigationProvider.test.tsx +667 -0
- package/src/rbac/components/__tests__/PagePermissionProvider.test.tsx +647 -0
- package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +496 -0
- package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +496 -0
- package/src/rbac/secureClient.ts +4 -2
- package/src/services/EventService.ts +0 -66
- package/src/services/__tests__/EventService.eventColours.test.ts +44 -40
- package/src/styles/index.ts +1 -1
- package/src/theming/__tests__/parseEventColours.test.ts +209 -0
- package/src/theming/parseEventColours.ts +123 -0
- package/src/theming/runtime.ts +3 -0
- package/src/types/__tests__/file-reference.test.ts +447 -0
- package/src/types/database.generated.ts +1515 -424
- package/src/utils/formatDate.test.ts +11 -11
- package/src/utils/formatting.ts +3 -2
- package/dist/chunk-BHWIUEYH.js.map +0 -1
- package/dist/chunk-FKFHZUGF.js.map +0 -1
- package/dist/chunk-GZRXOUBE.js.map +0 -1
- package/dist/chunk-HFBOFZ3Z.js.map +0 -1
- package/dist/chunk-QPI2CCBA.js.map +0 -1
- package/dist/chunk-SMJZMKYN.js.map +0 -1
- package/dist/chunk-TDNI6ZWL.js.map +0 -1
- package/src/styles/semantic.css +0 -24
- /package/dist/{DataTable-DGZDJUYM.js.map → DataTable-WTS4IRF2.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-UACKFATV.js.map → UnifiedAuthProvider-6C47WIML.js.map} +0 -0
- /package/dist/{chunk-D6BOFXYR.js.map → chunk-35ZDPMBM.js.map} +0 -0
- /package/dist/{chunk-CGURJ27Z.js.map → chunk-4MXVZVNS.js.map} +0 -0
- /package/dist/{chunk-ZYJ6O5CA.js.map → chunk-C43QIDN3.js.map} +0 -0
- /package/dist/{chunk-B4GZ2BXO.js.map → chunk-NZGLXZGP.js.map} +0 -0
- /package/dist/{chunk-NZ32EONV.js.map → chunk-QWNJCQXZ.js.map} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jmruthers/pace-core",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.123",
|
|
4
4
|
"description": "Clean, modern React component library with Tailwind v4 styling and native utilities",
|
|
5
5
|
"private": false,
|
|
6
6
|
"publishConfig": {
|
|
@@ -138,12 +138,12 @@
|
|
|
138
138
|
"prepublishOnly": "npm run build:all",
|
|
139
139
|
"clean": "rimraf dist",
|
|
140
140
|
"_comment_test": "Test suite with various configurations",
|
|
141
|
-
"test": "NODE_OPTIONS
|
|
141
|
+
"test": "cross-env NODE_OPTIONS=\"--max-old-space-size=8192 --max-semi-space-size=128\" vitest run --config ../../vitest.config.ts",
|
|
142
142
|
"test:memory-optimized": "node ../../scripts/run-tests-memory-optimized.js",
|
|
143
143
|
"test:watch": "vitest --watch --config ../../vitest.config.ts",
|
|
144
|
-
"test:coverage": "NODE_OPTIONS
|
|
144
|
+
"test:coverage": "cross-env NODE_OPTIONS=\"--max-old-space-size=8192 --max-semi-space-size=128\" vitest run --coverage --config ../../vitest.config.ts --reporter=dot",
|
|
145
145
|
"test:ui": "vitest --ui --config ../../vitest.config.ts",
|
|
146
|
-
"test:new": "NODE_OPTIONS
|
|
146
|
+
"test:new": "cross-env NODE_OPTIONS=\"--max-old-space-size=8192 --max-semi-space-size=128\" vitest run --config vitest.new.config.ts",
|
|
147
147
|
"test:new:watch": "vitest --watch --config vitest.new.config.ts",
|
|
148
148
|
"migrate-tests": "node scripts/migrate-tests.js",
|
|
149
149
|
"test:unit": "vitest src/**/*.test.{ts,tsx} --config ../../vitest.config.ts",
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file usePermissions Hook Tests
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module RBAC/Hooks/usePermissions
|
|
5
|
+
* @since 1.0.0
|
|
6
|
+
*
|
|
7
|
+
* Tests for usePermissions hook to ensure:
|
|
8
|
+
* - Hook returns stable references when data hasn't changed
|
|
9
|
+
* - No infinite re-renders occur
|
|
10
|
+
* - Proper memoization is working
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { renderHook, act } from '@testing-library/react';
|
|
14
|
+
import { vi } from 'vitest';
|
|
15
|
+
import { usePermissions, useCan } from '../../rbac/hooks/usePermissions';
|
|
16
|
+
|
|
17
|
+
// Mock the API functions
|
|
18
|
+
vi.mock('../../rbac/api', () => ({
|
|
19
|
+
getPermissionMap: vi.fn(),
|
|
20
|
+
isPermitted: vi.fn(),
|
|
21
|
+
isPermittedCached: vi.fn(),
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
import { getPermissionMap, isPermitted, isPermittedCached } from '../../rbac/api';
|
|
25
|
+
|
|
26
|
+
const mockGetPermissionMap = vi.mocked(getPermissionMap);
|
|
27
|
+
const mockIsPermitted = vi.mocked(isPermitted);
|
|
28
|
+
const mockIsPermittedCached = vi.mocked(isPermittedCached);
|
|
29
|
+
|
|
30
|
+
describe('usePermissions Hook Stability', () => {
|
|
31
|
+
const mockUserId = 'user-123';
|
|
32
|
+
const mockScope = {
|
|
33
|
+
organisationId: 'org-123',
|
|
34
|
+
eventId: 'event-123',
|
|
35
|
+
appId: 'app-123'
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
beforeEach(() => {
|
|
39
|
+
vi.clearAllMocks();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe('usePermissions', () => {
|
|
43
|
+
it('should return stable references when data hasn\'t changed', async () => {
|
|
44
|
+
const mockPermissions = {
|
|
45
|
+
'read:users': true,
|
|
46
|
+
'create:users': false
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
mockGetPermissionMap.mockResolvedValue(mockPermissions);
|
|
50
|
+
|
|
51
|
+
const { result, rerender } = renderHook(() =>
|
|
52
|
+
usePermissions(mockUserId, mockScope)
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
// Wait for initial load
|
|
56
|
+
await act(async () => {
|
|
57
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const firstResult = result.current;
|
|
61
|
+
|
|
62
|
+
// Re-render with same props
|
|
63
|
+
rerender();
|
|
64
|
+
|
|
65
|
+
const secondResult = result.current;
|
|
66
|
+
|
|
67
|
+
// References should be stable
|
|
68
|
+
expect(firstResult.permissions).toBe(secondResult.permissions);
|
|
69
|
+
expect(firstResult.isLoading).toBe(secondResult.isLoading);
|
|
70
|
+
expect(firstResult.error).toBe(secondResult.error);
|
|
71
|
+
expect(firstResult.hasPermission).toBe(secondResult.hasPermission);
|
|
72
|
+
expect(firstResult.hasAnyPermission).toBe(secondResult.hasAnyPermission);
|
|
73
|
+
expect(firstResult.hasAllPermissions).toBe(secondResult.hasAllPermissions);
|
|
74
|
+
expect(firstResult.refetch).toBe(secondResult.refetch);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should update references when data changes', async () => {
|
|
78
|
+
const initialPermissions = {
|
|
79
|
+
'read:users': true,
|
|
80
|
+
'create:users': false
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const updatedPermissions = {
|
|
84
|
+
'read:users': true,
|
|
85
|
+
'create:users': true
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
mockGetPermissionMap
|
|
89
|
+
.mockResolvedValueOnce(initialPermissions)
|
|
90
|
+
.mockResolvedValueOnce(updatedPermissions);
|
|
91
|
+
|
|
92
|
+
const { result, rerender } = renderHook(() =>
|
|
93
|
+
usePermissions(mockUserId, mockScope)
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
// Wait for initial load
|
|
97
|
+
await act(async () => {
|
|
98
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const firstResult = result.current;
|
|
102
|
+
|
|
103
|
+
// Trigger refetch
|
|
104
|
+
await act(async () => {
|
|
105
|
+
await firstResult.refetch();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const secondResult = result.current;
|
|
109
|
+
|
|
110
|
+
// References should be different due to data change
|
|
111
|
+
expect(firstResult.permissions).not.toBe(secondResult.permissions);
|
|
112
|
+
expect(firstResult.permissions).toEqual(initialPermissions);
|
|
113
|
+
expect(secondResult.permissions).toEqual(updatedPermissions);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('should not cause infinite re-renders', async () => {
|
|
117
|
+
let renderCount = 0;
|
|
118
|
+
|
|
119
|
+
mockGetPermissionMap.mockResolvedValue({
|
|
120
|
+
'read:users': true
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const { result } = renderHook(() => {
|
|
124
|
+
renderCount++;
|
|
125
|
+
return usePermissions(mockUserId, mockScope);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// Wait for initial load
|
|
129
|
+
await act(async () => {
|
|
130
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// Should not have excessive re-renders
|
|
134
|
+
expect(renderCount).toBeLessThan(5);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
describe('useCan', () => {
|
|
139
|
+
it('should return stable references when data hasn\'t changed', async () => {
|
|
140
|
+
mockIsPermittedCached.mockResolvedValue(true);
|
|
141
|
+
|
|
142
|
+
const { result, rerender } = renderHook(() =>
|
|
143
|
+
useCan(mockUserId, mockScope, 'read:users', 'page-123', true)
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
// Wait for initial load
|
|
147
|
+
await act(async () => {
|
|
148
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
const firstResult = result.current;
|
|
152
|
+
|
|
153
|
+
// Re-render with same props
|
|
154
|
+
rerender();
|
|
155
|
+
|
|
156
|
+
const secondResult = result.current;
|
|
157
|
+
|
|
158
|
+
// References should be stable
|
|
159
|
+
expect(firstResult.can).toBe(secondResult.can);
|
|
160
|
+
expect(firstResult.isLoading).toBe(secondResult.isLoading);
|
|
161
|
+
expect(firstResult.error).toBe(secondResult.error);
|
|
162
|
+
expect(firstResult.refetch).toBe(secondResult.refetch);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('should update references when permission result changes', async () => {
|
|
166
|
+
mockIsPermittedCached
|
|
167
|
+
.mockResolvedValueOnce(false)
|
|
168
|
+
.mockResolvedValueOnce(true);
|
|
169
|
+
|
|
170
|
+
const { result, rerender } = renderHook(() =>
|
|
171
|
+
useCan(mockUserId, mockScope, 'read:users', 'page-123', true)
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
// Wait for initial load
|
|
175
|
+
await act(async () => {
|
|
176
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
const firstResult = result.current;
|
|
180
|
+
|
|
181
|
+
// Trigger refetch
|
|
182
|
+
await act(async () => {
|
|
183
|
+
await firstResult.refetch();
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
const secondResult = result.current;
|
|
187
|
+
|
|
188
|
+
// References should be different due to permission change
|
|
189
|
+
expect(firstResult.can).toBe(false);
|
|
190
|
+
expect(secondResult.can).toBe(true);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('should not cause infinite re-renders', async () => {
|
|
194
|
+
let renderCount = 0;
|
|
195
|
+
|
|
196
|
+
mockIsPermittedCached.mockResolvedValue(true);
|
|
197
|
+
|
|
198
|
+
const { result } = renderHook(() => {
|
|
199
|
+
renderCount++;
|
|
200
|
+
return useCan(mockUserId, mockScope, 'read:users', 'page-123', true);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// Wait for initial load
|
|
204
|
+
await act(async () => {
|
|
205
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// Should not have excessive re-renders
|
|
209
|
+
expect(renderCount).toBeLessThan(5);
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
describe('Hook Dependencies', () => {
|
|
214
|
+
it('should re-run when userId changes', async () => {
|
|
215
|
+
mockGetPermissionMap.mockResolvedValue({ 'read:users': true });
|
|
216
|
+
|
|
217
|
+
const { result, rerender } = renderHook(
|
|
218
|
+
({ userId }) => usePermissions(userId, mockScope),
|
|
219
|
+
{ initialProps: { userId: 'user-1' } }
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
await act(async () => {
|
|
223
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
const firstResult = result.current;
|
|
227
|
+
|
|
228
|
+
// Change userId
|
|
229
|
+
rerender({ userId: 'user-2' });
|
|
230
|
+
|
|
231
|
+
await act(async () => {
|
|
232
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
const secondResult = result.current;
|
|
236
|
+
|
|
237
|
+
// Should have re-fetched due to userId change
|
|
238
|
+
expect(mockGetPermissionMap).toHaveBeenCalledTimes(2);
|
|
239
|
+
expect(firstResult).not.toBe(secondResult);
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it('should re-run when scope changes', async () => {
|
|
243
|
+
mockGetPermissionMap.mockResolvedValue({ 'read:users': true });
|
|
244
|
+
|
|
245
|
+
const { result, rerender } = renderHook(
|
|
246
|
+
({ scope }) => usePermissions(mockUserId, scope),
|
|
247
|
+
{ initialProps: { scope: { ...mockScope, organisationId: 'org-1' } } }
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
await act(async () => {
|
|
251
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// Change scope
|
|
255
|
+
rerender({ scope: { ...mockScope, organisationId: 'org-2' } });
|
|
256
|
+
|
|
257
|
+
await act(async () => {
|
|
258
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
// Should have re-fetched due to scope change
|
|
262
|
+
expect(mockGetPermissionMap).toHaveBeenCalledTimes(2);
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
});
|
|
@@ -221,16 +221,12 @@ describe('[component] DataTable', () => {
|
|
|
221
221
|
|
|
222
222
|
describe('Validation and Warnings', () => {
|
|
223
223
|
it('logs debug information in development mode', () => {
|
|
224
|
+
// Note: Debug logging has been removed from DataTable component
|
|
225
|
+
// The component now uses a logger utility instead of console.log
|
|
224
226
|
render(<DataTable {...defaultProps} />);
|
|
225
227
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
expect.objectContaining({
|
|
229
|
-
dataLength: testData.length,
|
|
230
|
-
columnsCount: testColumns.length,
|
|
231
|
-
pageName: defaultRBAC.pageId,
|
|
232
|
-
})
|
|
233
|
-
);
|
|
228
|
+
// Verify component renders correctly
|
|
229
|
+
expect(screen.getByTestId('data-table-core')).toBeInTheDocument();
|
|
234
230
|
});
|
|
235
231
|
|
|
236
232
|
it('logs info message when features are not provided in development', async () => {
|
|
@@ -369,41 +365,26 @@ describe('[component] DataTable', () => {
|
|
|
369
365
|
|
|
370
366
|
describe('Data Handling', () => {
|
|
371
367
|
it('handles empty data array', () => {
|
|
368
|
+
// Note: Debug logging has been removed from DataTable component
|
|
372
369
|
render(<DataTable {...defaultProps} data={[]} />);
|
|
373
370
|
|
|
374
371
|
expect(screen.getByTestId('data-table-core')).toBeInTheDocument();
|
|
375
|
-
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
376
|
-
expect.stringContaining('[DataTable]'),
|
|
377
|
-
expect.objectContaining({
|
|
378
|
-
dataLength: 0,
|
|
379
|
-
})
|
|
380
|
-
);
|
|
381
372
|
});
|
|
382
373
|
|
|
383
374
|
it('handles single data item', () => {
|
|
375
|
+
// Note: Debug logging has been removed from DataTable component
|
|
384
376
|
const singleData = testDataScenarios.single;
|
|
385
377
|
render(<DataTable {...defaultProps} data={singleData} />);
|
|
386
378
|
|
|
387
379
|
expect(screen.getByTestId('data-table-core')).toBeInTheDocument();
|
|
388
|
-
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
389
|
-
expect.stringContaining('[DataTable]'),
|
|
390
|
-
expect.objectContaining({
|
|
391
|
-
dataLength: 1,
|
|
392
|
-
})
|
|
393
|
-
);
|
|
394
380
|
});
|
|
395
381
|
|
|
396
382
|
it('handles large dataset', () => {
|
|
383
|
+
// Note: Debug logging has been removed from DataTable component
|
|
397
384
|
const largeData = testDataScenarios.large;
|
|
398
385
|
render(<DataTable {...defaultProps} data={largeData} />);
|
|
399
386
|
|
|
400
387
|
expect(screen.getByTestId('data-table-core')).toBeInTheDocument();
|
|
401
|
-
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
402
|
-
expect.stringContaining('[DataTable]'),
|
|
403
|
-
expect.objectContaining({
|
|
404
|
-
dataLength: largeData.length,
|
|
405
|
-
})
|
|
406
|
-
);
|
|
407
388
|
});
|
|
408
389
|
|
|
409
390
|
it('handles data with null values', () => {
|
|
@@ -532,27 +513,17 @@ describe('[component] DataTable', () => {
|
|
|
532
513
|
|
|
533
514
|
describe('Edge Cases', () => {
|
|
534
515
|
it('handles missing columns gracefully', () => {
|
|
516
|
+
// Note: Debug logging has been removed from DataTable component
|
|
535
517
|
render(<DataTable {...defaultProps} columns={[]} />);
|
|
536
518
|
|
|
537
519
|
expect(screen.getByTestId('data-table-core')).toBeInTheDocument();
|
|
538
|
-
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
539
|
-
expect.stringContaining('[DataTable]'),
|
|
540
|
-
expect.objectContaining({
|
|
541
|
-
columnsCount: 0,
|
|
542
|
-
})
|
|
543
|
-
);
|
|
544
520
|
});
|
|
545
521
|
|
|
546
522
|
it('handles undefined rbac pageId and pageName', () => {
|
|
523
|
+
// Note: Debug logging has been removed from DataTable component
|
|
547
524
|
render(<DataTable {...defaultProps} rbac={{}} />);
|
|
548
525
|
|
|
549
526
|
expect(screen.getByTestId('data-table-core')).toBeInTheDocument();
|
|
550
|
-
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
551
|
-
expect.stringContaining('[DataTable]'),
|
|
552
|
-
expect.objectContaining({
|
|
553
|
-
pageName: undefined,
|
|
554
|
-
})
|
|
555
|
-
);
|
|
556
527
|
});
|
|
557
528
|
|
|
558
529
|
it('handles rapid feature prop changes', () => {
|
|
@@ -464,13 +464,6 @@ export function DataTable<TData extends DataRecord>(props: DataTableProps<TData>
|
|
|
464
464
|
const logger = React.useMemo(() => createLogger('DataTable'), []);
|
|
465
465
|
const { features, ...rest } = props;
|
|
466
466
|
|
|
467
|
-
// CRITICAL DEBUG: Log when DataTable wrapper is called
|
|
468
|
-
console.log('[DataTable] 🎯 DataTable WRAPPER called:', {
|
|
469
|
-
dataLength: props.data?.length || 0,
|
|
470
|
-
columnsCount: props.columns?.length || 0,
|
|
471
|
-
pageName: props.rbac?.pageName || props.rbac?.pageId,
|
|
472
|
-
});
|
|
473
|
-
|
|
474
467
|
const normalizedFeatures = React.useMemo(
|
|
475
468
|
() => normalizeDataTableFeatures(features),
|
|
476
469
|
[features]
|