diff --git a/src/components/dialog/Dialog.jsx b/src/components/dialog/Dialog.jsx index 0835ac5f4..a8fe02673 100644 --- a/src/components/dialog/Dialog.jsx +++ b/src/components/dialog/Dialog.jsx @@ -6,6 +6,9 @@ import cx from 'classnames'; import {useBodyNoScroll} from './useBodyNoScroll'; import {useFocusTrap} from './useFocusTrap'; +// https://github.com/jsdom/jsdom/issues/1781 +const supportsTransitions = window.TransitionEvent !== undefined; + export type DialogPropsType = $ReadOnly<{ open: boolean, children: React.Node, @@ -78,44 +81,53 @@ function BaseDialog({ */ const [deferredOpen, setDeferredOpen] = React.useState(false); + const fireTransitionEndCallbacks = React.useCallback(() => { + if (open) { + if (onEntryTransitionEnd) { + onEntryTransitionEnd(); + } + } else if (onExitTransitionEnd) { + onExitTransitionEnd(); + } + }, [open, onEntryTransitionEnd, onExitTransitionEnd]); + React.useEffect(() => { setDeferredOpen(open); - }, [open]); - useBodyNoScroll(); - useFocusTrap({dialogRef: containerRef, overlayRef}); + if (!supportsTransitions) { + fireTransitionEndCallbacks(); + } + }, [open, fireTransitionEndCallbacks]); - const handleOverlayClick = React.useCallback( - (event: SyntheticMouseEvent) => { - if (onDismiss && event.target === event.currentTarget) { - onDismiss(); - } - }, - [onDismiss] - ); + useBodyNoScroll(); + useFocusTrap({ + dialogRef: containerRef, + overlayRef, + }); const handleTransitionEnd = React.useCallback( (event: TransitionEvent) => { if ( - event.target !== event.currentTarget || - event.propertyName !== lastTransitionName + event.target === event.currentTarget && + event.propertyName === lastTransitionName ) { - return; + fireTransitionEndCallbacks(); } + }, + [fireTransitionEndCallbacks, lastTransitionName] + ); - if (open) { - if (onEntryTransitionEnd) { - onEntryTransitionEnd(); - } - } else if (onExitTransitionEnd) { - onExitTransitionEnd(); + const handleOverlayClick = React.useCallback( + (event: SyntheticMouseEvent) => { + if (onDismiss && event.target === event.currentTarget) { + onDismiss(); } }, - [open, lastTransitionName, onEntryTransitionEnd, onExitTransitionEnd] + [onDismiss] ); const handleKeyUp = React.useCallback( - event => { + (event: SyntheticKeyboardEvent) => { if (onDismiss && event.key === 'Escape') { onDismiss(); event.stopPropagation(); @@ -158,7 +170,7 @@ function BaseDialog({ role="dialog" ref={containerRef} className={containerClass} - onTransitionEnd={handleTransitionEnd} + onTransitionEnd={supportsTransitions ? handleTransitionEnd : undefined} aria-modal="true" aria-labelledby={ariaLabelledBy} aria-label={ariaLabel} diff --git a/src/components/dialog/Dialog.spec.jsx b/src/components/dialog/Dialog.spec.jsx index b9dc28f87..cfd731316 100644 --- a/src/components/dialog/Dialog.spec.jsx +++ b/src/components/dialog/Dialog.spec.jsx @@ -43,7 +43,6 @@ describe('', () => { it('fires onDismiss callback on Escape key', () => { const onDismiss = jest.fn(); - const wrapper = mount( content text @@ -68,16 +67,13 @@ describe('', () => { it('fires onEntryTransitionEnd callback on entry', () => { const onEntryTransitionEnd = jest.fn(); - const wrapper = mount( + + mount( content text ); - wrapper.find('[role="dialog"]').simulate('transitionEnd', { - propertyName: 'transform', - }); - expect(onEntryTransitionEnd).toHaveBeenCalledTimes(1); }); @@ -90,22 +86,40 @@ describe('', () => { ); wrapper.setProps({open: false}); - wrapper.find('[role="dialog"]').simulate('transitionEnd', { - propertyName: 'opacity', - }); - expect(onExitTransitionEnd).toHaveBeenCalledTimes(1); }); + it('does not fire onEntryTransitionEnd callback before open', () => { + const onEntryTransitionEnd = jest.fn(); + + mount( + + content text + + ); + + expect(onEntryTransitionEnd).toHaveBeenCalledTimes(0); + }); + + it('does not fire onExitTransitionEnd callback before open', () => { + const onExitTransitionEnd = jest.fn(); + + mount( + + content text + + ); + + expect(onExitTransitionEnd).toHaveBeenCalledTimes(0); + }); + it('returns null after exit transition', () => { const wrapper = mount(content text); expect(wrapper.isEmptyRender()).toBe(false); wrapper.setProps({open: false}); - wrapper.find('[role="dialog"]').simulate('transitionEnd', { - propertyName: 'opacity', - }); + wrapper.update(); expect(wrapper.isEmptyRender()).toBe(true); });