@jmruthers/pace-core 0.5.61 → 0.5.62

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 (155) hide show
  1. package/dist/{DataTable-5M6MV2VY.js → DataTable-7BER7PDS.js} +6 -6
  2. package/dist/{DataTable-DqDDvBfI.d.ts → DataTable-D15XipLZ.d.ts} +7 -0
  3. package/dist/{PublicLoadingSpinner-CrMOrhNz.d.ts → PublicLoadingSpinner-CXJ-W9wZ.d.ts} +2 -2
  4. package/dist/{chunk-44SAHU2N.js → chunk-2LPYEFXI.js} +5 -5
  5. package/dist/{chunk-XMTHMOOM.js → chunk-BTCA3ENN.js} +4 -4
  6. package/dist/{chunk-ESXTFEE6.js → chunk-C7GUF747.js} +3 -3
  7. package/dist/{chunk-W7PPXKTZ.js → chunk-CKNY7HYS.js} +2 -2
  8. package/dist/{chunk-5MLDIGHB.js → chunk-FVDOEGGG.js} +3 -3
  9. package/dist/{chunk-HFDYTSAP.js → chunk-QVEOQVD4.js} +3 -3
  10. package/dist/{chunk-XDXG6QVH.js → chunk-S66AJVI2.js} +13 -6
  11. package/dist/chunk-S66AJVI2.js.map +1 -0
  12. package/dist/{chunk-E4FPK232.js → chunk-T2MQY57J.js} +2 -2
  13. package/dist/{chunk-4ULBJNIT.js → chunk-T6HVDA24.js} +2 -2
  14. package/dist/{chunk-STT7INZR.js → chunk-ULBI5JGB.js} +2 -1
  15. package/dist/{chunk-CGSYCF2W.js → chunk-VTJ5HCZB.js} +2 -2
  16. package/dist/components.d.ts +82 -5
  17. package/dist/components.js +258 -9
  18. package/dist/components.js.map +1 -1
  19. package/dist/hooks.d.ts +3 -2
  20. package/dist/hooks.js +5 -5
  21. package/dist/index.d.ts +6 -5
  22. package/dist/index.js +11 -11
  23. package/dist/{organisation-DD0yBbGU.d.ts → organisation-t-vvQC3g.d.ts} +1 -1
  24. package/dist/providers.d.ts +2 -2
  25. package/dist/providers.js +4 -4
  26. package/dist/rbac/index.js +6 -6
  27. package/dist/types.js +1 -1
  28. package/dist/{usePublicRouteParams-Cu6oKazv.d.ts → usePublicRouteParams-CdoFxnJK.d.ts} +2 -63
  29. package/dist/useToast-Bm6TnSK-.d.ts +63 -0
  30. package/dist/utils.d.ts +1 -1
  31. package/dist/utils.js +1 -1
  32. package/docs/api/classes/ErrorBoundary.md +1 -1
  33. package/docs/api/classes/InvalidScopeError.md +1 -1
  34. package/docs/api/classes/MissingUserContextError.md +1 -1
  35. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  36. package/docs/api/classes/PermissionDeniedError.md +1 -1
  37. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  38. package/docs/api/classes/RBACAuditManager.md +1 -1
  39. package/docs/api/classes/RBACCache.md +1 -1
  40. package/docs/api/classes/RBACEngine.md +1 -1
  41. package/docs/api/classes/RBACError.md +1 -1
  42. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  43. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  44. package/docs/api/classes/StorageUtils.md +1 -1
  45. package/docs/api/interfaces/AggregateConfig.md +1 -1
  46. package/docs/api/interfaces/ButtonProps.md +1 -1
  47. package/docs/api/interfaces/CardProps.md +1 -1
  48. package/docs/api/interfaces/ColorPalette.md +1 -1
  49. package/docs/api/interfaces/ColorShade.md +1 -1
  50. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  51. package/docs/api/interfaces/DataTableAction.md +1 -1
  52. package/docs/api/interfaces/DataTableColumn.md +1 -1
  53. package/docs/api/interfaces/DataTableProps.md +44 -18
  54. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  55. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  56. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  57. package/docs/api/interfaces/EventContextType.md +1 -1
  58. package/docs/api/interfaces/EventLogoProps.md +1 -1
  59. package/docs/api/interfaces/EventProviderProps.md +1 -1
  60. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  61. package/docs/api/interfaces/FileUploadProps.md +1 -1
  62. package/docs/api/interfaces/FooterProps.md +1 -1
  63. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  64. package/docs/api/interfaces/InputProps.md +1 -1
  65. package/docs/api/interfaces/LabelProps.md +1 -1
  66. package/docs/api/interfaces/LoginFormProps.md +1 -1
  67. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  68. package/docs/api/interfaces/NavigationContextType.md +1 -1
  69. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  70. package/docs/api/interfaces/NavigationItem.md +1 -1
  71. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  72. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  73. package/docs/api/interfaces/Organisation.md +1 -1
  74. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  75. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  76. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  77. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  78. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  79. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  80. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  81. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  82. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  83. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  84. package/docs/api/interfaces/PaletteData.md +1 -1
  85. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  86. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  87. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  88. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  89. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  90. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  91. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  92. package/docs/api/interfaces/RBACConfig.md +1 -1
  93. package/docs/api/interfaces/RBACContextType.md +1 -1
  94. package/docs/api/interfaces/RBACLogger.md +1 -1
  95. package/docs/api/interfaces/RBACProviderProps.md +1 -1
  96. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  97. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  98. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  99. package/docs/api/interfaces/RouteConfig.md +1 -1
  100. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  101. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  102. package/docs/api/interfaces/StorageConfig.md +1 -1
  103. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  104. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  105. package/docs/api/interfaces/StorageListOptions.md +1 -1
  106. package/docs/api/interfaces/StorageListResult.md +1 -1
  107. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  108. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  109. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  110. package/docs/api/interfaces/StyleImport.md +1 -1
  111. package/docs/api/interfaces/ToastActionElement.md +1 -1
  112. package/docs/api/interfaces/ToastProps.md +1 -1
  113. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  114. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  115. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  116. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  117. package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
  118. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  119. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  120. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  121. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  122. package/docs/api/interfaces/UserEventAccess.md +1 -1
  123. package/docs/api/interfaces/UserMenuProps.md +1 -1
  124. package/docs/api/interfaces/UserProfile.md +1 -1
  125. package/docs/api/modules.md +3 -3
  126. package/docs/implementation-guides/data-tables.md +67 -0
  127. package/package.json +1 -1
  128. package/src/components/DataTable/DataTable.tsx +13 -0
  129. package/src/components/DataTable/__tests__/DataTable.default-state.test.tsx +414 -0
  130. package/src/components/DataTable/components/DataTableCore.tsx +19 -2
  131. package/src/components/DataTable/types.ts +9 -0
  132. package/src/components/Dialog/examples/__tests__/SmartDialogExample.unit.test.tsx +151 -0
  133. package/src/components/Dialog/utils/__tests__/safeHtml.unit.test.ts +611 -0
  134. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.accessibility.test.tsx +287 -0
  135. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.integration.test.tsx +861 -0
  136. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.performance.test.tsx +628 -0
  137. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.security.test.tsx +777 -0
  138. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.unit.test.tsx +901 -0
  139. package/src/components/Toast/index.ts +2 -0
  140. package/src/components/index.ts +15 -0
  141. package/src/hooks/useFileReference.ts +37 -0
  142. package/src/styles/base.css +208 -0
  143. package/src/styles/semantic.css +24 -0
  144. package/dist/chunk-XDXG6QVH.js.map +0 -1
  145. /package/dist/{DataTable-5M6MV2VY.js.map → DataTable-7BER7PDS.js.map} +0 -0
  146. /package/dist/{chunk-44SAHU2N.js.map → chunk-2LPYEFXI.js.map} +0 -0
  147. /package/dist/{chunk-XMTHMOOM.js.map → chunk-BTCA3ENN.js.map} +0 -0
  148. /package/dist/{chunk-ESXTFEE6.js.map → chunk-C7GUF747.js.map} +0 -0
  149. /package/dist/{chunk-W7PPXKTZ.js.map → chunk-CKNY7HYS.js.map} +0 -0
  150. /package/dist/{chunk-5MLDIGHB.js.map → chunk-FVDOEGGG.js.map} +0 -0
  151. /package/dist/{chunk-HFDYTSAP.js.map → chunk-QVEOQVD4.js.map} +0 -0
  152. /package/dist/{chunk-E4FPK232.js.map → chunk-T2MQY57J.js.map} +0 -0
  153. /package/dist/{chunk-4ULBJNIT.js.map → chunk-T6HVDA24.js.map} +0 -0
  154. /package/dist/{chunk-STT7INZR.js.map → chunk-ULBI5JGB.js.map} +0 -0
  155. /package/dist/{chunk-CGSYCF2W.js.map → chunk-VTJ5HCZB.js.map} +0 -0
@@ -0,0 +1,287 @@
1
+ import React from 'react';
2
+ import { render, screen } from '@testing-library/react';
3
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
4
+ import { BrowserRouter } from 'react-router-dom';
5
+
6
+ // Mock React Router hooks
7
+ const mockNavigate = vi.fn();
8
+ vi.mock('react-router-dom', async () => {
9
+ const actual = await vi.importActual('react-router-dom');
10
+ return {
11
+ ...actual,
12
+ useNavigate: () => mockNavigate,
13
+ Outlet: () => <div data-testid="mock-outlet">Mock Outlet Content</div>
14
+ };
15
+ });
16
+
17
+ // Mock UnifiedAuthProvider
18
+ const mockSignOut = vi.fn().mockResolvedValue({ error: null });
19
+ const mockUpdatePassword = vi.fn().mockResolvedValue({ error: null });
20
+ const mockUser = {
21
+ id: 'test-user-id',
22
+ email: 'test@example.com',
23
+ user_metadata: { display_name: 'Test User' }
24
+ };
25
+
26
+ vi.mock('../../../providers/UnifiedAuthProvider', () => ({
27
+ useUnifiedAuth: () => ({
28
+ user: mockUser,
29
+ signOut: mockSignOut,
30
+ updatePassword: mockUpdatePassword
31
+ })
32
+ }));
33
+
34
+ // Mock OrganisationProvider
35
+ const mockOrganisation = {
36
+ id: 'test-org-id',
37
+ name: 'Test Organisation',
38
+ display_name: 'Test Organisation',
39
+ description: 'Test organisation for testing',
40
+ subscription_tier: 'basic',
41
+ settings: {},
42
+ is_active: true,
43
+ created_at: '2023-01-01T00:00:00Z',
44
+ updated_at: '2023-01-01T00:00:00Z'
45
+ };
46
+
47
+ const mockOrganisationContext = {
48
+ selectedOrganisation: mockOrganisation,
49
+ organisations: [mockOrganisation],
50
+ userMemberships: [{
51
+ id: 'test-membership-id',
52
+ user_id: 'test-user-id',
53
+ organisation_id: 'test-org-id',
54
+ role: 'org_admin',
55
+ granted_at: '2023-01-01T00:00:00Z',
56
+ status: 'active' as const,
57
+ created_at: '2023-01-01T00:00:00Z',
58
+ updated_at: '2023-01-01T00:00:00Z'
59
+ }],
60
+ isLoading: false,
61
+ error: null,
62
+ hasValidOrganisationContext: true,
63
+ setSelectedOrganisation: vi.fn(),
64
+ switchOrganisation: vi.fn().mockResolvedValue(undefined),
65
+ getUserRole: vi.fn().mockReturnValue('member'),
66
+ validateOrganisationAccess: vi.fn().mockReturnValue(true),
67
+ ensureOrganisationContext: vi.fn().mockReturnValue(mockOrganisation),
68
+ refreshOrganisations: vi.fn().mockResolvedValue(undefined),
69
+ getPrimaryOrganisation: vi.fn().mockReturnValue(mockOrganisation),
70
+ isOrganisationSecure: vi.fn().mockReturnValue(true)
71
+ };
72
+
73
+ vi.mock('../../../providers/OrganisationProvider', () => ({
74
+ OrganisationProvider: ({ children }: { children: React.ReactNode }) => <>{children}</>,
75
+ useOrganisations: () => mockOrganisationContext
76
+ }));
77
+
78
+ // Mock usePermissionCache
79
+ vi.mock('../../../hooks/usePermissionCache', () => ({
80
+ usePermissionCache: () => ({
81
+ checkPermission: vi.fn().mockResolvedValue(true),
82
+ hasPermission: vi.fn().mockReturnValue(true),
83
+ getPermissions: vi.fn().mockReturnValue(['read', 'write']),
84
+ refreshPermissions: vi.fn().mockResolvedValue(undefined)
85
+ })
86
+ }));
87
+
88
+ // Mock child components with proper accessibility attributes
89
+ vi.mock('../../Header', () => ({
90
+ Header: vi.fn(({ appName, user, onSignOut, onChangePassword, onNavigate, currentPath }) => (
91
+ <header data-testid="mock-header" role="banner" aria-label={`${appName ?? 'Test App'} header`}>
92
+ <div data-testid="app-name" aria-label="Application name">{appName ?? 'Test App'}</div>
93
+ <div data-testid="user-info" aria-label="User information">
94
+ {user?.user_metadata?.display_name || user?.email}
95
+ </div>
96
+ <button
97
+ data-testid="sign-out-button"
98
+ onClick={() => onSignOut()}
99
+ aria-label="Sign out of application"
100
+ >
101
+ Sign Out
102
+ </button>
103
+ <button
104
+ data-testid="change-password-button"
105
+ onClick={() => onChangePassword('newpassword123')}
106
+ aria-label="Change password"
107
+ >
108
+ Change Password
109
+ </button>
110
+ <button
111
+ data-testid="navigate-button"
112
+ onClick={() => onNavigate({ id: 'home', label: 'Home', href: '/' })}
113
+ aria-label="Navigate to home"
114
+ >
115
+ Navigate
116
+ </button>
117
+ <div data-testid="current-path" aria-label="Current page path">{currentPath}</div>
118
+ </header>
119
+ ))
120
+ }));
121
+
122
+ vi.mock('../../Footer', () => ({
123
+ Footer: vi.fn(() => (
124
+ <footer data-testid="mock-footer" role="contentinfo" aria-label="Application footer">
125
+ Mock Footer
126
+ </footer>
127
+ ))
128
+ }));
129
+
130
+ // Mock window.location
131
+ Object.defineProperty(window, 'location', {
132
+ value: {
133
+ pathname: '/test-path'
134
+ },
135
+ writable: true
136
+ });
137
+
138
+ import { PaceAppLayout } from '../PaceAppLayout';
139
+
140
+ // Wrapper component to provide Router context
141
+ const TestWrapper = ({ children }: { children: React.ReactNode }) => (
142
+ <BrowserRouter>
143
+ {children}
144
+ </BrowserRouter>
145
+ );
146
+
147
+ describe('PaceAppLayout Accessibility', () => {
148
+ beforeEach(() => {
149
+ vi.clearAllMocks();
150
+ mockNavigate.mockClear();
151
+ });
152
+
153
+ describe('Semantic HTML Structure', () => {
154
+ it('renders with proper semantic HTML elements', () => {
155
+ render(
156
+ <TestWrapper>
157
+ <PaceAppLayout appName="Test App" />
158
+ </TestWrapper>
159
+ );
160
+
161
+ // Check for proper semantic elements
162
+ expect(screen.getByRole('banner')).toBeInTheDocument();
163
+ expect(screen.getByRole('contentinfo')).toBeInTheDocument();
164
+ expect(screen.getByRole('main')).toBeInTheDocument();
165
+ });
166
+
167
+ it('provides proper main content area', () => {
168
+ render(
169
+ <TestWrapper>
170
+ <PaceAppLayout appName="Test App" />
171
+ </TestWrapper>
172
+ );
173
+
174
+ const main = screen.getByRole('main');
175
+ expect(main).toBeInTheDocument();
176
+ expect(main).toHaveClass('px-4', 'w-[min(var(--app-width),100%)]', 'mx-auto', 'py-8');
177
+ });
178
+ });
179
+
180
+ describe('ARIA Attributes and Labels', () => {
181
+ it('provides proper ARIA labels for header elements', () => {
182
+ render(
183
+ <TestWrapper>
184
+ <PaceAppLayout appName="Test App" />
185
+ </TestWrapper>
186
+ );
187
+
188
+ expect(screen.getByLabelText('Application name')).toBeInTheDocument();
189
+ expect(screen.getByLabelText('User information')).toBeInTheDocument();
190
+ expect(screen.getByLabelText('Current page path')).toBeInTheDocument();
191
+ });
192
+
193
+ it('provides proper ARIA labels for interactive elements', () => {
194
+ render(
195
+ <TestWrapper>
196
+ <PaceAppLayout appName="Test App" />
197
+ </TestWrapper>
198
+ );
199
+
200
+ expect(screen.getByLabelText('Sign out of application')).toBeInTheDocument();
201
+ expect(screen.getByLabelText('Change password')).toBeInTheDocument();
202
+ expect(screen.getByLabelText('Navigate to home')).toBeInTheDocument();
203
+ });
204
+
205
+ it('provides proper ARIA labels for footer', () => {
206
+ render(
207
+ <TestWrapper>
208
+ <PaceAppLayout appName="Test App" />
209
+ </TestWrapper>
210
+ );
211
+
212
+ expect(screen.getByLabelText('Application footer')).toBeInTheDocument();
213
+ });
214
+ });
215
+
216
+ describe('Screen Reader Support', () => {
217
+ it('provides meaningful text for screen readers', () => {
218
+ render(
219
+ <TestWrapper>
220
+ <PaceAppLayout appName="Test App" />
221
+ </TestWrapper>
222
+ );
223
+
224
+ // Check that user information is properly displayed
225
+ expect(screen.getByLabelText('User information')).toHaveTextContent('Test User');
226
+
227
+ // Check that app name is properly displayed
228
+ expect(screen.getByLabelText('Application name')).toHaveTextContent('Test App');
229
+ });
230
+
231
+ it('provides current path information for screen readers', () => {
232
+ render(
233
+ <TestWrapper>
234
+ <PaceAppLayout appName="Test App" />
235
+ </TestWrapper>
236
+ );
237
+
238
+ expect(screen.getByLabelText('Current page path')).toHaveTextContent('/test-path');
239
+ });
240
+ });
241
+
242
+ describe('Keyboard Navigation', () => {
243
+ it('provides proper focus management', () => {
244
+ render(
245
+ <TestWrapper>
246
+ <PaceAppLayout appName="Test App" />
247
+ </TestWrapper>
248
+ );
249
+
250
+ // Check that buttons can receive focus
251
+ const signOutButton = screen.getByLabelText('Sign out of application');
252
+ signOutButton.focus();
253
+ expect(document.activeElement).toBe(signOutButton);
254
+ });
255
+ });
256
+
257
+ describe('Color and Contrast', () => {
258
+ it('ensures proper contrast ratios are maintained', () => {
259
+ render(
260
+ <TestWrapper>
261
+ <PaceAppLayout appName="Test App" />
262
+ </TestWrapper>
263
+ );
264
+
265
+ // This would typically be tested with a visual regression test
266
+ // or with a tool like axe-core, but we can verify the structure
267
+ const main = screen.getByRole('main');
268
+ expect(main).toBeInTheDocument();
269
+ expect(main).toHaveClass('px-4', 'w-[min(var(--app-width),100%)]', 'mx-auto', 'py-8');
270
+ });
271
+ });
272
+
273
+ describe('Responsive Design Accessibility', () => {
274
+ it('maintains accessibility on different screen sizes', () => {
275
+ render(
276
+ <TestWrapper>
277
+ <PaceAppLayout appName="Test App" />
278
+ </TestWrapper>
279
+ );
280
+
281
+ // Check that the layout structure remains accessible
282
+ const main = screen.getByRole('main');
283
+ expect(main).toBeInTheDocument();
284
+ expect(main).toHaveClass('px-4', 'w-[min(var(--app-width),100%)]', 'mx-auto', 'py-8');
285
+ });
286
+ });
287
+ });