diff --git a/frontend/src/components/HomeComponents/Tasks/Tasks.tsx b/frontend/src/components/HomeComponents/Tasks/Tasks.tsx index 43b53077..439d9723 100644 --- a/frontend/src/components/HomeComponents/Tasks/Tasks.tsx +++ b/frontend/src/components/HomeComponents/Tasks/Tasks.tsx @@ -153,6 +153,10 @@ export const Tasks = ( useEffect(() => { const handler = (e: KeyboardEvent) => { + if (!hotkeysEnabled) { + return; + } + const target = e.target as HTMLElement; if ( target instanceof HTMLInputElement || @@ -1022,83 +1026,115 @@ export const Tasks = ( } }; - useHotkeys(['f'], () => { - if (!showReports) { - document.getElementById('search')?.focus(); - } - }); - useHotkeys(['a'], () => { - if (!showReports) { - document.getElementById('add-new-task')?.click(); - } - }); - useHotkeys(['r'], () => { - if (!showReports) { - document.getElementById('sync-task')?.click(); - } - }); - useHotkeys(['p'], () => { - if (!showReports) { - document.getElementById('projects')?.click(); - } - }); - useHotkeys(['s'], () => { - if (!showReports) { - document.getElementById('status')?.click(); - } - }); - useHotkeys(['t'], () => { - if (!showReports) { - document.getElementById('tags')?.click(); - } - }); - useHotkeys(['c'], () => { - if (!showReports && !_isDialogOpen) { - const task = currentTasks[selectedIndex]; - if (!task) return; - const openBtn = document.getElementById(`task-row-${task.id}`); - openBtn?.click(); - setTimeout(() => { - const confirmBtn = document.getElementById( - `mark-task-complete-${task.id}` - ); - confirmBtn?.click(); - }, 200); - } else { - if (_isDialogOpen) { + useHotkeys( + ['f'], + () => { + if (!showReports) { + document.getElementById('search')?.focus(); + } + }, + hotkeysEnabled + ); + useHotkeys( + ['a'], + () => { + if (!showReports) { + document.getElementById('add-new-task')?.click(); + } + }, + hotkeysEnabled + ); + useHotkeys( + ['r'], + () => { + if (!showReports) { + document.getElementById('sync-task')?.click(); + } + }, + hotkeysEnabled + ); + useHotkeys( + ['p'], + () => { + if (!showReports) { + document.getElementById('projects')?.click(); + } + }, + hotkeysEnabled + ); + useHotkeys( + ['s'], + () => { + if (!showReports) { + document.getElementById('status')?.click(); + } + }, + hotkeysEnabled + ); + useHotkeys( + ['t'], + () => { + if (!showReports) { + document.getElementById('tags')?.click(); + } + }, + hotkeysEnabled + ); + useHotkeys( + ['c'], + () => { + if (!showReports && !_isDialogOpen) { const task = currentTasks[selectedIndex]; if (!task) return; - const confirmBtn = document.getElementById( - `mark-task-complete-${task.id}` - ); - confirmBtn?.click(); + const openBtn = document.getElementById(`task-row-${task.id}`); + openBtn?.click(); + setTimeout(() => { + const confirmBtn = document.getElementById( + `mark-task-complete-${task.id}` + ); + confirmBtn?.click(); + }, 200); + } else { + if (_isDialogOpen) { + const task = currentTasks[selectedIndex]; + if (!task) return; + const confirmBtn = document.getElementById( + `mark-task-complete-${task.id}` + ); + confirmBtn?.click(); + } } - } - }); + }, + hotkeysEnabled + ); - useHotkeys(['d'], () => { - if (!showReports && !_isDialogOpen) { - const task = currentTasks[selectedIndex]; - if (!task) return; - const openBtn = document.getElementById(`task-row-${task.id}`); - openBtn?.click(); - setTimeout(() => { - const confirmBtn = document.getElementById( - `mark-task-as-deleted-${task.id}` - ); - confirmBtn?.click(); - }, 200); - } else { - if (_isDialogOpen) { + useHotkeys( + ['d'], + () => { + if (!showReports && !_isDialogOpen) { const task = currentTasks[selectedIndex]; if (!task) return; - const confirmBtn = document.getElementById( - `mark-task-as-deleted-${task.id}` - ); - confirmBtn?.click(); + const openBtn = document.getElementById(`task-row-${task.id}`); + openBtn?.click(); + setTimeout(() => { + const confirmBtn = document.getElementById( + `mark-task-as-deleted-${task.id}` + ); + confirmBtn?.click(); + }, 200); + } else { + if (_isDialogOpen) { + const task = currentTasks[selectedIndex]; + if (!task) return; + const confirmBtn = document.getElementById( + `mark-task-as-deleted-${task.id}` + ); + confirmBtn?.click(); + } } - } - }); + }, + hotkeysEnabled + ); return (
{ expect(callback).toHaveBeenCalledTimes(1); }); + it('should not call callback when hotkeys are disabled', () => { + renderHook(() => useHotkeys(['s'], callback, false)); + + const event = new KeyboardEvent('keydown', { + key: 's', + bubbles: true, + }); + window.dispatchEvent(event); + + expect(callback).not.toHaveBeenCalled(); + }); + it('should handle multiple modifier keys', () => { renderHook(() => useHotkeys(['ctrl', 'shift', 'a'], callback)); diff --git a/frontend/src/components/utils/use-hotkeys.ts b/frontend/src/components/utils/use-hotkeys.ts index e10612b7..e26bde56 100644 --- a/frontend/src/components/utils/use-hotkeys.ts +++ b/frontend/src/components/utils/use-hotkeys.ts @@ -1,8 +1,16 @@ import { useEffect } from 'react'; -export function useHotkeys(keys: string[], callback: () => void) { +export function useHotkeys( + keys: string[], + callback: () => void, + enabled = true +) { useEffect(() => { const handler = (e: KeyboardEvent) => { + if (!enabled) { + return; + } + const target = e.target as HTMLElement; if ( target instanceof HTMLInputElement || @@ -29,5 +37,5 @@ export function useHotkeys(keys: string[], callback: () => void) { window.addEventListener('keydown', handler); return () => window.removeEventListener('keydown', handler); - }, [keys, callback]); + }, [keys, callback, enabled]); }