From 945316d16d9fd7cc3c7519c0de1dfb549e2f5887 Mon Sep 17 00:00:00 2001 From: liuqiang Date: Mon, 1 Dec 2025 18:07:23 +0800 Subject: [PATCH 1/2] update --- docs/examples/optionFilterProp.tsx | 15 ++++++----- src/Select.tsx | 27 ++++++++++++++----- src/hooks/useFilterOptions.ts | 11 ++++---- src/hooks/useOptions.ts | 6 +++-- tests/Select.test.tsx | 42 ++++++++++++++++++++++++++++++ 5 files changed, 80 insertions(+), 21 deletions(-) diff --git a/docs/examples/optionFilterProp.tsx b/docs/examples/optionFilterProp.tsx index f3cc83876..91c16e00f 100644 --- a/docs/examples/optionFilterProp.tsx +++ b/docs/examples/optionFilterProp.tsx @@ -3,32 +3,33 @@ import Select, { Option } from '@rc-component/select'; import '../../assets/index.less'; const Test = () => { - const [value, setValue] = React.useState(''); + const [value, setValue] = React.useState('张三'); return (

Select optionFilterProp

{value} diff --git a/src/Select.tsx b/src/Select.tsx index c9ecf6744..28f2d7f52 100644 --- a/src/Select.tsx +++ b/src/Select.tsx @@ -117,10 +117,12 @@ export interface SearchConfig { onSearch?: (value: string) => void; filterOption?: boolean | FilterFunc; filterSort?: (optionA: OptionType, optionB: OptionType, info: { searchValue: string }) => number; - optionFilterProp?: string; + optionFilterProp?: string | string[]; } -export interface SelectProps - extends Omit { +export interface SelectProps< + ValueType = any, + OptionType extends BaseOptionType = DefaultOptionType, +> extends Omit { prefixCls?: string; id?: string; @@ -152,7 +154,7 @@ export interface SelectProps['filterSort']; /** @deprecated please use showSearch.optionFilterProp */ - optionFilterProp?: string; + optionFilterProp?: string | string[]; optionLabelProp?: string; children?: React.ReactNode; @@ -248,6 +250,11 @@ const Select = React.forwardRef { + if (!optionFilterProp) return []; + return Array.isArray(optionFilterProp) ? optionFilterProp : [optionFilterProp]; + }, [optionFilterProp]); + const mergedId = useId(id); const multiple = isMultiple(mode); const childrenAsData = !!(!options && children); @@ -280,7 +287,7 @@ const Select = React.forwardRef { + const hasItemMatchingSearch = (item: DefaultOptionType) => { + if (normalizedOptionFilterProp.length) { + return normalizedOptionFilterProp.some((prop) => item?.[prop] === mergedSearchValue); + } + return item?.value === mergedSearchValue; + }; if ( mode !== 'tags' || !mergedSearchValue || - filteredOptions.some((item) => item[optionFilterProp || 'value'] === mergedSearchValue) + filteredOptions.some((item) => hasItemMatchingSearch(item)) ) { return filteredOptions; } diff --git a/src/hooks/useFilterOptions.ts b/src/hooks/useFilterOptions.ts index 41884fd1f..53cb05712 100644 --- a/src/hooks/useFilterOptions.ts +++ b/src/hooks/useFilterOptions.ts @@ -12,9 +12,9 @@ export default ( fieldNames: FieldNames, searchValue?: string, filterOption?: SelectProps['filterOption'], - optionFilterProp?: string, -) => - React.useMemo(() => { + optionFilterProp?: string[], +) => { + return React.useMemo(() => { if (!searchValue || filterOption === false) { return options; } @@ -29,8 +29,8 @@ export default ( ? filterOption : (_: string, option: DefaultOptionType) => { // Use provided `optionFilterProp` - if (optionFilterProp) { - return includes(option[optionFilterProp], upperSearch); + if (optionFilterProp && optionFilterProp.length) { + return optionFilterProp.some((prop) => includes(option[prop], upperSearch)); } // Auto select `label` or `value` by option type @@ -76,3 +76,4 @@ export default ( return filteredOptions; }, [options, filterOption, optionFilterProp, searchValue, fieldNames]); +}; diff --git a/src/hooks/useOptions.ts b/src/hooks/useOptions.ts index ed4d4c134..28eee3293 100644 --- a/src/hooks/useOptions.ts +++ b/src/hooks/useOptions.ts @@ -10,7 +10,7 @@ const useOptions = ( options: OptionType[], children: React.ReactNode, fieldNames: FieldNames, - optionFilterProp: string, + optionFilterProp: string[], optionLabelProp: string, ) => { return React.useMemo(() => { @@ -42,7 +42,9 @@ const useOptions = ( valueOptions.set(option[fieldNames.value], option); setLabelOptions(labelOptions, option, fieldNames.label); // https://github.com/ant-design/ant-design/issues/35304 - setLabelOptions(labelOptions, option, optionFilterProp); + optionFilterProp.forEach((prop) => { + setLabelOptions(labelOptions, option, prop); + }); setLabelOptions(labelOptions, option, optionLabelProp); } else { dig(option[fieldNames.options], true); diff --git a/tests/Select.test.tsx b/tests/Select.test.tsx index e311e1a42..7ef2637a2 100644 --- a/tests/Select.test.tsx +++ b/tests/Select.test.tsx @@ -511,6 +511,48 @@ describe('Select.Basic', () => { expect(container.querySelector('.rc-select-item-option-content').textContent).toBe('2'); }); + it('supports optionFilterProp as array for multiple field searching (value + pinyin)', () => { + const { container } = render( + , + ); + + const input = container.querySelector('input'); + + fireEvent.change(input, { target: { value: 'zhang' } }); + + let items = container.querySelectorAll('.rc-select-item-option-content'); + expect(items).toHaveLength(1); + expect(items[0].textContent).toBe('张三'); + + fireEvent.change(input, { target: { value: '王' } }); + + items = container.querySelectorAll('.rc-select-item-option-content'); + expect(items).toHaveLength(1); + expect(items[0].textContent).toBe('王五'); + + fireEvent.change(input, { target: { value: 'li' } }); + + items = container.querySelectorAll('.rc-select-item-option-content'); + const texts = Array.from(items).map((item) => item.textContent); + + expect(items).toHaveLength(2); + expect(texts).toContain('李四'); + expect(texts).toContain('lisa'); + }); + it('filter array children', () => { const { container } = render(