diff --git a/README.md b/README.md index 91814eb77..2a4c1ad44 100644 --- a/README.md +++ b/README.md @@ -132,6 +132,7 @@ export default () => ( | direction | direction of dropdown | 'ltr' \| 'rtl' | 'ltr' | | optionRender | Custom rendering options | (oriOption: FlattenOptionData\ , info: { index: number }) => React.ReactNode | - | | labelRender | Custom rendering label | (props: LabelInValueType) => React.ReactNode | - | +| activeOptionFilter | Custom filter function for active option when searching. | (searchValue: string, option: OptionType) => boolean | - | | maxCount | The max number of items can be selected | number | - | ### Methods diff --git a/docs/examples/combobox.tsx b/docs/examples/combobox.tsx index 6e987be61..4db7f8f84 100644 --- a/docs/examples/combobox.tsx +++ b/docs/examples/combobox.tsx @@ -150,6 +150,40 @@ class Combobox extends React.Component { options={this.state.options} onChange={this.onAsyncChange} /> + +

Active Option Filter - Middle Match

+ + +

No Active Highlight

+ ); diff --git a/src/OptionList.tsx b/src/OptionList.tsx index cd8387761..0c0b1dbbd 100644 --- a/src/OptionList.tsx +++ b/src/OptionList.tsx @@ -60,6 +60,7 @@ const OptionList: React.ForwardRefRenderFunction = (_, r listHeight, listItemHeight, optionRender, + activeOptionFilter, classNames: contextClassNames, styles: contextStyles, } = React.useContext(SelectContext); @@ -159,9 +160,12 @@ const OptionList: React.ForwardRefRenderFunction = (_, r if (!multiple && open && rawValues.size === 1) { const value: RawValueType = Array.from(rawValues)[0]; // Scroll to the option closest to the searchValue if searching. - const index = memoFlattenOptions.findIndex(({ data }) => - searchValue ? String(data.value).startsWith(searchValue) : data.value === value, - ); + const index = memoFlattenOptions.findIndex(({ data }) => { + if (activeOptionFilter) { + return activeOptionFilter(searchValue, data); + } + return searchValue ? String(data.value).startsWith(searchValue) : data.value === value; + }); if (index !== -1) { setActive(index); diff --git a/src/Select.tsx b/src/Select.tsx index 8bad7c365..22be76bad 100644 --- a/src/Select.tsx +++ b/src/Select.tsx @@ -151,6 +151,7 @@ export interface SelectProps React.ReactNode; + activeOptionFilter?: (searchValue: string, option: OptionType) => boolean; // >>> Icon menuItemSelectedIcon?: RenderNode; @@ -213,6 +214,7 @@ const Select = React.forwardRef(null); diff --git a/tests/Combobox.test.tsx b/tests/Combobox.test.tsx index 17b0fc102..496c32585 100644 --- a/tests/Combobox.test.tsx +++ b/tests/Combobox.test.tsx @@ -117,6 +117,53 @@ describe('Select.Combobox', () => { expect(container.querySelector('input').value).toEqual('1'); }); + it('activeOptionFilter should work', async () => { + const { container } = render( + , + ); + + const input = container.querySelector('input')!; + fireEvent.change(input, { target: { value: 'an' } }); + await delay(); + + expect( + container.querySelector('.rc-select-item-option-active .rc-select-item-option-content'), + ).toHaveTextContent('banana'); + + fireEvent.change(input, { target: { value: 'ora' } }); + await delay(); + + expect( + container.querySelector('.rc-select-item-option-active .rc-select-item-option-content'), + ).toHaveTextContent('orange'); + }); + + it('activeOptionFilter with empty function should not activate any option', async () => { + const { container } = render( + , + ); + + const input = container.querySelector('input')!; + fireEvent.change(input, { target: { value: 'an' } }); + await delay(); + + expect(container.querySelector('.rc-select-item-option-active')).toBeNull(); + }); + describe('input value', () => { const createSelect = (props?: Partial) => (