fix(picker): ARIA combobox — listbox only when results; no tabindex on options
Status/error/empty messages rendered outside <ul role="listbox"> so the element only contains role="option" children (fixes aria-required-children axe violation). Options no longer carry tabindex or onkeydown — keyboard navigation stays on the input per ARIA combobox pattern. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -198,3 +198,46 @@ describe('DocumentPickerDropdown — search failure', () => {
|
||||
await expect.element(page.getByText(m.comp_typeahead_error())).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('DocumentPickerDropdown — ARIA listbox integrity', () => {
|
||||
it('does not render a listbox when results are empty (no aria-required-children violation)', async () => {
|
||||
mockSearchResponse([]);
|
||||
render(DocumentPickerDropdown, { onSelect: vi.fn() });
|
||||
|
||||
await userEvent.fill(page.getByRole('combobox'), 'xyz');
|
||||
await waitForDebounce();
|
||||
|
||||
// no-results message must be visible, but NOT inside a listbox
|
||||
await expect.element(page.getByText(m.comp_typeahead_no_results())).toBeInTheDocument();
|
||||
expect(document.querySelector('[role="listbox"]')).toBeNull();
|
||||
});
|
||||
|
||||
it('does not render a listbox when loading (no aria-required-children violation)', async () => {
|
||||
let resolveSearch!: (v: unknown) => void;
|
||||
vi.stubGlobal(
|
||||
'fetch',
|
||||
vi.fn().mockReturnValue(new Promise((resolve) => (resolveSearch = resolve)))
|
||||
);
|
||||
render(DocumentPickerDropdown, { onSelect: vi.fn() });
|
||||
|
||||
await userEvent.fill(page.getByRole('combobox'), 'Brief');
|
||||
|
||||
// While in-flight, no listbox should exist
|
||||
expect(document.querySelector('[role="listbox"]')).toBeNull();
|
||||
resolveSearch({ ok: true, json: () => Promise.resolve({ items: [] }) });
|
||||
});
|
||||
|
||||
it('option elements do not have tabindex (combobox pattern: focus stays on input)', async () => {
|
||||
mockSearchResponse([docFactory('d1', 'Brief A'), docFactory('d2', 'Brief B')]);
|
||||
render(DocumentPickerDropdown, { onSelect: vi.fn() });
|
||||
|
||||
await userEvent.fill(page.getByRole('combobox'), 'Brief');
|
||||
await waitForDebounce();
|
||||
|
||||
const options = document.querySelectorAll('[role="listbox"] [role="option"]');
|
||||
expect(options.length).toBeGreaterThan(0);
|
||||
options.forEach((opt) => {
|
||||
expect(opt).not.toHaveAttribute('tabindex');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user