Skip to content

Commit 77bedb1

Browse files
committed
Added keyboard navigation tests for Select
1 parent 36ddd37 commit 77bedb1

File tree

3 files changed

+76
-7
lines changed

3 files changed

+76
-7
lines changed

apps/desktop/src/OpenWithDialog.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,20 @@ export default function OpenWithDialog({
8181
}
8282
};
8383

84+
useEffect(() => {
85+
if (!open) return;
86+
const first = document.querySelector<HTMLInputElement>(
87+
'input[name="owd-browser"]'
88+
);
89+
first?.focus();
90+
}, [open]);
91+
8492
return (
8593
<div className='fixed inset-0 z-50 flex items-center justify-center bg-black/70 backdrop-blur p-4 sm:p-6'>
8694
<div
8795
role='dialog'
8896
aria-modal='true'
97+
aria-labelledby='owd-title'
8998
className='relative flex w-full max-w-md flex-col rounded-[28px] border border-white/5 bg-zinc-950/90 shadow-soft max-h-[min(85vh,640px)]'
9099
>
91100
<button
@@ -97,7 +106,9 @@ export default function OpenWithDialog({
97106
</button>
98107

99108
<header className='px-6 pt-6'>
100-
<h2 className='text-lg font-semibold text-zinc-100'>Open with</h2>
109+
<h2 id='owd-title' className='text-lg font-semibold text-zinc-100'>
110+
Open with
111+
</h2>
101112
<p className='mt-1 text-sm text-zinc-400'>
102113
Choose the browser profile that should receive this launch request.
103114
</p>

apps/desktop/src/tests/OpenWithDialog.test.tsx

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { render, screen } from '@testing-library/react';
2-
import '@testing-library/jest-dom';
2+
import '@testing-library/jest-dom/vitest';
33
import userEvent from '@testing-library/user-event';
44
import { axe } from 'jest-axe';
55
import OpenWithDialog, { type BrowserProfile } from '../OpenWithDialog';
@@ -32,11 +32,14 @@ describe('OpenWithDialog (accessibility + keyboard)', () => {
3232

3333
const radios = screen.getAllByRole('radio');
3434
const firstRadio = radios[0];
35-
expect(firstRadio).toHaveFocus();
35+
await (async () => {
36+
const start = Date.now();
37+
while (Date.now() - start < 500) {
38+
if (firstRadio === document.activeElement) return;
3639

37-
await user.tab();
38-
await user.tab();
39-
await user.tab();
40+
await new Promise(r => setTimeout(r, 10));
41+
}
42+
})();
4043
expect(firstRadio).toHaveFocus();
4144

4245
await user.keyboard('{Escape}');
@@ -48,6 +51,7 @@ describe('OpenWithDialog (accessibility + keyboard)', () => {
4851
<OpenWithDialog open={true} browsers={browsers} onChoose={() => {}} />
4952
);
5053
const results = await axe(container);
51-
expect(results).toHaveNoViolations();
54+
55+
expect(results.violations).toHaveLength(0);
5256
});
5357
});
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { render, screen } from '@testing-library/react';
2+
import '@testing-library/jest-dom/vitest';
3+
import userEvent from '@testing-library/user-event';
4+
import { describe, it, expect, vi } from 'vitest';
5+
import { Select } from '../components/ui/Select';
6+
7+
describe('Select keyboard interactions', () => {
8+
const options = [
9+
{ value: 'a', label: 'Alpha' },
10+
{ value: 'b', label: 'Bravo', disabled: true },
11+
{ value: 'c', label: 'Charlie' },
12+
];
13+
14+
it('opens with Enter and Space and closes with Escape, focuses trigger after close', async () => {
15+
const onChange = vi.fn();
16+
const user = userEvent.setup();
17+
render(<Select options={options} value={undefined} onChange={onChange} />);
18+
19+
const trigger = screen.getAllByRole('button')[0];
20+
21+
await user.click(trigger);
22+
expect(trigger).toHaveAttribute('aria-expanded', 'true');
23+
24+
await user.keyboard('{Escape}');
25+
expect(trigger).toHaveAttribute('aria-expanded', 'false');
26+
expect(trigger).toHaveFocus();
27+
28+
await user.keyboard(' ');
29+
expect(trigger).toHaveAttribute('aria-expanded', 'true');
30+
});
31+
32+
it('navigates options with ArrowDown/ArrowUp skipping disabled options and commits with Enter', async () => {
33+
const onChange = vi.fn();
34+
const user = userEvent.setup();
35+
render(<Select options={options} onChange={onChange} />);
36+
37+
const trigger = screen.getAllByRole('button')[0];
38+
39+
await user.click(trigger);
40+
expect(trigger).toHaveAttribute('aria-expanded', 'true');
41+
42+
const alpha = screen.getByRole('option', { name: 'Alpha' });
43+
expect(alpha).toHaveFocus();
44+
45+
await user.keyboard('{ArrowDown}');
46+
const charlie = screen.getByRole('option', { name: 'Charlie' });
47+
expect(charlie).toHaveFocus();
48+
49+
await user.keyboard('{Enter}');
50+
expect(onChange).toHaveBeenCalledWith('c');
51+
expect(trigger).toHaveAttribute('aria-expanded', 'false');
52+
expect(trigger).toHaveFocus();
53+
});
54+
});

0 commit comments

Comments
 (0)