Skip to content

Commit 2324995

Browse files
thinkasanyzombieJ
andauthored
feat: support semantic for baseSelect (#1133)
* feat: support semantic for baseSelect * fix * lint fix * add test * fix * rm * refactor: Remove SelectContext call from BaseSelect * chore: fix lint --------- Co-authored-by: 二货机器人 <[email protected]>
1 parent 327196e commit 2324995

File tree

5 files changed

+84
-30
lines changed

5 files changed

+84
-30
lines changed

src/BaseSelect/index.tsx

+23-20
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { AlignType, BuildInPlacements } from '@rc-component/trigger/lib/interface';
2-
import classNames from 'classnames';
2+
import cls from 'classnames';
33
import useLayoutEffect from '@rc-component/util/lib/hooks/useLayoutEffect';
44
import useMergedState from '@rc-component/util/lib/hooks/useMergedState';
55
import isMobile from '@rc-component/util/lib/isMobile';
@@ -27,8 +27,6 @@ import type { RefTriggerProps } from '../SelectTrigger';
2727
import SelectTrigger from '../SelectTrigger';
2828
import TransBtn from '../TransBtn';
2929
import { getSeparatedContent, isValidCount } from '../utils/valueUtil';
30-
import SelectContext from '../SelectContext';
31-
import type { SelectContextProps } from '../SelectContext';
3230
import Polite from './Polite';
3331
export type BaseSelectSemanticName = 'prefix' | 'suffix' | 'input';
3432

@@ -130,22 +128,27 @@ export interface BaseSelectPrivateProps {
130128
export type BaseSelectPropsWithoutPrivate = Omit<BaseSelectProps, keyof BaseSelectPrivateProps>;
131129

132130
export interface BaseSelectProps extends BaseSelectPrivateProps, React.AriaAttributes {
131+
// Style
133132
className?: string;
134133
style?: React.CSSProperties;
135134
classNames?: Partial<Record<BaseSelectSemanticName, string>>;
136135
styles?: Partial<Record<BaseSelectSemanticName, React.CSSProperties>>;
137-
title?: string;
136+
137+
// Selector
138138
showSearch?: boolean;
139139
tagRender?: (props: CustomTagProps) => React.ReactElement;
140140
direction?: 'ltr' | 'rtl';
141-
maxLength?: number;
142-
showScrollBar?: boolean | 'optional';
141+
autoFocus?: boolean;
142+
placeholder?: React.ReactNode;
143+
maxCount?: number;
144+
143145
// MISC
146+
title?: string;
144147
tabIndex?: number;
145-
autoFocus?: boolean;
146148
notFoundContent?: React.ReactNode;
147-
placeholder?: React.ReactNode;
148149
onClear?: () => void;
150+
maxLength?: number;
151+
showScrollBar?: boolean | 'optional';
149152

150153
choiceTransitionName?: string;
151154

@@ -224,6 +227,8 @@ const BaseSelect = React.forwardRef<BaseSelectRef, BaseSelectProps>((props, ref)
224227
id,
225228
prefixCls,
226229
className,
230+
styles,
231+
classNames,
227232
showSearch,
228233
tagRender,
229234
showScrollBar = 'optional',
@@ -236,6 +241,7 @@ const BaseSelect = React.forwardRef<BaseSelectRef, BaseSelectProps>((props, ref)
236241
emptyOptions,
237242
notFoundContent = 'Not Found',
238243
onClear,
244+
maxCount,
239245

240246
// Mode
241247
mode,
@@ -408,15 +414,8 @@ const BaseSelect = React.forwardRef<BaseSelectRef, BaseSelectProps>((props, ref)
408414
[tokenSeparators],
409415
);
410416

411-
const {
412-
maxCount,
413-
rawValues,
414-
classNames: selectClassNames,
415-
styles,
416-
} = React.useContext<SelectContextProps>(SelectContext) || {};
417-
418417
const onInternalSearch = (searchText: string, fromTyping: boolean, isCompositing: boolean) => {
419-
if (multiple && isValidCount(maxCount) && rawValues?.size >= maxCount) {
418+
if (multiple && isValidCount(maxCount) && displayValues.length >= maxCount) {
420419
return;
421420
}
422421
let ret = true;
@@ -426,7 +425,7 @@ const BaseSelect = React.forwardRef<BaseSelectRef, BaseSelectProps>((props, ref)
426425
const separatedList = getSeparatedContent(
427426
searchText,
428427
tokenSeparators,
429-
isValidCount(maxCount) ? maxCount - rawValues.size : undefined,
428+
isValidCount(maxCount) ? maxCount - displayValues.length : undefined,
430429
);
431430

432431
// Check if match the `tokenSeparators`
@@ -703,6 +702,8 @@ const BaseSelect = React.forwardRef<BaseSelectRef, BaseSelectProps>((props, ref)
703702
multiple,
704703
toggleOpen: onToggleOpen,
705704
showScrollBar,
705+
styles,
706+
classNames,
706707
}),
707708
[
708709
props,
@@ -714,6 +715,8 @@ const BaseSelect = React.forwardRef<BaseSelectRef, BaseSelectProps>((props, ref)
714715
multiple,
715716
onToggleOpen,
716717
showScrollBar,
718+
styles,
719+
classNames,
717720
],
718721
);
719722

@@ -728,7 +731,7 @@ const BaseSelect = React.forwardRef<BaseSelectRef, BaseSelectProps>((props, ref)
728731
if (showSuffixIcon) {
729732
arrowNode = (
730733
<TransBtn
731-
className={classNames(`${prefixCls}-arrow`, selectClassNames?.suffix, {
734+
className={cls(`${prefixCls}-arrow`, classNames?.suffix, {
732735
[`${prefixCls}-arrow-loading`]: loading,
733736
})}
734737
style={styles?.suffix}
@@ -772,7 +775,7 @@ const BaseSelect = React.forwardRef<BaseSelectRef, BaseSelectProps>((props, ref)
772775
const optionList = <OptionList ref={listRef} />;
773776

774777
// ============================= Select =============================
775-
const mergedClassName = classNames(prefixCls, className, {
778+
const mergedClassName = cls(prefixCls, className, {
776779
[`${prefixCls}-focused`]: mockFocused,
777780
[`${prefixCls}-multiple`]: multiple,
778781
[`${prefixCls}-single`]: !multiple,
@@ -821,7 +824,7 @@ const BaseSelect = React.forwardRef<BaseSelectRef, BaseSelectProps>((props, ref)
821824
) : (
822825
<Selector
823826
{...props}
824-
prefixClassName={selectClassNames?.prefix}
827+
prefixClassName={classNames?.prefix}
825828
prefixStyle={styles?.prefix}
826829
domRef={selectorDomRef}
827830
prefixCls={prefixCls}

src/Select.tsx

+8-4
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ const Select = React.forwardRef<BaseSelectRef, SelectProps<any, DefaultOptionTyp
208208
labelInValue,
209209
onChange,
210210
maxCount,
211-
classNames: selectClassNames,
211+
classNames,
212212
styles,
213213
...restProps
214214
} = props;
@@ -631,12 +631,10 @@ const Select = React.forwardRef<BaseSelectRef, SelectProps<any, DefaultOptionTyp
631631
childrenAsData,
632632
maxCount,
633633
optionRender,
634-
classNames: selectClassNames,
634+
classNames,
635635
styles,
636636
};
637637
}, [
638-
selectClassNames,
639-
styles,
640638
maxCount,
641639
parsedOptions,
642640
displayOptions,
@@ -653,6 +651,8 @@ const Select = React.forwardRef<BaseSelectRef, SelectProps<any, DefaultOptionTyp
653651
listItemHeight,
654652
childrenAsData,
655653
optionRender,
654+
classNames,
655+
styles,
656656
]);
657657

658658
// ========================== Warning ===========================
@@ -674,9 +674,13 @@ const Select = React.forwardRef<BaseSelectRef, SelectProps<any, DefaultOptionTyp
674674
ref={ref}
675675
omitDomProps={OMIT_DOM_PROPS}
676676
mode={mode}
677+
// >>> Style
678+
classNames={classNames}
679+
styles={styles}
677680
// >>> Values
678681
displayValues={displayValues}
679682
onDisplayValuesChange={onDisplayValuesChange}
683+
maxCount={maxCount}
680684
// >>> Trigger
681685
direction={direction}
682686
// >>> Search

src/SelectContext.ts

+3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ import type {
1111
import type { FlattenOptionData } from './interface';
1212

1313
// Use any here since we do not get the type during compilation
14+
/**
15+
* SelectContext is only used for Select. BaseSelect should not consume this context.
16+
*/
1417
export interface SelectContextProps {
1518
classNames?: Partial<Record<SemanticName, string>>;
1619
styles?: Partial<Record<SemanticName, React.CSSProperties>>;

src/Selector/Input.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as React from 'react';
22
import classNames from 'classnames';
33
import { composeRef } from '@rc-component/util/lib/ref';
44
import { warning } from '@rc-component/util/lib/warning';
5-
import SelectContext from '../SelectContext';
5+
import useBaseProps from '../hooks/useBaseProps';
66
type InputRef = HTMLInputElement | HTMLTextAreaElement;
77

88
interface InputProps {
@@ -57,8 +57,8 @@ const Input: React.ForwardRefRenderFunction<InputRef, InputProps> = (props, ref)
5757
open,
5858
attrs,
5959
} = props;
60-
const { classNames: contextClassNames, styles: contextStyles } =
61-
React.useContext(SelectContext) || {};
60+
61+
const { classNames: contextClassNames, styles: contextStyles } = useBaseProps() || {};
6262

6363
let inputNode: React.ComponentElement<any, any> = inputElement || <input />;
6464

tests/Select.test.tsx

+47-3
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ import type { ScrollConfig } from 'rc-virtual-list/lib/List';
1313
import React from 'react';
1414
import type { SelectProps } from '../src';
1515
import Select, { OptGroup, Option, useBaseProps } from '../src';
16-
import type { BaseSelectRef } from '../src/BaseSelect';
16+
import BaseSelect from '../src/BaseSelect';
17+
import type { BaseSelectRef, RefOptionListProps } from '../src/BaseSelect';
1718
import allowClearTest from './shared/allowClearTest';
1819
import blurTest from './shared/blurTest';
1920
import focusTest from './shared/focusTest';
@@ -2417,9 +2418,10 @@ describe('Select.Basic', () => {
24172418
expect(onBlur).toHaveBeenCalledTimes(2);
24182419
expect(inputElem.value).toEqual('bb');
24192420
});
2420-
it('support classnames and styles', () => {
2421+
2422+
it('support classnames and styles for select', () => {
24212423
const customClassNames = {
2422-
prefix: 'cutsom-prefix',
2424+
prefix: 'custom-prefix',
24232425
suffix: 'custom-suffix',
24242426
list: 'custom-list',
24252427
listItem: 'custom-item',
@@ -2464,4 +2466,46 @@ describe('Select.Basic', () => {
24642466
expect(input).toHaveClass(customClassNames.input);
24652467
expect(input).toHaveStyle(customStyle.input);
24662468
});
2469+
2470+
it('support classnames and styles for baseSelect', () => {
2471+
const customClassNames = {
2472+
prefix: 'custom-prefix',
2473+
suffix: 'custom-suffix',
2474+
input: 'custom-input',
2475+
};
2476+
const customStyle = {
2477+
prefix: { color: 'red' },
2478+
suffix: { color: 'green' },
2479+
input: { color: 'black' },
2480+
};
2481+
const OptionList = React.forwardRef<RefOptionListProps, any>((props, ref) => {
2482+
return <div ref={ref as unknown as React.Ref<HTMLDivElement>}>Option List</div>;
2483+
});
2484+
const { container } = render(
2485+
<BaseSelect
2486+
displayValues={[]}
2487+
prefixCls="rc-select"
2488+
id="base-select"
2489+
open
2490+
classNames={customClassNames}
2491+
styles={customStyle}
2492+
suffixIcon={<div>arrow</div>}
2493+
prefix="Foobar"
2494+
onDisplayValuesChange={() => {}}
2495+
searchValue=""
2496+
onSearch={() => {}}
2497+
OptionList={OptionList}
2498+
emptyOptions={false}
2499+
/>,
2500+
);
2501+
const prefix = container.querySelector('.rc-select-prefix');
2502+
const suffix = container.querySelector('.rc-select-arrow');
2503+
const input = container.querySelector('.rc-select-selection-search-input');
2504+
expect(prefix).toHaveClass(customClassNames.prefix);
2505+
expect(prefix).toHaveStyle(customStyle.prefix);
2506+
expect(suffix).toHaveClass(customClassNames.suffix);
2507+
expect(suffix).toHaveStyle(customStyle.suffix);
2508+
expect(input).toHaveClass(customClassNames.input);
2509+
expect(input).toHaveStyle(customStyle.input);
2510+
});
24672511
});

0 commit comments

Comments
 (0)