@jmruthers/pace-core 0.6.4 → 0.6.5

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 (101) hide show
  1. package/dist/{DataTable-E7YQZD7D.js → DataTable-AOVNCPTX.js} +8 -8
  2. package/dist/{PublicPageProvider-DEMpysFR.d.ts → PublicPageProvider-QTFVrL-Z.d.ts} +65 -83
  3. package/dist/{UnifiedAuthProvider-QPXO24B4.js → UnifiedAuthProvider-4SBX4LU5.js} +4 -4
  4. package/dist/{api-6LVZTHDS.js → api-O6HTBX5Y.js} +3 -3
  5. package/dist/{chunk-I6DAQMWX.js → chunk-6COVEUS7.js} +130 -106
  6. package/dist/chunk-6COVEUS7.js.map +1 -0
  7. package/dist/{chunk-36LVWXB2.js → chunk-AFVQODI2.js} +37 -1
  8. package/dist/{chunk-36LVWXB2.js.map → chunk-AFVQODI2.js.map} +1 -1
  9. package/dist/{chunk-3LPHPB62.js → chunk-EFN2EIMK.js} +2 -2
  10. package/dist/{chunk-ATKZM7RX.js → chunk-G7QEZTYQ.js} +31 -31
  11. package/dist/{chunk-ATKZM7RX.js.map → chunk-G7QEZTYQ.js.map} +1 -1
  12. package/dist/{chunk-NN6WWZ5U.js → chunk-HU2C6SSC.js} +29 -18
  13. package/dist/chunk-HU2C6SSC.js.map +1 -0
  14. package/dist/{chunk-AVMLPIM7.js → chunk-IHB5DR3H.js} +102 -51
  15. package/dist/chunk-IHB5DR3H.js.map +1 -0
  16. package/dist/{chunk-7JPAB3T5.js → chunk-IVOFDYWT.js} +364 -208
  17. package/dist/chunk-IVOFDYWT.js.map +1 -0
  18. package/dist/{chunk-6SOIHG6Z.js → chunk-JGRYX5UX.js} +120 -20
  19. package/dist/chunk-JGRYX5UX.js.map +1 -0
  20. package/dist/{chunk-OEWDTMG7.js → chunk-NTM7ZSB6.js} +4 -4
  21. package/dist/chunk-NTM7ZSB6.js.map +1 -0
  22. package/dist/{chunk-5EC5MEWX.js → chunk-RGAWHO7N.js} +4 -4
  23. package/dist/chunk-RGAWHO7N.js.map +1 -0
  24. package/dist/{chunk-YKRAFF5K.js → chunk-UPPMRMYG.js} +3 -3
  25. package/dist/{chunk-YKRAFF5K.js.map → chunk-UPPMRMYG.js.map} +1 -1
  26. package/dist/components.d.ts +2 -3
  27. package/dist/components.js +24 -28
  28. package/dist/components.js.map +1 -1
  29. package/dist/{contextValidator-OOPCLPZW.js → contextValidator-5OGXSPKS.js} +2 -2
  30. package/dist/hooks.d.ts +3 -3
  31. package/dist/hooks.js +41 -139
  32. package/dist/hooks.js.map +1 -1
  33. package/dist/index.d.ts +27 -18
  34. package/dist/index.js +41 -50
  35. package/dist/index.js.map +1 -1
  36. package/dist/providers.js +3 -3
  37. package/dist/rbac/index.d.ts +16 -9
  38. package/dist/rbac/index.js +6 -6
  39. package/dist/{usePublicRouteParams-i3qtoBgg.d.ts → usePublicRouteParams-ClnV4tnv.d.ts} +8 -8
  40. package/dist/utils.js +1 -1
  41. package/docs/api/modules.md +210 -100
  42. package/package.json +1 -2
  43. package/scripts/validate-master.js +1 -1
  44. package/src/components/DataTable/__tests__/keyboard.test.tsx +15 -2
  45. package/src/components/DataTable/components/ImportModal.tsx +4 -6
  46. package/src/components/DataTable/components/ViewRowModal.tsx +4 -4
  47. package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +455 -96
  48. package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +122 -58
  49. package/src/components/DataTable/core/DataTableContext.tsx +1 -1
  50. package/src/components/DateTimeField/DateTimeField.tsx +17 -19
  51. package/src/components/DateTimeField/README.md +5 -2
  52. package/src/components/Dialog/Dialog.test.tsx +248 -228
  53. package/src/components/Dialog/Dialog.tsx +455 -325
  54. package/src/components/Dialog/index.ts +3 -3
  55. package/src/components/FileDisplay/FileDisplay.test.tsx +41 -0
  56. package/src/components/FileDisplay/FileDisplay.tsx +5 -5
  57. package/src/components/Form/Form.test.tsx +3 -2
  58. package/src/components/Form/Form.tsx +4 -5
  59. package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +28 -28
  60. package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +40 -54
  61. package/src/components/LoginForm/LoginForm.tsx +2 -2
  62. package/src/components/NavigationMenu/NavigationMenu.tsx +2 -2
  63. package/src/components/PaceAppLayout/PaceAppLayout.tsx +32 -39
  64. package/src/components/PaceAppLayout/README.md +10 -9
  65. package/src/components/PaceAppLayout/test-setup.tsx +40 -31
  66. package/src/components/PasswordChange/PasswordChangeForm.test.tsx +61 -0
  67. package/src/components/PasswordChange/PasswordChangeForm.tsx +20 -13
  68. package/src/components/PublicLayout/PublicLayout.test.tsx +7 -3
  69. package/src/components/PublicLayout/PublicPageLayout.tsx +5 -8
  70. package/src/components/UserMenu/UserMenu.test.tsx +38 -6
  71. package/src/components/UserMenu/UserMenu.tsx +36 -34
  72. package/src/components/index.ts +3 -4
  73. package/src/hooks/useEventTheme.ts +4 -4
  74. package/src/hooks/useEvents.ts +11 -7
  75. package/src/hooks/useKeyboardShortcuts.ts +1 -1
  76. package/src/hooks/useOrganisationPermissions.ts +4 -4
  77. package/src/hooks/useOrganisations.ts +13 -7
  78. package/src/index.ts +11 -1
  79. package/src/rbac/README.md +20 -20
  80. package/src/rbac/hooks/useRBAC.test.ts +21 -3
  81. package/src/rbac/hooks/useRBAC.ts +4 -3
  82. package/src/rbac/hooks/useResourcePermissions.test.ts +125 -30
  83. package/src/rbac/hooks/useResourcePermissions.ts +57 -29
  84. package/src/rbac/permissions.ts +17 -17
  85. package/src/rbac/utils/contextValidator.ts +36 -0
  86. package/src/services/AuthService.ts +2 -5
  87. package/src/services/InactivityService.ts +139 -58
  88. package/src/styles/core.css +4 -0
  89. package/src/utils/formatting/formatTime.test.ts +3 -2
  90. package/dist/chunk-5EC5MEWX.js.map +0 -1
  91. package/dist/chunk-6SOIHG6Z.js.map +0 -1
  92. package/dist/chunk-7JPAB3T5.js.map +0 -1
  93. package/dist/chunk-AVMLPIM7.js.map +0 -1
  94. package/dist/chunk-I6DAQMWX.js.map +0 -1
  95. package/dist/chunk-NN6WWZ5U.js.map +0 -1
  96. package/dist/chunk-OEWDTMG7.js.map +0 -1
  97. /package/dist/{DataTable-E7YQZD7D.js.map → DataTable-AOVNCPTX.js.map} +0 -0
  98. /package/dist/{UnifiedAuthProvider-QPXO24B4.js.map → UnifiedAuthProvider-4SBX4LU5.js.map} +0 -0
  99. /package/dist/{api-6LVZTHDS.js.map → api-O6HTBX5Y.js.map} +0 -0
  100. /package/dist/{chunk-3LPHPB62.js.map → chunk-EFN2EIMK.js.map} +0 -0
  101. /package/dist/{contextValidator-OOPCLPZW.js.map → contextValidator-5OGXSPKS.js.map} +0 -0
@@ -266,6 +266,67 @@ describe('PasswordChangeForm', () => {
266
266
 
267
267
  expect(mockOnSubmit).not.toHaveBeenCalled();
268
268
  });
269
+
270
+ it('calls onSuccess callback when password change succeeds', async () => {
271
+ const user = userEvent.setup();
272
+ const mockOnSuccess = vi.fn();
273
+ mockOnSubmit.mockResolvedValue({});
274
+
275
+ render(<PasswordChangeForm {...baseProps} onSuccess={mockOnSuccess} />);
276
+
277
+ const newPasswordInput = screen.getByLabelText('New Password');
278
+ const confirmPasswordInput = screen.getByLabelText('Confirm Password');
279
+ const submitButton = screen.getByRole('button', { name: 'Change Password' });
280
+
281
+ await user.type(newPasswordInput, 'newpassword123');
282
+ await user.type(confirmPasswordInput, 'newpassword123');
283
+ await user.click(submitButton);
284
+
285
+ await waitFor(() => {
286
+ expect(mockOnSuccess).toHaveBeenCalledTimes(1);
287
+ });
288
+ });
289
+
290
+ it('does not call onSuccess callback when password change fails', async () => {
291
+ const user = userEvent.setup();
292
+ const mockOnSuccess = vi.fn();
293
+ mockOnSubmit.mockResolvedValue({ error: { message: 'Password too weak' } });
294
+
295
+ render(<PasswordChangeForm {...baseProps} onSuccess={mockOnSuccess} />);
296
+
297
+ const newPasswordInput = screen.getByLabelText('New Password');
298
+ const confirmPasswordInput = screen.getByLabelText('Confirm Password');
299
+ const submitButton = screen.getByRole('button', { name: 'Change Password' });
300
+
301
+ await user.type(newPasswordInput, 'newpassword123');
302
+ await user.type(confirmPasswordInput, 'newpassword123');
303
+ await user.click(submitButton);
304
+
305
+ await waitFor(() => {
306
+ expect(screen.getByText('Password too weak')).toBeInTheDocument();
307
+ expect(mockOnSuccess).not.toHaveBeenCalled();
308
+ });
309
+ });
310
+
311
+ it('works without onSuccess callback (backward compatibility)', async () => {
312
+ const user = userEvent.setup();
313
+ mockOnSubmit.mockResolvedValue({});
314
+
315
+ render(<PasswordChangeForm {...baseProps} />);
316
+
317
+ const newPasswordInput = screen.getByLabelText('New Password');
318
+ const confirmPasswordInput = screen.getByLabelText('Confirm Password');
319
+ const submitButton = screen.getByRole('button', { name: 'Change Password' });
320
+
321
+ await user.type(newPasswordInput, 'newpassword123');
322
+ await user.type(confirmPasswordInput, 'newpassword123');
323
+ await user.click(submitButton);
324
+
325
+ await waitFor(() => {
326
+ expect(mockOnSubmit).toHaveBeenCalled();
327
+ });
328
+ // Should not throw error when onSuccess is not provided
329
+ });
269
330
  });
270
331
 
271
332
  describe('Loading States', () => {
@@ -47,7 +47,7 @@
47
47
  * }}
48
48
  * />
49
49
  *
50
- * // Password change form in a modal
50
+ * // Password change form in a modal with onSuccess callback
51
51
  * <Modal isOpen={showPasswordChange} onClose={() => setShowPasswordChange(false)}>
52
52
  * <ModalContent>
53
53
  * <ModalHeader>
@@ -58,13 +58,15 @@
58
58
  * onSubmit={async (values) => {
59
59
  * const result = await changePassword(values.newPassword);
60
60
  * if (result.success) {
61
- * setShowPasswordChange(false);
62
- * toast.success('Password changed successfully');
63
61
  * return {};
64
62
  * } else {
65
63
  * return { error: { message: result.error } };
66
64
  * }
67
65
  * }}
66
+ * onSuccess={() => {
67
+ * setShowPasswordChange(false);
68
+ * toast.success('Password changed successfully');
69
+ * }}
68
70
  * />
69
71
  * </ModalBody>
70
72
  * </ModalContent>
@@ -124,10 +126,11 @@ export interface PasswordChangeFormError {
124
126
  */
125
127
  export interface PasswordChangeFormProps {
126
128
  onSubmit: (values: PasswordChangeFormValues) => Promise<{ error?: PasswordChangeFormError }>;
129
+ onSuccess?: () => void;
127
130
  className?: string;
128
131
  }
129
132
 
130
- export function PasswordChangeForm({ onSubmit, className }: PasswordChangeFormProps) {
133
+ export function PasswordChangeForm({ onSubmit, onSuccess, className }: PasswordChangeFormProps) {
131
134
  const [newPassword, setNewPassword] = useState('');
132
135
  const [confirmPassword, setConfirmPassword] = useState('');
133
136
  const [error, setError] = useState<string | null>(null);
@@ -151,6 +154,9 @@ export function PasswordChangeForm({ onSubmit, className }: PasswordChangeFormPr
151
154
  const result = await onSubmit({ newPassword, confirmPassword });
152
155
  if (result && result.error) {
153
156
  setError(result.error.message || 'Failed to change password.');
157
+ } else {
158
+ // Success - call onSuccess callback
159
+ onSuccess?.();
154
160
  }
155
161
  } catch (err) {
156
162
  const errorObj = err instanceof Error ? err : new Error('An unexpected error occurred');
@@ -163,23 +169,23 @@ export function PasswordChangeForm({ onSubmit, className }: PasswordChangeFormPr
163
169
  return (
164
170
  <form onSubmit={handleSubmit} className={cn('space-y-4', className)}>
165
171
  {error && (
166
- <div role="alert">
172
+ <p className="grid place-items-center text-center size-full" role="alert">
167
173
  {error}
168
- </div>
174
+ </p>
169
175
  )}
170
- <div className="space-y-2">
171
- <Label htmlFor="new-password">New Password</Label>
172
- <Input
176
+ <Label htmlFor="new-password" className="block mb-4">New Password
177
+ <Input
173
178
  id="new-password"
174
179
  type="password"
175
180
  value={newPassword}
176
181
  onChange={(e) => setNewPassword(e.target.value)}
177
182
  required
178
183
  disabled={isSubmitting}
184
+ className="mt-2"
179
185
  />
180
- </div>
181
- <div className="space-y-2">
182
- <Label htmlFor="confirm-password">Confirm Password</Label>
186
+ </Label>
187
+
188
+ <Label htmlFor="confirm-password" className="block mb-4">Confirm Password
183
189
  <Input
184
190
  id="confirm-password"
185
191
  type="password"
@@ -187,8 +193,9 @@ export function PasswordChangeForm({ onSubmit, className }: PasswordChangeFormPr
187
193
  onChange={(e) => setConfirmPassword(e.target.value)}
188
194
  required
189
195
  disabled={isSubmitting}
196
+ className="mt-2"
190
197
  />
191
- </div>
198
+ </Label>
192
199
  <Button
193
200
  type="submit"
194
201
  className="w-full"
@@ -241,15 +241,19 @@ describe('[component] PublicPageLayout', () => {
241
241
  });
242
242
 
243
243
  it('renders loading state with proper layout classes', () => {
244
- const { container } = render(
244
+ render(
245
245
  <PublicPageLayout eventCode="EVENT123" isLoading={true}>
246
246
  <div>Test Content</div>
247
247
  </PublicPageLayout>
248
248
  );
249
249
 
250
- const loadingContainer = container.querySelector('.min-h-screen');
250
+ // The loading state renders a <p> tag with grid and place-items-center classes
251
+ // There may be multiple "Loading..." texts (from LoadingSpinner mock and actual text)
252
+ // So we query by the container element using getAllByText and find the one in the <p> tag
253
+ const loadingTexts = screen.getAllByText('Loading...');
254
+ const loadingContainer = loadingTexts.find(text => text.closest('p'))?.closest('p');
251
255
  expect(loadingContainer).toBeInTheDocument();
252
- expect(loadingContainer).toHaveClass('bg-background', 'flex', 'items-center', 'justify-center');
256
+ expect(loadingContainer).toHaveClass('grid', 'place-items-center', 'text-center', 'size-full');
253
257
  });
254
258
  });
255
259
 
@@ -312,14 +312,11 @@ export function PublicPageLayout({
312
312
  return <LoadingFallback />;
313
313
  }
314
314
  return (
315
- <div className="min-h-screen bg-background flex items-center justify-center">
316
- <div className="max-w-md mx-auto text-center px-4">
317
- <LoadingSpinner size="lg" className="mx-auto mb-4" />
318
- {loadingMessage && (
319
- <p className="text-sec-600">{loadingMessage}</p>
320
- )}
321
- </div>
322
- </div>
315
+ <p className="grid place-items-center text-center size-full">
316
+ <LoadingSpinner
317
+ size="lg"/><br />
318
+ {loadingMessage || 'Loading...'}
319
+ </p>
323
320
  );
324
321
  }
325
322
 
@@ -21,7 +21,7 @@ vi.mock('lucide-react', () => ({
21
21
 
22
22
  // Mock the PasswordChangeForm component
23
23
  vi.mock('../PasswordChange/PasswordChangeForm', () => ({
24
- PasswordChangeForm: ({ onSubmit }: { onSubmit: (values: { newPassword: string; confirmPassword: string }) => Promise<{ error?: { message?: string; code?: string } }> }) => {
24
+ PasswordChangeForm: ({ onSubmit, onSuccess }: { onSubmit: (values: { newPassword: string; confirmPassword: string }) => Promise<{ error?: { message?: string; code?: string } }>; onSuccess?: () => void }) => {
25
25
  const [isSubmitting, setIsSubmitting] = React.useState(false);
26
26
  const [error, setError] = React.useState<string | null>(null);
27
27
 
@@ -33,6 +33,9 @@ vi.mock('../PasswordChange/PasswordChangeForm', () => ({
33
33
  const result = await onSubmit({ newPassword: 'newpass123', confirmPassword: 'newpass123' });
34
34
  if (result.error) {
35
35
  setError(result.error.message);
36
+ } else {
37
+ // Call onSuccess on successful password change
38
+ onSuccess?.();
36
39
  }
37
40
  } catch (err) {
38
41
  const errorObj = err instanceof Error ? err : new Error('An unexpected error occurred');
@@ -92,9 +95,7 @@ vi.mock('../Select', () => ({
92
95
  // Mock the Dialog components
93
96
  vi.mock('../Dialog', () => ({
94
97
  Dialog: ({ children, open, onOpenChange }: { children: React.ReactNode; open: boolean; onOpenChange: (open: boolean) => void }) => (
95
- <div data-testid="dialog" data-open={open}>
96
- {children}
97
- </div>
98
+ open ? <div data-testid="dialog" role="dialog" data-open={open}>{children}</div> : null
98
99
  ),
99
100
  DialogContent: ({ children, className }: { children: React.ReactNode; className?: string }) => (
100
101
  <div data-testid="dialog-content" className={className}>
@@ -109,12 +110,17 @@ vi.mock('../Dialog', () => ({
109
110
  DialogTitle: ({ children }: { children: React.ReactNode }) => (
110
111
  <h2 data-testid="dialog-title">{children}</h2>
111
112
  ),
113
+ DialogDescription: ({ children }: { children: React.ReactNode }) => (
114
+ <p data-testid="dialog-description">{children}</p>
115
+ ),
112
116
  DialogTrigger: ({ children, asChild }: { children: React.ReactNode; asChild?: boolean }) => (
113
117
  <div data-testid="dialog-trigger">
114
118
  {asChild ? children : <button>{children}</button>}
115
119
  </div>
116
120
  ),
117
- DialogOverlay: () => <div data-testid="dialog-overlay" />,
121
+ DialogClose: ({ children, onClick }: { children?: React.ReactNode; onClick?: () => void }) => (
122
+ <button data-testid="dialog-close" onClick={onClick}>{children || 'Close'}</button>
123
+ ),
118
124
  }));
119
125
 
120
126
  // Mock the Avatar component
@@ -412,6 +418,32 @@ describe('UserMenu Component', () => {
412
418
  });
413
419
  });
414
420
 
421
+ it('closes dialog when password change succeeds', async () => {
422
+ const user = createMockUser();
423
+ const handleChangePassword = vi.fn().mockResolvedValue({});
424
+ const userEventInstance = userEvent.setup();
425
+
426
+ renderWithProviders(<UserMenu user={user} onChangePassword={handleChangePassword} />);
427
+
428
+ const trigger = screen.getByRole('button', { name: 'Test User' });
429
+ await userEventInstance.click(trigger);
430
+
431
+ const changePasswordButton = screen.getByTestId('select-item-change-password');
432
+ await userEventInstance.click(changePasswordButton);
433
+
434
+ // Verify dialog is open
435
+ expect(screen.getByTestId('dialog-content')).toBeInTheDocument();
436
+
437
+ const submitButton = screen.getByRole('button', { name: 'Change Password' });
438
+ await userEventInstance.click(submitButton);
439
+
440
+ // Wait for dialog to close
441
+ await waitFor(() => {
442
+ const dialog = screen.queryByTestId('dialog-content');
443
+ expect(dialog).not.toBeInTheDocument();
444
+ }, { timeout: 3000 });
445
+ });
446
+
415
447
  it('keeps dialog open when password change fails', async () => {
416
448
  const user = createMockUser();
417
449
  const handleChangePassword = vi.fn().mockResolvedValue({ error: { message: 'Password too weak' } });
@@ -434,7 +466,7 @@ describe('UserMenu Component', () => {
434
466
  expect(screen.getByTestId('error-message')).toHaveTextContent('Password too weak');
435
467
  });
436
468
 
437
- // Dialog should still be open
469
+ // Dialog should still be open (onSuccess should not be called when there's an error)
438
470
  expect(screen.getByTestId('dialog-content')).toBeInTheDocument();
439
471
  });
440
472
 
@@ -117,9 +117,7 @@ import {
117
117
  Dialog,
118
118
  DialogContent,
119
119
  DialogHeader,
120
- DialogTitle,
121
- DialogTrigger,
122
- DialogOverlay
120
+ DialogTitle
123
121
  } from '../Dialog';
124
122
  import { PasswordChangeForm, PasswordChangeFormError } from '../PasswordChange/PasswordChangeForm';
125
123
  import { Avatar } from '../Avatar';
@@ -144,8 +142,6 @@ export const UserMenu = React.memo<UserMenuProps>(function UserMenu({
144
142
  className,
145
143
  showAvatar = true,
146
144
  }) {
147
- const [isPasswordDialogOpen, setPasswordDialogOpen] = useState(false);
148
-
149
145
  const userInfo = useMemo(() => {
150
146
  if (!user) return null;
151
147
  return {
@@ -164,8 +160,18 @@ export const UserMenu = React.memo<UserMenuProps>(function UserMenu({
164
160
  return null; // Or a loading/login button
165
161
  }
166
162
 
163
+ // Password change dialog state and handlers (moved closer to Dialog usage)
164
+ const [isPasswordDialogOpen, setPasswordDialogOpen] = useState(false);
165
+
166
+ const handlePasswordChange = useCallback(async (newPassword: string, confirmPassword: string) => {
167
+ if (!onChangePassword) {
168
+ return { error: { message: 'Password change not configured' } };
169
+ }
170
+ return await onChangePassword(newPassword, confirmPassword);
171
+ }, [onChangePassword]);
172
+
167
173
  return (
168
- <Dialog open={isPasswordDialogOpen} onOpenChange={setPasswordDialogOpen}>
174
+ <>
169
175
  <Select className={className}>
170
176
  <SelectTrigger asChild>
171
177
  <Button variant="outline" className="flex items-center gap-2" aria-label={userInfo.displayName}>
@@ -185,42 +191,38 @@ export const UserMenu = React.memo<UserMenuProps>(function UserMenu({
185
191
  <SelectLabel className="font-normal">
186
192
  <li className="pt-2">
187
193
  {userInfo.displayName}<br/>
188
- <span className="text-muted-foreground">{userInfo.email}</span>
194
+ <small>{userInfo.email}</small>
189
195
  </li>
190
196
  </SelectLabel>
191
197
  <SelectSeparator />
192
- <DialogTrigger asChild>
193
- <SelectItem value="change-password">
194
- <KeyRound className="mr-2 size-4" />
195
- <span>Change Password</span>
196
- </SelectItem>
197
- </DialogTrigger>
198
+ <SelectItem
199
+ value="change-password"
200
+ onClick={() => setPasswordDialogOpen(true)}
201
+ >
202
+ <KeyRound className="inline-block mr-2 size-4" />
203
+ Change Password
204
+ </SelectItem>
198
205
  <SelectItem value="sign-out" onClick={handleSignOut}>
199
- <LogOut className="mr-2 size-4" />
200
- <span>Sign out</span>
206
+ <LogOut className="inline-block mr-2 size-4" />
207
+ Sign out
201
208
  </SelectItem>
202
209
  </SelectContent>
203
210
  </Select>
204
211
 
205
- <DialogOverlay />
206
- <DialogContent className={className}>
207
- <DialogHeader>
208
- <DialogTitle>Change Password</DialogTitle>
209
- </DialogHeader>
210
- <PasswordChangeForm
211
- onSubmit={async ({ newPassword, confirmPassword }) => {
212
- if (onChangePassword) {
213
- const { error } = await onChangePassword(newPassword, confirmPassword);
214
- if (!error) {
215
- setPasswordDialogOpen(false);
216
- }
217
- return { error };
218
- }
219
- return {};
220
- }}
221
- />
222
- </DialogContent>
223
- </Dialog>
212
+ <Dialog open={isPasswordDialogOpen} onOpenChange={setPasswordDialogOpen}>
213
+ <DialogContent>
214
+ <DialogHeader>
215
+ <DialogTitle>Change Password</DialogTitle>
216
+ </DialogHeader>
217
+ <PasswordChangeForm
218
+ onSubmit={async ({ newPassword, confirmPassword }) => {
219
+ return await handlePasswordChange(newPassword, confirmPassword);
220
+ }}
221
+ onSuccess={() => setPasswordDialogOpen(false)}
222
+ />
223
+ </DialogContent>
224
+ </Dialog>
225
+ </>
224
226
  );
225
227
  });
226
228
 
@@ -84,7 +84,6 @@ export {
84
84
  export {
85
85
  Dialog,
86
86
  DialogPortal,
87
- DialogOverlay,
88
87
  DialogTrigger,
89
88
  DialogClose,
90
89
  DialogContent,
@@ -98,11 +97,11 @@ export type {
98
97
  DialogProps,
99
98
  DialogTriggerProps,
100
99
  DialogContentProps,
101
- DialogOverlayProps,
100
+ DialogPortalProps,
101
+ DialogCloseProps,
102
102
  DialogHeaderProps,
103
103
  DialogFooterProps,
104
- DialogTitleProps,
105
- DialogDescriptionProps,
104
+ DialogBodyProps,
106
105
  DialogSize
107
106
  } from './Dialog/Dialog';
108
107
 
@@ -20,7 +20,7 @@
20
20
  * // Automatically applies event colors when event is selected via EventProvider
21
21
  * useEventTheme();
22
22
  *
23
- * return <div>Your app content</div>;
23
+ * return <main>Your app content</main>;
24
24
  * }
25
25
  * ```
26
26
  *
@@ -33,7 +33,7 @@
33
33
  * // Applies event colors directly from event prop
34
34
  * useEventTheme(event);
35
35
  *
36
- * return <div>Public page content</div>;
36
+ * return <main>Public page content</main>;
37
37
  * }
38
38
  * ```
39
39
  */
@@ -79,7 +79,7 @@ const log = createLogger('useEventTheme');
79
79
  * // Authenticated mode - uses EventProvider
80
80
  * function MyApp() {
81
81
  * useEventTheme(); // Watches selectedEvent from EventProvider
82
- * return <div>App content</div>;
82
+ * return <main>App content</main>;
83
83
  * }
84
84
  * ```
85
85
  *
@@ -88,7 +88,7 @@ const log = createLogger('useEventTheme');
88
88
  * // Public page mode - uses event prop
89
89
  * function PublicPageLayout({ event }) {
90
90
  * useEventTheme(event); // Uses event prop directly
91
- * return <div>Public content</div>;
91
+ * return <main>Public content</main>;
92
92
  * }
93
93
  * ```
94
94
  */
@@ -37,13 +37,17 @@ export interface EventContextType {
37
37
  * const { events, selectedEvent, setSelectedEvent } = useEvents();
38
38
  *
39
39
  * return (
40
- * <div>
41
- * {events.map(event => (
42
- * <button key={event.id} onClick={() => setSelectedEvent(event)}>
43
- * {event.event_name}
44
- * </button>
45
- * ))}
46
- * </div>
40
+ * <nav>
41
+ * <ul>
42
+ * {events.map(event => (
43
+ * <li key={event.id}>
44
+ * <button onClick={() => setSelectedEvent(event)}>
45
+ * {event.event_name}
46
+ * </button>
47
+ * </li>
48
+ * ))}
49
+ * </ul>
50
+ * </nav>
47
51
  * );
48
52
  * }
49
53
  * ```
@@ -86,7 +86,7 @@ function matchesKeyCombo(event: KeyboardEvent, combo: string): boolean {
86
86
  *
87
87
  * useKeyboardShortcuts(shortcuts);
88
88
  *
89
- * return <div>Content</div>;
89
+ * return <main>Content</main>;
90
90
  * }
91
91
  * ```
92
92
  */
@@ -18,11 +18,11 @@
18
18
  * } = useOrganisationPermissions();
19
19
  *
20
20
  * return (
21
- * <div>
21
+ * <section>
22
22
  * {isOrgAdmin && <AdminPanel />}
23
23
  * {canManageMembers && <MemberManagement />}
24
24
  * <p>Your role: {userRole}</p>
25
- * </div>
25
+ * </section>
26
26
  * );
27
27
  * }
28
28
  *
@@ -31,10 +31,10 @@
31
31
  * const permissions = useOrganisationPermissions('org-123');
32
32
  *
33
33
  * if (!permissions.hasOrganisationAccess) {
34
- * return <div>No access to this organisation</div>;
34
+ * return <main>No access to this organisation</main>;
35
35
  * }
36
36
  *
37
- * return <div>Role in org-123: {permissions.userRole}</div>;
37
+ * return <main>Role in org-123: {permissions.userRole}</main>;
38
38
  * }
39
39
  * ```
40
40
  *
@@ -26,14 +26,20 @@ import { Organisation, OrganisationMembership, OrganisationContextType } from '.
26
26
  * const { selectedOrganisation, organisations, switchOrganisation } = useOrganisations();
27
27
  *
28
28
  * return (
29
- * <div>
29
+ * <main>
30
30
  * <h1>Current Organisation: {selectedOrganisation.display_name}</h1>
31
- * {organisations.map(org => (
32
- * <button key={org.id} onClick={() => switchOrganisation(org.id)}>
33
- * {org.display_name}
34
- * </button>
35
- * ))}
36
- * </div>
31
+ * <nav>
32
+ * <ul>
33
+ * {organisations.map(org => (
34
+ * <li key={org.id}>
35
+ * <button onClick={() => switchOrganisation(org.id)}>
36
+ * {org.display_name}
37
+ * </button>
38
+ * </li>
39
+ * ))}
40
+ * </ul>
41
+ * </nav>
42
+ * </main>
37
43
  * );
38
44
  * }
39
45
  * ```
package/src/index.ts CHANGED
@@ -101,7 +101,6 @@ export type { ProgressProps } from './components/Progress';
101
101
  export {
102
102
  Dialog,
103
103
  DialogPortal,
104
- DialogOverlay,
105
104
  DialogTrigger,
106
105
  DialogClose,
107
106
  DialogContent,
@@ -111,6 +110,17 @@ export {
111
110
  DialogTitle,
112
111
  DialogDescription,
113
112
  } from './components/Dialog/Dialog';
113
+ export type {
114
+ DialogProps,
115
+ DialogTriggerProps,
116
+ DialogContentProps,
117
+ DialogPortalProps,
118
+ DialogCloseProps,
119
+ DialogHeaderProps,
120
+ DialogFooterProps,
121
+ DialogBodyProps,
122
+ DialogSize
123
+ } from './components/Dialog/Dialog';
114
124
 
115
125
  // Dropdown Menu exports
116
126
  // DropdownMenu components have been merged into Select components