Skip to content

Commit 5d7c05e

Browse files
authored
fix: controlled combobox with downshiftProps (#18441)
ensure onChange is called when downshiftProps & onStateChange is provided
1 parent 2dc4bc2 commit 5d7c05e

File tree

2 files changed

+46
-7
lines changed

2 files changed

+46
-7
lines changed

packages/react/src/components/ComboBox/ComboBox-test.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import React, { useState } from 'react';
99
import { render, screen, within, fireEvent, act } from '@testing-library/react';
10+
import { useCombobox } from 'downshift';
1011
import userEvent from '@testing-library/user-event';
1112
import {
1213
findListBoxNode,
@@ -493,6 +494,40 @@ describe('ComboBox', () => {
493494
expect(findInputNode()).toHaveDisplayValue('');
494495
expect(mockProps.onChange).toHaveBeenCalled();
495496
});
497+
it('should call onChange when downshiftProps onStateChange is provided', async () => {
498+
const downshiftProps = {
499+
onStateChange: jest.fn(),
500+
};
501+
render(
502+
<ComboBox
503+
{...mockProps}
504+
selectedItem={mockProps.items[0]}
505+
downshiftProps={downshiftProps}
506+
/>
507+
);
508+
expect(mockProps.onChange).not.toHaveBeenCalled();
509+
expect(downshiftProps.onStateChange).not.toHaveBeenCalled();
510+
await openMenu();
511+
expect(downshiftProps.onStateChange).toHaveBeenCalledTimes(1);
512+
await userEvent.click(screen.getByRole('option', { name: 'Item 2' }));
513+
expect(mockProps.onChange).toHaveBeenCalledTimes(1);
514+
expect(downshiftProps.onStateChange).toHaveBeenCalledTimes(3);
515+
expect(downshiftProps.onStateChange).toHaveBeenNthCalledWith(2, {
516+
selectedItem: {
517+
id: 'id-2',
518+
label: 'Item 2',
519+
value: 2,
520+
},
521+
type: useCombobox.stateChangeTypes.ItemClick,
522+
});
523+
expect(downshiftProps.onStateChange).toHaveBeenLastCalledWith({
524+
selectedItem: undefined,
525+
type: useCombobox.stateChangeTypes.FunctionSetHighlightedIndex,
526+
});
527+
expect(
528+
screen.getByRole('combobox', { value: 'Item 2' })
529+
).toBeInTheDocument();
530+
});
496531
});
497532

498533
describe('when disabled', () => {

packages/react/src/components/ComboBox/ComboBox.tsx

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -797,7 +797,18 @@ const ComboBox = forwardRef(
797797
}
798798
}
799799
},
800+
initialSelectedItem: initialSelectedItem,
801+
inputId: id,
802+
stateReducer,
803+
isItemDisabled(item, _index) {
804+
return (item as any)?.disabled;
805+
},
806+
...downshiftProps,
800807
onStateChange: ({ type, selectedItem: newSelectedItem }) => {
808+
downshiftProps?.onStateChange?.({
809+
type,
810+
selectedItem: newSelectedItem,
811+
});
801812
if (
802813
type === useCombobox.stateChangeTypes.ItemClick &&
803814
!isEqual(selectedItemProp, newSelectedItem)
@@ -812,13 +823,6 @@ const ComboBox = forwardRef(
812823
onChange({ selectedItem: newSelectedItem });
813824
}
814825
},
815-
initialSelectedItem: initialSelectedItem,
816-
inputId: id,
817-
stateReducer,
818-
isItemDisabled(item, _index) {
819-
return (item as any)?.disabled;
820-
},
821-
...downshiftProps,
822826
});
823827

824828
useEffect(() => {

0 commit comments

Comments
 (0)