diff --git a/packages/react-native-web/src/exports/Modal/ModalAnimation.js b/packages/react-native-web/src/exports/Modal/ModalAnimation.js index 1ef6a422..b053d353 100644 --- a/packages/react-native-web/src/exports/Modal/ModalAnimation.js +++ b/packages/react-native-web/src/exports/Modal/ModalAnimation.js @@ -37,6 +37,7 @@ function ModalAnimation(props: ModalAnimationProps): React.Node { const [isRendering, setIsRendering] = React.useState(false); const wasVisible = React.useRef(false); + const wasRendering = React.useRef(false); const isAnimated = animationType && animationType !== 'none'; @@ -54,14 +55,18 @@ function ModalAnimation(props: ModalAnimationProps): React.Node { } } else { setIsRendering(false); - if (onDismiss) { - onDismiss(); - } } }, - [onDismiss, onShow, visible] + [onShow, visible] ); + React.useEffect(() => { + if (wasRendering.current && !isRendering && onDismiss) { + onDismiss(); + } + wasRendering.current = isRendering; + }, [isRendering, onDismiss]); + React.useEffect(() => { if (visible) { setIsRendering(true); diff --git a/packages/react-native-web/src/exports/Modal/__tests__/index-test.js b/packages/react-native-web/src/exports/Modal/__tests__/index-test.js index 78f5d94f..9e82fcc5 100644 --- a/packages/react-native-web/src/exports/Modal/__tests__/index-test.js +++ b/packages/react-native-web/src/exports/Modal/__tests__/index-test.js @@ -309,6 +309,32 @@ describe('components/Modal', () => { expect(document.activeElement).toBe(insideElement); }); + test('focus is not trapped after closing modal', () => { + const { rerender } = render( + <> + + Outside + + + + ); + + const outsideElement = document.querySelector('[data-testid="outside"]'); + const onDismissCallback = jest.fn(() => outsideElement.focus()); + + rerender( + <> + + Outside + + + + ); + + expect(onDismissCallback).toBeCalledTimes(1); + expect(document.activeElement).toBe(outsideElement); + }); + test('focus is brought back to the element that triggered modal after closing', () => { const { rerender } = render( <>