fix(combobox): merged input ref so the field is typeable by mouse
The <input> carried only floating-ui's reference ref, so the component's own inputRef stayed null. The field container's onMouseDown then preventDefault()-ed every press (its `e.target !== inputRef.current` guard was always true) and the compensating inputRef.current?.focus() was a no-op — so clicking the box opened the list but never focused the input, and mouse users could not type to filter. Tab focus still worked, which is why it read as 'click, can't type'. Feed a merged callback ref that assigns the node to both floating-ui's setReference and inputRef. Fixes the Channel, Your tags, Topic, and Language sidebar filters at once (all use Combobox); the channel list's 50-row cap is now reachable past 50 via search. Single-select commit no longer re-focuses (that now actually fires and would re-open the just-closed list). Regression test presses the field chrome and asserts the input receives focus. Verified live: clicking the Channel field focuses it and typing 'cold' narrows the list to ColdFusion / wehatethecold.
This commit is contained in:
@@ -64,6 +64,19 @@ describe("Combobox", () => {
|
||||
expect(document.getElementById(controls!)?.getAttribute("role")).toBe("listbox");
|
||||
});
|
||||
|
||||
it("focuses the input when the field chrome is pressed (merged ref, not a dead inputRef)", () => {
|
||||
// Regression: the input carried only floating-ui's ref, so `inputRef` stayed null. The field's
|
||||
// onMouseDown then preventDefault()-ed every press (its `e.target !== inputRef.current` guard was
|
||||
// always true) and its compensating inputRef.current?.focus() was a no-op → the box couldn't be
|
||||
// typed into with the mouse. A merged ref feeds the node to both, so pressing the chrome focuses it.
|
||||
wrap(<Combobox label="Genre" options={OPTIONS} selected={[]} onChange={vi.fn()} />);
|
||||
const input = screen.getByRole("combobox");
|
||||
const field = input.parentElement as HTMLElement;
|
||||
expect(document.activeElement).not.toBe(input);
|
||||
fireEvent.mouseDown(field); // press the field padding, not the input itself
|
||||
expect(document.activeElement).toBe(input);
|
||||
});
|
||||
|
||||
it("keeps the option list unmounted until the field is focused", () => {
|
||||
wrap(<Combobox label="Genre" options={OPTIONS} selected={[]} onChange={vi.fn()} />);
|
||||
expect(screen.queryByRole("option", { hidden: true })).toBeNull();
|
||||
|
||||
Reference in New Issue
Block a user