From 579d037b795144519aab26ad1568eb83f1ee7661 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Thu, 17 Jul 2025 14:16:53 +0800 Subject: [PATCH 01/14] feat: Merge 'AfterClose' to 'Closable' --- README.md | 3 +-- src/Dialog/index.tsx | 4 ++-- src/DialogWrap.tsx | 5 +++-- src/IDialogPropTypes.tsx | 9 ++++++++- tests/index.spec.tsx | 34 ++++++++++++++++++++++++++++++++++ 5 files changed, 48 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 6b3dc134..9eb9617a 100644 --- a/README.md +++ b/README.md @@ -64,13 +64,12 @@ ReactDOM.render( | maskTransitionName | String | | mask animation css class name | | | title | String\|React.Element | | Title of the dialog | | | footer | React.Element | | footer of the dialog | | -| closable | Boolean \| ({ closeIcon?: React.ReactNode; disabled?: boolean } & React.AriaAttributes | true | whether show close button | | +| closable | Boolean \| ({ closeIcon?: React.ReactNode; disabled?: boolean, afterClose:function } & React.AriaAttributes) | true | whether show close button | | | mask | Boolean | true | whether show mask | | | maskClosable | Boolean | true | whether click mask to close | | | keyboard | Boolean | true | whether support press esc to close | | | mousePosition | {x:number,y:number} | | set pageX and pageY of current mouse(it will cause transform origin to be set). | | | onClose | function() | | called when click close button or mask | | -| afterClose | function() | | called when close animation end | | | getContainer | function(): HTMLElement | | to determine where Dialog will be mounted | | | destroyOnHidden | Boolean | false | to unmount child compenents on onClose | | | closeIcon | ReactNode | | specific the close icon. | | diff --git a/src/Dialog/index.tsx b/src/Dialog/index.tsx index 3512e772..7ac31ead 100644 --- a/src/Dialog/index.tsx +++ b/src/Dialog/index.tsx @@ -5,7 +5,7 @@ import KeyCode from '@rc-component/util/lib/KeyCode'; import pickAttrs from '@rc-component/util/lib/pickAttrs'; import * as React from 'react'; import { useEffect, useRef } from 'react'; -import type { IDialogPropTypes } from '../IDialogPropTypes'; +import type { ClosableType, IDialogPropTypes } from '../IDialogPropTypes'; import { getMotionName } from '../util'; import Content, { type ContentRef } from './Content'; import Mask from './Mask'; @@ -95,7 +95,7 @@ const Dialog: React.FC = (props) => { // Trigger afterClose only when change visible from true to false if (animatedVisible) { - afterClose?.(); + ((closable as ClosableType)?.afterClose ?? afterClose)?.(); } } diff --git a/src/DialogWrap.tsx b/src/DialogWrap.tsx index 58db3a5c..c022e7f5 100644 --- a/src/DialogWrap.tsx +++ b/src/DialogWrap.tsx @@ -2,7 +2,7 @@ import Portal from '@rc-component/portal'; import * as React from 'react'; import { RefContext } from './context'; import Dialog from './Dialog'; -import type { IDialogPropTypes } from './IDialogPropTypes'; +import type { ClosableType, IDialogPropTypes } from './IDialogPropTypes'; // fix issue #10656 /* @@ -20,6 +20,7 @@ const DialogWrap: React.FC = (props) => { forceRender, destroyOnHidden = false, afterClose, + closable, panelRef, } = props; const [animatedVisible, setAnimatedVisible] = React.useState(visible); @@ -49,7 +50,7 @@ const DialogWrap: React.FC = (props) => { {...props} destroyOnHidden={destroyOnHidden} afterClose={() => { - afterClose?.(); + ((closable as ClosableType)?.afterClose ?? afterClose)?.(); setAnimatedVisible(false); }} /> diff --git a/src/IDialogPropTypes.tsx b/src/IDialogPropTypes.tsx index 8a5b305c..5ada9259 100644 --- a/src/IDialogPropTypes.tsx +++ b/src/IDialogPropTypes.tsx @@ -7,6 +7,12 @@ export type ModalClassNames = Partial>; export type ModalStyles = Partial>; +export type ClosableType = { + closeIcon?: React.ReactNode; + disabled?: boolean; + afterClose?: () => any; +}; + export type IDialogPropTypes = { className?: string; keyboard?: boolean; @@ -14,10 +20,11 @@ export type IDialogPropTypes = { rootStyle?: CSSProperties; mask?: boolean; children?: React.ReactNode; + /** @description please use `closable.afterClose` instead */ afterClose?: () => any; afterOpenChange?: (open: boolean) => void; onClose?: (e: SyntheticEvent) => any; - closable?: boolean | ({ closeIcon?: React.ReactNode; disabled?: boolean } & React.AriaAttributes); + closable?: boolean | (ClosableType & React.AriaAttributes); maskClosable?: boolean; visible?: boolean; destroyOnHidden?: boolean; diff --git a/tests/index.spec.tsx b/tests/index.spec.tsx index e6c075ea..0e572b6e 100644 --- a/tests/index.spec.tsx +++ b/tests/index.spec.tsx @@ -552,6 +552,40 @@ describe('dialog', () => { expect(afterClose).toHaveBeenCalledTimes(0); }); + + it('should trigger closable.afterClose when using new API', () => { + const afterClose = jest.fn(); + + const { rerender } = render(); + act(() => { + jest.runAllTimers(); + }); + + rerender(); + act(() => { + jest.runAllTimers(); + }); + expect(afterClose).toHaveBeenCalledTimes(1); + }); + + it('should prioritize closable.afterClose when both exist', () => { + const afterClose = jest.fn(); + const legacyAfterClose = jest.fn(); + + const { rerender } = render( + , + ); + act(() => { + jest.runAllTimers(); + }); + + rerender(); + act(() => { + jest.runAllTimers(); + }); + expect(afterClose).toHaveBeenCalledTimes(1); + expect(legacyAfterClose).toHaveBeenCalledTimes(0); + }); }); describe('afterOpenChange', () => { From 9f42c3ff4996927aa2cedce85c0844ede00c00fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Thu, 17 Jul 2025 14:22:35 +0800 Subject: [PATCH 02/14] docs: Add a through line to afterClose --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 9eb9617a..d7eb340e 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,7 @@ ReactDOM.render( | keyboard | Boolean | true | whether support press esc to close | | | mousePosition | {x:number,y:number} | | set pageX and pageY of current mouse(it will cause transform origin to be set). | | | onClose | function() | | called when click close button or mask | | +| ~~afterClose~~ | function() | | called when close animation end | | | getContainer | function(): HTMLElement | | to determine where Dialog will be mounted | | | destroyOnHidden | Boolean | false | to unmount child compenents on onClose | | | closeIcon | ReactNode | | specific the close icon. | | From a7859663803349241ad5b1056e2ef71738904a8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Thu, 17 Jul 2025 15:11:40 +0800 Subject: [PATCH 03/14] feat: change closable AfterClose ts --- src/Dialog/index.tsx | 3 ++- src/DialogWrap.tsx | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Dialog/index.tsx b/src/Dialog/index.tsx index 7ac31ead..2bd46958 100644 --- a/src/Dialog/index.tsx +++ b/src/Dialog/index.tsx @@ -95,7 +95,8 @@ const Dialog: React.FC = (props) => { // Trigger afterClose only when change visible from true to false if (animatedVisible) { - ((closable as ClosableType)?.afterClose ?? afterClose)?.(); + const { afterClose: closableAfterClose } = typeof closable === 'object' ? closable : {}; + (closableAfterClose ?? afterClose)?.(); } } diff --git a/src/DialogWrap.tsx b/src/DialogWrap.tsx index c022e7f5..a3594d93 100644 --- a/src/DialogWrap.tsx +++ b/src/DialogWrap.tsx @@ -50,7 +50,8 @@ const DialogWrap: React.FC = (props) => { {...props} destroyOnHidden={destroyOnHidden} afterClose={() => { - ((closable as ClosableType)?.afterClose ?? afterClose)?.(); + const { afterClose: closableAfterClose } = typeof closable === 'object' ? closable : {}; + (closableAfterClose ?? afterClose)?.(); setAnimatedVisible(false); }} /> From 64b952f7c859f5fa9288d709d4eeb7a0696954ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Thu, 17 Jul 2025 15:24:20 +0800 Subject: [PATCH 04/14] fix: lint error --- src/Dialog/index.tsx | 2 +- src/DialogWrap.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Dialog/index.tsx b/src/Dialog/index.tsx index 2bd46958..ddf37a4b 100644 --- a/src/Dialog/index.tsx +++ b/src/Dialog/index.tsx @@ -5,7 +5,7 @@ import KeyCode from '@rc-component/util/lib/KeyCode'; import pickAttrs from '@rc-component/util/lib/pickAttrs'; import * as React from 'react'; import { useEffect, useRef } from 'react'; -import type { ClosableType, IDialogPropTypes } from '../IDialogPropTypes'; +import type { IDialogPropTypes } from '../IDialogPropTypes'; import { getMotionName } from '../util'; import Content, { type ContentRef } from './Content'; import Mask from './Mask'; diff --git a/src/DialogWrap.tsx b/src/DialogWrap.tsx index a3594d93..ad500216 100644 --- a/src/DialogWrap.tsx +++ b/src/DialogWrap.tsx @@ -2,7 +2,7 @@ import Portal from '@rc-component/portal'; import * as React from 'react'; import { RefContext } from './context'; import Dialog from './Dialog'; -import type { ClosableType, IDialogPropTypes } from './IDialogPropTypes'; +import type { IDialogPropTypes } from './IDialogPropTypes'; // fix issue #10656 /* From 713af57c5ac74a6b868e5942e762cf17f2655c27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Thu, 17 Jul 2025 16:26:32 +0800 Subject: [PATCH 05/14] feat: add default value to closableAfterClose --- src/Dialog/index.tsx | 3 ++- src/DialogWrap.tsx | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Dialog/index.tsx b/src/Dialog/index.tsx index ddf37a4b..09de0f5d 100644 --- a/src/Dialog/index.tsx +++ b/src/Dialog/index.tsx @@ -95,7 +95,8 @@ const Dialog: React.FC = (props) => { // Trigger afterClose only when change visible from true to false if (animatedVisible) { - const { afterClose: closableAfterClose } = typeof closable === 'object' ? closable : {}; + const { afterClose: closableAfterClose = undefined } = + typeof closable === 'object' ? closable : {}; (closableAfterClose ?? afterClose)?.(); } } diff --git a/src/DialogWrap.tsx b/src/DialogWrap.tsx index ad500216..c98e30b0 100644 --- a/src/DialogWrap.tsx +++ b/src/DialogWrap.tsx @@ -50,7 +50,8 @@ const DialogWrap: React.FC = (props) => { {...props} destroyOnHidden={destroyOnHidden} afterClose={() => { - const { afterClose: closableAfterClose } = typeof closable === 'object' ? closable : {}; + const { afterClose: closableAfterClose = undefined } = + typeof closable === 'object' ? closable : {}; (closableAfterClose ?? afterClose)?.(); setAnimatedVisible(false); }} From 32c4e06cfde1709cd72b165ad2b2526a5b1ac2a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Tue, 22 Jul 2025 10:15:33 +0800 Subject: [PATCH 06/14] feat: Call separately --- src/Dialog/index.tsx | 4 +--- src/DialogWrap.tsx | 3 ++- tests/index.spec.tsx | 17 +---------------- 3 files changed, 4 insertions(+), 20 deletions(-) diff --git a/src/Dialog/index.tsx b/src/Dialog/index.tsx index 09de0f5d..3512e772 100644 --- a/src/Dialog/index.tsx +++ b/src/Dialog/index.tsx @@ -95,9 +95,7 @@ const Dialog: React.FC = (props) => { // Trigger afterClose only when change visible from true to false if (animatedVisible) { - const { afterClose: closableAfterClose = undefined } = - typeof closable === 'object' ? closable : {}; - (closableAfterClose ?? afterClose)?.(); + afterClose?.(); } } diff --git a/src/DialogWrap.tsx b/src/DialogWrap.tsx index c98e30b0..9d3f338c 100644 --- a/src/DialogWrap.tsx +++ b/src/DialogWrap.tsx @@ -52,7 +52,8 @@ const DialogWrap: React.FC = (props) => { afterClose={() => { const { afterClose: closableAfterClose = undefined } = typeof closable === 'object' ? closable : {}; - (closableAfterClose ?? afterClose)?.(); + closableAfterClose?.(); + afterClose?.(); setAnimatedVisible(false); }} /> diff --git a/tests/index.spec.tsx b/tests/index.spec.tsx index 0e572b6e..1f233f41 100644 --- a/tests/index.spec.tsx +++ b/tests/index.spec.tsx @@ -553,21 +553,6 @@ describe('dialog', () => { expect(afterClose).toHaveBeenCalledTimes(0); }); - it('should trigger closable.afterClose when using new API', () => { - const afterClose = jest.fn(); - - const { rerender } = render(); - act(() => { - jest.runAllTimers(); - }); - - rerender(); - act(() => { - jest.runAllTimers(); - }); - expect(afterClose).toHaveBeenCalledTimes(1); - }); - it('should prioritize closable.afterClose when both exist', () => { const afterClose = jest.fn(); const legacyAfterClose = jest.fn(); @@ -584,7 +569,7 @@ describe('dialog', () => { jest.runAllTimers(); }); expect(afterClose).toHaveBeenCalledTimes(1); - expect(legacyAfterClose).toHaveBeenCalledTimes(0); + expect(legacyAfterClose).toHaveBeenCalledTimes(1); }); }); From 0a6a67df934bc1bd7dcfe611019bc7d4e76b2948 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Tue, 22 Jul 2025 10:57:56 +0800 Subject: [PATCH 07/14] fix: closable !== null --- src/DialogWrap.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DialogWrap.tsx b/src/DialogWrap.tsx index 9d3f338c..c34ab4c4 100644 --- a/src/DialogWrap.tsx +++ b/src/DialogWrap.tsx @@ -51,7 +51,7 @@ const DialogWrap: React.FC = (props) => { destroyOnHidden={destroyOnHidden} afterClose={() => { const { afterClose: closableAfterClose = undefined } = - typeof closable === 'object' ? closable : {}; + typeof closable === 'object' && closable !== null ? closable : {}; closableAfterClose?.(); afterClose?.(); setAnimatedVisible(false); From 9607d1b6060935aba86a399a876085d8a32516b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Tue, 22 Jul 2025 11:06:53 +0800 Subject: [PATCH 08/14] feat: change ts --- README.md | 2 +- src/IDialogPropTypes.tsx | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index d7eb340e..e1fb934d 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ ReactDOM.render( | keyboard | Boolean | true | whether support press esc to close | | | mousePosition | {x:number,y:number} | | set pageX and pageY of current mouse(it will cause transform origin to be set). | | | onClose | function() | | called when click close button or mask | | -| ~~afterClose~~ | function() | | called when close animation end | | +| afterClose | function() | | called when close animation end | | | getContainer | function(): HTMLElement | | to determine where Dialog will be mounted | | | destroyOnHidden | Boolean | false | to unmount child compenents on onClose | | | closeIcon | ReactNode | | specific the close icon. | | diff --git a/src/IDialogPropTypes.tsx b/src/IDialogPropTypes.tsx index 5ada9259..6a9c47d1 100644 --- a/src/IDialogPropTypes.tsx +++ b/src/IDialogPropTypes.tsx @@ -20,7 +20,6 @@ export type IDialogPropTypes = { rootStyle?: CSSProperties; mask?: boolean; children?: React.ReactNode; - /** @description please use `closable.afterClose` instead */ afterClose?: () => any; afterOpenChange?: (open: boolean) => void; onClose?: (e: SyntheticEvent) => any; From 0d8071326e126a5f8377853ec4ca6d08411e1c04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Thu, 24 Jul 2025 15:29:36 +0800 Subject: [PATCH 09/14] feat: closable null --- src/DialogWrap.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DialogWrap.tsx b/src/DialogWrap.tsx index c34ab4c4..707b06e9 100644 --- a/src/DialogWrap.tsx +++ b/src/DialogWrap.tsx @@ -50,8 +50,8 @@ const DialogWrap: React.FC = (props) => { {...props} destroyOnHidden={destroyOnHidden} afterClose={() => { - const { afterClose: closableAfterClose = undefined } = - typeof closable === 'object' && closable !== null ? closable : {}; + const { afterClose: closableAfterClose } = + typeof closable === 'object' && closable ? closable : {}; closableAfterClose?.(); afterClose?.(); setAnimatedVisible(false); From cb629f3a3df88da436e9b7be02c48e18b221e4c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Thu, 24 Jul 2025 16:13:11 +0800 Subject: [PATCH 10/14] feat: null --- src/DialogWrap.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DialogWrap.tsx b/src/DialogWrap.tsx index 707b06e9..819be69d 100644 --- a/src/DialogWrap.tsx +++ b/src/DialogWrap.tsx @@ -51,7 +51,7 @@ const DialogWrap: React.FC = (props) => { destroyOnHidden={destroyOnHidden} afterClose={() => { const { afterClose: closableAfterClose } = - typeof closable === 'object' && closable ? closable : {}; + closable && typeof closable === 'object' ? closable : {}; closableAfterClose?.(); afterClose?.(); setAnimatedVisible(false); From 47f3e4073d473be9ebea1d4434315c0029cece40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Thu, 24 Jul 2025 16:42:06 +0800 Subject: [PATCH 11/14] feat: useEffect dependencies --- src/Dialog/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Dialog/index.tsx b/src/Dialog/index.tsx index 3512e772..91153d22 100644 --- a/src/Dialog/index.tsx +++ b/src/Dialog/index.tsx @@ -167,7 +167,7 @@ const Dialog: React.FC = (props) => { ) { doClose(); } - }, [visible]); + }, [visible, animatedVisible, doClose]); // Remove direct should also check the scroll bar update useEffect( From 8ff145220ebe104ad648d3f2acfde4a152fbfd6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Thu, 24 Jul 2025 16:48:56 +0800 Subject: [PATCH 12/14] fix: lint error --- src/DialogWrap.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DialogWrap.tsx b/src/DialogWrap.tsx index 819be69d..7d337eaa 100644 --- a/src/DialogWrap.tsx +++ b/src/DialogWrap.tsx @@ -51,7 +51,7 @@ const DialogWrap: React.FC = (props) => { destroyOnHidden={destroyOnHidden} afterClose={() => { const { afterClose: closableAfterClose } = - closable && typeof closable === 'object' ? closable : {}; + (typeof closable === 'object' ? closable : {}) || {}; closableAfterClose?.(); afterClose?.(); setAnimatedVisible(false); From c1e9939bacf33a4e34b6a521701e83ca0c98d1a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Thu, 24 Jul 2025 17:00:38 +0800 Subject: [PATCH 13/14] fix: lint error (Initializer provides no value) --- src/DialogWrap.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DialogWrap.tsx b/src/DialogWrap.tsx index 7d337eaa..36c20bf8 100644 --- a/src/DialogWrap.tsx +++ b/src/DialogWrap.tsx @@ -50,8 +50,8 @@ const DialogWrap: React.FC = (props) => { {...props} destroyOnHidden={destroyOnHidden} afterClose={() => { - const { afterClose: closableAfterClose } = - (typeof closable === 'object' ? closable : {}) || {}; + const closableObj = closable && typeof closable === 'object' ? closable : {}; + const { afterClose: closableAfterClose } = closableObj || {}; closableAfterClose?.(); afterClose?.(); setAnimatedVisible(false); From 9fbc534ae3bdcae3c7653eb14cbe6fce0c88d6f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Wed, 30 Jul 2025 09:51:06 +0800 Subject: [PATCH 14/14] fix: Delete non current modifications --- src/Dialog/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Dialog/index.tsx b/src/Dialog/index.tsx index 91153d22..3512e772 100644 --- a/src/Dialog/index.tsx +++ b/src/Dialog/index.tsx @@ -167,7 +167,7 @@ const Dialog: React.FC = (props) => { ) { doClose(); } - }, [visible, animatedVisible, doClose]); + }, [visible]); // Remove direct should also check the scroll bar update useEffect(