Skip to content

feat(mergeProps): enhance mergeProps to handle className and style#745

Open
guoyunhe wants to merge 1 commit intoreact-component:masterfrom
guoyunhe:merge-props-classname-style
Open

feat(mergeProps): enhance mergeProps to handle className and style#745
guoyunhe wants to merge 1 commit intoreact-component:masterfrom
guoyunhe:merge-props-classname-style

Conversation

@guoyunhe
Copy link
Contributor

@guoyunhe guoyunhe commented Mar 26, 2026

className 和 style 是所有 antd 组件都支持的的 config,他们的 merge 规则和普通属性不同,这里做特殊处理,以减少 antd 中的重复代码逻辑。

Summary by CodeRabbit

Bug Fixes

  • 改进属性合并逻辑,className 属性现在使用空格连接而非覆盖
  • 优化样式对象合并,现在采用浅合并方式而非完全替换

Tests

  • 添加了 className 连接合并的测试用例
  • 添加了样式对象浅合并的测试用例

@vercel
Copy link

vercel bot commented Mar 26, 2026

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

A member of the Team first needs to authorize it.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, 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 refines the mergeProps utility to intelligently combine className and style properties, which are common configurations in Ant Design components. This ensures that these properties are handled with specific merging rules—concatenating className strings and shallowly merging style objects—thereby preventing unintended overwrites and improving the utility's robustness for UI component development.

Highlights

  • Enhanced mergeProps utility: The mergeProps function now includes specialized logic for handling className and style properties, ensuring they are merged correctly rather than simply overwritten.
  • Specific merging rules for className: When merging className properties, existing and new class names are concatenated with a space, allowing multiple classes to be applied.
  • Specific merging rules for style: When merging style properties, the objects are shallowly merged using object spread syntax, combining individual style declarations.
  • Added unit tests: New test cases have been introduced to validate the correct merging behavior for both className and style properties.

🧠 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.

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.

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

coderabbitai bot commented Mar 26, 2026

概览

该PR增强了 mergeProps 工具函数,使其在合并非 undefined 值时对特定属性键进行特殊处理。className 值现在通过空格连接而非覆盖,style 对象则通过浅合并来组合属性而非替换整个对象。其他属性保持原有的覆盖行为。

变更

内聚群/文件 摘要
mergeProps 逻辑增强
src/mergeProps.ts
添加了 className 的空格连接逻辑和 style 的浅合并逻辑,处理特定属性键的合并方式,同时为函数注释补充了文档/示例说明。
测试覆盖扩展
tests/mergeProps.test.ts
新增测试用例验证 className 值的连接(如 'a' + 'b''a b')以及 style 对象的浅合并行为(如合并嵌套样式属性)。

预估代码审查工作量

🎯 2 (简单) | ⏱️ ~10 分钟

可能相关的PR

  • feat(mergeProps): add mergeProps function #740:两个PR都修改了 src/mergeProps.ts 中的同一实现——该PR添加了函数及测试,而本PR改变了 classNamestyle 的合并行为(浅合并/连接),具有直接的代码级关联。

建议的审核人

  • zombieJ

诗句

🐰 道具合并有新法,
className 携空格舞,
style 浅深有妙招,
属性缤纷无覆盖,
代码更优雅!✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 标题准确地总结了拉取请求的主要变更,即增强 mergeProps 函数以特殊处理 className 和 style 两个关键属性。

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

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 enhances the mergeProps utility function by adding an example to its JSDoc and introducing specific merging logic for className and style properties. For className, it concatenates values, and for style, it merges objects. New test cases for these specific merges have also been added. The review highlights that the current className merging logic could be more robust by filtering falsy values to avoid issues with null or empty strings. Additionally, the style merging logic is identified as potentially unsafe, as it assumes both values are objects, and suggests adding type checks before merging. It is also recommended to add more edge case tests for className and style to improve test coverage and prevent these issues.

if (item[key] !== undefined) {
ret[key] = item[key];
if (key === 'className') {
ret[key] = ret[key] ? `${ret[key]} ${item[key]}` : item[key];
Copy link
Contributor

Choose a reason for hiding this comment

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

high

当前合并 className 的逻辑不够健壮,在某些情况下可能产生不正确的结果:

  • 如果 className 的值为 null,它会被字符串化,例如 'a null'
  • 如果 className 的值是空字符串,可能会导致多余的空格,例如 'a '

更稳妥的方法是在连接 class 名称之前过滤掉所有假值(falsy values)。

Suggested change
ret[key] = ret[key] ? `${ret[key]} ${item[key]}` : item[key];
ret[key] = [ret[key], item[key]].filter(Boolean).join(' ');

if (key === 'className') {
ret[key] = ret[key] ? `${ret[key]} ${item[key]}` : item[key];
} else if (key === 'style') {
ret[key] = { ...ret[key], ...item[key] };
Copy link
Contributor

Choose a reason for hiding this comment

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

high

当前合并 style 对象的逻辑是不安全的。它无条件地对 ret[key]item[key] 使用了展开语法,假定它们都是对象。如果其中一个是其他类型(例如字符串),这可能导致意外行为。例如,展开一个字符串 'foo' 会得到 { '0': 'f', '1': 'o', '2': 'o' },这将创建一个无效的样式对象。

合并操作只应在 style 的现有值和新值都是对象时执行。否则,它应该回退到默认的覆盖属性的行为。

            if (ret[key] && typeof ret[key] === 'object' && item[key] && typeof item[key] === 'object') {
              ret[key] = { ...ret[key], ...item[key] };
            } else {
              ret[key] = item[key];
            }

style: { color: 'red', backgroundColor: 'blue' },
});
});

Copy link
Contributor

Choose a reason for hiding this comment

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

medium

classNamestyle 合并添加的测试用例很棒!为了使它们更健壮,建议增加一些边缘情况的测试用例,例如 null 值、className 的空字符串,以及 style 的非对象值。这将有助于防止我在 src/mergeProps.ts 中指出的问题再次出现。

@codecov
Copy link

codecov bot commented Mar 26, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 86.37%. Comparing base (7e36953) to head (53a3949).

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #745      +/-   ##
==========================================
+ Coverage   86.31%   86.37%   +0.06%     
==========================================
  Files          39       39              
  Lines        1052     1057       +5     
  Branches      372      391      +19     
==========================================
+ Hits          908      913       +5     
  Misses        142      142              
  Partials        2        2              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link

@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: 1

🧹 Nitpick comments (2)
tests/mergeProps.test.ts (1)

10-23: 建议补两组边界用例,锁定新语义

建议再加:

  1. style 同名 key 冲突时右侧覆盖左侧;
  2. className''/null 时不引入多余空格或 "null" 字符串。
    这样可以直接防止本次逻辑在后续重构中回退。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/mergeProps.test.ts` around lines 10 - 23, Add two boundary tests to
tests/mergeProps.test.ts to lock the intended semantics for mergeProps: one test
where both inputs have the same style property (e.g., a = { style: { color:
'red' } }, b = { style: { color: 'blue' } }) asserting mergeProps(a,
b).style.color === 'blue' to ensure right-side overrides left; and one or two
tests for className where left or right is '' or null (e.g., a = { className: ''
}, b = { className: 'b' } and a = { className: null }, b = { className: 'b' })
asserting merged className === 'b' (no extra spaces or "null" string) to prevent
regressions in mergeProps behavior.
src/mergeProps.ts (1)

5-8: 建议补充能体现新规则的示例

当前示例没有展示 className 拼接和 style 浅合并,建议补一段最小示例,避免调用方误以为仍是普通覆盖。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/mergeProps.ts` around lines 5 - 8, 在 mergeProps 的文档示例中补充一个最小示例,展示
className 的拼接规则和 style 的浅合并行为:说明用 mergeProps(defaults, config, props) 时若
defaults/className 与 props.className 同时存在会被拼接(比如 "btn" + " primary"),并说明 style
会进行浅合并(例如 defaults.style = { color: 'red', padding: 4 } 与 props.style = {
padding: 8, fontSize: 12 } 合并后为 { color: 'red', padding: 8, fontSize: 12
});在说明中引用 mergeProps、className、style 等符号以便定位代码并避免误解为普通覆盖。
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/mergeProps.ts`:
- Around line 19-21: The className concatenation in mergeProps may produce "a "
or "a null" because it doesn't skip empty/null/undefined values; update the
className branch in the mergeProps logic (the block referencing ret, item and
key === 'className') to first check that item[key] is a non-empty string (and
not null/undefined) before concatenating, and if item[key] is empty skip it
entirely (or trim and only join when both sides are non-empty) so ret[key] never
becomes "a " or "a null".

---

Nitpick comments:
In `@src/mergeProps.ts`:
- Around line 5-8: 在 mergeProps 的文档示例中补充一个最小示例,展示 className 的拼接规则和 style
的浅合并行为:说明用 mergeProps(defaults, config, props) 时若 defaults/className 与
props.className 同时存在会被拼接(比如 "btn" + " primary"),并说明 style 会进行浅合并(例如
defaults.style = { color: 'red', padding: 4 } 与 props.style = { padding: 8,
fontSize: 12 } 合并后为 { color: 'red', padding: 8, fontSize: 12 });在说明中引用
mergeProps、className、style 等符号以便定位代码并避免误解为普通覆盖。

In `@tests/mergeProps.test.ts`:
- Around line 10-23: Add two boundary tests to tests/mergeProps.test.ts to lock
the intended semantics for mergeProps: one test where both inputs have the same
style property (e.g., a = { style: { color: 'red' } }, b = { style: { color:
'blue' } }) asserting mergeProps(a, b).style.color === 'blue' to ensure
right-side overrides left; and one or two tests for className where left or
right is '' or null (e.g., a = { className: '' }, b = { className: 'b' } and a =
{ className: null }, b = { className: 'b' }) asserting merged className === 'b'
(no extra spaces or "null" string) to prevent regressions in mergeProps
behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7068d635-bc73-4a28-ab1b-63aa5fc58e68

📥 Commits

Reviewing files that changed from the base of the PR and between 7e36953 and 53a3949.

📒 Files selected for processing (2)
  • src/mergeProps.ts
  • tests/mergeProps.test.ts

Comment on lines +19 to +21
if (key === 'className') {
ret[key] = ret[key] ? `${ret[key]} ${item[key]}` : item[key];
} else if (key === 'style') {
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

className 在空值场景会拼出脏结果

Line 20 对空字符串/null 没有过滤,可能得到 "a ""a null"。建议在拼接前跳过空值。

🔧 建议修改
-          if (key === 'className') {
-            ret[key] = ret[key] ? `${ret[key]} ${item[key]}` : item[key];
+          if (key === 'className') {
+            const nextClassName = item[key];
+            if (nextClassName === '' || nextClassName == null) {
+              continue;
+            }
+            ret[key] = ret[key] ? `${ret[key]} ${nextClassName}` : nextClassName;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (key === 'className') {
ret[key] = ret[key] ? `${ret[key]} ${item[key]}` : item[key];
} else if (key === 'style') {
if (key === 'className') {
const nextClassName = item[key];
if (nextClassName === '' || nextClassName == null) {
continue;
}
ret[key] = ret[key] ? `${ret[key]} ${nextClassName}` : nextClassName;
} else if (key === 'style') {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/mergeProps.ts` around lines 19 - 21, The className concatenation in
mergeProps may produce "a " or "a null" because it doesn't skip
empty/null/undefined values; update the className branch in the mergeProps logic
(the block referencing ret, item and key === 'className') to first check that
item[key] is a non-empty string (and not null/undefined) before concatenating,
and if item[key] is empty skip it entirely (or trim and only join when both
sides are non-empty) so ret[key] never becomes "a " or "a null".

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