Skip to content

fix: select option style, add demo and test#1211

Open
Rain120 wants to merge 1 commit intoreact-component:masterfrom
Rain120:fix/select-input_option
Open

fix: select option style, add demo and test#1211
Rain120 wants to merge 1 commit intoreact-component:masterfrom
Rain120:fix/select-input_option

Conversation

@Rain120
Copy link

@Rain120 Rain120 commented Feb 16, 2026

What I do

Fix bug

The bug from the commit

Initial

image

ant-design/ant-design#56932

Fixed

image

add demo and test

image

Summary by CodeRabbit

发布说明

  • 新功能

    • 新增Select组件的使用示例和文档说明。
  • 改进

    • 优化Select组件选中项的样式和属性处理,确保样式、类名和标题属性的一致应用。
  • 测试

    • 添加全面的测试套件,验证选中项的样式、类名和标题属性处理。

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @Rain120, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request improves the Select component by ensuring that styling, class names, and titles defined on individual options are correctly reflected in the selected item's display. The changes involve a minor refactor in the component's rendering logic to consistently wrap the selected value in a div element, allowing these properties to be applied. To validate and demonstrate this enhanced functionality, a new interactive demo and extensive unit tests have been added, covering different use cases and edge conditions.

Highlights

  • Bug Fix: Resolved an issue where Select component options' style and className properties were not correctly applied to the displayed selected item.
  • New Demo: Added a new demo (select-content-option.tsx) to showcase the application of style, className, and title props to selected Select options.
  • Enhanced Testing: Introduced a comprehensive suite of tests for the Select component to ensure proper application of option style, className, and title properties in various scenarios.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • docs/demo/select-content-option.md
    • Added a new demo page for select-content-option.
  • docs/examples/select-content-option.tsx
    • Added a new example file demonstrating Select option styling, class names, and titles.
  • src/SelectInput/Content/SingleContent.tsx
    • Modified the SingleContent component to always render the selected value within a div element, ensuring style, className, and title props from options are correctly applied.
  • tests/Select.test.tsx
    • Added a new test suite to verify the application of style, className, and title props to selected Select options, covering various scenarios including overrides and dynamic updates.
Activity
  • The pull request was created by Rain120.
  • Initial screenshots were provided to illustrate the bug and its resolution.
  • A screenshot of the new demo and tests was included.
  • The PR addresses issue #56932.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 16, 2026

概览

添加了 Select 组件的内容选项样式设置文档和示例,重构了 SingleContent 组件使其始终将选中值包装在样式化的 div 中,并引入了全面的测试覆盖以验证样式、类名和标题属性的应用。

变更详情

Cohort / File(s) Summary
文档和示例
docs/demo/select-content-option.md, docs/examples/select-content-option.tsx
添加了 Select 组件内容选项样式的新文档页面和 React 示例组件,展示了内联样式、自定义类名、标题提示、Option 子组件、动态选项等多个使用场景。
组件逻辑更新
src/SelectInput/Content/SingleContent.tsx
移除了根据 hasOptionStyle 条件判断的渲染路径,改为始终将选中值包装在具有 rc-select-content-value 和自定义类名的 div 中,并一致地应用选项样式和标题属性。
测试套件
tests/Select.test.tsx
新增 406 行测试用例,全面验证选中项显示的样式应用、className 传播、title 属性优先级、content-has-option-style 类的出现时机、搜索模式下的渲染行为、以及 combobox 和 labelInValue 等特殊模式的交互。

预估代码审查工作量

🎯 3 (中等) | ⏱️ ~25 分钟

可能相关的 PR

建议审查人

  • afc163
  • zombieJ

诗歌

🐰 选项妙装扮,样式齐绽放,
div 轻轻包裹,标题轻声唱,
测试细入微,每个角落亮,
兔子欢呼跳,代码更闪烁!

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 标题简明扼要地总结了此次变更的核心内容:修复 select 选项样式问题,并添加了演示和测试。
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into master

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@vercel
Copy link

vercel bot commented Feb 16, 2026

@Rain120 is attempting to deploy a commit to the React Component Team on Vercel.

A member of the Team first needs to authorize it.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request effectively addresses an issue with select option styling by simplifying the rendering logic in SingleContent.tsx. The change ensures that styles and class names from options are consistently applied to the selected value. The addition of a comprehensive demo and an extensive test suite is excellent, covering numerous scenarios and edge cases, which greatly improves the feature's reliability. I have one minor suggestion to improve the demo component by following React's best practices for state updates.

Comment on lines +24 to +31
setDynamicOptions([
...dynamicOptions,
{
value: searchValue,
label: searchValue,
style: { color: '#faad14' },
},
]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For state updates that depend on the previous state, it's a best practice to use the functional update form of useState's setter function. This avoids potential issues with stale state, for example if handleSearch were to be called in quick succession.

Suggested change
setDynamicOptions([
...dynamicOptions,
{
value: searchValue,
label: searchValue,
style: { color: '#faad14' },
},
]);
setDynamicOptions((prevDynamicOptions) => [
...prevDynamicOptions,
{
value: searchValue,
label: searchValue,
style: { color: '#faad14' },
},
]);

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In `@docs/examples/select-content-option.tsx`:
- Around line 22-33: handleSearch has a stale closure risk because it spreads
the current dynamicOptions when calling setDynamicOptions; change
setDynamicOptions to use the functional updater form to avoid reading an
out‑of‑date dynamicOptions (i.e., call setDynamicOptions(prev => { check prev
for existing value and return [...prev, newOption] }) while keeping the same new
option shape and the existing early-exit check logic; update references inside
handleSearch to use the prev argument instead of dynamicOptions.
- Around line 132-181: The "Custom Input Element" demo block is accidentally
nested inside the "Root Title Override" container: the <h3>Custom Input
Element...</h3> and its Select (mode="combobox", getInputElement,
options={dynamicOptions}) are inside the same outer div that wraps the Root
Title Override Select; split them into two sibling container divs so each demo
has its own wrapper and remove the extra closing </div> that currently closes
the Root Title Override wrapper incorrectly; locate the two Select usages and
their surrounding <div> blocks to separate them into distinct sections.

In `@src/SelectInput/Content/SingleContent.tsx`:
- Around line 74-83: The inner value div (`.rc-select-content-value`) always
gets title={optionTitle} causing duplicate tooltips when the outer content div
(`.rc-select-content`) also sets title in the !hasOptionStyle branch; update the
JSX in SingleContent.tsx so the inner div only receives the title when
hasOptionStyle is true (i.e. make the title on the inner element conditional on
hasOptionStyle), keeping the outer content div's existing logic unchanged;
reference the hasOptionStyle flag, optionTitle variable, the inner element
rendering displayValue.label and the optionClassName/optionStyle props to locate
and adjust the title assignment.
- Around line 74-83: The current JSX spreads mergedSearchValue visibility before
optionStyle so optionStyle can override it; in SingleContent.tsx adjust the
spread order so optionStyle is applied first and then the conditional visibility
from mergedSearchValue is spread (or explicitly set visibility when
mergedSearchValue is true) on the div that uses prefixCls-content-value,
optionClassName, optionStyle, mergedSearchValue, optionTitle and
displayValue.label to ensure the search-hide logic always wins.

Comment on lines +22 to +33
const handleSearch = (searchValue: string) => {
if (searchValue && !dynamicOptions.find((opt) => opt.value === searchValue)) {
setDynamicOptions([
...dynamicOptions,
{
value: searchValue,
label: searchValue,
style: { color: '#faad14' },
},
]);
}
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

handleSearch 中存在闭包陈旧引用问题

setDynamicOptions 使用了展开当前 dynamicOptions 的方式,在快速连续输入时可能引用到过时的 state。建议使用函数式更新:

♻️ 建议使用函数式更新
   const handleSearch = (searchValue: string) => {
-    if (searchValue && !dynamicOptions.find((opt) => opt.value === searchValue)) {
-      setDynamicOptions([
-        ...dynamicOptions,
+    if (searchValue) {
+      setDynamicOptions((prev) => {
+        if (prev.find((opt) => opt.value === searchValue)) return prev;
+        return [
+        ...prev,
         {
           value: searchValue,
           label: searchValue,
           style: { color: '#faad14' },
         },
-      ]);
+        ];
+      });
     }
   };
🤖 Prompt for AI Agents
In `@docs/examples/select-content-option.tsx` around lines 22 - 33, handleSearch
has a stale closure risk because it spreads the current dynamicOptions when
calling setDynamicOptions; change setDynamicOptions to use the functional
updater form to avoid reading an out‑of‑date dynamicOptions (i.e., call
setDynamicOptions(prev => { check prev for existing value and return [...prev,
newOption] }) while keeping the same new option shape and the existing
early-exit check logic; update references inside handleSearch to use the prev
argument instead of dynamicOptions.

Comment on lines +132 to +181
<div style={{ marginBottom: 24 }}>
<h3>Root Title Override</h3>
<Select
value="override"
title="This title overrides option title"
style={{ width: 200 }}
options={[
{
value: 'override',
label: 'Hover to see title',
title: 'Option title (will be overridden)',
style: { color: 'teal' },
},
]}
/>
<h3>Custom Input Element with getInputElement (combobox mode)</h3>
<div style={{ marginBottom: 24 }}>
<p>
Use <code>getInputElement</code> to customize the input element. This only works with{' '}
<code>mode=&quot;combobox&quot;</code>. Type to add new options dynamically.
</p>
<Select
mode="combobox"
style={{ width: 300 }}
suffixIcon={null}
showSearch
onSearch={handleSearch}
classNames={{
prefix: 'custom-prefix',
suffix: 'custom-suffix',
}}
styles={{
prefix: { marginRight: 8 },
suffix: { marginLeft: 8 },
}}
getInputElement={() => (
<input
style={{
border: '2px solid #1890ff',
borderRadius: 4,
padding: '4px 8px',
outline: 'none',
}}
placeholder="Type to add new option"
/>
)}
options={dynamicOptions}
/>
</div>
</div>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

"Custom Input Element" 部分嵌套在 "Root Title Override" 的 div 内

Line 147 的 <h3>Custom Input Element...</h3> 及其内容位于 Line 132 开始的 "Root Title Override" div 中(该 div 在 Line 181 关闭)。这两个是独立的 demo 部分,应该各自有自己的容器 div。

♻️ 建议拆分为独立的 section
         />
+      </div>
+
+      <div style={{ marginBottom: 24 }}>
         <h3>Custom Input Element with getInputElement (combobox mode)</h3>

同时移除 Line 181 多余的 </div> 闭合标签。

🤖 Prompt for AI Agents
In `@docs/examples/select-content-option.tsx` around lines 132 - 181, The "Custom
Input Element" demo block is accidentally nested inside the "Root Title
Override" container: the <h3>Custom Input Element...</h3> and its Select
(mode="combobox", getInputElement, options={dynamicOptions}) are inside the same
outer div that wraps the Root Title Override Select; split them into two sibling
container divs so each demo has its own wrapper and remove the extra closing
</div> that currently closes the Root Title Override wrapper incorrectly; locate
the two Select usages and their surrounding <div> blocks to separate them into
distinct sections.

Comment on lines +74 to +83
<div
className={clsx(`${prefixCls}-content-value`, optionClassName)}
style={{
...(mergedSearchValue ? { visibility: 'hidden' } : {}),
...optionStyle,
}}
title={optionTitle}
>
{displayValue.label}
</div>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

hasOptionStyle 为 false 时,title 同时出现在内层 div 和外层 content div 上

Line 80 始终将 optionTitle 设置在内层 .rc-select-content-value div 上,而 Line 99 在 !hasOptionStyle 时也将 optionTitle 设置在外层 .rc-select-content div 上。这会导致在没有 option style 的情况下,两个嵌套元素都有 title 属性,tooltip 可能出现重复。

考虑仅在 hasOptionStyle 为 true 时才在内层 div 设置 title,与外层 content div 的逻辑互斥。

Also applies to: 99-99

🤖 Prompt for AI Agents
In `@src/SelectInput/Content/SingleContent.tsx` around lines 74 - 83, The inner
value div (`.rc-select-content-value`) always gets title={optionTitle} causing
duplicate tooltips when the outer content div (`.rc-select-content`) also sets
title in the !hasOptionStyle branch; update the JSX in SingleContent.tsx so the
inner div only receives the title when hasOptionStyle is true (i.e. make the
title on the inner element conditional on hasOptionStyle), keeping the outer
content div's existing logic unchanged; reference the hasOptionStyle flag,
optionTitle variable, the inner element rendering displayValue.label and the
optionClassName/optionStyle props to locate and adjust the title assignment.

⚠️ Potential issue | 🟡 Minor

optionStyle 可能覆盖搜索时的 visibility: 'hidden'

mergedSearchValue 为真时,visibility: 'hidden' 先被展开,但随后 optionStyle 被展开在后面。如果 option 的 style 中包含 visibility 属性,会覆盖掉隐藏逻辑,导致搜索时选中值仍然可见。

建议将 optionStyle 放在 visibility 之前展开,确保搜索隐藏逻辑优先:

🐛 建议修复样式合并顺序
       style={{
+        ...optionStyle,
         ...(mergedSearchValue ? { visibility: 'hidden' } : {}),
-        ...optionStyle,
       }}
🤖 Prompt for AI Agents
In `@src/SelectInput/Content/SingleContent.tsx` around lines 74 - 83, The current
JSX spreads mergedSearchValue visibility before optionStyle so optionStyle can
override it; in SingleContent.tsx adjust the spread order so optionStyle is
applied first and then the conditional visibility from mergedSearchValue is
spread (or explicitly set visibility when mergedSearchValue is true) on the div
that uses prefixCls-content-value, optionClassName, optionStyle,
mergedSearchValue, optionTitle and displayValue.label to ensure the search-hide
logic always wins.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant