From 4d0641e21ae2fce12c57a4a64d6f319f4df893e3 Mon Sep 17 00:00:00 2001 From: Pareder Date: Fri, 7 Aug 2026 12:39:03 +0300 Subject: [PATCH 1/2] fix: clear button keyboard Enter/Space press --- src/SelectInput/index.tsx | 13 +++++++++++++ tests/shared/allowClearTest.tsx | 16 ++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/SelectInput/index.tsx b/src/SelectInput/index.tsx index 7df30183..995c31c1 100644 --- a/src/SelectInput/index.tsx +++ b/src/SelectInput/index.tsx @@ -211,6 +211,18 @@ export default React.forwardRef(function Selec onMouseDown?.(event); }); + // ===================== Clear ====================== + // The clear button lives inside the select root, whose `onKeyDown` treats + // Enter/Space as "open the dropdown" and calls `preventDefault` on them. + // That would cancel the native button activation, so keyboard users would + // never get the `click` event which performs the clear. Keep the activation + // keys scoped to the button itself. + const onClearKeyDown: React.KeyboardEventHandler = (event) => { + if (event.key === 'Enter' || event.key === ' ') { + event.stopPropagation(); + } + }; + // =================== Components =================== const { root: RootComponent } = components; @@ -298,6 +310,7 @@ export default React.forwardRef(function Selec e.preventDefault(); (e.nativeEvent as any)._select_lazy = true; }} + onKeyDown={onClearKeyDown} // Clearing happens on click so it works for both pointer and // keyboard (Enter/Space) activation. onClick={onClearMouseDown} diff --git a/tests/shared/allowClearTest.tsx b/tests/shared/allowClearTest.tsx index 012f79fc..86e93025 100644 --- a/tests/shared/allowClearTest.tsx +++ b/tests/shared/allowClearTest.tsx @@ -23,6 +23,22 @@ export default function allowClearTest(mode: any, value: any) { fireEvent(clear, mouseDownEvent); expect(mouseDownEvent.defaultPrevented).toBe(true); }); + it('keeps Enter/Space on the clear button local to it', () => { + // The root handler prevents default on Enter/Space to open the dropdown. + // If it received them from the clear button, the native button activation + // would be canceled and no `click` (thus no clear) would ever happen. + ['Enter', ' '].forEach((key) => { + const { container } = render(); - const clear = container.querySelector('.rc-select-clear'); - const keyDownEvent = createEvent.keyDown(clear, { key }); - - fireEvent(clear, keyDownEvent); - - expect(keyDownEvent.defaultPrevented).toBe(false); - expect(container.querySelector('.rc-select-open')).toBeFalsy(); - }); - }); it('clears value', () => { const onClear = jest.fn(); @@ -78,5 +63,47 @@ export default function allowClearTest(mode: any, value: any) { expect(container.querySelector('input').value).toEqual(''); expect(onClear).toHaveBeenCalled(); }); + + it('clears value with keyboard', () => { + ['Enter', ' '].forEach((key) => { + const onClear = jest.fn(); + const onChange = jest.fn(); + const onDeselect = jest.fn(); + const useArrayValue = ['tags', 'multiple'].includes(mode); + + const { container } = render( + , + ); + const clear = container.querySelector('.rc-select-clear'); + const keyDownEvent = createEvent.keyDown(clear, { key }); + + fireEvent(clear, keyDownEvent); + + expect(keyDownEvent.defaultPrevented).toBe(false); + expect(container.querySelector('.rc-select-open')).toBeFalsy(); + + // The native button activation should fire a click + fireEvent.click(clear); + + if (useArrayValue) { + expect(onChange).toHaveBeenCalledWith([], []); + } else { + expect(onChange).toHaveBeenCalledWith(undefined, undefined); + } + expect(onDeselect).not.toBeCalled(); + expect(container.querySelector('input').value).toEqual(''); + expect(onClear).toHaveBeenCalled(); + }); + }); }); }