Skip to content

fix(Tabs): apply styles.popup.root to the more dropdown - #59221

Merged
QDyanbing merged 3 commits into
ant-design:masterfrom
giaBaoJS:fix/tabs-popup-semantic-style
Sep 10, 2026
Merged

QDyanbing merged 3 commits into
ant-design:masterfrom
giaBaoJS:fix/tabs-popup-semantic-style

Conversation

@giaBaoJS

@giaBaoJS giaBaoJS commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

🤔 This is a ...

  • 🆕 New feature
  • 🐞 Bug fix
  • 📝 Site / documentation improvement
  • 📽️ Demo improvement
  • 💄 Component style improvement
  • 🤖 TypeScript definition improvement
  • 📦 Bundle size optimization
  • ⚡️ Performance optimization
  • ⭐️ Feature enhancement
  • 🌐 Internationalization
  • 🛠 Refactoring
  • 🎨 Code style optimization
  • ✅ Test Case
  • 🔀 Branch merge
  • ⏩ Workflow
  • ⌨️ Accessibility improvement
  • ❓ Other (about what?)

🔗 Related Issues

No linked issue. The problem is visible in the repository itself: components/tabs/demo/_semantic.tsx sets styles={{ popup: { root: { background: '#fff' } } }}, and that value never reaches the DOM.

💡 Background and Solution

Tabs declares the popup semantic node as a nested object on both sides (components/tabs/index.tsx):

classNames?: { ...; popup?: { root?: string } };
styles?:     { ...; popup?: { root?: React.CSSProperties } };

@rc-component/tabs keeps popup flat (Partial<Record<SemanticName, string>> / Partial<Record<SemanticName, CSSProperties>>), so the nested value has to be unwrapped on the way down. classNames is unwrapped:

classNames={{
  ...mergedClassNames,
  popup: clsx(popupClassName, hashId, cssVarCls, rootCls, mergedClassNames.popup?.root),
}}
styles={mergedStyles}

styles is not. mergedStyles.popup therefore arrives at rc-tabs as { root: CSSProperties }, is forwarded as popupStyle -> overlayStyle on the more dropdown, and React drops it because root is not a CSS property. The result is that classNames.popup.root works and styles.popup.root silently does nothing on the same element.

styles is now unwrapped the same way classNames already is. This is how every sibling component with the same nested popup semantic already behaves, for example components/select/index.tsx (popupStyle={{ ...mergedStyles.popup.root, ... }}), components/cascader/index.tsx and components/auto-complete/AutoComplete.tsx.

Nothing else changes: the layout, the public API, and the classNames path are untouched, and no snapshot moves because the more dropdown is not opened in any snapshot test.

components/tabs/__tests__/semantic.test.tsx gains a case that overflows the tab bar, opens the more dropdown and asserts both halves on the popup element. Reverting only components/tabs/index.tsx turns it red on the style assertion while the class assertion above it still passes:

● Tabs.Semantic › support popup classNames and styles

  expect(element).toHaveStyle()

  - Expected

  - color: rgb(255, 0, 0);
  + color: var(--ant-color-text);

Verification:

  • npx jest --config .jest.js --no-cache components/tabs components/config-provider -> 27 suites passed, 662 tests passed, 455 snapshots passed, 0 snapshots written.
  • npx vitest run components/tabs/__tests__/semantic.test.tsx -> 4 passed.
  • npm run tsc, eslint, biome lint, biome format -> clean.

📝 Change Log

Language Changelog
🇺🇸 English 🐞 Fix Tabs ignoring styles.popup.root on the more dropdown.
🇨🇳 Chinese 🐞 修复 Tabs 折叠下拉菜单未应用 styles.popup.root 的问题。

@bolt-new-by-stackblitz

Copy link
Copy Markdown

Review PR in StackBlitz Codeflow Run & review this pull request in StackBlitz Codeflow.

@github-actions

github-actions Bot commented Sep 5, 2026 •

Copy link
Copy Markdown
Contributor

Preview is ready

@github-actions

github-actions Bot commented Sep 5, 2026 •

Copy link
Copy Markdown
Contributor

👁 Visual Regression Report for PR #59221 Passed ✅

🎯 Target branch: master (5b3d4a8)
📖 View Full Report ↗︎
📖 Alternative Report ↗︎

🎊 Congrats! No visual-regression diff found.

📖 中文报告

👁 PR #59221 视觉回归报告 通过 ✅

🎯 目标分支: master (5b3d4a8)

🎊 恭喜!未发现视觉回归差异。

@coderabbitai

coderabbitai Bot commented Sep 5, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Team

Run ID: 4078ba88-06dd-45e3-b1fd-b6513178daf6

📥 Commits

Reviewing files that changed from the base of the PR and between 5d471ce and a632e5b.

📒 Files selected for processing (1)
  • components/tabs/__tests__/semantic.test.tsx

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Summary

Summary by CodeRabbit

  • Bug 修复
    • 修复 Tabs“更多”下拉菜单的语义化 popup 样式未正确生效的问题。
    • 现在可正常应用下拉菜单的自定义类名与样式,确保下拉菜单外观符合配置预期。

Walkthrough

Tabs 将语义化配置中的 popup.root 样式传递给 rc-tabs 的弹出菜单。测试显示 more 菜单,并验证下拉菜单的 className 和颜色样式。

Changes

Tabs 弹出层语义样式

Layer / File(s) Summary
弹出层样式传递与验证
components/tabs/index.tsx, components/tabs/__tests__/semantic.test.tsx
InternalTabs 将 mergedStyles.popup.root 映射为 rc-tabs 的 popup 样式。测试通过 more={{ visible: true }} 显示下拉菜单,并验证其 className 和红色样式。

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to a632e

Tabs more-menu dropdowns now receive configured popup root styles, with coverage confirming the rendered class and color styling. No current merge-blocking risk remains.

Suggested reviewers: qdyanbing

Poem

小兔检查样式,耳朵轻轻晃
popup 样式进入下拉窗
红色文字清晰亮
className 稳稳站岗
测试路径更简洁
小兔收工回月光

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed 标题准确说明了主要修复内容:将 styles.popup.root 应用到 Tabs 的更多下拉菜单。标题简洁且与变更完全相关。
Description check ✅ Passed 描述详细说明了问题、解决方案、测试覆盖和验证结果。内容与代码变更完全相关。
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 2…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ 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.

@coderabbitai
coderabbitai Bot requested review from QDyanbing and zombieJ September 5, 2026 07:33
@pkg-pr-new

pkg-pr-new Bot commented Sep 5, 2026 •

Copy link
Copy Markdown
More templates

npm i https://pkg.pr.new/antd@59221

commit: a632e5b

@codecov

codecov Bot commented Sep 5, 2026 •

Copy link
Copy Markdown

Bundle Report

Changes will decrease total bundle size by 5.77MB (-64.09%) ⬇️. This is within the configured threshold ✅

Detailed changes
Bundle name Size Change
antd.min-array-push 3.23MB -5.77MB (-64.09%) ⬇️

Affected Assets, Files, and Routes:

view changes for bundle: antd.min-array-push

Assets Changed:

Asset Name Size Change Total Size Change (%)
antd-with-locales.min.js (New) 1.8MB 1.8MB 100.0% 🚀
antd.min.js (New) 1.43MB 1.43MB 100.0% 🚀
antd-with-locales.js (Deleted) -4.79MB 0 bytes -100.0% 🗑️
antd.js (Deleted) -4.21MB 0 bytes -100.0% 🗑️

@codecov

codecov Bot commented Sep 5, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (58b0ee1) to head (a632e5b).
⚠️ Report is 10 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff            @@
##            master    #59221   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files          834       834           
  Lines        15994     15991    -3     
  Branches      4204      4202    -2     
=========================================
- Hits         15994     15991    -3     

☔ View full report in Codecov by Harness.
📢 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.

@nrps9909 nrps9909 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed exact head f91167d0c96b96af3926a20fbd3ba1ad12b61d03 against base 58b0ee1554835b3ea2d0541f4986306f3d70d0ce. The projection of mergedStyles.popup.root matches rc-tabs' flat popup style contract; I found no blocker in this scoped change.

Validation:

  • The new overflow-popup regression fails against the base implementation and passes on the PR head. The four semantic tests also pass under Vitest.
  • Two independent DOM probes pass: provider-only popup fallback, local popup precedence, merged popup class names, preservation of root/header styles, callback evaluation, and clearing obsolete inline styles after rerenders.
  • The complete Tabs + ConfigProvider run reports 663 passed, 4 skipped, and one failed test; 454 snapshots pass and one differs. The remaining tabs/demo/nest.tsx extend-context snapshot gains Select aria-posinset/aria-setsize attributes. The exact base produces the identical 16-line diff with the same installed dependencies. I did not update that snapshot or count this as a clean full-suite pass.
  • ESLint, Biome and diff checks pass for the changed files. All 42 applicable remote checks pass; two are skipped.

Separate existing behavior observed during probing: when both ConfigProvider and Tabs supply styles.popup.root, the shared mergeStyles helper replaces the nested root object, so unrelated provider properties are lost. That helper is unchanged by this PR. Provider-only fallback and the local styles work with this fix; property-level merging of nested style slots would be a separate follow-up.

Review and independent validation performed with OpenAI Codex assistance.

it('support popup classNames and styles', async () => {
// The more dropdown only mounts when the tabs overflow, so fake the layout
// Longest first: `ant-tabs-nav` is a prefix of the other two
const sizes: Record<string, number> = {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Could we open the dropdown directly by configuring more, then verify the popup's class and style? This test mainly checks whether the styles are applied, so it should not need to simulate element dimensions, tab overflow, or resize and hover events. Could we simplify the test setup?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Updated. The test now passes more={{ visible: true }} and opens the popup directly.

In rc-tabs OperationNode, more is spread onto the Dropdown after its own visible: tabs.length ? open : false, so more.visible wins and the popup mounts without any overflowing tabs. That removed the offsetWidth/offsetHeight/offsetLeft prototype patching, the resize trigger, the mouseEnter event and the fake timers. The test is synchronous now and asserts the same class and style on .ant-tabs-dropdown.

Coverage is unchanged: with components/tabs/index.tsx reverted to the base version, only the style assertion fails (color: var(--ant-color-text) instead of rgb(255, 0, 0)) while the class assertion still passes. Green under both jest and vitest.

@giaBaoJS
giaBaoJS force-pushed the fix/tabs-popup-semantic-style branch from d0977d1 to 5d471ce Compare September 5, 2026 16:08
@coderabbitai
coderabbitai Bot requested review from QDyanbing and nrps9909 September 5, 2026 16:09
Comment on lines 14 to 31
const customClassnames = {
root: 'test-class',
item: 'test-item',
remove: 'test-remove',
indicator: 'test-indicator',
header: 'test-header',
body: 'test-body',
content: 'test-content',
};
const customStyles = {
root: { color: 'rgb(255, 0, 0)' },
item: { color: 'rgb(0, 0, 255)' },
remove: { color: 'rgb(255, 0, 255)' },
indicator: { color: 'rgb(255, 255, 0)' },
header: { color: 'rgb(0, 255, 0)' },
body: { color: 'rgb(0, 128, 128)' },
content: { color: 'rgb(128, 0, 128)' },
};

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can the new case be added directly to this case?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done, folded into support classnames and styles. popup: { root: ... } now sits in the existing customClassnames and customStyles objects, the render gets more={{ visible: true }} so the dropdown mounts, and the two assertions moved in beside the others. The separate case is gone.

One thing worth a look: the popup is portaled, so it is read off document.body rather than container like the rest.

Counterfactual still holds. With components/tabs/index.tsx reverted, the case fails on color: var(--ant-color-text) instead of rgb(0, 255, 255). components/tabs goes from 88 to 87 tests, no snapshot change.

@coderabbitai
coderabbitai Bot requested a review from QDyanbing September 7, 2026 02:10
@QDyanbing

Copy link
Copy Markdown
Member

LGTM。这里参照 classNames.popup.root 的处理方式,将 styles.popup.root 转为 rc-tabs 接收的扁平结构。再看看其他 reviewer 是否还有需要确认的地方。


LGTM. This handles styles.popup.root the same way as classNames.popup.root, adapting it to the flat structure expected by rc-tabs. Let’s see if the other reviewers have anything else to check.

@zombieJ zombieJ left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM

@QDyanbing
QDyanbing merged commit a6ec655 into ant-design:master Sep 10, 2026
42 checks passed
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.

4 participants