diff --git a/src/BaseSelect/index.tsx b/src/BaseSelect/index.tsx index c7f79449..788ecd05 100644 --- a/src/BaseSelect/index.tsx +++ b/src/BaseSelect/index.tsx @@ -645,6 +645,7 @@ const BaseSelect = React.forwardRef((props, ref) notFoundContent, open: mergedOpen, triggerOpen: mergedOpen, + rawOpen, id, showSearch, multiple, @@ -662,6 +663,7 @@ const BaseSelect = React.forwardRef((props, ref) showSearch, multiple, mergedOpen, + rawOpen, showScrollBar, styles, classNames, diff --git a/src/SelectInput/Content/MultipleContent.tsx b/src/SelectInput/Content/MultipleContent.tsx index 66ed4551..0ea065e6 100644 --- a/src/SelectInput/Content/MultipleContent.tsx +++ b/src/SelectInput/Content/MultipleContent.tsx @@ -36,6 +36,7 @@ export default React.forwardRef(function M disabled, showSearch, triggerOpen, + rawOpen, toggleOpen, autoClearSearchValue, tagRender: tagRenderFromContext, @@ -50,8 +51,9 @@ export default React.forwardRef(function M // ===================== Search ====================== // Apply autoClearSearchValue logic: when dropdown is closed and autoClearSearchValue is not false (default true), clear search value + // Use rawOpen to avoid clearing search when emptyListContent blocks open let computedSearchValue = searchValue; - if (!triggerOpen && mode === 'multiple' && autoClearSearchValue !== false) { + if (!rawOpen && mode === 'multiple' && autoClearSearchValue !== false) { computedSearchValue = ''; } diff --git a/src/hooks/useBaseProps.ts b/src/hooks/useBaseProps.ts index 569fa8b8..2b7101d8 100644 --- a/src/hooks/useBaseProps.ts +++ b/src/hooks/useBaseProps.ts @@ -8,6 +8,7 @@ import type { BaseSelectProps } from '../BaseSelect'; export interface BaseSelectContextProps extends BaseSelectProps { triggerOpen: boolean; + rawOpen: boolean; multiple: boolean; toggleOpen: (open?: boolean) => void; lockOptions: boolean; diff --git a/tests/Select.test.tsx b/tests/Select.test.tsx index 66dd165a..660f9ca3 100644 --- a/tests/Select.test.tsx +++ b/tests/Select.test.tsx @@ -1997,32 +1997,37 @@ describe('Select.Basic', () => { expect(container.querySelector('.rc-select-dropdown-empty')).toBeFalsy(); }); - it('should allow typing when notFoundContent is null and no options match', () => { - const onSearch = jest.fn(); - const { container } = render( - , - ); + describe('should allow typing when notFoundContent is null and no options match', () => { + it.each<{ mode: 'multiple' | undefined; label: string }>([ + { mode: undefined, label: 'single' }, + { mode: 'multiple', label: 'multiple' }, + ])('$label', ({ mode }) => { + const onSearch = jest.fn(); + const { container } = render( + , + ); - const input = container.querySelector('input'); + const input = container.querySelector('input'); + + // Type 'j' - should match 'Jack' + fireEvent.change(input, { target: { value: 'j' } }); + expect(onSearch).toHaveBeenLastCalledWith('j'); + expect(input.value).toBe('j'); + expect(container.querySelectorAll('.rc-select-item-option')).toHaveLength(1); - // Type 'j' - should match 'Jack' - fireEvent.change(input, { target: { value: 'j' } }); - expect(onSearch).toHaveBeenLastCalledWith('j'); - expect(input.value).toBe('j'); - expect(container.querySelectorAll('.rc-select-item-option')).toHaveLength(1); - - // Type 'x' - no match, but input should still work - fireEvent.change(input, { target: { value: 'x' } }); - expect(onSearch).toHaveBeenLastCalledWith('x'); - expect(input.value).toBe('x'); - - // Type more characters - should continue working - fireEvent.change(input, { target: { value: 'xyz' } }); - expect(onSearch).toHaveBeenLastCalledWith('xyz'); - expect(input.value).toBe('xyz'); + // Type 'x' - no match, but input should still work + fireEvent.change(input, { target: { value: 'x' } }); + expect(onSearch).toHaveBeenLastCalledWith('x'); + expect(input.value).toBe('x'); + + // Type more characters - should continue working + fireEvent.change(input, { target: { value: 'xyz' } }); + expect(onSearch).toHaveBeenLastCalledWith('xyz'); + expect(input.value).toBe('xyz'); + }); }); it('click outside to close select', () => {