From 2f6846d2a5119fa69233bccb3c2bc96cc6efd9f4 Mon Sep 17 00:00:00 2001 From: ustcfury <94168407+ustcfury@users.noreply.github.com> Date: Thu, 6 Nov 2025 10:21:35 +0800 Subject: [PATCH 1/5] fix: Select underlined height (#55607) Co-authored-by: furyzhao --- components/select/style/variants.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/components/select/style/variants.ts b/components/select/style/variants.ts index a653710a0f..a7b942568e 100644 --- a/components/select/style/variants.ts +++ b/components/select/style/variants.ts @@ -242,20 +242,20 @@ const genBaseUnderlinedStyle = ( return { [`&:not(${componentCls}-customize-input) ${componentCls}-selector`]: { - borderWidth: `0 0 ${unit(token.lineWidth)} 0`, - borderStyle: `none none ${token.lineType} none`, - borderColor: options.borderColor, + borderWidth: `${unit(token.lineWidth)} 0`, + borderStyle: `${token.lineType} none`, + borderColor: `transparent transparent ${options.borderColor} transparent`, background: token.selectorBg, borderRadius: 0, }, [`&:not(${componentCls}-disabled):not(${componentCls}-customize-input):not(${antCls}-pagination-size-changer)`]: { [`&:hover ${componentCls}-selector`]: { - borderColor: options.hoverBorderHover, + borderColor: `transparent transparent ${options.hoverBorderHover} transparent`, }, [`${componentCls}-focused& ${componentCls}-selector`]: { - borderColor: options.activeBorderColor, + borderColor: `transparent transparent ${options.activeBorderColor} transparent`, outline: 0, }, [`${componentCls}-prefix`]: { From 44526a56a7208eda5bc0c36b425d955a6401c975 Mon Sep 17 00:00:00 2001 From: Wanpan Date: Fri, 7 Nov 2025 19:05:35 +0800 Subject: [PATCH 2/5] fix: use dotActiveWidth token (#55615) --- components/carousel/style/index.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/components/carousel/style/index.ts b/components/carousel/style/index.ts index b2cc0479bf..c35e2dd022 100644 --- a/components/carousel/style/index.ts +++ b/components/carousel/style/index.ts @@ -351,7 +351,7 @@ const genCarouselVerticalStyle: GenerateStyle = (token) => { height: 0, }, to: { - height: token.dotWidth, + height: token.dotActiveWidth, }, }); @@ -416,8 +416,12 @@ const genCarouselVerticalStyle: GenerateStyle = (token) => { '&.slick-active': { ...reverseSizeOfDot, + height: token.dotActiveWidth, - button: reverseSizeOfDot, + button: { + ...reverseSizeOfDot, + height: token.dotActiveWidth, + }, '&::after': { ...reverseSizeOfDot, From 2b12ef528d167a05d60923579ed2c6fd0249f1b3 Mon Sep 17 00:00:00 2001 From: TianHua Liu <61778232+Taoister39@users.noreply.github.com> Date: Sat, 8 Nov 2025 00:07:19 +0800 Subject: [PATCH 3/5] fix: clicking does nothing after occur error in actionFn (#55519) --- components/_util/ActionButton.tsx | 16 ++- .../popconfirm/__tests__/index.test.tsx | 115 ++++++++++++++++++ 2 files changed, 130 insertions(+), 1 deletion(-) diff --git a/components/_util/ActionButton.tsx b/components/_util/ActionButton.tsx index 48c8f5f020..04e014278c 100644 --- a/components/_util/ActionButton.tsx +++ b/components/_util/ActionButton.tsx @@ -98,8 +98,22 @@ const ActionButton: React.FC = (props) => { return; } let returnValueOfOnOk: PromiseLike; + // Currently only Popconfirm passes `emitEvent` as true if (emitEvent) { - returnValueOfOnOk = actionFn(e); + // if actionFn has been throw error, just reset clickedRef + try { + returnValueOfOnOk = actionFn(e); + } catch (error) { + clickedRef.current = false; + + // Log error for debugging unless in silent mode + if (!isSilent?.()) { + console.error('Error in actionFn:', error); + } + + return; + } + if (quitOnNullishReturnValue && !isThenable(returnValueOfOnOk)) { clickedRef.current = false; onInternalClose(e); diff --git a/components/popconfirm/__tests__/index.test.tsx b/components/popconfirm/__tests__/index.test.tsx index 13e0985d30..856dc912eb 100644 --- a/components/popconfirm/__tests__/index.test.tsx +++ b/components/popconfirm/__tests__/index.test.tsx @@ -407,4 +407,119 @@ describe('Popconfirm', () => { expect(popconfirmElement).toHaveStyle({ padding: '20px' }); expect(popconfirmBodyElement).toHaveStyle({ padding: '10px' }); }); + + it('should allow retry after onConfirm throws synchronous error', async () => { + const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + let callCount = 0; + const onConfirm = jest.fn(() => { + callCount += 1; + if (callCount === 1) { + throw new Error('Test synchronous error'); + } + return Promise.resolve(); + }); + + const { container } = render( + + + , + ); + + const triggerNode = container.querySelector('.trigger')!; + + // Open popconfirm + fireEvent.click(triggerNode); + await waitFakeTimer(); + + const okButton = container.querySelector('.ant-btn-primary')!; + + // First click - should throw error + fireEvent.click(okButton); + await waitFakeTimer(); + + expect(onConfirm).toHaveBeenCalledTimes(1); + expect(consoleErrorSpy).toHaveBeenCalledWith('Error in actionFn:', expect.any(Error)); + + // Popconfirm should still be open after error + expect(container.querySelector('.ant-popover')).toBeTruthy(); + + // Second click - should succeed + fireEvent.click(okButton); + await waitFakeTimer(); + + expect(onConfirm).toHaveBeenCalledTimes(2); + + consoleErrorSpy.mockRestore(); + }); + + it('should not log error when onConfirm throws error in silent mode', async () => { + const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + const onConfirm = jest.fn(() => { + throw new Error('Test error in silent mode'); + }); + + // Create a wrapper component that simulates silent mode + const TestComponent = () => { + const [open, setOpen] = React.useState(false); + + return ( + + + + ); + }; + + const { container } = render(); + + const triggerNode = container.querySelector('.trigger')!; + + // Open popconfirm + fireEvent.click(triggerNode); + await waitFakeTimer(); + + const okButton = container.querySelector('.ant-btn-primary')!; + + // Click OK button - should throw error but not log in normal case + fireEvent.click(okButton); + await waitFakeTimer(); + + expect(onConfirm).toHaveBeenCalledTimes(1); + // In current implementation, error should be logged + expect(consoleErrorSpy).toHaveBeenCalled(); + + consoleErrorSpy.mockRestore(); + }); + + it('should reset button loading state after synchronous error', async () => { + const onConfirm = jest.fn(() => { + throw new Error('Synchronous error'); + }); + + const { container } = render( + + + , + ); + + const triggerNode = container.querySelector('.trigger')!; + + // Open popconfirm + fireEvent.click(triggerNode); + await waitFakeTimer(); + + const okButton = container.querySelector('.ant-btn-primary')! as HTMLButtonElement; + + // Click OK button - should throw error + fireEvent.click(okButton); + await waitFakeTimer(); + + // Button should not be in loading state after error + expect(okButton.querySelector('.ant-btn-loading-icon')).toBeFalsy(); + + // Should be able to click again + fireEvent.click(okButton); + await waitFakeTimer(); + + expect(onConfirm).toHaveBeenCalledTimes(2); + }); }); From ce9a5dc2fd9a4eb3a1d9e75639fece1ade5dffeb Mon Sep 17 00:00:00 2001 From: Tony <1821955020@qq.com> Date: Sat, 8 Nov 2025 08:41:55 +0800 Subject: [PATCH 4/5] docs: add classNames and styles to Tooltip shared API documentation (#55645) --- components/tooltip/shared/sharedProps.en-US.md | 2 ++ components/tooltip/shared/sharedProps.zh-CN.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/components/tooltip/shared/sharedProps.en-US.md b/components/tooltip/shared/sharedProps.en-US.md index 5a55634b8a..d8085be201 100644 --- a/components/tooltip/shared/sharedProps.en-US.md +++ b/components/tooltip/shared/sharedProps.en-US.md @@ -6,6 +6,7 @@ | align | Please refer to the settings [dom-align](https://github.com/yiminghe/dom-align) | object | - | | | arrow | Change arrow's visible state and change whether the arrow is pointed at the center of target. | boolean \| { pointAtCenter: boolean } | true | 5.2.0 | | autoAdjustOverflow | Whether to adjust popup placement automatically when popup is off screen | boolean | true | | +| classNames | Semantic DOM class | [Record](#semantic-dom) | - | 5.23.0 | | color | The background color | string | - | 4.3.0 | | defaultOpen | Whether the floating tooltip card is open by default | boolean | false | 4.23.0 | | ~~destroyTooltipOnHide~~ | Whether destroy dom when close | boolean | false | | @@ -18,6 +19,7 @@ | ~~overlayStyle~~ | Style of the tooltip card, please use `styles={{ root: {} }}` | React.CSSProperties | - | | | ~~overlayInnerStyle~~ | Style of the tooltip inner content, please use `styles={{ body: {} }}` | React.CSSProperties | - | | | placement | The position of the tooltip relative to the target, which can be one of `top` `left` `right` `bottom` `topLeft` `topRight` `bottomLeft` `bottomRight` `leftTop` `leftBottom` `rightTop` `rightBottom` | string | `top` | | +| styles | Semantic DOM style | [Record](#semantic-dom) | - | 5.23.0 | | trigger | Tooltip trigger mode. Could be multiple by passing an array | `hover` \| `focus` \| `click` \| `contextMenu` \| Array<string> | `hover` | | | open | Whether the floating tooltip card is open or not. Use `visible` under 4.23.0 ([why?](/docs/react/faq#why-open)) | boolean | false | 4.23.0 | | zIndex | Config `z-index` of Tooltip | number | - | | diff --git a/components/tooltip/shared/sharedProps.zh-CN.md b/components/tooltip/shared/sharedProps.zh-CN.md index 940a5d8b14..972d67c341 100644 --- a/components/tooltip/shared/sharedProps.zh-CN.md +++ b/components/tooltip/shared/sharedProps.zh-CN.md @@ -6,6 +6,7 @@ | align | 请参考 [dom-align](https://github.com/yiminghe/dom-align) 进行配置 | object | - | | | arrow | 修改箭头的显示状态以及修改箭头是否指向目标元素中心 | boolean \| { pointAtCenter: boolean } | true | 5.2.0 | | autoAdjustOverflow | 气泡被遮挡时自动调整位置 | boolean | true | | +| classNames | 语义化结构 class | [Record](#semantic-dom) | - | 5.23.0 | | color | 背景颜色 | string | - | 4.3.0 | | defaultOpen | 默认是否显隐 | boolean | false | 4.23.0 | | ~~destroyTooltipOnHide~~ | 关闭后是否销毁 dom | boolean | false | | @@ -18,6 +19,7 @@ | ~~overlayStyle~~ | 卡片样式, 请使用 `styles={{ root: {} }}` 替换| React.CSSProperties | - | | | ~~overlayInnerStyle~~ | 卡片内容区域的样式对象, 请使用 `styles={{ body: {} }}` 替换 | React.CSSProperties | - | | | placement | 气泡框位置,可选 `top` `left` `right` `bottom` `topLeft` `topRight` `bottomLeft` `bottomRight` `leftTop` `leftBottom` `rightTop` `rightBottom` | string | `top` | | +| styles | 语义化结构 style | [Record](#semantic-dom) | - | 5.23.0 | | trigger | 触发行为,可选 `hover` \| `focus` \| `click` \| `contextMenu`,可使用数组设置多个触发行为 | string \| string\[] | `hover` | | | open | 用于手动控制浮层显隐,小于 4.23.0 使用 `visible`([为什么?](/docs/react/faq#弹层类组件为什么要统一至-open-属性)) | boolean | false | 4.23.0 | | zIndex | 设置 Tooltip 的 `z-index` | number | - | | From a4b8185a74840a4d2334abbfecbe17af96ba931b Mon Sep 17 00:00:00 2001 From: afc163 Date: Sun, 9 Nov 2025 01:21:02 +0800 Subject: [PATCH 5/5] Revert "fix: clicking does nothing after occur error in actionFn (#55519)" (#55651) This reverts commit 2b12ef528d167a05d60923579ed2c6fd0249f1b3. --- components/_util/ActionButton.tsx | 16 +-- .../popconfirm/__tests__/index.test.tsx | 115 ------------------ 2 files changed, 1 insertion(+), 130 deletions(-) diff --git a/components/_util/ActionButton.tsx b/components/_util/ActionButton.tsx index 04e014278c..48c8f5f020 100644 --- a/components/_util/ActionButton.tsx +++ b/components/_util/ActionButton.tsx @@ -98,22 +98,8 @@ const ActionButton: React.FC = (props) => { return; } let returnValueOfOnOk: PromiseLike; - // Currently only Popconfirm passes `emitEvent` as true if (emitEvent) { - // if actionFn has been throw error, just reset clickedRef - try { - returnValueOfOnOk = actionFn(e); - } catch (error) { - clickedRef.current = false; - - // Log error for debugging unless in silent mode - if (!isSilent?.()) { - console.error('Error in actionFn:', error); - } - - return; - } - + returnValueOfOnOk = actionFn(e); if (quitOnNullishReturnValue && !isThenable(returnValueOfOnOk)) { clickedRef.current = false; onInternalClose(e); diff --git a/components/popconfirm/__tests__/index.test.tsx b/components/popconfirm/__tests__/index.test.tsx index 856dc912eb..13e0985d30 100644 --- a/components/popconfirm/__tests__/index.test.tsx +++ b/components/popconfirm/__tests__/index.test.tsx @@ -407,119 +407,4 @@ describe('Popconfirm', () => { expect(popconfirmElement).toHaveStyle({ padding: '20px' }); expect(popconfirmBodyElement).toHaveStyle({ padding: '10px' }); }); - - it('should allow retry after onConfirm throws synchronous error', async () => { - const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); - let callCount = 0; - const onConfirm = jest.fn(() => { - callCount += 1; - if (callCount === 1) { - throw new Error('Test synchronous error'); - } - return Promise.resolve(); - }); - - const { container } = render( - - - , - ); - - const triggerNode = container.querySelector('.trigger')!; - - // Open popconfirm - fireEvent.click(triggerNode); - await waitFakeTimer(); - - const okButton = container.querySelector('.ant-btn-primary')!; - - // First click - should throw error - fireEvent.click(okButton); - await waitFakeTimer(); - - expect(onConfirm).toHaveBeenCalledTimes(1); - expect(consoleErrorSpy).toHaveBeenCalledWith('Error in actionFn:', expect.any(Error)); - - // Popconfirm should still be open after error - expect(container.querySelector('.ant-popover')).toBeTruthy(); - - // Second click - should succeed - fireEvent.click(okButton); - await waitFakeTimer(); - - expect(onConfirm).toHaveBeenCalledTimes(2); - - consoleErrorSpy.mockRestore(); - }); - - it('should not log error when onConfirm throws error in silent mode', async () => { - const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); - const onConfirm = jest.fn(() => { - throw new Error('Test error in silent mode'); - }); - - // Create a wrapper component that simulates silent mode - const TestComponent = () => { - const [open, setOpen] = React.useState(false); - - return ( - - - - ); - }; - - const { container } = render(); - - const triggerNode = container.querySelector('.trigger')!; - - // Open popconfirm - fireEvent.click(triggerNode); - await waitFakeTimer(); - - const okButton = container.querySelector('.ant-btn-primary')!; - - // Click OK button - should throw error but not log in normal case - fireEvent.click(okButton); - await waitFakeTimer(); - - expect(onConfirm).toHaveBeenCalledTimes(1); - // In current implementation, error should be logged - expect(consoleErrorSpy).toHaveBeenCalled(); - - consoleErrorSpy.mockRestore(); - }); - - it('should reset button loading state after synchronous error', async () => { - const onConfirm = jest.fn(() => { - throw new Error('Synchronous error'); - }); - - const { container } = render( - - - , - ); - - const triggerNode = container.querySelector('.trigger')!; - - // Open popconfirm - fireEvent.click(triggerNode); - await waitFakeTimer(); - - const okButton = container.querySelector('.ant-btn-primary')! as HTMLButtonElement; - - // Click OK button - should throw error - fireEvent.click(okButton); - await waitFakeTimer(); - - // Button should not be in loading state after error - expect(okButton.querySelector('.ant-btn-loading-icon')).toBeFalsy(); - - // Should be able to click again - fireEvent.click(okButton); - await waitFakeTimer(); - - expect(onConfirm).toHaveBeenCalledTimes(2); - }); });