Skip to content

Commit 38e8cd6

Browse files
committed
feat(sidebar): shortcut key bindings and hints
Signed-off-by: Adam Setch <[email protected]>
1 parent 56dfbdc commit 38e8cd6

File tree

1 file changed

+183
-0
lines changed

1 file changed

+183
-0
lines changed

src/renderer/components/Sidebar.test.tsx

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,4 +272,187 @@ describe('renderer/components/Sidebar.tsx', () => {
272272

273273
expect(quitAppSpy).toHaveBeenCalledTimes(1);
274274
});
275+
276+
describe('keyboard bindings', () => {
277+
it('should navigate home when pressing H key', async () => {
278+
renderWithAppContext(
279+
<MemoryRouter>
280+
<Sidebar />
281+
</MemoryRouter>,
282+
);
283+
284+
await userEvent.keyboard('h');
285+
286+
expect(navigateMock).toHaveBeenCalledTimes(1);
287+
expect(navigateMock).toHaveBeenCalledWith('/', { replace: true });
288+
});
289+
290+
it('should refresh notifications when pressing R key', async () => {
291+
renderWithAppContext(
292+
<MemoryRouter>
293+
<Sidebar />
294+
</MemoryRouter>,
295+
{
296+
fetchNotifications: fetchNotificationsMock,
297+
status: 'success',
298+
},
299+
);
300+
301+
await userEvent.keyboard('r');
302+
303+
expect(navigateMock).toHaveBeenCalledWith('/', { replace: true });
304+
expect(fetchNotificationsMock).toHaveBeenCalledTimes(1);
305+
});
306+
307+
it('should not refresh notifications when pressing R key if status is loading', async () => {
308+
renderWithAppContext(
309+
<MemoryRouter>
310+
<Sidebar />
311+
</MemoryRouter>,
312+
{
313+
fetchNotifications: fetchNotificationsMock,
314+
status: 'loading',
315+
},
316+
);
317+
318+
await userEvent.keyboard('r');
319+
320+
expect(fetchNotificationsMock).not.toHaveBeenCalled();
321+
});
322+
323+
it('should toggle settings when pressing S key while logged in', async () => {
324+
renderWithAppContext(
325+
<MemoryRouter>
326+
<Sidebar />
327+
</MemoryRouter>,
328+
{
329+
isLoggedIn: true,
330+
},
331+
);
332+
333+
await userEvent.keyboard('s');
334+
335+
expect(navigateMock).toHaveBeenCalledTimes(1);
336+
expect(navigateMock).toHaveBeenCalledWith('/settings');
337+
});
338+
339+
it('should not toggle settings when pressing S key while logged out', async () => {
340+
renderWithAppContext(
341+
<MemoryRouter>
342+
<Sidebar />
343+
</MemoryRouter>,
344+
{
345+
isLoggedIn: false,
346+
},
347+
);
348+
349+
await userEvent.keyboard('s');
350+
351+
expect(navigateMock).not.toHaveBeenCalled();
352+
});
353+
354+
it('should toggle filters when pressing F key while logged in', async () => {
355+
renderWithAppContext(
356+
<MemoryRouter>
357+
<Sidebar />
358+
</MemoryRouter>,
359+
{
360+
isLoggedIn: true,
361+
},
362+
);
363+
364+
await userEvent.keyboard('f');
365+
366+
expect(navigateMock).toHaveBeenCalledTimes(1);
367+
expect(navigateMock).toHaveBeenCalledWith('/filters');
368+
});
369+
370+
it('should not toggle filters when pressing F key while logged out', async () => {
371+
renderWithAppContext(
372+
<MemoryRouter>
373+
<Sidebar />
374+
</MemoryRouter>,
375+
{
376+
isLoggedIn: false,
377+
},
378+
);
379+
380+
await userEvent.keyboard('f');
381+
382+
expect(navigateMock).not.toHaveBeenCalled();
383+
});
384+
385+
it('should ignore keyboard shortcuts when typing in an input', async () => {
386+
renderWithAppContext(
387+
<MemoryRouter>
388+
<Sidebar />
389+
</MemoryRouter>,
390+
);
391+
392+
const input = document.createElement('input');
393+
document.body.appendChild(input);
394+
input.focus();
395+
396+
await userEvent.keyboard('h');
397+
398+
expect(navigateMock).not.toHaveBeenCalled();
399+
400+
document.body.removeChild(input);
401+
});
402+
403+
it('should ignore keyboard shortcuts when typing in a textarea', async () => {
404+
renderWithAppContext(
405+
<MemoryRouter>
406+
<Sidebar />
407+
</MemoryRouter>,
408+
);
409+
410+
const textarea = document.createElement('textarea');
411+
document.body.appendChild(textarea);
412+
textarea.focus();
413+
414+
await userEvent.keyboard('r');
415+
416+
expect(navigateMock).not.toHaveBeenCalled();
417+
expect(fetchNotificationsMock).not.toHaveBeenCalled();
418+
419+
document.body.removeChild(textarea);
420+
});
421+
422+
it('should ignore keyboard shortcuts when modifier keys are pressed', async () => {
423+
renderWithAppContext(
424+
<MemoryRouter>
425+
<Sidebar />
426+
</MemoryRouter>,
427+
{
428+
fetchNotifications: fetchNotificationsMock,
429+
status: 'success',
430+
},
431+
);
432+
433+
// Note: userEvent.keyboard with modifier syntax like {Shift>h</Shift>}
434+
// would require holding shift, but we test that metaKey/ctrlKey/altKey
435+
// prevent action through manual event dispatch
436+
const event = new KeyboardEvent('keydown', {
437+
key: 'h',
438+
metaKey: true,
439+
});
440+
document.dispatchEvent(event);
441+
442+
expect(navigateMock).not.toHaveBeenCalled();
443+
});
444+
445+
it('should work with uppercase key press', async () => {
446+
renderWithAppContext(
447+
<MemoryRouter>
448+
<Sidebar />
449+
</MemoryRouter>,
450+
);
451+
452+
// userEvent.keyboard converts to lowercase automatically
453+
await userEvent.keyboard('H');
454+
455+
expect(navigateMock).toHaveBeenCalledWith('/', { replace: true });
456+
});
457+
});
275458
});

0 commit comments

Comments
 (0)