@jmruthers/pace-core 0.5.118 → 0.5.120
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/{DataTable-ZOAKQ3SU.js → DataTable-DGZDJUYM.js} +7 -7
- package/dist/{UnifiedAuthProvider-YFN7YGVN.js → UnifiedAuthProvider-UACKFATV.js} +3 -3
- package/dist/{chunk-7OTQLFVI.js → chunk-B4GZ2BXO.js} +3 -3
- package/dist/{chunk-KA3PSVNV.js → chunk-BHWIUEYH.js} +2 -1
- package/dist/chunk-BHWIUEYH.js.map +1 -0
- package/dist/{chunk-LFS45U62.js → chunk-CGURJ27Z.js} +2 -2
- package/dist/{chunk-PHDAXDHB.js → chunk-D6BOFXYR.js} +3 -3
- package/dist/{chunk-P3PUOL6B.js → chunk-FKFHZUGF.js} +4 -4
- package/dist/{chunk-2GJ5GL77.js → chunk-GKHF54DI.js} +2 -2
- package/dist/chunk-GKHF54DI.js.map +1 -0
- package/dist/{chunk-UKZWNQMB.js → chunk-HFBOFZ3Z.js} +5 -18
- package/dist/chunk-HFBOFZ3Z.js.map +1 -0
- package/dist/{chunk-O3FTRYEU.js → chunk-NZ32EONV.js} +2 -2
- package/dist/{chunk-2LM4QQGH.js → chunk-QPI2CCBA.js} +9 -9
- package/dist/chunk-QPI2CCBA.js.map +1 -0
- package/dist/{chunk-ECOVPXYS.js → chunk-RIEJGKD3.js} +4 -4
- package/dist/{chunk-HIWXXDXO.js → chunk-TDNI6ZWL.js} +5 -5
- package/dist/{chunk-VN3OOE35.js → chunk-ZYJ6O5CA.js} +2 -2
- package/dist/components.d.ts +1 -1
- package/dist/components.js +9 -9
- package/dist/hooks.d.ts +1 -1
- package/dist/hooks.js +8 -8
- package/dist/index.d.ts +1 -1
- package/dist/index.js +12 -12
- package/dist/providers.js +2 -2
- package/dist/rbac/index.js +7 -7
- package/dist/{useToast-Cs_g32bg.d.ts → useToast-C8gR5ir4.d.ts} +2 -2
- package/dist/utils.js +1 -1
- 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 +1 -1
- 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 +1 -1
- 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 +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
- package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
- package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
- package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
- package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
- 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 +2 -2
- package/package.json +1 -1
- package/src/components/DataTable/__tests__/DataTableCore.test.tsx +697 -0
- package/src/components/DataTable/components/DataTableCore.tsx +5 -0
- package/src/components/DataTable/components/EditableRow.tsx +9 -18
- package/src/components/DataTable/components/__tests__/EditableRow.test.tsx +616 -9
- package/src/components/DataTable/components/__tests__/UnifiedTableBody.test.tsx +1004 -0
- package/src/components/DataTable/utils/__tests__/a11yUtils.test.ts +612 -0
- package/src/components/DataTable/utils/__tests__/errorHandling.test.ts +266 -0
- package/src/components/DataTable/utils/__tests__/exportUtils.test.ts +455 -1
- package/src/components/Toast/Toast.tsx +1 -1
- package/src/hooks/__tests__/index.unit.test.ts +223 -0
- package/src/hooks/__tests__/useDataTablePerformance.unit.test.ts +748 -0
- package/src/hooks/__tests__/useEvents.unit.test.ts +251 -0
- package/src/hooks/__tests__/useFileDisplay.unit.test.ts +1060 -0
- package/src/hooks/__tests__/useFileUrl.unit.test.ts +958 -0
- package/src/hooks/__tests__/useFocusManagement.unit.test.ts +19 -9
- package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +540 -1
- package/src/hooks/__tests__/useIsMobile.unit.test.ts +205 -5
- package/src/hooks/__tests__/useKeyboardShortcuts.unit.test.ts +616 -1
- package/src/hooks/__tests__/useOrganisations.unit.test.ts +369 -0
- package/src/hooks/__tests__/usePerformanceMonitor.unit.test.ts +661 -0
- package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +2 -0
- package/src/hooks/__tests__/useSessionRestoration.unit.test.tsx +371 -0
- package/src/hooks/__tests__/useToast.unit.test.tsx +449 -30
- package/src/hooks/useSecureDataAccess.test.ts +1 -0
- package/src/hooks/useToast.ts +4 -4
- package/src/rbac/audit-enhanced.ts +339 -0
- package/src/services/EventService.ts +1 -0
- package/src/services/__tests__/AuthService.test.ts +473 -0
- package/src/services/__tests__/EventService.test.ts +390 -0
- package/src/services/__tests__/InactivityService.test.ts +217 -0
- package/src/services/__tests__/OrganisationService.test.ts +371 -0
- package/src/styles/core.css +1 -0
- package/dist/chunk-2GJ5GL77.js.map +0 -1
- package/dist/chunk-2LM4QQGH.js.map +0 -1
- package/dist/chunk-KA3PSVNV.js.map +0 -1
- package/dist/chunk-UKZWNQMB.js.map +0 -1
- package/src/components/DataTable/utils/debugTools.ts +0 -609
- package/src/rbac/testing/index.tsx +0 -340
- /package/dist/{DataTable-ZOAKQ3SU.js.map → DataTable-DGZDJUYM.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-YFN7YGVN.js.map → UnifiedAuthProvider-UACKFATV.js.map} +0 -0
- /package/dist/{chunk-7OTQLFVI.js.map → chunk-B4GZ2BXO.js.map} +0 -0
- /package/dist/{chunk-LFS45U62.js.map → chunk-CGURJ27Z.js.map} +0 -0
- /package/dist/{chunk-PHDAXDHB.js.map → chunk-D6BOFXYR.js.map} +0 -0
- /package/dist/{chunk-P3PUOL6B.js.map → chunk-FKFHZUGF.js.map} +0 -0
- /package/dist/{chunk-O3FTRYEU.js.map → chunk-NZ32EONV.js.map} +0 -0
- /package/dist/{chunk-ECOVPXYS.js.map → chunk-RIEJGKD3.js.map} +0 -0
- /package/dist/{chunk-HIWXXDXO.js.map → chunk-TDNI6ZWL.js.map} +0 -0
- /package/dist/{chunk-VN3OOE35.js.map → chunk-ZYJ6O5CA.js.map} +0 -0
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import { renderHook } from '@testing-library/react';
|
|
11
11
|
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
12
|
-
import { useKeyboardShortcuts } from '../useKeyboardShortcuts';
|
|
12
|
+
import { useKeyboardShortcuts, useAccessibilityShortcuts } from '../useKeyboardShortcuts';
|
|
13
13
|
import { fireEvent } from '@testing-library/react';
|
|
14
14
|
|
|
15
15
|
describe('useKeyboardShortcuts', () => {
|
|
@@ -291,5 +291,620 @@ describe('useKeyboardShortcuts', () => {
|
|
|
291
291
|
expect(handler).toHaveBeenCalledTimes(1);
|
|
292
292
|
});
|
|
293
293
|
});
|
|
294
|
+
|
|
295
|
+
describe('Complex Modifier Combinations', () => {
|
|
296
|
+
it('handles Ctrl+Alt+key combination', () => {
|
|
297
|
+
const handler = vi.fn();
|
|
298
|
+
const shortcuts = [
|
|
299
|
+
{
|
|
300
|
+
key: 'ctrl+alt+s',
|
|
301
|
+
handler
|
|
302
|
+
}
|
|
303
|
+
];
|
|
304
|
+
|
|
305
|
+
renderHook(() => useKeyboardShortcuts(shortcuts));
|
|
306
|
+
|
|
307
|
+
const event = new KeyboardEvent('keydown', {
|
|
308
|
+
key: 's',
|
|
309
|
+
ctrlKey: true,
|
|
310
|
+
altKey: true,
|
|
311
|
+
bubbles: true
|
|
312
|
+
});
|
|
313
|
+
document.dispatchEvent(event);
|
|
314
|
+
|
|
315
|
+
expect(handler).toHaveBeenCalledTimes(1);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
it('handles Ctrl+Shift+key combination', () => {
|
|
319
|
+
const handler = vi.fn();
|
|
320
|
+
const shortcuts = [
|
|
321
|
+
{
|
|
322
|
+
key: 'ctrl+shift+s',
|
|
323
|
+
handler
|
|
324
|
+
}
|
|
325
|
+
];
|
|
326
|
+
|
|
327
|
+
renderHook(() => useKeyboardShortcuts(shortcuts));
|
|
328
|
+
|
|
329
|
+
const event = new KeyboardEvent('keydown', {
|
|
330
|
+
key: 's',
|
|
331
|
+
ctrlKey: true,
|
|
332
|
+
shiftKey: true,
|
|
333
|
+
bubbles: true
|
|
334
|
+
});
|
|
335
|
+
document.dispatchEvent(event);
|
|
336
|
+
|
|
337
|
+
expect(handler).toHaveBeenCalledTimes(1);
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
it('handles Meta+key combination (Mac)', () => {
|
|
341
|
+
const handler = vi.fn();
|
|
342
|
+
const shortcuts = [
|
|
343
|
+
{
|
|
344
|
+
key: 'meta+s',
|
|
345
|
+
handler
|
|
346
|
+
}
|
|
347
|
+
];
|
|
348
|
+
|
|
349
|
+
renderHook(() => useKeyboardShortcuts(shortcuts));
|
|
350
|
+
|
|
351
|
+
const event = new KeyboardEvent('keydown', {
|
|
352
|
+
key: 's',
|
|
353
|
+
metaKey: true,
|
|
354
|
+
bubbles: true
|
|
355
|
+
});
|
|
356
|
+
document.dispatchEvent(event);
|
|
357
|
+
|
|
358
|
+
expect(handler).toHaveBeenCalledTimes(1);
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
it('handles Alt+key combination', () => {
|
|
362
|
+
const handler = vi.fn();
|
|
363
|
+
const shortcuts = [
|
|
364
|
+
{
|
|
365
|
+
key: 'alt+f',
|
|
366
|
+
handler
|
|
367
|
+
}
|
|
368
|
+
];
|
|
369
|
+
|
|
370
|
+
renderHook(() => useKeyboardShortcuts(shortcuts));
|
|
371
|
+
|
|
372
|
+
const event = new KeyboardEvent('keydown', {
|
|
373
|
+
key: 'f',
|
|
374
|
+
altKey: true,
|
|
375
|
+
bubbles: true
|
|
376
|
+
});
|
|
377
|
+
document.dispatchEvent(event);
|
|
378
|
+
|
|
379
|
+
expect(handler).toHaveBeenCalledTimes(1);
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
it('handles Shift+key combination', () => {
|
|
383
|
+
const handler = vi.fn();
|
|
384
|
+
const shortcuts = [
|
|
385
|
+
{
|
|
386
|
+
key: 'shift+arrowup',
|
|
387
|
+
handler
|
|
388
|
+
}
|
|
389
|
+
];
|
|
390
|
+
|
|
391
|
+
renderHook(() => useKeyboardShortcuts(shortcuts));
|
|
392
|
+
|
|
393
|
+
const event = new KeyboardEvent('keydown', {
|
|
394
|
+
key: 'ArrowUp',
|
|
395
|
+
shiftKey: true,
|
|
396
|
+
bubbles: true
|
|
397
|
+
});
|
|
398
|
+
document.dispatchEvent(event);
|
|
399
|
+
|
|
400
|
+
expect(handler).toHaveBeenCalledTimes(1);
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
it('does not trigger when modifiers do not match', () => {
|
|
404
|
+
const handler = vi.fn();
|
|
405
|
+
const shortcuts = [
|
|
406
|
+
{
|
|
407
|
+
key: 'ctrl+alt+s',
|
|
408
|
+
handler
|
|
409
|
+
}
|
|
410
|
+
];
|
|
411
|
+
|
|
412
|
+
renderHook(() => useKeyboardShortcuts(shortcuts));
|
|
413
|
+
|
|
414
|
+
const event = new KeyboardEvent('keydown', {
|
|
415
|
+
key: 's',
|
|
416
|
+
ctrlKey: true,
|
|
417
|
+
altKey: false, // Missing Alt
|
|
418
|
+
bubbles: true
|
|
419
|
+
});
|
|
420
|
+
document.dispatchEvent(event);
|
|
421
|
+
|
|
422
|
+
expect(handler).not.toHaveBeenCalled();
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
it('does not trigger when extra modifiers are pressed', () => {
|
|
426
|
+
const handler = vi.fn();
|
|
427
|
+
const shortcuts = [
|
|
428
|
+
{
|
|
429
|
+
key: 'ctrl+s',
|
|
430
|
+
handler
|
|
431
|
+
}
|
|
432
|
+
];
|
|
433
|
+
|
|
434
|
+
renderHook(() => useKeyboardShortcuts(shortcuts));
|
|
435
|
+
|
|
436
|
+
const event = new KeyboardEvent('keydown', {
|
|
437
|
+
key: 's',
|
|
438
|
+
ctrlKey: true,
|
|
439
|
+
altKey: true, // Extra modifier
|
|
440
|
+
bubbles: true
|
|
441
|
+
});
|
|
442
|
+
document.dispatchEvent(event);
|
|
443
|
+
|
|
444
|
+
expect(handler).not.toHaveBeenCalled();
|
|
445
|
+
});
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
describe('Key Parsing Edge Cases', () => {
|
|
449
|
+
it('handles cmd alias for meta key', () => {
|
|
450
|
+
const handler = vi.fn();
|
|
451
|
+
const shortcuts = [
|
|
452
|
+
{
|
|
453
|
+
key: 'cmd+s',
|
|
454
|
+
handler
|
|
455
|
+
}
|
|
456
|
+
];
|
|
457
|
+
|
|
458
|
+
renderHook(() => useKeyboardShortcuts(shortcuts));
|
|
459
|
+
|
|
460
|
+
const event = new KeyboardEvent('keydown', {
|
|
461
|
+
key: 's',
|
|
462
|
+
metaKey: true,
|
|
463
|
+
bubbles: true
|
|
464
|
+
});
|
|
465
|
+
document.dispatchEvent(event);
|
|
466
|
+
|
|
467
|
+
expect(handler).toHaveBeenCalledTimes(1);
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
it('handles control alias for ctrl key', () => {
|
|
471
|
+
const handler = vi.fn();
|
|
472
|
+
const shortcuts = [
|
|
473
|
+
{
|
|
474
|
+
key: 'control+s',
|
|
475
|
+
handler
|
|
476
|
+
}
|
|
477
|
+
];
|
|
478
|
+
|
|
479
|
+
renderHook(() => useKeyboardShortcuts(shortcuts));
|
|
480
|
+
|
|
481
|
+
const event = new KeyboardEvent('keydown', {
|
|
482
|
+
key: 's',
|
|
483
|
+
ctrlKey: true,
|
|
484
|
+
bubbles: true
|
|
485
|
+
});
|
|
486
|
+
document.dispatchEvent(event);
|
|
487
|
+
|
|
488
|
+
expect(handler).toHaveBeenCalledTimes(1);
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
it('handles case-insensitive key matching', () => {
|
|
492
|
+
const handler = vi.fn();
|
|
493
|
+
const shortcuts = [
|
|
494
|
+
{
|
|
495
|
+
key: 'CTRL+S',
|
|
496
|
+
handler
|
|
497
|
+
}
|
|
498
|
+
];
|
|
499
|
+
|
|
500
|
+
renderHook(() => useKeyboardShortcuts(shortcuts));
|
|
501
|
+
|
|
502
|
+
const event = new KeyboardEvent('keydown', {
|
|
503
|
+
key: 's',
|
|
504
|
+
ctrlKey: true,
|
|
505
|
+
bubbles: true
|
|
506
|
+
});
|
|
507
|
+
document.dispatchEvent(event);
|
|
508
|
+
|
|
509
|
+
expect(handler).toHaveBeenCalledTimes(1);
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
it('handles keys with special characters', () => {
|
|
513
|
+
const handler = vi.fn();
|
|
514
|
+
const shortcuts = [
|
|
515
|
+
{
|
|
516
|
+
key: '?',
|
|
517
|
+
handler
|
|
518
|
+
}
|
|
519
|
+
];
|
|
520
|
+
|
|
521
|
+
renderHook(() => useKeyboardShortcuts(shortcuts));
|
|
522
|
+
|
|
523
|
+
const event = new KeyboardEvent('keydown', {
|
|
524
|
+
key: '?',
|
|
525
|
+
bubbles: true
|
|
526
|
+
});
|
|
527
|
+
document.dispatchEvent(event);
|
|
528
|
+
|
|
529
|
+
expect(handler).toHaveBeenCalledTimes(1);
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
it('handles space key', () => {
|
|
533
|
+
const handler = vi.fn();
|
|
534
|
+
const shortcuts = [
|
|
535
|
+
{
|
|
536
|
+
key: ' ',
|
|
537
|
+
handler
|
|
538
|
+
}
|
|
539
|
+
];
|
|
540
|
+
|
|
541
|
+
renderHook(() => useKeyboardShortcuts(shortcuts));
|
|
542
|
+
|
|
543
|
+
const event = new KeyboardEvent('keydown', {
|
|
544
|
+
key: ' ',
|
|
545
|
+
bubbles: true
|
|
546
|
+
});
|
|
547
|
+
document.dispatchEvent(event);
|
|
548
|
+
|
|
549
|
+
expect(handler).toHaveBeenCalledTimes(1);
|
|
550
|
+
});
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
describe('Event Handling', () => {
|
|
554
|
+
it('stopPropagation option works correctly', () => {
|
|
555
|
+
const handler = vi.fn((e) => {
|
|
556
|
+
// Handler should not affect propagation test
|
|
557
|
+
});
|
|
558
|
+
const parentHandler = vi.fn();
|
|
559
|
+
|
|
560
|
+
const shortcuts = [
|
|
561
|
+
{
|
|
562
|
+
key: 'Escape',
|
|
563
|
+
handler,
|
|
564
|
+
stopPropagation: true
|
|
565
|
+
}
|
|
566
|
+
];
|
|
567
|
+
|
|
568
|
+
document.addEventListener('keydown', parentHandler);
|
|
569
|
+
|
|
570
|
+
renderHook(() => useKeyboardShortcuts(shortcuts));
|
|
571
|
+
|
|
572
|
+
const event = new KeyboardEvent('keydown', { key: 'Escape', bubbles: true });
|
|
573
|
+
document.dispatchEvent(event);
|
|
574
|
+
|
|
575
|
+
expect(handler).toHaveBeenCalledTimes(1);
|
|
576
|
+
// Note: stopPropagation is called on the event, but we can't easily test
|
|
577
|
+
// event propagation in this context without more complex setup
|
|
578
|
+
|
|
579
|
+
document.removeEventListener('keydown', parentHandler);
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
it('events propagate when stopPropagation is false', () => {
|
|
583
|
+
const handler = vi.fn();
|
|
584
|
+
const parentHandler = vi.fn();
|
|
585
|
+
|
|
586
|
+
const shortcuts = [
|
|
587
|
+
{
|
|
588
|
+
key: 'Escape',
|
|
589
|
+
handler,
|
|
590
|
+
stopPropagation: false
|
|
591
|
+
}
|
|
592
|
+
];
|
|
593
|
+
|
|
594
|
+
document.addEventListener('keydown', parentHandler);
|
|
595
|
+
|
|
596
|
+
renderHook(() => useKeyboardShortcuts(shortcuts));
|
|
597
|
+
|
|
598
|
+
const event = new KeyboardEvent('keydown', { key: 'Escape', bubbles: true });
|
|
599
|
+
document.dispatchEvent(event);
|
|
600
|
+
|
|
601
|
+
expect(handler).toHaveBeenCalledTimes(1);
|
|
602
|
+
// Parent handler should also be called since we're not stopping propagation
|
|
603
|
+
expect(parentHandler).toHaveBeenCalled();
|
|
604
|
+
|
|
605
|
+
document.removeEventListener('keydown', parentHandler);
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
it('multiple shortcuts with same key - first match wins', () => {
|
|
609
|
+
const handler1 = vi.fn();
|
|
610
|
+
const handler2 = vi.fn();
|
|
611
|
+
const handler3 = vi.fn();
|
|
612
|
+
|
|
613
|
+
const shortcuts = [
|
|
614
|
+
{
|
|
615
|
+
key: 'Escape',
|
|
616
|
+
handler: handler1
|
|
617
|
+
},
|
|
618
|
+
{
|
|
619
|
+
key: 'Escape',
|
|
620
|
+
handler: handler2
|
|
621
|
+
},
|
|
622
|
+
{
|
|
623
|
+
key: 'Escape',
|
|
624
|
+
handler: handler3
|
|
625
|
+
}
|
|
626
|
+
];
|
|
627
|
+
|
|
628
|
+
renderHook(() => useKeyboardShortcuts(shortcuts));
|
|
629
|
+
|
|
630
|
+
const event = new KeyboardEvent('keydown', { key: 'Escape' });
|
|
631
|
+
document.dispatchEvent(event);
|
|
632
|
+
|
|
633
|
+
expect(handler1).toHaveBeenCalledTimes(1);
|
|
634
|
+
expect(handler2).not.toHaveBeenCalled();
|
|
635
|
+
expect(handler3).not.toHaveBeenCalled();
|
|
636
|
+
});
|
|
637
|
+
|
|
638
|
+
it('shortcuts with different keys do not interfere', () => {
|
|
639
|
+
const escapeHandler = vi.fn();
|
|
640
|
+
const enterHandler = vi.fn();
|
|
641
|
+
const spaceHandler = vi.fn();
|
|
642
|
+
|
|
643
|
+
const shortcuts = [
|
|
644
|
+
{
|
|
645
|
+
key: 'Escape',
|
|
646
|
+
handler: escapeHandler
|
|
647
|
+
},
|
|
648
|
+
{
|
|
649
|
+
key: 'Enter',
|
|
650
|
+
handler: enterHandler
|
|
651
|
+
},
|
|
652
|
+
{
|
|
653
|
+
key: ' ',
|
|
654
|
+
handler: spaceHandler
|
|
655
|
+
}
|
|
656
|
+
];
|
|
657
|
+
|
|
658
|
+
renderHook(() => useKeyboardShortcuts(shortcuts));
|
|
659
|
+
|
|
660
|
+
// Test Escape
|
|
661
|
+
const escapeEvent = new KeyboardEvent('keydown', { key: 'Escape' });
|
|
662
|
+
document.dispatchEvent(escapeEvent);
|
|
663
|
+
expect(escapeHandler).toHaveBeenCalledTimes(1);
|
|
664
|
+
expect(enterHandler).not.toHaveBeenCalled();
|
|
665
|
+
expect(spaceHandler).not.toHaveBeenCalled();
|
|
666
|
+
|
|
667
|
+
// Test Enter
|
|
668
|
+
const enterEvent = new KeyboardEvent('keydown', { key: 'Enter' });
|
|
669
|
+
document.dispatchEvent(enterEvent);
|
|
670
|
+
expect(enterHandler).toHaveBeenCalledTimes(1);
|
|
671
|
+
expect(escapeHandler).toHaveBeenCalledTimes(1); // Still 1
|
|
672
|
+
|
|
673
|
+
// Test Space
|
|
674
|
+
const spaceEvent = new KeyboardEvent('keydown', { key: ' ' });
|
|
675
|
+
document.dispatchEvent(spaceEvent);
|
|
676
|
+
expect(spaceHandler).toHaveBeenCalledTimes(1);
|
|
677
|
+
});
|
|
678
|
+
});
|
|
679
|
+
|
|
680
|
+
describe('useAccessibilityShortcuts Hook', () => {
|
|
681
|
+
it('creates shortcuts for all provided handlers', () => {
|
|
682
|
+
const onEscape = vi.fn();
|
|
683
|
+
const onEnter = vi.fn();
|
|
684
|
+
const onSpace = vi.fn();
|
|
685
|
+
const onArrowUp = vi.fn();
|
|
686
|
+
const onArrowDown = vi.fn();
|
|
687
|
+
const onArrowLeft = vi.fn();
|
|
688
|
+
const onArrowRight = vi.fn();
|
|
689
|
+
const onHome = vi.fn();
|
|
690
|
+
const onEnd = vi.fn();
|
|
691
|
+
const onTab = vi.fn();
|
|
692
|
+
const onShiftTab = vi.fn();
|
|
693
|
+
|
|
694
|
+
renderHook(() =>
|
|
695
|
+
useAccessibilityShortcuts({
|
|
696
|
+
onEscape,
|
|
697
|
+
onEnter,
|
|
698
|
+
onSpace,
|
|
699
|
+
onArrowUp,
|
|
700
|
+
onArrowDown,
|
|
701
|
+
onArrowLeft,
|
|
702
|
+
onArrowRight,
|
|
703
|
+
onHome,
|
|
704
|
+
onEnd,
|
|
705
|
+
onTab,
|
|
706
|
+
onShiftTab
|
|
707
|
+
})
|
|
708
|
+
);
|
|
709
|
+
|
|
710
|
+
// Test each handler
|
|
711
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
|
|
712
|
+
expect(onEscape).toHaveBeenCalledTimes(1);
|
|
713
|
+
|
|
714
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }));
|
|
715
|
+
expect(onEnter).toHaveBeenCalledTimes(1);
|
|
716
|
+
|
|
717
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: ' ' }));
|
|
718
|
+
expect(onSpace).toHaveBeenCalledTimes(1);
|
|
719
|
+
|
|
720
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' }));
|
|
721
|
+
expect(onArrowUp).toHaveBeenCalledTimes(1);
|
|
722
|
+
|
|
723
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }));
|
|
724
|
+
expect(onArrowDown).toHaveBeenCalledTimes(1);
|
|
725
|
+
|
|
726
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowLeft' }));
|
|
727
|
+
expect(onArrowLeft).toHaveBeenCalledTimes(1);
|
|
728
|
+
|
|
729
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight' }));
|
|
730
|
+
expect(onArrowRight).toHaveBeenCalledTimes(1);
|
|
731
|
+
|
|
732
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Home' }));
|
|
733
|
+
expect(onHome).toHaveBeenCalledTimes(1);
|
|
734
|
+
|
|
735
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'End' }));
|
|
736
|
+
expect(onEnd).toHaveBeenCalledTimes(1);
|
|
737
|
+
|
|
738
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Tab' }));
|
|
739
|
+
expect(onTab).toHaveBeenCalledTimes(1);
|
|
740
|
+
|
|
741
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Tab', shiftKey: true }));
|
|
742
|
+
expect(onShiftTab).toHaveBeenCalledTimes(1);
|
|
743
|
+
});
|
|
744
|
+
|
|
745
|
+
it('handles Escape key handler', () => {
|
|
746
|
+
const onEscape = vi.fn();
|
|
747
|
+
|
|
748
|
+
renderHook(() => useAccessibilityShortcuts({ onEscape }));
|
|
749
|
+
|
|
750
|
+
const event = new KeyboardEvent('keydown', { key: 'Escape' });
|
|
751
|
+
document.dispatchEvent(event);
|
|
752
|
+
|
|
753
|
+
expect(onEscape).toHaveBeenCalledTimes(1);
|
|
754
|
+
});
|
|
755
|
+
|
|
756
|
+
it('handles Enter key handler', () => {
|
|
757
|
+
const onEnter = vi.fn();
|
|
758
|
+
|
|
759
|
+
renderHook(() => useAccessibilityShortcuts({ onEnter }));
|
|
760
|
+
|
|
761
|
+
const event = new KeyboardEvent('keydown', { key: 'Enter' });
|
|
762
|
+
document.dispatchEvent(event);
|
|
763
|
+
|
|
764
|
+
expect(onEnter).toHaveBeenCalledTimes(1);
|
|
765
|
+
});
|
|
766
|
+
|
|
767
|
+
it('handles Space key handler with preventDefault', () => {
|
|
768
|
+
const onSpace = vi.fn();
|
|
769
|
+
const preventDefaultSpy = vi.fn();
|
|
770
|
+
|
|
771
|
+
renderHook(() => useAccessibilityShortcuts({ onSpace }));
|
|
772
|
+
|
|
773
|
+
const event = new KeyboardEvent('keydown', { key: ' ', cancelable: true });
|
|
774
|
+
event.preventDefault = preventDefaultSpy;
|
|
775
|
+
document.dispatchEvent(event);
|
|
776
|
+
|
|
777
|
+
expect(onSpace).toHaveBeenCalledTimes(1);
|
|
778
|
+
expect(preventDefaultSpy).toHaveBeenCalled();
|
|
779
|
+
});
|
|
780
|
+
|
|
781
|
+
it('handles Arrow key handlers', () => {
|
|
782
|
+
const onArrowUp = vi.fn();
|
|
783
|
+
const onArrowDown = vi.fn();
|
|
784
|
+
const onArrowLeft = vi.fn();
|
|
785
|
+
const onArrowRight = vi.fn();
|
|
786
|
+
|
|
787
|
+
renderHook(() =>
|
|
788
|
+
useAccessibilityShortcuts({
|
|
789
|
+
onArrowUp,
|
|
790
|
+
onArrowDown,
|
|
791
|
+
onArrowLeft,
|
|
792
|
+
onArrowRight
|
|
793
|
+
})
|
|
794
|
+
);
|
|
795
|
+
|
|
796
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' }));
|
|
797
|
+
expect(onArrowUp).toHaveBeenCalledTimes(1);
|
|
798
|
+
|
|
799
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }));
|
|
800
|
+
expect(onArrowDown).toHaveBeenCalledTimes(1);
|
|
801
|
+
|
|
802
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowLeft' }));
|
|
803
|
+
expect(onArrowLeft).toHaveBeenCalledTimes(1);
|
|
804
|
+
|
|
805
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight' }));
|
|
806
|
+
expect(onArrowRight).toHaveBeenCalledTimes(1);
|
|
807
|
+
});
|
|
808
|
+
|
|
809
|
+
it('handles Home and End keys', () => {
|
|
810
|
+
const onHome = vi.fn();
|
|
811
|
+
const onEnd = vi.fn();
|
|
812
|
+
|
|
813
|
+
renderHook(() => useAccessibilityShortcuts({ onHome, onEnd }));
|
|
814
|
+
|
|
815
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Home' }));
|
|
816
|
+
expect(onHome).toHaveBeenCalledTimes(1);
|
|
817
|
+
|
|
818
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'End' }));
|
|
819
|
+
expect(onEnd).toHaveBeenCalledTimes(1);
|
|
820
|
+
});
|
|
821
|
+
|
|
822
|
+
it('handles Tab and Shift+Tab keys', () => {
|
|
823
|
+
const onTab = vi.fn();
|
|
824
|
+
const onShiftTab = vi.fn();
|
|
825
|
+
|
|
826
|
+
renderHook(() => useAccessibilityShortcuts({ onTab, onShiftTab }));
|
|
827
|
+
|
|
828
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Tab' }));
|
|
829
|
+
expect(onTab).toHaveBeenCalledTimes(1);
|
|
830
|
+
|
|
831
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Tab', shiftKey: true }));
|
|
832
|
+
expect(onShiftTab).toHaveBeenCalledTimes(1);
|
|
833
|
+
});
|
|
834
|
+
|
|
835
|
+
it('only creates shortcuts for provided handlers', () => {
|
|
836
|
+
const onEscape = vi.fn();
|
|
837
|
+
const onEnter = vi.fn();
|
|
838
|
+
|
|
839
|
+
renderHook(() => useAccessibilityShortcuts({ onEscape, onEnter }));
|
|
840
|
+
|
|
841
|
+
// Should only respond to Escape and Enter
|
|
842
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
|
|
843
|
+
expect(onEscape).toHaveBeenCalledTimes(1);
|
|
844
|
+
|
|
845
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }));
|
|
846
|
+
expect(onEnter).toHaveBeenCalledTimes(1);
|
|
847
|
+
|
|
848
|
+
// Should not respond to other keys
|
|
849
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Space' }));
|
|
850
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' }));
|
|
851
|
+
|
|
852
|
+
// Only Escape and Enter should have been called
|
|
853
|
+
expect(onEscape).toHaveBeenCalledTimes(1);
|
|
854
|
+
expect(onEnter).toHaveBeenCalledTimes(1);
|
|
855
|
+
});
|
|
856
|
+
});
|
|
857
|
+
|
|
858
|
+
describe('Integration', () => {
|
|
859
|
+
it('useAccessibilityShortcuts integrates with useKeyboardShortcuts', () => {
|
|
860
|
+
const onEscape = vi.fn();
|
|
861
|
+
const customHandler = vi.fn();
|
|
862
|
+
|
|
863
|
+
// Use accessibility shortcuts
|
|
864
|
+
renderHook(() => useAccessibilityShortcuts({ onEscape }));
|
|
865
|
+
|
|
866
|
+
// Also use regular shortcuts
|
|
867
|
+
renderHook(() =>
|
|
868
|
+
useKeyboardShortcuts([
|
|
869
|
+
{
|
|
870
|
+
key: 'Enter',
|
|
871
|
+
handler: customHandler
|
|
872
|
+
}
|
|
873
|
+
])
|
|
874
|
+
);
|
|
875
|
+
|
|
876
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
|
|
877
|
+
expect(onEscape).toHaveBeenCalledTimes(1);
|
|
878
|
+
|
|
879
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }));
|
|
880
|
+
expect(customHandler).toHaveBeenCalledTimes(1);
|
|
881
|
+
});
|
|
882
|
+
|
|
883
|
+
it('multiple accessibility shortcuts work together', () => {
|
|
884
|
+
const handlers1 = {
|
|
885
|
+
onEscape: vi.fn(),
|
|
886
|
+
onEnter: vi.fn()
|
|
887
|
+
};
|
|
888
|
+
const handlers2 = {
|
|
889
|
+
onSpace: vi.fn(),
|
|
890
|
+
onArrowUp: vi.fn()
|
|
891
|
+
};
|
|
892
|
+
|
|
893
|
+
renderHook(() => useAccessibilityShortcuts(handlers1));
|
|
894
|
+
renderHook(() => useAccessibilityShortcuts(handlers2));
|
|
895
|
+
|
|
896
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
|
|
897
|
+
expect(handlers1.onEscape).toHaveBeenCalledTimes(1);
|
|
898
|
+
|
|
899
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }));
|
|
900
|
+
expect(handlers1.onEnter).toHaveBeenCalledTimes(1);
|
|
901
|
+
|
|
902
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: ' ' }));
|
|
903
|
+
expect(handlers2.onSpace).toHaveBeenCalledTimes(1);
|
|
904
|
+
|
|
905
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' }));
|
|
906
|
+
expect(handlers2.onArrowUp).toHaveBeenCalledTimes(1);
|
|
907
|
+
});
|
|
908
|
+
});
|
|
294
909
|
});
|
|
295
910
|
|