diff --git a/.github/workflows/issue-inactivity-reminder.yml b/.github/workflows/issue-inactivity-reminder.yml index 829d3429ae..ffc1b8f060 100644 --- a/.github/workflows/issue-inactivity-reminder.yml +++ b/.github/workflows/issue-inactivity-reminder.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Send reminders for inactive issues - uses: actions/github-script@v7 + uses: actions/github-script@v8 with: script: | const daysBeforeReminder = 14; diff --git a/.github/workflows/issue-schedule.yml b/.github/workflows/issue-schedule.yml index af7af19c9d..df7c93aed8 100644 --- a/.github/workflows/issue-schedule.yml +++ b/.github/workflows/issue-schedule.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Send Unconfirmed Issues to DingTalk - uses: actions/github-script@v7 + uses: actions/github-script@v8 env: DINGDING_BOT_TOKEN: ${{ secrets.DINGDING_BOT_COLLABORATOR_TOKEN }} actionTitle: 近 7 天未确认的 issue diff --git a/.github/workflows/preview-deploy.yml b/.github/workflows/preview-deploy.yml index 6ab852e7de..2d3fb072d8 100644 --- a/.github/workflows/preview-deploy.yml +++ b/.github/workflows/preview-deploy.yml @@ -22,7 +22,7 @@ jobs: build-failure: ${{ steps.prep-summary.outputs.build-failure }} steps: - name: summary jobs status - uses: actions/github-script@v7 + uses: actions/github-script@v8 id: prep-summary with: script: | diff --git a/.github/workflows/visual-regression-diff-approver.yml b/.github/workflows/visual-regression-diff-approver.yml index 4c8b4b88e6..3b8b5b3b68 100644 --- a/.github/workflows/visual-regression-diff-approver.yml +++ b/.github/workflows/visual-regression-diff-approver.yml @@ -24,7 +24,7 @@ jobs: # Return type: `success` or `failed` or `waiting` - name: Statistic Visual Diff Approval id: check_approval - uses: actions/github-script@v7 + uses: actions/github-script@v8 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | diff --git a/.github/workflows/visual-regression-diff-finish.yml b/.github/workflows/visual-regression-diff-finish.yml index 51980fedd6..d4bb5c5c00 100644 --- a/.github/workflows/visual-regression-diff-finish.yml +++ b/.github/workflows/visual-regression-diff-finish.yml @@ -21,7 +21,7 @@ jobs: build-status: ${{ steps.visual_diff_build_job_status.outputs.build-status }} steps: - name: summary jobs status - uses: actions/github-script@v7 + uses: actions/github-script@v8 id: visual_diff_build_job_status with: script: | @@ -152,7 +152,7 @@ jobs: # Resync commit status. This will be also reset by `visual-regression-diff-approver` - name: Reset Commit Status - uses: actions/github-script@v7 + uses: actions/github-script@v8 if: ${{ steps.report.outcome == 'success' }} env: PR_ID: ${{ steps.pr.outputs.id }} diff --git a/.github/workflows/visual-regression-persist-finish.yml b/.github/workflows/visual-regression-persist-finish.yml index 663228ca79..73c108c4fa 100644 --- a/.github/workflows/visual-regression-persist-finish.yml +++ b/.github/workflows/visual-regression-persist-finish.yml @@ -21,7 +21,7 @@ jobs: build-failure: ${{ steps.persist_start_job_status.outputs.build-failure }} steps: - name: summary jobs status - uses: actions/github-script@v7 + uses: actions/github-script@v8 id: persist_start_job_status with: script: | diff --git a/CHANGELOG.en-US.md b/CHANGELOG.en-US.md index ad76d2ed1f..e72432f846 100644 --- a/CHANGELOG.en-US.md +++ b/CHANGELOG.en-US.md @@ -173,6 +173,27 @@ tag: vVERSION - 🚫 Modal `closable` support `onClose` props that trigger by any type of close. [#54607](https://github.com/ant-design/ant-design/pull/54607) [@EmilyyyLiu](https://github.com/EmilyyyLiu) - 🛠 Remove Dropdown.Button, please use Space.Compact instead. [#53793](https://github.com/ant-design/ant-design/pull/53793) [@Meet-student](https://github.com/Meet-student) +## 5.27.3 + +`2025-09-04` + +- Table + - 🐞 Fix Table header width compression issues and render flickering issues when `scroll.y` or `sticky` is set. [#54824](https://github.com/ant-design/ant-design/pull/54824) [@afc163](https://github.com/afc163) + | Before ❌ | After ✅ | + | ---------- | --------- | + | before fix | after fix | + - 🐞 Fix Table `scroll.scrollToFirstRowOnChange` not working with virtual scroll. [#54734](https://github.com/ant-design/ant-design/issues/54734) [@Wxh16144](https://github.com/Wxh16144) + - 🐞 Fix Table columns with `children` not working with `fixed: 'right'`. [#51812](https://github.com/ant-design/ant-design/issues/51812) [@ryantang247](https://github.com/ryantang247) + - 🐞 Fix Table expand column not being displayed when `expandable.fixed` is set to true. [@inottn](https://github.com/inottn) + - 🐞 Fix Table columns `minWidth` props not working when virtualized. [#54856](https://github.com/ant-design/ant-design/issues/54856) [@cactuser-Lu](https://github.com/cactuser-Lu) +- 🐞 Fix Pagination style issues with `simple` and `small` size props. [#54837](https://github.com/ant-design/ant-design/pull/54837) [@MrWangJustToDo](https://github.com/MrWangJustToDo) +- 🐞 Fix Button padding issue when `shape="round"`. [#54845](https://github.com/ant-design/ant-design/pull/54845) [@guoyunhe](https://github.com/guoyunhe) +- 🐞 Fix Input.OTP not allowing continuous deletion. [#54850](https://github.com/ant-design/ant-design/pull/54850) [@765477020](https://github.com/765477020) +- 🐞 Fix Dropdown `onOpenChange` closure issue. [#54880](https://github.com/ant-design/ant-design/pull/54880) [@zombieJ](https://github.com/zombieJ) +- 🐞 Fix Carousel component style and button switching issues in RTL mode. [#54868](https://github.com/ant-design/ant-design/pull/54868) [@EmilyyyLiu](https://github.com/EmilyyyLiu) +- 🐞 Fix incorrect margin in Typography when editable. [#54871](https://github.com/ant-design/ant-design/pull/54871) [@Tarun2605](https://github.com/Tarun2605) +- 🇮🇹 Add missing Italian translations for ColorPicker and QRCode components. [#54842](https://github.com/ant-design/ant-design/pull/54842) [@nikzanda](https://github.com/nikzanda) + ## 5.27.2 `2025-09-02` diff --git a/CHANGELOG.zh-CN.md b/CHANGELOG.zh-CN.md index 688767b920..452c7a7b9f 100644 --- a/CHANGELOG.zh-CN.md +++ b/CHANGELOG.zh-CN.md @@ -173,6 +173,27 @@ tag: vVERSION - 🚫 Modal `closable` 支持 `onClose` 属性以任意方式关闭时触发。[#54607](https://github.com/ant-design/ant-design/pull/54607) [@EmilyyyLiu](https://github.com/EmilyyyLiu) - 🛠 移除 Dropdown.Button,请使用 Space.Compact。[#53793](https://github.com/ant-design/ant-design/pull/53793) [@Meet-student](https://github.com/Meet-student) +## 5.27.3 + +`2025-09-04` + +- Table + - 🐞 修复 Table 设置 `scroll.y` 或者 `sticky` 时表头列宽度被挤压或渲染闪烁的问题。[#54824](https://github.com/ant-design/ant-design/pull/54824) [@afc163](https://github.com/afc163) + | Before ❌ | After ✅ | + | ---------- | --------- | + | before fix | after fix | + - 🐞 修复 Table 在虚拟滚动时,`scroll.scrollToFirstRowOnChange` 配置不生效的问题。[#54734](https://github.com/ant-design/ant-design/issues/54734) [@Wxh16144](https://github.com/Wxh16144) + - 🐞 修复 Table 的列配置了 `children` 时,无法 `fixed: 'right'` 的问题。[#51812](https://github.com/ant-design/ant-design/issues/51812) [@ryantang247](https://github.com/ryantang247) + - 🐞 修复 Table 配置 `expandable.fixed` 时,展开列没有显示的问题。[@inottn](https://github.com/inottn) + - 🐞 修复 Table 虚拟化时列 `minWidth` 属性不生效的问题。[#54856](https://github.com/ant-design/ant-design/issues/54856) [@cactuser-Lu](https://github.com/cactuser-Lu) +- 🐞 修复 Pagination 在 `simple` 和 `small` 尺寸下的样式问题。[#54837](https://github.com/ant-design/ant-design/pull/54837) [@MrWangJustToDo](https://github.com/MrWangJustToDo) +- 🐞 修复 Button `shape="round"` 时的 padding 样式问题。[#54845](https://github.com/ant-design/ant-design/pull/54845) [@guoyunhe](https://github.com/guoyunhe) +- 🐞 修复 Input.OTP 不允许连续删除的问题。[#54850](https://github.com/ant-design/ant-design/pull/54850) [@765477020](https://github.com/765477020) +- 🐞 修复 Dropdown `onOpenChange` 闭包问题。[#54880](https://github.com/ant-design/ant-design/pull/54880) [@zombieJ](https://github.com/zombieJ) +- 🐞 修复 Carousel 组件在 RTL 模式下的样式和按钮切换问题。[#54868](https://github.com/ant-design/ant-design/pull/54868) [@EmilyyyLiu](https://github.com/EmilyyyLiu) +- 🐞 修复 Typography 在可编辑状态下的错误边距。[#54871](https://github.com/ant-design/ant-design/pull/54871) [@Tarun2605](https://github.com/Tarun2605) +- 🇮🇹 补充 ColorPicker 和 QRCode 的意大利语翻译。[#54842](https://github.com/ant-design/ant-design/pull/54842) [@nikzanda](https://github.com/nikzanda) + ## 5.27.2 `2025-09-02` diff --git a/components/carousel/__tests__/index.test.tsx b/components/carousel/__tests__/index.test.tsx index ac748ed5f3..2e1940ba6d 100644 --- a/components/carousel/__tests__/index.test.tsx +++ b/components/carousel/__tests__/index.test.tsx @@ -4,7 +4,8 @@ import type { CarouselRef, DotPlacement } from '..'; import Carousel from '..'; import mountTest from '../../../tests/shared/mountTest'; import rtlTest from '../../../tests/shared/rtlTest'; -import { render, waitFakeTimer } from '../../../tests/utils'; +import { fireEvent, render, waitFakeTimer } from '../../../tests/utils'; +import ConfigProvider from '../../config-provider'; describe('Carousel', () => { mountTest(Carousel); @@ -314,4 +315,30 @@ describe('Carousel', () => { ); }); }); + describe('RTL Direction', () => { + it('should trigger correct slide change when clicking arrows in RTL', async () => { + const { container } = render( + + +
Slide 1
+
Slide 2
+
Slide 3
+
+
, + ); + + expect(container.querySelector('.ant-carousel-rtl')).toBeTruthy(); + + const prevArrow = container.querySelector('.slick-prev') as HTMLElement; + const nextArrow = container.querySelector('.slick-next') as HTMLElement; + expect(prevArrow).toHaveAttribute('aria-label', 'next'); + expect(nextArrow).toHaveAttribute('aria-label', 'prev'); + + expect(container.querySelector('.slick-active')?.textContent).toBe('Slide 2'); + fireEvent.click(prevArrow); + expect(container.querySelector('.slick-active')?.textContent).toBe('Slide 3'); + fireEvent.click(nextArrow); + expect(container.querySelector('.slick-active')?.textContent).toBe('Slide 2'); + }); + }); }); diff --git a/components/carousel/index.tsx b/components/carousel/index.tsx index 1707120d6c..86485dc370 100644 --- a/components/carousel/index.tsx +++ b/components/carousel/index.tsx @@ -48,8 +48,8 @@ const Carousel = React.forwardRef((props, ref) => { const { dots = true, arrows = false, - prevArrow = , - nextArrow = , + prevArrow, + nextArrow, draggable = false, waitForAnimate = false, dotPosition, @@ -61,6 +61,7 @@ const Carousel = React.forwardRef((props, ref) => { id, autoplay = false, autoplaySpeed = 3000, + rtl, ...otherProps } = props; @@ -102,15 +103,16 @@ const Carousel = React.forwardRef((props, ref) => { }), [slickRef.current], ); - - const prevCount = React.useRef(React.Children.count(props.children)); + const { children, initialSlide = 0 } = props; + const count = React.Children.count(children); + const isRTL = (rtl ?? direction === 'rtl') && !vertical; React.useEffect(() => { - if (prevCount.current !== React.Children.count(props.children)) { - goTo(props.initialSlide || 0, false); - prevCount.current = React.Children.count(props.children); + if (count > 0) { + const newIndex = isRTL ? count - initialSlide - 1 : initialSlide; + goTo(newIndex, false); } - }, [props.children]); + }, [count, initialSlide, isRTL]); // ========================== Warn ========================== if (process.env.NODE_ENV !== 'production') { @@ -145,7 +147,7 @@ const Carousel = React.forwardRef((props, ref) => { const className = classNames( prefixCls, { - [`${prefixCls}-rtl`]: direction === 'rtl', + [`${prefixCls}-rtl`]: isRTL, [`${prefixCls}-vertical`]: newProps.vertical, }, hashId, @@ -168,12 +170,13 @@ const Carousel = React.forwardRef((props, ref) => { dots={enableDots} dotsClass={dsClass} arrows={arrows} - prevArrow={prevArrow} - nextArrow={nextArrow} + prevArrow={prevArrow ?? } + nextArrow={nextArrow ?? } draggable={draggable} verticalSwiping={mergedVertical} autoplaySpeed={autoplaySpeed} waitForAnimate={waitForAnimate} + rtl={isRTL} /> ); diff --git a/components/carousel/style/index.ts b/components/carousel/style/index.ts index 5f87501231..1296abbfff 100644 --- a/components/carousel/style/index.ts +++ b/components/carousel/style/index.ts @@ -417,13 +417,6 @@ const genCarouselRtlStyle: GenerateStyle = (token) => { { [`${componentCls}-rtl`]: { direction: 'rtl', - - // Dots - '.slick-dots': { - [`${componentCls}-rtl&`]: { - flexDirection: 'row-reverse', - }, - }, }, }, { diff --git a/components/dropdown/__tests__/index.test.tsx b/components/dropdown/__tests__/index.test.tsx index f051614f15..611d340df2 100644 --- a/components/dropdown/__tests__/index.test.tsx +++ b/components/dropdown/__tests__/index.test.tsx @@ -472,4 +472,53 @@ describe('Dropdown', () => { expect(itemTitle).toHaveClass(testClassNames.itemTitle); expect(itemTitle).toHaveStyle(testStyles.itemTitle); }); + it('closure item click', () => { + let latestCnt = -1; + + const Demo = () => { + const [cnt, setCnt] = React.useState(0); + + const onOpenChange = () => { + latestCnt = cnt; + }; + + return ( + { + e.stopPropagation(); + setCnt((v) => v + 1); + }} + /> + ), + key: '1', + }, + { + label: , + key: '2', + }, + ], + }} + open + > + + + ); + }; + + const { container } = render(); + + // Change + fireEvent.click(container.querySelector('.bamboo')!); + + // Close + fireEvent.click(container.querySelector('.little')!); + expect(latestCnt).toBe(1); + }); }); diff --git a/components/dropdown/dropdown.tsx b/components/dropdown/dropdown.tsx index 8fe45c44b0..678ef505e4 100644 --- a/components/dropdown/dropdown.tsx +++ b/components/dropdown/dropdown.tsx @@ -242,13 +242,13 @@ const Dropdown: CompoundedComponent = (props) => { borderRadius: token.borderRadius, }); - const onMenuClick = React.useCallback(() => { + const onMenuClick = useEvent(() => { if (menu?.selectable && menu?.multiple) { return; } onOpenChange?.(false, { source: 'menu' }); setOpen(false); - }, [menu?.selectable, menu?.multiple]); + }); const renderOverlay = () => { // @rc-component/dropdown already can process the function of overlay, but we have check logic here. diff --git a/components/input/OTP/OTPInput.tsx b/components/input/OTP/OTPInput.tsx index b51b506bd9..df40482ea3 100644 --- a/components/input/OTP/OTPInput.tsx +++ b/components/input/OTP/OTPInput.tsx @@ -50,13 +50,7 @@ const OTPInput = React.forwardRef((props, ref) => { onActiveChange(index + 1); } else if (key === 'z' && (ctrlKey || metaKey)) { event.preventDefault(); - } - - syncSelection(); - }; - - const onInternalKeyUp: React.KeyboardEventHandler = (e) => { - if (e.key === 'Backspace' && !value) { + } else if (key === 'Backspace' && !value) { onActiveChange(index - 1); } @@ -82,7 +76,6 @@ const OTPInput = React.forwardRef((props, ref) => { onInput={onInternalChange} onFocus={syncSelection} onKeyDown={onInternalKeyDown} - onKeyUp={onInternalKeyUp} onMouseDown={syncSelection} onMouseUp={syncSelection} className={classNames(className, { diff --git a/components/input/style/index.ts b/components/input/style/index.ts index 830fa09c65..4fdec6f07f 100644 --- a/components/input/style/index.ts +++ b/components/input/style/index.ts @@ -659,7 +659,7 @@ const genSearchInputStyle: GenerateStyle = (token: InputToken) => { return { [searchPrefixCls]: { [componentCls]: { - '&:hover, &:focus': { + '&:not([disabled]):hover, &:not([disabled]):focus': { [`+ ${componentCls}-group-addon ${searchPrefixCls}-button:not(${antCls}-btn-color-primary):not(${antCls}-btn-variant-text)`]: { borderInlineStartColor: token.colorPrimaryHover, @@ -695,7 +695,7 @@ const genSearchInputStyle: GenerateStyle = (token: InputToken) => { [`${searchPrefixCls}-button:not(${antCls}-btn-color-primary)`]: { color: token.colorTextDescription, - '&:hover': { + '&:not([disabled]):hover': { color: token.colorPrimaryHover, }, diff --git a/components/theme/interface/seeds.ts b/components/theme/interface/seeds.ts index 98a185ed11..a56aec52fd 100644 --- a/components/theme/interface/seeds.ts +++ b/components/theme/interface/seeds.ts @@ -209,6 +209,8 @@ export interface SeedToken extends PresetColorType { /** * @nameZH 动画基础时长。 * @nameEN Animation Base Duration. + * @desc 动画基础时长。 + * @descEN Animation Base Duration. */ motionBase: number; diff --git a/components/typography/style/mixins.ts b/components/typography/style/mixins.ts index 75307e0926..1ce84b3933 100644 --- a/components/typography/style/mixins.ts +++ b/components/typography/style/mixins.ts @@ -8,7 +8,6 @@ } */ import { gold } from '@ant-design/colors'; -import { unit } from '@ant-design/cssinjs'; import type { CSSObject } from '@ant-design/cssinjs'; import type { TypographyToken } from '.'; @@ -191,8 +190,8 @@ export const getEditableStyles: GenerateStyle = (tok 'div&': { insetInlineStart: token.calc(token.paddingSM).mul(-1).equal(), - marginTop: token.calc(inputShift).mul(-1).equal(), - marginBottom: `calc(1em - ${unit(inputShift)})`, + insetBlockStart: token.calc(inputShift).div(-2).add(1).equal(), + marginBottom: token.calc(inputShift).div(2).sub(2).equal(), }, [`${componentCls}-edit-content-confirm`]: { diff --git a/docs/react/v5-for-19.en-US.md b/docs/react/v5-for-19.en-US.md index 528243607c..c874b1d1fc 100644 --- a/docs/react/v5-for-19.en-US.md +++ b/docs/react/v5-for-19.en-US.md @@ -6,15 +6,15 @@ tag: New :::info{title="Compatibility Interface"} -antd v5 compatibility with React 16 ~ 18 by default. For React 19, you can use the following compatibility methods to adapt. -::: +antd v5 is compatible with React 16 ~ 18 by default, and most features are also compatible with React 19. A few issues are listed below, and the following compatibility methods can be used for adaptation. This method and interface will be removed in v6. +:::: ### React 19 Compatibility Issues -Since React 19 adjusted the export method of `react-dom`, antd cannot directly use the `ReactDOM.render` method. Therefore, using antd will encounter the following problems: +Due to React 19 adjusting the export method of `react-dom`, antd cannot directly use the `ReactDOM.render` method. Therefore, using antd will encounter the following issues: -- Wave effect does not show -- Static methods of `Modal`, `Notification`, `Message` not working +- Wave effect does not work properly +- `Modal`、`Notification`、`Message` and other components' static methods are invalid (hooks invocation methods are not affected). Therefore, you need to use a compatibility configuration to make antd work properly in React 19. diff --git a/docs/react/v5-for-19.zh-CN.md b/docs/react/v5-for-19.zh-CN.md index 396de75197..7615cbb4e2 100644 --- a/docs/react/v5-for-19.zh-CN.md +++ b/docs/react/v5-for-19.zh-CN.md @@ -6,7 +6,7 @@ tag: New :::info{title="兼容接口"} -antd v5 默认兼容 React 16 ~ 18 版本,对于 React 19 版本,可以使用以下兼容方法进行适配。该兼容方式以及接口将在 v6 被移除。 +antd v5 默认兼容 React 16 ~ 18 版本,绝大多数功能也兼容于 React 19 版本。少数问题如下所示,可使用以下兼容方法进行适配。该方式以及接口将在 v6 被移除。 ::: ### React 19 兼容问题 @@ -14,7 +14,7 @@ antd v5 默认兼容 React 16 ~ 18 版本,对于 React 19 版本,可以使 由于 React 19 调整了 `react-dom` 的导出方式,导致 antd 无法直接使用 `ReactDOM.render` 方法。因而使用 antd 会遇到以下问题: - 波纹特效无法正常工作 -- `Modal`、`Notification`、`Message` 等组件的静态方法无效 +- `Modal`、`Notification`、`Message` 等组件的静态方法无效(hooks 调用方式不受影响) 因而需要通过兼容配置,使 antd 在 React 19 中正常工作。 diff --git a/package.json b/package.json index 606ef9c052..a2c265524a 100644 --- a/package.json +++ b/package.json @@ -181,7 +181,7 @@ "@inquirer/prompts": "^7.1.0", "@madccc/duplicate-package-checker-webpack-plugin": "^1.0.0", "@microflash/rehype-figure": "^2.1.1", - "@npmcli/run-script": "^9.0.1", + "@npmcli/run-script": "^10.0.0", "@octokit/rest": "^22.0.0", "@prettier/sync": "^0.6.1", "@qixian.cs/github-contributors-list": "^2.0.2", diff --git a/scripts/generate-token-meta.ts b/scripts/generate-token-meta.ts index 04178b400d..cd99391932 100644 --- a/scripts/generate-token-meta.ts +++ b/scripts/generate-token-meta.ts @@ -40,6 +40,18 @@ function getTokenList(list?: DeclarationReflection[], source?: string) { })); } +function getPresetColorsTokenList(presetColors: string[]) { + return presetColors.map((item) => ({ + source: 'seed', + token: item, + type: 'color', + desc: `预设${item}颜色`, + descEn: `Preset ${item} color`, + name: `预设${item}颜色`, + nameEn: `Preset ${item} color`, + })); +} + const main = async () => { const app = await (Application as any).bootstrap( { @@ -79,10 +91,11 @@ const main = async () => { } }); - // Exclude preset colors - tokenMeta.seed = tokenMeta.seed.filter( - (item) => !presetColors.some((color) => item.token.startsWith(color)), - ); + // Exclude preset colors e.g. 'blue' 'blue-1' 'blue-2' ... + tokenMeta.seed = tokenMeta.seed + .filter((item) => !presetColors.some((color) => item.token.startsWith(color))) + // Incorporate preset colors + .concat(getPresetColorsTokenList(presetColors)); tokenMeta.map = tokenMeta.map.filter( (item) => !presetColors.some((color) => item.token.startsWith(color)), );