From 96bce2b2cc990f4de8eb881a207e991ad309b825 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: Mon, 20 Oct 2025 17:33:46 +0800 Subject: [PATCH 1/3] feat: add semantic --- src/BaseSelect/index.tsx | 11 +- src/SelectInput/Content/MultipleContent.tsx | 25 ++- src/SelectInput/Content/Placeholder.tsx | 6 +- src/SelectInput/Content/SingleContent.tsx | 4 +- tests/semantic.test.tsx | 168 ++++++++++++++++++++ 5 files changed, 205 insertions(+), 9 deletions(-) create mode 100644 tests/semantic.test.tsx diff --git a/src/BaseSelect/index.tsx b/src/BaseSelect/index.tsx index f2935015..f093559a 100644 --- a/src/BaseSelect/index.tsx +++ b/src/BaseSelect/index.tsx @@ -28,7 +28,16 @@ import SelectInput from '../SelectInput'; import type { ComponentsConfig } from '../hooks/useComponents'; import useComponents from '../hooks/useComponents'; -export type BaseSelectSemanticName = 'prefix' | 'suffix' | 'input' | 'clear'; +export type BaseSelectSemanticName = + | 'prefix' + | 'suffix' + | 'input' + | 'clear' + | 'placeholder' + | 'content' + | 'item' + | 'itemContent' + | 'itemRemove'; /** * ZombieJ: diff --git a/src/SelectInput/Content/MultipleContent.tsx b/src/SelectInput/Content/MultipleContent.tsx index 55497cc3..d49b5acf 100644 --- a/src/SelectInput/Content/MultipleContent.tsx +++ b/src/SelectInput/Content/MultipleContent.tsx @@ -42,6 +42,8 @@ export default React.forwardRef(function M maxTagPlaceholder: maxTagPlaceholderFromContext, maxTagTextLength, maxTagCount, + classNames, + styles, } = useBaseProps(); const selectionItemPrefixCls = `${prefixCls}-selection-item`; @@ -85,14 +87,25 @@ export default React.forwardRef(function M ) => ( - {content} + + {content} + {closable && ( (function M return ( : null} data={displayValues} renderItem={renderItem} diff --git a/src/SelectInput/Content/Placeholder.tsx b/src/SelectInput/Content/Placeholder.tsx index 6a1befd8..fec17d9e 100644 --- a/src/SelectInput/Content/Placeholder.tsx +++ b/src/SelectInput/Content/Placeholder.tsx @@ -1,5 +1,7 @@ import * as React from 'react'; +import { clsx } from 'clsx'; import { useSelectInputContext } from '../context'; +import useBaseProps from '../../hooks/useBaseProps'; export interface PlaceholderProps { show?: boolean; @@ -7,6 +9,7 @@ export interface PlaceholderProps { export default function Placeholder(props: PlaceholderProps) { const { prefixCls, placeholder, displayValues } = useSelectInputContext(); + const { classNames, styles } = useBaseProps(); const { show = true } = props; if (displayValues.length) { @@ -15,9 +18,10 @@ export default function Placeholder(props: PlaceholderProps) { return (
{placeholder} diff --git a/src/SelectInput/Content/SingleContent.tsx b/src/SelectInput/Content/SingleContent.tsx index 7ac0173b..47978e26 100644 --- a/src/SelectInput/Content/SingleContent.tsx +++ b/src/SelectInput/Content/SingleContent.tsx @@ -12,7 +12,7 @@ const SingleContent = React.forwardRef( ({ inputProps }, ref) => { const { prefixCls, searchValue, activeValue, displayValues, maxLength, mode } = useSelectInputContext(); - const { triggerOpen, title: rootTitle, showSearch } = useBaseProps(); + const { triggerOpen, title: rootTitle, showSearch, classNames, styles } = useBaseProps(); const selectContext = React.useContext(SelectContext); const [inputChanged, setInputChanged] = React.useState(false); @@ -71,7 +71,7 @@ const SingleContent = React.forwardRef( }, [combobox, activeValue]); return ( -
+
{displayValue ? (
{displayValue.label}
) : ( diff --git a/tests/semantic.test.tsx b/tests/semantic.test.tsx new file mode 100644 index 00000000..bf032fd6 --- /dev/null +++ b/tests/semantic.test.tsx @@ -0,0 +1,168 @@ +import React, { forwardRef } from 'react'; +import { render } from '@testing-library/react'; +import BaseSelect from '../src/BaseSelect'; +import type { OptionListProps, RefOptionListProps } from '@/OptionList'; + +const OptionList = forwardRef(() => ( +
Popup
+)); + +describe('BaseSelect Semantic Styles', () => { + const defaultProps = { + prefixCls: 'rc-select', + OptionList, + displayValues: [], + emptyOptions: true, + id: 'test', + onDisplayValuesChange: () => {}, + onSearch: () => {}, + searchValue: '', + }; + + it('should apply semantic classNames correctly', () => { + const classNames = { + prefix: 'custom-prefix', + suffix: 'custom-suffix', + input: 'custom-input', + clear: 'custom-clear', + placeholder: 'custom-placeholder', + content: 'custom-content', + item: 'custom-item', + itemContent: 'custom-item-content', + itemRemove: 'custom-item-remove', + }; + + const { container } = render( + Prefix} + suffix={Suffix} + />, + ); + + // Test prefix className + expect(container.querySelector('.rc-select-prefix')).toHaveClass('custom-prefix'); + + // Test suffix className + expect(container.querySelector('.rc-select-suffix')).toHaveClass('custom-suffix'); + + // Test input className + expect(container.querySelector('.rc-select-input')).toHaveClass('custom-input'); + + // Test content className + expect(container.querySelector('.rc-select-content')).toHaveClass('custom-content'); + + // Test placeholder className + expect(container.querySelector('.rc-select-placeholder')).toHaveClass('custom-placeholder'); + }); + + it('should apply semantic styles correctly', () => { + const styles = { + prefix: { color: 'red' }, + suffix: { color: 'blue' }, + input: { fontSize: '16px' }, + clear: { cursor: 'pointer' }, + placeholder: { opacity: 0.6 }, + content: { padding: '4px' }, + item: { margin: '2px' }, + itemContent: { fontWeight: 'bold' }, + itemRemove: { background: 'transparent' }, + }; + + const { container } = render( + Prefix} + suffix={Suffix} + />, + ); + + // Test prefix style + expect(container.querySelector('.rc-select-prefix')).toHaveStyle({ color: 'red' }); + + // Test suffix style + expect(container.querySelector('.rc-select-suffix')).toHaveStyle({ color: 'blue' }); + + // Test input style + expect(container.querySelector('.rc-select-input')).toHaveStyle({ fontSize: '16px' }); + + // Test content style + expect(container.querySelector('.rc-select-content')).toHaveStyle({ padding: '4px' }); + + // Test placeholder style + expect(container.querySelector('.rc-select-placeholder')).toHaveStyle({ opacity: 0.6 }); + }); + + it('should apply item semantic styles in multiple mode', () => { + const classNames = { + item: 'custom-item', + itemContent: 'custom-item-content', + itemRemove: 'custom-item-remove', + }; + + const styles = { + item: { margin: '2px' }, + itemContent: { fontWeight: 'bold' }, + itemRemove: { background: 'transparent' }, + }; + + const displayValues = [ + { key: '1', label: 'Option 1', value: '1' }, + { key: '2', label: 'Option 2', value: '2' }, + ]; + + const { container } = render( + , + ); + + // Test item className and style + const items = container.querySelectorAll('.rc-select-selection-item'); + expect(items[0]).toHaveClass('custom-item'); + expect(items[0]).toHaveStyle({ margin: '2px' }); + + // Test item content className and style + const itemContents = container.querySelectorAll('.rc-select-selection-item-content'); + expect(itemContents[0]).toHaveClass('custom-item-content'); + expect(itemContents[0]).toHaveStyle({ fontWeight: 'bold' }); + + // Test item remove className and style + const removeButtons = container.querySelectorAll('.rc-select-selection-item-remove'); + expect(removeButtons[0]).toHaveClass('custom-item-remove'); + expect(removeButtons[0]).toHaveStyle({ background: 'transparent' }); + }); + + it('should apply clear icon semantic styles when allowClear is enabled', () => { + const classNames = { + clear: 'custom-clear', + }; + + const styles = { + clear: { cursor: 'pointer' }, + }; + + const { container } = render( + , + ); + + // Test clear icon className and style + const clearIcon = container.querySelector('.rc-select-clear'); + expect(clearIcon).toHaveClass('custom-clear'); + expect(clearIcon).toHaveStyle({ cursor: 'pointer' }); + }); +}); From 34ce21d4afab60096984c02ea51a3511605a33b0 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: Mon, 20 Oct 2025 17:37:22 +0800 Subject: [PATCH 2/3] chore: AI suggestion --- tests/BaseSelect.test.tsx | 2 +- tests/semantic.test.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/BaseSelect.test.tsx b/tests/BaseSelect.test.tsx index 444d2872..d107743d 100644 --- a/tests/BaseSelect.test.tsx +++ b/tests/BaseSelect.test.tsx @@ -1,4 +1,4 @@ -import type { OptionListProps, RefOptionListProps } from '@/OptionList'; +import type { OptionListProps, RefOptionListProps } from '../src/OptionList'; import { fireEvent, render } from '@testing-library/react'; import { forwardRef, act } from 'react'; import BaseSelect from '../src/BaseSelect'; diff --git a/tests/semantic.test.tsx b/tests/semantic.test.tsx index bf032fd6..e4145cef 100644 --- a/tests/semantic.test.tsx +++ b/tests/semantic.test.tsx @@ -1,7 +1,7 @@ import React, { forwardRef } from 'react'; import { render } from '@testing-library/react'; import BaseSelect from '../src/BaseSelect'; -import type { OptionListProps, RefOptionListProps } from '@/OptionList'; +import type { OptionListProps, RefOptionListProps } from '../src/OptionList'; const OptionList = forwardRef(() => (
Popup
From bab393421dae7284efa6b0add8f3f2480a6b68bd 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: Mon, 20 Oct 2025 17:40:28 +0800 Subject: [PATCH 3/3] test: clean up --- tests/semantic.test.tsx | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/tests/semantic.test.tsx b/tests/semantic.test.tsx index e4145cef..f3a1b39e 100644 --- a/tests/semantic.test.tsx +++ b/tests/semantic.test.tsx @@ -1,16 +1,10 @@ -import React, { forwardRef } from 'react'; +import React from 'react'; import { render } from '@testing-library/react'; -import BaseSelect from '../src/BaseSelect'; -import type { OptionListProps, RefOptionListProps } from '../src/OptionList'; +import Select from '../src'; -const OptionList = forwardRef(() => ( -
Popup
-)); - -describe('BaseSelect Semantic Styles', () => { +describe('Select Semantic Styles', () => { const defaultProps = { prefixCls: 'rc-select', - OptionList, displayValues: [], emptyOptions: true, id: 'test', @@ -33,7 +27,7 @@ describe('BaseSelect Semantic Styles', () => { }; const { container } = render( - { }; const { container } = render( - { ]; const { container } = render( - , @@ -151,9 +145,9 @@ describe('BaseSelect Semantic Styles', () => { }; const { container } = render( -