From 2f71b22c9c0692b7c8862edc77bacd4682435505 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Ba=CC=88chler?= Date: Wed, 14 May 2025 20:15:36 +0200 Subject: [PATCH] Ignore blur event inside the open menu This can happen on iOS. If the select is opened a blur event is fired immediately. Possibly fixes #443. --- src/__tests__/downshift.lifecycle.js | 63 ++++++++++++++++++++++++++++ src/downshift.js | 8 +++- 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/src/__tests__/downshift.lifecycle.js b/src/__tests__/downshift.lifecycle.js index dfd3782e..1b9ccef1 100644 --- a/src/__tests__/downshift.lifecycle.js +++ b/src/__tests__/downshift.lifecycle.js @@ -16,6 +16,8 @@ jest.mock('../utils', () => { afterEach(() => { utils.scrollIntoView.mockReset() + // Ensure any pending timers are cleared if not already handled by tests + jest.clearAllTimers() }) test('do not set state after unmount', () => { @@ -256,6 +258,67 @@ test('controlled highlighted index change scrolls the item into view', () => { ) }) +test('input blur when menu is open does not reset state', () => { + const handleStateChange = jest.fn() + const items = ['apple', 'banana'] + + render( + String(i)}> + {({ + getInputProps, + getToggleButtonProps, + getMenuProps, + getItemProps, + getRootProps, + }) => ( +
+ +
+ )} +
, + ) + + const input = screen.getByTestId('downshift-input') + const toggleButton = screen.getByTestId('toggle-button') + + fireEvent.click(toggleButton) + jest.runAllTimers() + + expect(screen.getByTestId('downshift-menu')).toBeInTheDocument() + expect(handleStateChange).toHaveBeenCalledWith( + expect.objectContaining({ + type: Downshift.stateChangeTypes.clickButton, + isOpen: true, + }), + expect.anything(), + ) + + act(() => { + input.focus() + }) + fireEvent.blur(input) + + jest.runAllTimers() + + expect(handleStateChange).toHaveBeenCalledTimes(1) +}) + function mouseDownAndUp(node) { fireEvent.mouseDown(node) fireEvent.mouseUp(node) diff --git a/src/downshift.js b/src/downshift.js index 49bdda0f..b13f9fca 100644 --- a/src/downshift.js +++ b/src/downshift.js @@ -883,7 +883,13 @@ class Downshift extends Component { this._rootNode && this._rootNode.contains(activeElement) - if (!downshiftButtonIsActive) { + // We check for the containment of the newly activeElement in the _menuNode here due to iOS emitting a blur event right away when the menu is opened (maybe due to not showing the keyboard?) + const activeElementWithinMenu = + this._menuNode && + activeElement && + this._menuNode.contains(activeElement) + + if (!downshiftButtonIsActive && !activeElementWithinMenu) { this.reset({type: stateChangeTypes.blurInput}) } })