From ea47855f8b9a4341407d8eff665ad72d73f29224 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Wed, 21 May 2025 15:49:12 +0800 Subject: [PATCH 01/16] feat: combine searchProps =>showSearch --- src/Select.tsx | 52 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/src/Select.tsx b/src/Select.tsx index 8bad7c365..0645f32b1 100644 --- a/src/Select.tsx +++ b/src/Select.tsx @@ -110,8 +110,18 @@ type ArrayElementType = T extends (infer E)[] ? E : T; export type SemanticName = BaseSelectSemanticName; export type PopupSemantic = 'listItem' | 'list'; +interface ShowSearch { + searchValue?: string; + autoClearSearchValue?: boolean; + onSearch?: (value: string) => void; + tokenSeparators?: string | string[]; + filterOption?: boolean | FilterFunc; + filterSort?: (optionA: OptionType, optionB: OptionType, info: { searchValue: string }) => number; + optionFilterProp?: string; + optionLabelProp?: string; +} export interface SelectProps - extends BaseSelectPropsWithoutPrivate { + extends Omit { prefixCls?: string; id?: string; @@ -119,7 +129,7 @@ export interface SelectProps>> Field Names fieldNames?: FieldNames; - + showSearch?: boolean | ShowSearch; searchValue?: string; onSearch?: (value: string) => void; autoClearSearchValue?: boolean; @@ -177,23 +187,13 @@ const Select = React.forwardRef = {}; + legacySearchProps.forEach((propsName) => { + legacyShowSearch[propsName] = restProps?.[propsName]; + }); + const mergedShowSearch = typeof showSearch === 'object' ? showSearch : legacyShowSearch; + const { + filterOption, + searchValue, + optionFilterProp, + optionLabelProp, + filterSort, + onSearch, + autoClearSearchValue, + } = mergedShowSearch; + const mergedId = useId(id); const multiple = isMultiple(mode); const childrenAsData = !!(!options && children); @@ -698,6 +723,7 @@ const Select = React.forwardRef>> Trigger direction={direction} // >>> Search + showSearch={showSearch === undefined ? undefined : !!showSearch} searchValue={mergedSearchValue} onSearch={onInternalSearch} autoClearSearchValue={autoClearSearchValue} From 40c8d668fb7ae83024b887b75dd09b0940d0f7cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Thu, 5 Jun 2025 10:57:33 +0800 Subject: [PATCH 02/16] feat: change ts name --- src/Select.tsx | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/Select.tsx b/src/Select.tsx index 0645f32b1..1c8f61448 100644 --- a/src/Select.tsx +++ b/src/Select.tsx @@ -110,11 +110,11 @@ type ArrayElementType = T extends (infer E)[] ? E : T; export type SemanticName = BaseSelectSemanticName; export type PopupSemantic = 'listItem' | 'list'; -interface ShowSearch { +interface SearchConfig { searchValue?: string; autoClearSearchValue?: boolean; onSearch?: (value: string) => void; - tokenSeparators?: string | string[]; + tokenSeparators?: string[]; filterOption?: boolean | FilterFunc; filterSort?: (optionA: OptionType, optionB: OptionType, info: { searchValue: string }) => number; optionFilterProp?: string; @@ -129,9 +129,10 @@ export interface SelectProps>> Field Names fieldNames?: FieldNames; - showSearch?: boolean | ShowSearch; - searchValue?: string; - onSearch?: (value: string) => void; + showSearch?: boolean | SearchConfig; + /** @deprecated pleace use SearchConfig.searchValue */ + searchValue?: SearchConfig['searchValue']; + /** @deprecated pleace use SearchConfig.autoClearSearchValue */ autoClearSearchValue?: boolean; // >>> Select @@ -139,16 +140,25 @@ export interface SelectProps, OptionType>; onActive?: (value: ValueType) => void; + /** @deprecated pleace use SearchConfig.onSearch */ + onSearch?: SearchConfig['onSearch']; + /** @deprecated pleace use SearchConfig.tokenSeparators */ + tokenSeparators?: string[]; // >>> Options /** * In Select, `false` means do nothing. * In TreeSelect, `false` will highlight match item. * It's by design. */ - filterOption?: boolean | FilterFunc; - filterSort?: (optionA: OptionType, optionB: OptionType, info: { searchValue: string }) => number; + /** @deprecated pleace use SearchConfig.filterOption */ + filterOption?: SearchConfig['filterOption']; + /** @deprecated pleace use SearchConfig.filterSort */ + filterSort?: SearchConfig['filterSort']; + /** @deprecated pleace use SearchConfig.optionFilterProp */ optionFilterProp?: string; + /** @deprecated pleace use SearchConfig.optionLabelProp */ optionLabelProp?: string; + children?: React.ReactNode; options?: OptionType[]; optionRender?: ( @@ -226,7 +236,7 @@ const Select = React.forwardRef = {}; + const legacyShowSearch: SearchConfig = {}; legacySearchProps.forEach((propsName) => { legacyShowSearch[propsName] = restProps?.[propsName]; }); @@ -239,6 +249,7 @@ const Select = React.forwardRef>> OptionList OptionList={OptionList} From ec0b918a1b28b990417b52edc6867857d9c623bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Thu, 5 Jun 2025 11:54:09 +0800 Subject: [PATCH 03/16] feat: set autoClearSearchValue default value --- src/Select.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Select.tsx b/src/Select.tsx index 1c8f61448..6e27c6b49 100644 --- a/src/Select.tsx +++ b/src/Select.tsx @@ -238,7 +238,11 @@ const Select = React.forwardRef = {}; legacySearchProps.forEach((propsName) => { - legacyShowSearch[propsName] = restProps?.[propsName]; + if (propsName === 'autoClearSearchValue') { + legacyShowSearch[propsName] = props?.[propsName] ?? true; + return; + } + legacyShowSearch[propsName] = props?.[propsName]; }); const mergedShowSearch = typeof showSearch === 'object' ? showSearch : legacyShowSearch; const { @@ -248,7 +252,7 @@ const Select = React.forwardRef Date: Thu, 5 Jun 2025 13:34:53 +0800 Subject: [PATCH 04/16] feat: set autoClearSearchValue default value --- src/Select.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Select.tsx b/src/Select.tsx index 6e27c6b49..cd4ba9fc1 100644 --- a/src/Select.tsx +++ b/src/Select.tsx @@ -238,10 +238,6 @@ const Select = React.forwardRef = {}; legacySearchProps.forEach((propsName) => { - if (propsName === 'autoClearSearchValue') { - legacyShowSearch[propsName] = props?.[propsName] ?? true; - return; - } legacyShowSearch[propsName] = props?.[propsName]; }); const mergedShowSearch = typeof showSearch === 'object' ? showSearch : legacyShowSearch; From b601c63bcf19a3e72f1d1107c73631776adea975 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Thu, 5 Jun 2025 16:05:02 +0800 Subject: [PATCH 05/16] feat: use hooks => useSearchConfig --- src/Select.tsx | 38 ++++++++++++++++++---------------- src/hooks/useSearchConfig.ts | 40 ++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 18 deletions(-) create mode 100644 src/hooks/useSearchConfig.ts diff --git a/src/Select.tsx b/src/Select.tsx index cd4ba9fc1..0dbc14e2c 100644 --- a/src/Select.tsx +++ b/src/Select.tsx @@ -56,6 +56,7 @@ import type { FlattenOptionData } from './interface'; import { hasValue, isComboNoValue, toArray } from './utils/commonUtil'; import { fillFieldNames, flattenOptions, injectPropsWithOption } from './utils/valueUtil'; import warningProps, { warningNullOptions } from './utils/warningPropsUtil'; +import useSearchConfig from './hooks/useSearchConfig'; const OMIT_DOM_PROPS = ['inputValue']; @@ -110,7 +111,7 @@ type ArrayElementType = T extends (infer E)[] ? E : T; export type SemanticName = BaseSelectSemanticName; export type PopupSemantic = 'listItem' | 'list'; -interface SearchConfig { +export interface SearchConfig { searchValue?: string; autoClearSearchValue?: boolean; onSearch?: (value: string) => void; @@ -226,21 +227,22 @@ const Select = React.forwardRef = {}; - legacySearchProps.forEach((propsName) => { - legacyShowSearch[propsName] = props?.[propsName]; - }); - const mergedShowSearch = typeof showSearch === 'object' ? showSearch : legacyShowSearch; + // const legacySearchProps = [ + // 'filterOption', + // 'searchValue', + // 'optionFilterProp', + // 'optionLabelProp', + // 'filterSort', + // 'onSearch', + // 'autoClearSearchValue', + // 'tokenSeparators', + // ]; + // const legacyShowSearch: SearchConfig = {}; + // legacySearchProps.forEach((propsName) => { + // legacyShowSearch[propsName] = props?.[propsName]; + // }); + const [mergedShowSearch, searchConfig] = useSearchConfig(showSearch, props); + // const mergedShowSearch = typeof showSearch === 'object' ? showSearch : legacyShowSearch; const { filterOption, searchValue, @@ -250,7 +252,7 @@ const Select = React.forwardRef>> Trigger direction={direction} // >>> Search - showSearch={showSearch === undefined ? undefined : !!showSearch} + showSearch={mergedShowSearch} searchValue={mergedSearchValue} onSearch={onInternalSearch} autoClearSearchValue={autoClearSearchValue} diff --git a/src/hooks/useSearchConfig.ts b/src/hooks/useSearchConfig.ts new file mode 100644 index 000000000..ec36b3a0a --- /dev/null +++ b/src/hooks/useSearchConfig.ts @@ -0,0 +1,40 @@ +import type { SearchConfig, DefaultOptionType } from '@/Select'; +import * as React from 'react'; +const legacySearchProps = [ + 'filterOption', + 'searchValue', + 'optionFilterProp', + 'optionLabelProp', + 'filterSort', + 'onSearch', + 'autoClearSearchValue', + 'tokenSeparators', +]; +// Convert `showSearch` to unique config +export default function useSearchConfig(showSearch, props) { + return React.useMemo<[boolean, SearchConfig]>(() => { + const legacyShowSearch: SearchConfig = {}; + legacySearchProps.forEach((propsName) => { + legacyShowSearch[propsName] = props?.[propsName]; + }); + const searchConfig: SearchConfig = + typeof showSearch === 'object' ? showSearch : legacyShowSearch; + if (showSearch === undefined) { + return [undefined, searchConfig]; + } + if (!showSearch) { + return [false, {}]; + } + return [true, searchConfig]; + }, [ + showSearch, + props?.filterOption, + props?.searchValue, + props?.optionFilterProp, + props?.optionLabelProp, + props?.filterSort, + props?.onSearch, + props?.autoClearSearchValue, + props?.tokenSeparators, + ]); +} From f8edb489b9faaa1e931b6b4dbfa8013d67acf778 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Thu, 5 Jun 2025 16:09:16 +0800 Subject: [PATCH 06/16] feat: Delete useless cod(SearchConfig) --- src/Select.tsx | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/Select.tsx b/src/Select.tsx index 0dbc14e2c..d92f971a5 100644 --- a/src/Select.tsx +++ b/src/Select.tsx @@ -227,22 +227,7 @@ const Select = React.forwardRef = {}; - // legacySearchProps.forEach((propsName) => { - // legacyShowSearch[propsName] = props?.[propsName]; - // }); const [mergedShowSearch, searchConfig] = useSearchConfig(showSearch, props); - // const mergedShowSearch = typeof showSearch === 'object' ? showSearch : legacyShowSearch; const { filterOption, searchValue, From 656e611528302345e44a9434cfd758d1446583b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Thu, 12 Jun 2025 16:55:34 +0800 Subject: [PATCH 07/16] docs: add showSearch discreption --- README.md | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 91814eb77..44afc2e4a 100644 --- a/README.md +++ b/README.md @@ -85,11 +85,10 @@ export default () => ( | dropdownAlign | additional align applied to dropdown | [AlignType](https://github.com/react-component/trigger/blob/728d7e92394aa4b3214650f743fc47e1382dfa68/src/interface.ts#L25-L80) | {} | | dropdownMenuStyle | additional style applied to dropdown menu | Object | React.CSSProperties | | notFoundContent | specify content to show when no result matches. | ReactNode | 'Not Found' | -| tokenSeparators | separator used to tokenize on tag/multiple mode | string[]? | | | open | control select open | boolean | | | defaultOpen | control select default open | boolean | | | placeholder | select placeholder | React Node | | -| showSearch | whether show search input in single mode | boolean | true | +| showSearch | whether show search input in single mode | boolean \| Object | true | | allowClear | whether allowClear | boolean | { clearIcon?: ReactNode } | false | | tags | when tagging is enabled the user can select from pre-existing options or create a new tag by picking the first choice, which is what the user has typed into the search box so far. | boolean | false | | tagRender | render custom tags. | (props: CustomTagProps) => ReactNode | - | @@ -99,16 +98,11 @@ export default () => ( | combobox | enable combobox mode(can not set multiple at the same time) | boolean | false | | multiple | whether multiple select | boolean | false | | disabled | whether disabled select | boolean | false | -| filterOption | whether filter options by input value. default filter by option's optionFilterProp prop's value | boolean | true/Function(inputValue:string, option:Option) | -| optionFilterProp | which prop value of option will be used for filter if filterOption is true | String | 'value' | -| filterSort | Sort function for search options sorting, see [Array.sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)'s compareFunction. | Function(optionA:Option, optionB: Option) | - | -| optionLabelProp | render option value or option children as content of select | String: 'value'/'children' | 'value' | | defaultValue | initial selected option(s) | String \| String[] | - | | value | current selected option(s) | String \| String[] \| {key:String, label:React.Node} \| {key:String, label:React.Node}[] | - | | labelInValue | whether to embed label in value, see above value type. Not support `combobox` mode | boolean | false | | backfill | whether backfill select option to search input (Only works in single and combobox mode) | boolean | false | | onChange | called when select an option or input value change(combobox) | function(value, option:Option \| Option[]) | - | -| onSearch | called when input changed | function | - | | onBlur | called when blur | function | - | | onFocus | called when focus | function | - | | onPopupScroll | called when menu is scrolled | function | - | @@ -120,7 +114,6 @@ export default () => ( | getInputElement | customize input element | function(): Element | - | | showAction | actions trigger the dropdown to show | String[]? | - | | autoFocus | focus select after mount | boolean | - | -| autoClearSearchValue | auto clear search input value when multiple select is selected/deselected | boolean | true | | prefix | specify the select prefix icon or text | ReactNode | - | | suffixIcon | specify the select arrow icon | ReactNode | - | | clearIcon | specify the clear icon | ReactNode | - | @@ -141,6 +134,19 @@ export default () => ( | focus | focus select programmably | - | - | | blur | blur select programmably | - | - | +### showSearch + +| name | description | type | default | +| --- | --- | --- | --- | +| autoClearSearchValue | auto clear search input value when multiple select is selected/deselected | boolean | true | +| filterOption | whether filter options by input value. default filter by option's optionFilterProp prop's value | boolean | true/Function(inputValue:string, option:Option) | +| filterSort | Sort function for search options sorting, see [Array.sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)'s compareFunction. | Function(optionA:Option, optionB: Option) | - | +| optionFilterProp | which prop value of option will be used for filter if filterOption is true | String | 'value' | +| optionLabelProp | render option value or option children as content of select | String: 'value'/'children' | 'value' | +| searchValue | The current input "search" text | string | - | +| tokenSeparators | separator used to tokenize on tag/multiple mode | string[]? | | +| onSearch | called when input changed | function | - | + ### Option props | name | description | type | default | From a76b1371731eab8501d8d271d911d00a23c3fc3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Fri, 13 Jun 2025 13:59:30 +0800 Subject: [PATCH 08/16] =?UTF-8?q?test:=20=20add=20test=20to=20=E2=80=9Ccom?= =?UTF-8?q?bine=20showSearch=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/Select.test.tsx | 157 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 156 insertions(+), 1 deletion(-) diff --git a/tests/Select.test.tsx b/tests/Select.test.tsx index 4f3bd2f2a..8cfc26bf7 100644 --- a/tests/Select.test.tsx +++ b/tests/Select.test.tsx @@ -1,4 +1,4 @@ -import type { LabelInValueType } from '@/Select'; +import type { DefaultOptionType, LabelInValueType, SearchConfig } from '@/Select'; import { createEvent, fireEvent, @@ -2521,4 +2521,159 @@ describe('Select.Basic', () => { expect(input).toHaveClass(customClassNames.input); expect(input).toHaveStyle(customStyle.input); }); + // searchValue?: string; + // autoClearSearchValue?: boolean; + // onSearch?: (value: string) => void; + // tokenSeparators?: string[]; + // filterOption?: boolean | FilterFunc; + // filterSort?: (optionA: OptionType, optionB: OptionType, info: { searchValue: string }) => number; + // optionFilterProp?: string; + // optionLabelProp?: string; + // const props = { + // searchValue: '1', + // autoClearSearchValue: false, + // filterSort: (optionA, optionB) => { + // return Number(optionB.value) - Number(optionA.value); + // }, + // optionFilterProp: 'label', + // optionLabelProp: 'value', + // }; + describe('combine showSearch', () => { + let errorSpy; + + beforeAll(() => { + errorSpy = jest.spyOn(console, 'error').mockImplementation(() => null); + }); + + beforeEach(() => { + errorSpy.mockReset(); + resetWarned(); + }); + + afterAll(() => { + errorSpy.mockRestore(); + }); + const currentSearchFn = jest.fn(); + const legacySearchFn = jest.fn(); + + const LegacyDemo = (props) => { + return ( + + ); + }; + it('onSearch', () => { + const { container } = render( + <> +
+ +
+
+ +
+ , + ); + const legacyInput = container.querySelector('#test1 input'); + const currentInput = container.querySelector('#test2 input'); + fireEvent.change(legacyInput, { target: { value: '2' } }); + fireEvent.change(currentInput, { target: { value: '2' } }); + expect(currentSearchFn).toHaveBeenCalledWith('2'); + expect(legacySearchFn).toHaveBeenCalledWith('2'); + }); + it('searchValue', () => { + const { container } = render( + <> +
+ +
+
+ +
+ , + ); + const legacyInput = container.querySelector('#test1 input'); + const currentInput = container.querySelector('#test2 input'); + expect(legacyInput).toHaveValue('1'); + expect(currentInput).toHaveValue('1'); + expect(legacyInput.value).toBe(currentInput.value); + }); + it('option:sort,FilterProp,LabelProp ', () => { + const { container } = render( + <> +
+ { + return Number(b.label) - Number(a.label); + }} + optionFilterProp="label" + optionLabelProp="value" + /> +
+
+ { + return Number(b.label) - Number(a.label); + }} + optionFilterProp="label" + optionLabelProp="value" + /> +
+ , + ); + const items = container.querySelectorAll('.rc-select-item-option'); + expect(items.length).toBe(4); + expect(items[0].title).toBe('12'); + expect(items[2].title).toBe('12'); + }); + it('autoClearSearchValue,tokenSeparators', () => { + const { container } = render( + <> +
+ +
+
+ +
+ , + ); + const legacyInput = container.querySelector('#test1 input'); + const currentInput = container.querySelector('#test2 input'); + fireEvent.change(legacyInput, { target: { value: 'a' } }); + fireEvent.change(currentInput, { target: { value: 'a' } }); + expect(legacyInput).toHaveValue('a'); + expect(currentInput).toHaveValue('a'); + const items = container.querySelectorAll('.rc-select-item-option'); + fireEvent.click(items[0]); + fireEvent.click(items[1]); + expect(legacyInput).toHaveValue('a'); + expect(currentInput).toHaveValue('a'); + fireEvent.change(legacyInput, { target: { value: '1,' } }); + fireEvent.change(currentInput, { target: { value: '1,' } }); + expect(legacyInput).toHaveValue(''); + expect(currentInput).toHaveValue(''); + }); + }); }); From ec8ee9a3e6fa6241545356fdd10590970dd3c753 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Fri, 13 Jun 2025 14:13:55 +0800 Subject: [PATCH 09/16] test: fix lint error --- tests/Select.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Select.test.tsx b/tests/Select.test.tsx index 8cfc26bf7..917e63eb6 100644 --- a/tests/Select.test.tsx +++ b/tests/Select.test.tsx @@ -1,4 +1,4 @@ -import type { DefaultOptionType, LabelInValueType, SearchConfig } from '@/Select'; +import type { LabelInValueType } from '@/Select'; import { createEvent, fireEvent, From 07c4c5ac2d72cc8b0cc2c200d54d37ddfdc8af8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Fri, 13 Jun 2025 14:23:22 +0800 Subject: [PATCH 10/16] feat: change useSearchConfig --- src/hooks/useSearchConfig.ts | 38 ++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/src/hooks/useSearchConfig.ts b/src/hooks/useSearchConfig.ts index ec36b3a0a..622d6d68a 100644 --- a/src/hooks/useSearchConfig.ts +++ b/src/hooks/useSearchConfig.ts @@ -11,11 +11,25 @@ const legacySearchProps = [ 'tokenSeparators', ]; // Convert `showSearch` to unique config -export default function useSearchConfig(showSearch, props) { - return React.useMemo<[boolean, SearchConfig]>(() => { +export default function useSearchConfig( + showSearch: boolean | SearchConfig | undefined, + props: any, +) { + const { + filterOption, + searchValue, + optionFilterProp, + optionLabelProp, + filterSort, + onSearch, + autoClearSearchValue, + tokenSeparators, + } = props || {}; + return React.useMemo<[boolean | undefined, SearchConfig]>(() => { const legacyShowSearch: SearchConfig = {}; - legacySearchProps.forEach((propsName) => { - legacyShowSearch[propsName] = props?.[propsName]; + legacySearchProps.forEach((name) => { + const val = props?.[name]; + if (val !== undefined) legacyShowSearch[name] = val; }); const searchConfig: SearchConfig = typeof showSearch === 'object' ? showSearch : legacyShowSearch; @@ -28,13 +42,13 @@ export default function useSearchConfig(showSearch, props) { return [true, searchConfig]; }, [ showSearch, - props?.filterOption, - props?.searchValue, - props?.optionFilterProp, - props?.optionLabelProp, - props?.filterSort, - props?.onSearch, - props?.autoClearSearchValue, - props?.tokenSeparators, + filterOption, + searchValue, + optionFilterProp, + optionLabelProp, + filterSort, + onSearch, + autoClearSearchValue, + tokenSeparators, ]); } From ba17f537b54fa3a551e2d94c9ad0d686abe83edf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Fri, 13 Jun 2025 16:19:20 +0800 Subject: [PATCH 11/16] fix: Remove optionLabelProp from showSearch --- README.md | 2 +- src/Select.tsx | 4 +--- src/hooks/useSearchConfig.ts | 3 --- tests/Select.test.tsx | 22 ++-------------------- 4 files changed, 4 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 44afc2e4a..33e7fcd17 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,7 @@ export default () => ( | combobox | enable combobox mode(can not set multiple at the same time) | boolean | false | | multiple | whether multiple select | boolean | false | | disabled | whether disabled select | boolean | false | +| optionLabelProp | render option value or option children as content of select | String: 'value'/'children' | 'value' | | defaultValue | initial selected option(s) | String \| String[] | - | | value | current selected option(s) | String \| String[] \| {key:String, label:React.Node} \| {key:String, label:React.Node}[] | - | | labelInValue | whether to embed label in value, see above value type. Not support `combobox` mode | boolean | false | @@ -142,7 +143,6 @@ export default () => ( | filterOption | whether filter options by input value. default filter by option's optionFilterProp prop's value | boolean | true/Function(inputValue:string, option:Option) | | filterSort | Sort function for search options sorting, see [Array.sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)'s compareFunction. | Function(optionA:Option, optionB: Option) | - | | optionFilterProp | which prop value of option will be used for filter if filterOption is true | String | 'value' | -| optionLabelProp | render option value or option children as content of select | String: 'value'/'children' | 'value' | | searchValue | The current input "search" text | string | - | | tokenSeparators | separator used to tokenize on tag/multiple mode | string[]? | | | onSearch | called when input changed | function | - | diff --git a/src/Select.tsx b/src/Select.tsx index d92f971a5..9ca033fe9 100644 --- a/src/Select.tsx +++ b/src/Select.tsx @@ -119,7 +119,6 @@ export interface SearchConfig { filterOption?: boolean | FilterFunc; filterSort?: (optionA: OptionType, optionB: OptionType, info: { searchValue: string }) => number; optionFilterProp?: string; - optionLabelProp?: string; } export interface SelectProps extends Omit { @@ -157,7 +156,6 @@ export interface SelectProps['filterSort']; /** @deprecated pleace use SearchConfig.optionFilterProp */ optionFilterProp?: string; - /** @deprecated pleace use SearchConfig.optionLabelProp */ optionLabelProp?: string; children?: React.ReactNode; @@ -205,6 +203,7 @@ const Select = React.forwardRef { expect(input).toHaveClass(customClassNames.input); expect(input).toHaveStyle(customStyle.input); }); - // searchValue?: string; - // autoClearSearchValue?: boolean; - // onSearch?: (value: string) => void; - // tokenSeparators?: string[]; - // filterOption?: boolean | FilterFunc; - // filterSort?: (optionA: OptionType, optionB: OptionType, info: { searchValue: string }) => number; - // optionFilterProp?: string; - // optionLabelProp?: string; - // const props = { - // searchValue: '1', - // autoClearSearchValue: false, - // filterSort: (optionA, optionB) => { - // return Number(optionB.value) - Number(optionA.value); - // }, - // optionFilterProp: 'label', - // optionLabelProp: 'value', - // }; + describe('combine showSearch', () => { let errorSpy; @@ -2617,7 +2601,7 @@ describe('Select.Basic', () => { expect(currentInput).toHaveValue('1'); expect(legacyInput.value).toBe(currentInput.value); }); - it('option:sort,FilterProp,LabelProp ', () => { + it('option:sort,FilterProp ', () => { const { container } = render( <>
@@ -2628,7 +2612,6 @@ describe('Select.Basic', () => { return Number(b.label) - Number(a.label); }} optionFilterProp="label" - optionLabelProp="value" />
@@ -2638,7 +2621,6 @@ describe('Select.Basic', () => { return Number(b.label) - Number(a.label); }} optionFilterProp="label" - optionLabelProp="value" />
, From cc00ba658a22bb098413bb4a18ab875732759306 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Fri, 13 Jun 2025 17:57:19 +0800 Subject: [PATCH 12/16] fix: Remove tokenSeparators from showSearch --- README.md | 4 ++-- src/Select.tsx | 4 ---- src/hooks/useSearchConfig.ts | 3 --- tests/Select.test.tsx | 10 +++------- 4 files changed, 5 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 33e7fcd17..51ef9512e 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,7 @@ export default () => ( | dropdownAlign | additional align applied to dropdown | [AlignType](https://github.com/react-component/trigger/blob/728d7e92394aa4b3214650f743fc47e1382dfa68/src/interface.ts#L25-L80) | {} | | dropdownMenuStyle | additional style applied to dropdown menu | Object | React.CSSProperties | | notFoundContent | specify content to show when no result matches. | ReactNode | 'Not Found' | +| tokenSeparators | separator used to tokenize on tag/multiple mode | string[]? | | | open | control select open | boolean | | | defaultOpen | control select default open | boolean | | | placeholder | select placeholder | React Node | | @@ -140,11 +141,10 @@ export default () => ( | name | description | type | default | | --- | --- | --- | --- | | autoClearSearchValue | auto clear search input value when multiple select is selected/deselected | boolean | true | -| filterOption | whether filter options by input value. default filter by option's optionFilterProp prop's value | boolean | true/Function(inputValue:string, option:Option) | +| filterOption | whether filter options by input value. default filter by option's optionFilterProp prop's value | boolean\| (inputValue: string, option: Option) => boolean | true | | filterSort | Sort function for search options sorting, see [Array.sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)'s compareFunction. | Function(optionA:Option, optionB: Option) | - | | optionFilterProp | which prop value of option will be used for filter if filterOption is true | String | 'value' | | searchValue | The current input "search" text | string | - | -| tokenSeparators | separator used to tokenize on tag/multiple mode | string[]? | | | onSearch | called when input changed | function | - | ### Option props diff --git a/src/Select.tsx b/src/Select.tsx index 9ca033fe9..228acbf40 100644 --- a/src/Select.tsx +++ b/src/Select.tsx @@ -115,7 +115,6 @@ export interface SearchConfig { searchValue?: string; autoClearSearchValue?: boolean; onSearch?: (value: string) => void; - tokenSeparators?: string[]; filterOption?: boolean | FilterFunc; filterSort?: (optionA: OptionType, optionB: OptionType, info: { searchValue: string }) => number; optionFilterProp?: string; @@ -142,7 +141,6 @@ export interface SelectProps['onSearch']; - /** @deprecated pleace use SearchConfig.tokenSeparators */ tokenSeparators?: string[]; // >>> Options /** @@ -234,7 +232,6 @@ const Select = React.forwardRef>> OptionList OptionList={OptionList} diff --git a/src/hooks/useSearchConfig.ts b/src/hooks/useSearchConfig.ts index 62a2de320..1fe8579c8 100644 --- a/src/hooks/useSearchConfig.ts +++ b/src/hooks/useSearchConfig.ts @@ -7,7 +7,6 @@ const legacySearchProps = [ 'filterSort', 'onSearch', 'autoClearSearchValue', - 'tokenSeparators', ]; // Convert `showSearch` to unique config export default function useSearchConfig( @@ -21,7 +20,6 @@ export default function useSearchConfig( filterSort, onSearch, autoClearSearchValue, - tokenSeparators, } = props || {}; return React.useMemo<[boolean | undefined, SearchConfig]>(() => { const legacyShowSearch: SearchConfig = {}; @@ -46,6 +44,5 @@ export default function useSearchConfig( filterSort, onSearch, autoClearSearchValue, - tokenSeparators, ]); } diff --git a/tests/Select.test.tsx b/tests/Select.test.tsx index de7ec022d..3c90ef594 100644 --- a/tests/Select.test.tsx +++ b/tests/Select.test.tsx @@ -2630,14 +2630,14 @@ describe('Select.Basic', () => { expect(items[0].title).toBe('12'); expect(items[2].title).toBe('12'); }); - it('autoClearSearchValue,tokenSeparators', () => { + it('autoClearSearchValue', () => { const { container } = render( <>
- +
- +
, ); @@ -2652,10 +2652,6 @@ describe('Select.Basic', () => { fireEvent.click(items[1]); expect(legacyInput).toHaveValue('a'); expect(currentInput).toHaveValue('a'); - fireEvent.change(legacyInput, { target: { value: '1,' } }); - fireEvent.change(currentInput, { target: { value: '1,' } }); - expect(legacyInput).toHaveValue(''); - expect(currentInput).toHaveValue(''); }); }); }); From 81f5c8b9f50a26832f7247cb547b9ac6c678fab2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Fri, 13 Jun 2025 18:09:09 +0800 Subject: [PATCH 13/16] fix: change ts --- src/Select.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Select.tsx b/src/Select.tsx index 228acbf40..2a08e1101 100644 --- a/src/Select.tsx +++ b/src/Select.tsx @@ -128,6 +128,8 @@ export interface SelectProps>> Field Names fieldNames?: FieldNames; + /** @deprecated pleace use SearchConfig.onSearch */ + onSearch?: SearchConfig['onSearch']; showSearch?: boolean | SearchConfig; /** @deprecated pleace use SearchConfig.searchValue */ searchValue?: SearchConfig['searchValue']; @@ -139,9 +141,6 @@ export interface SelectProps, OptionType>; onActive?: (value: ValueType) => void; - /** @deprecated pleace use SearchConfig.onSearch */ - onSearch?: SearchConfig['onSearch']; - tokenSeparators?: string[]; // >>> Options /** * In Select, `false` means do nothing. From 8331c6223b4c70fa80588bceeb838f5102b887b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Mon, 16 Jun 2025 15:43:34 +0800 Subject: [PATCH 14/16] fix: SearchConfig.***-> showSearch.*** --- src/Select.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Select.tsx b/src/Select.tsx index 2a08e1101..aacfbffc9 100644 --- a/src/Select.tsx +++ b/src/Select.tsx @@ -128,12 +128,12 @@ export interface SelectProps>> Field Names fieldNames?: FieldNames; - /** @deprecated pleace use SearchConfig.onSearch */ + /** @deprecated pleace use showSearch.onSearch */ onSearch?: SearchConfig['onSearch']; showSearch?: boolean | SearchConfig; - /** @deprecated pleace use SearchConfig.searchValue */ + /** @deprecated pleace use showSearch.searchValue */ searchValue?: SearchConfig['searchValue']; - /** @deprecated pleace use SearchConfig.autoClearSearchValue */ + /** @deprecated pleace use showSearch.autoClearSearchValue */ autoClearSearchValue?: boolean; // >>> Select @@ -147,11 +147,11 @@ export interface SelectProps['filterOption']; - /** @deprecated pleace use SearchConfig.filterSort */ + /** @deprecated pleace use showSearch.filterSort */ filterSort?: SearchConfig['filterSort']; - /** @deprecated pleace use SearchConfig.optionFilterProp */ + /** @deprecated pleace use showSearch.optionFilterProp */ optionFilterProp?: string; optionLabelProp?: string; From 5b683b82a713663b745fa3bab3b70ed8a5055cb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Mon, 16 Jun 2025 16:11:28 +0800 Subject: [PATCH 15/16] feat: change useSearchConfig --- src/hooks/useSearchConfig.ts | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/hooks/useSearchConfig.ts b/src/hooks/useSearchConfig.ts index 1fe8579c8..f5d8fa859 100644 --- a/src/hooks/useSearchConfig.ts +++ b/src/hooks/useSearchConfig.ts @@ -1,4 +1,4 @@ -import type { SearchConfig, DefaultOptionType } from '@/Select'; +import type { SearchConfig, DefaultOptionType, SelectProps } from '@/Select'; import * as React from 'react'; const legacySearchProps = [ 'filterOption', @@ -11,7 +11,7 @@ const legacySearchProps = [ // Convert `showSearch` to unique config export default function useSearchConfig( showSearch: boolean | SearchConfig | undefined, - props: any, + props: SelectProps, ) { const { filterOption, @@ -22,19 +22,25 @@ export default function useSearchConfig( autoClearSearchValue, } = props || {}; return React.useMemo<[boolean | undefined, SearchConfig]>(() => { - const legacyShowSearch: SearchConfig = {}; - legacySearchProps.forEach((name) => { - const val = props?.[name]; - if (val !== undefined) legacyShowSearch[name] = val; - }); - const searchConfig: SearchConfig = - typeof showSearch === 'object' ? showSearch : legacyShowSearch; - if (showSearch === undefined) { - return [undefined, searchConfig]; + const legacyShowSearch: SearchConfig = { + filterOption, + searchValue, + optionFilterProp, + filterSort, + onSearch, + autoClearSearchValue, + }; + + if (showSearch === undefined || showSearch === true) { + return [showSearch as undefined | boolean, legacyShowSearch]; } if (!showSearch) { return [false, {}]; } + const searchConfig = { + ...legacyShowSearch, + ...showSearch, + }; return [true, searchConfig]; }, [ showSearch, From 2222c64a1a04cc1c969db22f53b4609969d54405 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Mon, 16 Jun 2025 16:16:21 +0800 Subject: [PATCH 16/16] feat: fix ci --- src/hooks/useSearchConfig.ts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/hooks/useSearchConfig.ts b/src/hooks/useSearchConfig.ts index f5d8fa859..f989b0d83 100644 --- a/src/hooks/useSearchConfig.ts +++ b/src/hooks/useSearchConfig.ts @@ -1,13 +1,6 @@ import type { SearchConfig, DefaultOptionType, SelectProps } from '@/Select'; import * as React from 'react'; -const legacySearchProps = [ - 'filterOption', - 'searchValue', - 'optionFilterProp', - 'filterSort', - 'onSearch', - 'autoClearSearchValue', -]; + // Convert `showSearch` to unique config export default function useSearchConfig( showSearch: boolean | SearchConfig | undefined,