feat(pickers): a list that failed to load offers Retry

A picker whose options failed to load said so, but the only way back was closing its dialog.
Combobox takes `onRetry`: a Retry beside the message for the pointer, and Enter in the field for
the keyboard (the panel is a listbox, which may hold options only, so the alert names Enter for
assistive tech). Every caller with an error state passes its query's refetch: both user pickers,
the channel picker and the two Plex pickers.
This commit is contained in:
2026-09-27 17:47:01 +02:00
parent e0453fa07d
commit 52fb999af6
9 changed files with 73 additions and 4 deletions
@@ -311,6 +311,34 @@ describe("Combobox · honest states", () => {
expect(screen.queryByText("common.noResults")).toBeNull();
});
// A failed list with no way back but closing its dialog was a dead end (E28 review, owner go).
it("offers a Retry on the error, by click and by Enter in the field, keeping the focus", () => {
const onRetry = vi.fn();
wrap(
<Combobox
label="User"
options={[]}
selected={[]}
onChange={vi.fn()}
error="Load failed"
onRetry={onRetry}
/>,
);
const field = screen.getByRole("combobox");
field.focus();
fireEvent.focus(field);
// The listbox holds no button for assistive tech; the alert says how (Enter) instead.
expect(screen.getByRole("alert", { hidden: true }).textContent).toContain(
"common.retryWithEnter",
);
const retry = screen.getByText("common.retry");
expect(fireEvent.mouseDown(retry)).toBe(false); // the press does not take the focus
fireEvent.click(retry);
fireEvent.keyDown(field, { key: "Enter" });
expect(onRetry).toHaveBeenCalledTimes(2);
expect(document.activeElement).toBe(field);
});
it("disabled: keeps the focus, opens nothing and picks nothing", () => {
const onChange = vi.fn();
wrap(<Combobox label="Genre" options={OPTIONS} selected={[]} onChange={onChange} disabled />);