diff --git a/react-responsive-modal/__tests__/index.test.tsx b/react-responsive-modal/__tests__/index.test.tsx
index a8695f30..a6a77881 100644
--- a/react-responsive-modal/__tests__/index.test.tsx
+++ b/react-responsive-modal/__tests__/index.test.tsx
@@ -746,6 +746,49 @@ describe('modal', () => {
fireEvent.animationEnd(getByTestId('modal'));
expect(onAnimationEnd).toHaveBeenCalledTimes(1);
});
+
+ it('should pass the animation event to onAnimationEnd', async () => {
+ const onAnimationEnd = vitest.fn();
+ const { getByTestId } = render(
+ null} onAnimationEnd={onAnimationEnd}>
+ modal content
+ ,
+ );
+
+ const modal = getByTestId('modal');
+ fireEvent.animationEnd(modal);
+ expect(onAnimationEnd).toHaveBeenCalledTimes(1);
+ expect(onAnimationEnd.mock.calls[0][0].target).toBe(modal);
+ });
+
+ it('should not be called when a child animation ends', async () => {
+ const onAnimationEnd = vitest.fn();
+ const { getByTestId } = render(
+ null} onAnimationEnd={onAnimationEnd}>
+ modal content
+ ,
+ );
+
+ fireEvent.animationEnd(getByTestId('child'));
+ expect(onAnimationEnd).not.toHaveBeenCalled();
+ });
+
+ it('should not hide the modal when a child animation ends while closing', async () => {
+ const { getByTestId, queryByTestId, rerender } = render(
+ null} animationDuration={0.01}>
+ modal content
+ ,
+ );
+
+ rerender(
+ null} animationDuration={0.01}>
+ modal content
+ ,
+ );
+
+ fireEvent.animationEnd(getByTestId('child'));
+ expect(queryByTestId('modal')).toBeTruthy();
+ });
});
describe('prop: containerId', () => {
diff --git a/react-responsive-modal/src/index.tsx b/react-responsive-modal/src/index.tsx
index 78465784..15d761eb 100644
--- a/react-responsive-modal/src/index.tsx
+++ b/react-responsive-modal/src/index.tsx
@@ -164,7 +164,7 @@ export interface ModalProps {
/**
* Callback fired when the Modal has exited and the animation is finished.
*/
- onAnimationEnd?: () => void;
+ onAnimationEnd?: (event: React.AnimationEvent) => void;
children?: React.ReactNode;
}
@@ -313,12 +313,19 @@ export const Modal = React.forwardRef(
refShouldClose.current = false;
};
- const handleAnimationEnd = () => {
+ const handleAnimationEnd = (
+ event: React.AnimationEvent,
+ ) => {
+ // Ignore animations bubbling up from elements inside the modal
+ if (event.target !== event.currentTarget) {
+ return;
+ }
+
if (!open) {
setShowPortal(false);
}
- onAnimationEnd?.();
+ onAnimationEnd?.(event);
};
const containerModal = container || refContainer.current;
diff --git a/website/src/docs/index.mdx b/website/src/docs/index.mdx
index 055d96e2..52065c70 100644
--- a/website/src/docs/index.mdx
+++ b/website/src/docs/index.mdx
@@ -219,7 +219,7 @@ By default, the Modal will be rendered at the end of the html body tag. If you w
| **onClose\*** | `() => void` | | Callback fired when the Modal is requested to be closed by a click on the overlay or when user press esc key. |
| **onEscKeyDown\*** | `(event: KeyboardEvent) => void` | | Callback fired when the escape key is pressed. |
| **onOverlayClick\*** | `(event: React.MouseEvent) => void` | | Callback fired when the overlay is clicked. |
-| **onAnimationEnd\*** | `() => void` | | Callback fired when the Modal has exited and the animation is finished. |
+| **onAnimationEnd\*** | `(event: React.AnimationEvent) => void` | | Callback fired when the Modal has exited and the animation is finished. |
## License