diff --git a/packages/react-native-web/src/exports/Modal/ModalAnimation.js b/packages/react-native-web/src/exports/Modal/ModalAnimation.js
index a7d9b16a..e028a9fa 100644
--- a/packages/react-native-web/src/exports/Modal/ModalAnimation.js
+++ b/packages/react-native-web/src/exports/Modal/ModalAnimation.js
@@ -40,18 +40,27 @@ function ModalAnimation(props: ModalAnimationProps) {
const isAnimated = animationType && animationType !== 'none';
- const animationEndCallback = useCallback(() => {
- if (visible) {
- if (onShow) {
- onShow();
+ const animationEndCallback = useCallback(
+ (e: any) => {
+ if (e && e.currentTarget !== e.target) {
+ // If the event was generated for something NOT this element we
+ // should ignore it as it's not relevant to us
+ return;
}
- } else {
- setIsRendering(false);
- if (onDismiss) {
- onDismiss();
+
+ if (visible) {
+ if (onShow) {
+ onShow();
+ }
+ } else {
+ setIsRendering(false);
+ if (onDismiss) {
+ onDismiss();
+ }
}
- }
- }, [onDismiss, onShow, visible]);
+ },
+ [onDismiss, onShow, visible]
+ );
useEffect(() => {
if (visible) {
diff --git a/packages/react-native-web/src/exports/Modal/__tests__/index.js b/packages/react-native-web/src/exports/Modal/__tests__/index.js
index 8aeceb06..41c11bb2 100644
--- a/packages/react-native-web/src/exports/Modal/__tests__/index.js
+++ b/packages/react-native-web/src/exports/Modal/__tests__/index.js
@@ -2,7 +2,7 @@
import Modal from '..';
import React from 'react';
-import { render } from '@testing-library/react';
+import { fireEvent, render } from '@testing-library/react';
describe('components/Modal', () => {
test('visible by default', () => {
@@ -545,4 +545,74 @@ describe('components/Modal', () => {
expect(spy).toHaveBeenNthCalledWith(1, 'ref');
expect(spy).toHaveBeenNthCalledWith(2, 'mount');
});
+
+ test('escape key fires onRequestClose', () => {
+ const spy = jest.fn();
+
+ render();
+
+ fireEvent.keyUp(document, { key: 'Escape' });
+
+ expect(spy).toHaveBeenCalledTimes(1);
+ });
+
+ test('escape key fires onRequestClose for top modal only', () => {
+ const spyA = jest.fn();
+ const spyB = jest.fn();
+
+ render(
+ <>
+
+
+ >
+ );
+
+ fireEvent.keyUp(document, { key: 'Escape' });
+
+ expect(spyA).toHaveBeenCalledTimes(0);
+ expect(spyB).toHaveBeenCalledTimes(1);
+ });
+
+ test('escape key fires onRequestClose for top modal only with animation', () => {
+ const spyA = jest.fn();
+ const spyB = jest.fn();
+
+ const { getByTestId, rerender } = render(
+ <>
+
+
+
+
+
+
+
+ >
+ );
+
+ rerender(
+ <>
+
+
+
+
+
+
+
+ >
+ );
+
+ // This is kind of ugly but I can't find a better way to target just the animation div
+ const animationAElement = getByTestId('a').parentElement.parentElement.parentElement
+ .parentElement;
+ const animationBElement = getByTestId('b').parentElement.parentElement.parentElement
+ .parentElement;
+
+ fireEvent.animationEnd(animationAElement);
+ fireEvent.animationEnd(animationBElement);
+
+ fireEvent.keyUp(document, { key: 'Escape' });
+
+ expect(spyA).toHaveBeenCalledTimes(0);
+ expect(spyB).toHaveBeenCalledTimes(1);
+ });
});