Skip to content

Commit 49ec5c0

Browse files
ug-heroliuqiang
andauthored
feat: The optionFilterProp supports string and string[]. (#1179)
Co-authored-by: liuqiang <qiang.liu@xinjifamily.com>
1 parent 18f176f commit 49ec5c0

File tree

5 files changed

+81
-22
lines changed

5 files changed

+81
-22
lines changed

docs/examples/optionFilterProp.tsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,33 @@ import Select, { Option } from '@rc-component/select';
33
import '../../assets/index.less';
44

55
const Test = () => {
6-
const [value, setValue] = React.useState<string>('');
6+
const [value, setValue] = React.useState<string>('张三');
77

88
return (
99
<div>
1010
<h2>Select optionFilterProp</h2>
1111
<div style={{ width: 300 }}>
1212
<Select
13-
defaultValue="张三"
1413
style={{ width: 500 }}
14+
showSearch={{ optionFilterProp: ['value', 'pinyin'] }}
1515
placeholder="placeholder"
16-
optionFilterProp="desc"
1716
onChange={(val: string) => {
1817
setValue(val);
1918
}}
2019
value={value}
21-
showSearch
2220
>
23-
<Option value="张三" desc="张三 zhang san">
21+
<Option value="张三" pinyin="zhangsan">
2422
张三
2523
</Option>
26-
<Option value="李四" desc="李四 li si">
24+
<Option value="李四" pinyin="lisi">
2725
李四
2826
</Option>
29-
<Option value="王五" desc="王五 wang wu">
27+
<Option value="王五" pinyin="wangwu">
3028
王五
3129
</Option>
30+
<Option value="lisa" pinyin="lisa">
31+
lisa
32+
</Option>
3233
</Select>
3334
</div>
3435
{value}

src/Select.tsx

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -117,10 +117,12 @@ export interface SearchConfig<OptionType> {
117117
onSearch?: (value: string) => void;
118118
filterOption?: boolean | FilterFunc<OptionType>;
119119
filterSort?: (optionA: OptionType, optionB: OptionType, info: { searchValue: string }) => number;
120-
optionFilterProp?: string;
120+
optionFilterProp?: string | string[];
121121
}
122-
export interface SelectProps<ValueType = any, OptionType extends BaseOptionType = DefaultOptionType>
123-
extends Omit<BaseSelectPropsWithoutPrivate, 'showSearch'> {
122+
export interface SelectProps<
123+
ValueType = any,
124+
OptionType extends BaseOptionType = DefaultOptionType,
125+
> extends Omit<BaseSelectPropsWithoutPrivate, 'showSearch'> {
124126
prefixCls?: string;
125127
id?: string;
126128

@@ -152,7 +154,7 @@ export interface SelectProps<ValueType = any, OptionType extends BaseOptionType
152154
/** @deprecated please use showSearch.filterSort */
153155
filterSort?: SearchConfig<OptionType>['filterSort'];
154156
/** @deprecated please use showSearch.optionFilterProp */
155-
optionFilterProp?: string;
157+
optionFilterProp?: string | string[];
156158
optionLabelProp?: string;
157159

158160
children?: React.ReactNode;
@@ -248,6 +250,11 @@ const Select = React.forwardRef<BaseSelectRef, SelectProps<any, DefaultOptionTyp
248250
autoClearSearchValue = true,
249251
} = searchConfig;
250252

253+
const normalizedOptionFilterProp = React.useMemo(() => {
254+
if (!optionFilterProp) return [];
255+
return Array.isArray(optionFilterProp) ? optionFilterProp : [optionFilterProp];
256+
}, [optionFilterProp]);
257+
251258
const mergedId = useId(id);
252259
const multiple = isMultiple(mode);
253260
const childrenAsData = !!(!options && children);
@@ -280,7 +287,7 @@ const Select = React.forwardRef<BaseSelectRef, SelectProps<any, DefaultOptionTyp
280287
options,
281288
children,
282289
mergedFieldNames,
283-
optionFilterProp,
290+
normalizedOptionFilterProp,
284291
optionLabelProp,
285292
);
286293
const { valueOptions, labelOptions, options: mergedOptions } = parsedOptions;
@@ -432,15 +439,21 @@ const Select = React.forwardRef<BaseSelectRef, SelectProps<any, DefaultOptionTyp
432439
mergedFieldNames,
433440
mergedSearchValue,
434441
mergedFilterOption,
435-
optionFilterProp,
442+
normalizedOptionFilterProp,
436443
);
437444

438445
// Fill options with search value if needed
439446
const filledSearchOptions = React.useMemo(() => {
447+
const hasItemMatchingSearch = (item: DefaultOptionType) => {
448+
if (normalizedOptionFilterProp.length) {
449+
return normalizedOptionFilterProp.some((prop) => item?.[prop] === mergedSearchValue);
450+
}
451+
return item?.value === mergedSearchValue;
452+
};
440453
if (
441454
mode !== 'tags' ||
442455
!mergedSearchValue ||
443-
filteredOptions.some((item) => item[optionFilterProp || 'value'] === mergedSearchValue)
456+
filteredOptions.some((item) => hasItemMatchingSearch(item))
444457
) {
445458
return filteredOptions;
446459
}
@@ -452,7 +465,7 @@ const Select = React.forwardRef<BaseSelectRef, SelectProps<any, DefaultOptionTyp
452465
return [createTagOption(mergedSearchValue), ...filteredOptions];
453466
}, [
454467
createTagOption,
455-
optionFilterProp,
468+
normalizedOptionFilterProp,
456469
mode,
457470
filteredOptions,
458471
mergedSearchValue,

src/hooks/useFilterOptions.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ export default (
1212
fieldNames: FieldNames,
1313
searchValue?: string,
1414
filterOption?: SelectProps['filterOption'],
15-
optionFilterProp?: string,
16-
) =>
17-
React.useMemo(() => {
15+
optionFilterProp?: string[],
16+
) => {
17+
return React.useMemo(() => {
1818
if (!searchValue || filterOption === false) {
1919
return options;
2020
}
@@ -29,8 +29,8 @@ export default (
2929
? filterOption
3030
: (_: string, option: DefaultOptionType) => {
3131
// Use provided `optionFilterProp`
32-
if (optionFilterProp) {
33-
return includes(option[optionFilterProp], upperSearch);
32+
if (optionFilterProp && optionFilterProp.length) {
33+
return optionFilterProp.some((prop) => includes(option[prop], upperSearch));
3434
}
3535

3636
// Auto select `label` or `value` by option type
@@ -76,3 +76,4 @@ export default (
7676

7777
return filteredOptions;
7878
}, [options, filterOption, optionFilterProp, searchValue, fieldNames]);
79+
};

src/hooks/useOptions.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const useOptions = <OptionType>(
1010
options: OptionType[],
1111
children: React.ReactNode,
1212
fieldNames: FieldNames,
13-
optionFilterProp: string,
13+
optionFilterProp: string[],
1414
optionLabelProp: string,
1515
) => {
1616
return React.useMemo(() => {
@@ -42,7 +42,9 @@ const useOptions = <OptionType>(
4242
valueOptions.set(option[fieldNames.value], option);
4343
setLabelOptions(labelOptions, option, fieldNames.label);
4444
// https://github.com/ant-design/ant-design/issues/35304
45-
setLabelOptions(labelOptions, option, optionFilterProp);
45+
optionFilterProp.forEach((prop) => {
46+
setLabelOptions(labelOptions, option, prop);
47+
});
4648
setLabelOptions(labelOptions, option, optionLabelProp);
4749
} else {
4850
dig(option[fieldNames.options], true);

tests/Select.test.tsx

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,48 @@ describe('Select.Basic', () => {
511511
expect(container.querySelector('.rc-select-item-option-content').textContent).toBe('2');
512512
});
513513

514+
it('supports optionFilterProp as array for multiple field searching (value + pinyin)', () => {
515+
const { container } = render(
516+
<Select showSearch={{ optionFilterProp: ['value', 'pinyin'] }} style={{ width: 300 }}>
517+
<Option value="张三" pinyin="zhangsan">
518+
张三
519+
</Option>
520+
<Option value="李四" pinyin="lisi">
521+
李四
522+
</Option>
523+
<Option value="王五" pinyin="wangwu">
524+
王五
525+
</Option>
526+
<Option value="lisa" pinyin="lisa">
527+
lisa
528+
</Option>
529+
</Select>,
530+
);
531+
532+
const input = container.querySelector('input');
533+
534+
fireEvent.change(input, { target: { value: 'zhang' } });
535+
536+
let items = container.querySelectorAll('.rc-select-item-option-content');
537+
expect(items).toHaveLength(1);
538+
expect(items[0].textContent).toBe('张三');
539+
540+
fireEvent.change(input, { target: { value: '王' } });
541+
542+
items = container.querySelectorAll('.rc-select-item-option-content');
543+
expect(items).toHaveLength(1);
544+
expect(items[0].textContent).toBe('王五');
545+
546+
fireEvent.change(input, { target: { value: 'li' } });
547+
548+
items = container.querySelectorAll('.rc-select-item-option-content');
549+
const texts = Array.from(items).map((item) => item.textContent);
550+
551+
expect(items).toHaveLength(2);
552+
expect(texts).toContain('李四');
553+
expect(texts).toContain('lisa');
554+
});
555+
514556
it('filter array children', () => {
515557
const { container } = render(
516558
<Select optionFilterProp="children" showSearch>

0 commit comments

Comments
 (0)