From fbabc913e60554ce2e02247f0308aa5f6af75cbf Mon Sep 17 00:00:00 2001 From: thinkasany <480968828@qq.com> Date: Tue, 18 Mar 2025 11:03:47 +0800 Subject: [PATCH 1/8] feat: support semantic for baseSelect --- src/BaseSelect/index.tsx | 17 ++++++++++++----- src/Selector/Input.tsx | 6 +++++- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/BaseSelect/index.tsx b/src/BaseSelect/index.tsx index 4c04f713e..887471a34 100644 --- a/src/BaseSelect/index.tsx +++ b/src/BaseSelect/index.tsx @@ -283,6 +283,8 @@ const BaseSelect = React.forwardRef((props, ref) placement, builtinPlacements, getPopupContainer, + styles: baseSelectStyles, + classNames: baseSelectClassNames, // Focus showAction = [], @@ -412,9 +414,12 @@ const BaseSelect = React.forwardRef((props, ref) maxCount, rawValues, classNames: selectClassNames, - styles, + styles: selectStyles, } = React.useContext(SelectContext) || {}; + const contextClassNames = baseSelectClassNames ?? selectClassNames; + const contextStyles = baseSelectStyles ?? selectStyles; + const onInternalSearch = (searchText: string, fromTyping: boolean, isCompositing: boolean) => { if (multiple && isValidCount(maxCount) && rawValues?.size >= maxCount) { return; @@ -703,6 +708,8 @@ const BaseSelect = React.forwardRef((props, ref) multiple, toggleOpen: onToggleOpen, showScrollBar, + styles: contextStyles, + classNames: contextClassNames, }), [ props, @@ -728,10 +735,10 @@ const BaseSelect = React.forwardRef((props, ref) if (showSuffixIcon) { arrowNode = ( ((props, ref) ) : ( = (props, ref) open, attrs, } = props; - const { classNames: contextClassNames, styles: contextStyles } = + const { classNames: selectClassNames, styles: selectStyles } = React.useContext(SelectContext) || {}; + const { classNames: baseSelectClassNames, styles: baseSelectStyles } = useBaseProps() || {}; + const contextClassNames = selectClassNames ?? baseSelectClassNames; + const contextStyles = selectStyles ?? baseSelectStyles; let inputNode: React.ComponentElement = inputElement || ; From 1a5e262a96f06d3184a921dfd2bc6afdeded5161 Mon Sep 17 00:00:00 2001 From: thinkasany <480968828@qq.com> Date: Tue, 18 Mar 2025 11:35:41 +0800 Subject: [PATCH 2/8] fix --- src/Selector/Input.tsx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Selector/Input.tsx b/src/Selector/Input.tsx index 4eda54b20..ee991e2de 100644 --- a/src/Selector/Input.tsx +++ b/src/Selector/Input.tsx @@ -58,11 +58,8 @@ const Input: React.ForwardRefRenderFunction = (props, ref) open, attrs, } = props; - const { classNames: selectClassNames, styles: selectStyles } = - React.useContext(SelectContext) || {}; - const { classNames: baseSelectClassNames, styles: baseSelectStyles } = useBaseProps() || {}; - const contextClassNames = selectClassNames ?? baseSelectClassNames; - const contextStyles = selectStyles ?? baseSelectStyles; + + const { classNames: contextClassNames, styles: contextStyles } = useBaseProps() || {}; let inputNode: React.ComponentElement = inputElement || ; From cd8465ab9ddb54157b4ab2b0c0ce38df5e4ddf20 Mon Sep 17 00:00:00 2001 From: thinkasany <480968828@qq.com> Date: Tue, 18 Mar 2025 11:37:46 +0800 Subject: [PATCH 3/8] lint fix --- src/Selector/Input.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Selector/Input.tsx b/src/Selector/Input.tsx index ee991e2de..b5c4ed079 100644 --- a/src/Selector/Input.tsx +++ b/src/Selector/Input.tsx @@ -2,7 +2,6 @@ import * as React from 'react'; import classNames from 'classnames'; import { composeRef } from '@rc-component/util/lib/ref'; import { warning } from '@rc-component/util/lib/warning'; -import SelectContext from '../SelectContext'; import useBaseProps from '../hooks/useBaseProps'; type InputRef = HTMLInputElement | HTMLTextAreaElement; From 2b2a1a75bec6581e8dabf3115682aa4cee5b3216 Mon Sep 17 00:00:00 2001 From: thinkasany <480968828@qq.com> Date: Thu, 20 Mar 2025 11:04:50 +0800 Subject: [PATCH 4/8] add test --- tests/Select.test.tsx | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/tests/Select.test.tsx b/tests/Select.test.tsx index d1e0cf70c..d5e480e55 100644 --- a/tests/Select.test.tsx +++ b/tests/Select.test.tsx @@ -13,6 +13,7 @@ import type { ScrollConfig } from 'rc-virtual-list/lib/List'; import React from 'react'; import type { SelectProps } from '../src'; import Select, { OptGroup, Option, useBaseProps } from '../src'; +import BaseSelect from '../src/BaseSelect'; import type { BaseSelectRef } from '../src/BaseSelect'; import allowClearTest from './shared/allowClearTest'; import blurTest from './shared/blurTest'; @@ -2464,4 +2465,46 @@ describe('Select.Basic', () => { expect(input).toHaveClass(customClassNames.input); expect(input).toHaveStyle(customStyle.input); }); + it('support classnames and styles for baseSelect', () => { + const customClassNames = { + prefix: 'cutsom-prefix', + suffix: 'custom-suffix', + list: 'custom-list', + listItem: 'custom-item', + input: 'custom-input', + }; + const customStyle = { + prefix: { color: 'red' }, + suffix: { color: 'green' }, + list: { color: 'yellow' }, + listItem: { color: 'blue' }, + input: { color: 'black' }, + }; + const { container } = render( + arrow} + prefix="Foobar" + onDisplayValuesChange={() => {}} + searchValue="" + onSearch={() => {}} + OptionList={() =>
Option List
} + emptyOptions={false} + />, + ); + const prefix = container.querySelector('.rc-select-prefix'); + const suffix = container.querySelector('.rc-select-arrow'); + const input = container.querySelector('.rc-select-selection-search-input'); + expect(prefix).toHaveClass(customClassNames.prefix); + expect(prefix).toHaveStyle(customStyle.prefix); + expect(suffix).toHaveClass(customClassNames.suffix); + expect(suffix).toHaveStyle(customStyle.suffix); + expect(input).toHaveClass(customClassNames.input); + expect(input).toHaveStyle(customStyle.input); + }); }); From 1a6f835d96ada58668de4caba223fbd67efd80c0 Mon Sep 17 00:00:00 2001 From: thinkasany <480968828@qq.com> Date: Thu, 20 Mar 2025 11:09:06 +0800 Subject: [PATCH 5/8] fix --- tests/Select.test.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/Select.test.tsx b/tests/Select.test.tsx index d5e480e55..44b5295f6 100644 --- a/tests/Select.test.tsx +++ b/tests/Select.test.tsx @@ -14,7 +14,7 @@ import React from 'react'; import type { SelectProps } from '../src'; import Select, { OptGroup, Option, useBaseProps } from '../src'; import BaseSelect from '../src/BaseSelect'; -import type { BaseSelectRef } from '../src/BaseSelect'; +import type { BaseSelectRef, RefOptionListProps } from '../src/BaseSelect'; import allowClearTest from './shared/allowClearTest'; import blurTest from './shared/blurTest'; import focusTest from './shared/focusTest'; @@ -2480,6 +2480,9 @@ describe('Select.Basic', () => { listItem: { color: 'blue' }, input: { color: 'black' }, }; + const OptionList = React.forwardRef((props, ref) => { + return
}>Option List
; + }); const { container } = render( { onDisplayValuesChange={() => {}} searchValue="" onSearch={() => {}} - OptionList={() =>
Option List
} + OptionList={OptionList} emptyOptions={false} />, ); From 0bf2efcc9b2cb4fcc5bb49f683616e98c06c6be7 Mon Sep 17 00:00:00 2001 From: thinkasany <480968828@qq.com> Date: Thu, 20 Mar 2025 11:22:25 +0800 Subject: [PATCH 6/8] rm --- tests/Select.test.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/Select.test.tsx b/tests/Select.test.tsx index 44b5295f6..ee0d59c62 100644 --- a/tests/Select.test.tsx +++ b/tests/Select.test.tsx @@ -2469,15 +2469,11 @@ describe('Select.Basic', () => { const customClassNames = { prefix: 'cutsom-prefix', suffix: 'custom-suffix', - list: 'custom-list', - listItem: 'custom-item', input: 'custom-input', }; const customStyle = { prefix: { color: 'red' }, suffix: { color: 'green' }, - list: { color: 'yellow' }, - listItem: { color: 'blue' }, input: { color: 'black' }, }; const OptionList = React.forwardRef((props, ref) => { From c0bd1da1cdcab43abdfd9c8404f7514afe89bede Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Thu, 20 Mar 2025 14:52:30 +0800 Subject: [PATCH 7/8] refactor: Remove SelectContext call from BaseSelect --- src/BaseSelect/index.tsx | 52 +++++++++++++++++++--------------------- src/Select.tsx | 12 ++++++---- src/SelectContext.ts | 3 +++ tests/Select.test.tsx | 8 ++++--- 4 files changed, 41 insertions(+), 34 deletions(-) diff --git a/src/BaseSelect/index.tsx b/src/BaseSelect/index.tsx index 887471a34..d60cfca0b 100644 --- a/src/BaseSelect/index.tsx +++ b/src/BaseSelect/index.tsx @@ -1,5 +1,5 @@ import type { AlignType, BuildInPlacements } from '@rc-component/trigger/lib/interface'; -import classNames from 'classnames'; +import cls from 'classnames'; import useLayoutEffect from '@rc-component/util/lib/hooks/useLayoutEffect'; import useMergedState from '@rc-component/util/lib/hooks/useMergedState'; import isMobile from '@rc-component/util/lib/isMobile'; @@ -130,22 +130,27 @@ export interface BaseSelectPrivateProps { export type BaseSelectPropsWithoutPrivate = Omit; export interface BaseSelectProps extends BaseSelectPrivateProps, React.AriaAttributes { + // Style className?: string; style?: React.CSSProperties; classNames?: Partial>; styles?: Partial>; - title?: string; + + // Selector showSearch?: boolean; tagRender?: (props: CustomTagProps) => React.ReactElement; direction?: 'ltr' | 'rtl'; - maxLength?: number; - showScrollBar?: boolean | 'optional'; + autoFocus?: boolean; + placeholder?: React.ReactNode; + maxCount?: number; + // MISC + title?: string; tabIndex?: number; - autoFocus?: boolean; notFoundContent?: React.ReactNode; - placeholder?: React.ReactNode; onClear?: () => void; + maxLength?: number; + showScrollBar?: boolean | 'optional'; choiceTransitionName?: string; @@ -224,6 +229,8 @@ const BaseSelect = React.forwardRef((props, ref) id, prefixCls, className, + styles, + classNames, showSearch, tagRender, showScrollBar = 'optional', @@ -236,6 +243,7 @@ const BaseSelect = React.forwardRef((props, ref) emptyOptions, notFoundContent = 'Not Found', onClear, + maxCount, // Mode mode, @@ -283,8 +291,6 @@ const BaseSelect = React.forwardRef((props, ref) placement, builtinPlacements, getPopupContainer, - styles: baseSelectStyles, - classNames: baseSelectClassNames, // Focus showAction = [], @@ -410,18 +416,8 @@ const BaseSelect = React.forwardRef((props, ref) [tokenSeparators], ); - const { - maxCount, - rawValues, - classNames: selectClassNames, - styles: selectStyles, - } = React.useContext(SelectContext) || {}; - - const contextClassNames = baseSelectClassNames ?? selectClassNames; - const contextStyles = baseSelectStyles ?? selectStyles; - const onInternalSearch = (searchText: string, fromTyping: boolean, isCompositing: boolean) => { - if (multiple && isValidCount(maxCount) && rawValues?.size >= maxCount) { + if (multiple && isValidCount(maxCount) && displayValues.length >= maxCount) { return; } let ret = true; @@ -431,7 +427,7 @@ const BaseSelect = React.forwardRef((props, ref) const separatedList = getSeparatedContent( searchText, tokenSeparators, - isValidCount(maxCount) ? maxCount - rawValues.size : undefined, + isValidCount(maxCount) ? maxCount - displayValues.length : undefined, ); // Check if match the `tokenSeparators` @@ -708,8 +704,8 @@ const BaseSelect = React.forwardRef((props, ref) multiple, toggleOpen: onToggleOpen, showScrollBar, - styles: contextStyles, - classNames: contextClassNames, + styles, + classNames, }), [ props, @@ -721,6 +717,8 @@ const BaseSelect = React.forwardRef((props, ref) multiple, onToggleOpen, showScrollBar, + styles, + classNames, ], ); @@ -735,10 +733,10 @@ const BaseSelect = React.forwardRef((props, ref) if (showSuffixIcon) { arrowNode = ( ((props, ref) const optionList = ; // ============================= Select ============================= - const mergedClassName = classNames(prefixCls, className, { + const mergedClassName = cls(prefixCls, className, { [`${prefixCls}-focused`]: mockFocused, [`${prefixCls}-multiple`]: multiple, [`${prefixCls}-single`]: !multiple, @@ -828,8 +826,8 @@ const BaseSelect = React.forwardRef((props, ref) ) : ( >> Style + classNames={classNames} + styles={styles} // >>> Values displayValues={displayValues} onDisplayValuesChange={onDisplayValuesChange} + maxCount={maxCount} // >>> Trigger direction={direction} // >>> Search diff --git a/src/SelectContext.ts b/src/SelectContext.ts index 8d1e1b5b2..8918e4935 100644 --- a/src/SelectContext.ts +++ b/src/SelectContext.ts @@ -11,6 +11,9 @@ import type { import type { FlattenOptionData } from './interface'; // Use any here since we do not get the type during compilation +/** + * SelectContext is only used for Select. BaseSelect should not consume this context. + */ export interface SelectContextProps { classNames?: Partial>; styles?: Partial>; diff --git a/tests/Select.test.tsx b/tests/Select.test.tsx index ee0d59c62..b79b9a131 100644 --- a/tests/Select.test.tsx +++ b/tests/Select.test.tsx @@ -2418,9 +2418,10 @@ describe('Select.Basic', () => { expect(onBlur).toHaveBeenCalledTimes(2); expect(inputElem.value).toEqual('bb'); }); - it('support classnames and styles', () => { + + it('support classnames and styles for select', () => { const customClassNames = { - prefix: 'cutsom-prefix', + prefix: 'custom-prefix', suffix: 'custom-suffix', list: 'custom-list', listItem: 'custom-item', @@ -2465,9 +2466,10 @@ describe('Select.Basic', () => { expect(input).toHaveClass(customClassNames.input); expect(input).toHaveStyle(customStyle.input); }); + it('support classnames and styles for baseSelect', () => { const customClassNames = { - prefix: 'cutsom-prefix', + prefix: 'custom-prefix', suffix: 'custom-suffix', input: 'custom-input', }; From fb71cba346f2213e83dec359f6c2272a1e2073e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Thu, 20 Mar 2025 15:13:23 +0800 Subject: [PATCH 8/8] chore: fix lint --- src/BaseSelect/index.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/BaseSelect/index.tsx b/src/BaseSelect/index.tsx index d60cfca0b..d19ee824a 100644 --- a/src/BaseSelect/index.tsx +++ b/src/BaseSelect/index.tsx @@ -27,8 +27,6 @@ import type { RefTriggerProps } from '../SelectTrigger'; import SelectTrigger from '../SelectTrigger'; import TransBtn from '../TransBtn'; import { getSeparatedContent, isValidCount } from '../utils/valueUtil'; -import SelectContext from '../SelectContext'; -import type { SelectContextProps } from '../SelectContext'; import Polite from './Polite'; export type BaseSelectSemanticName = 'prefix' | 'suffix' | 'input';