Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions src/components/center-popup/center-popup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { renderToContainer } from '../../utils/render-to-container'
import { ShouldRender } from '../../utils/should-render'
import { useInnerVisible } from '../../utils/use-inner-visible'
import { useLockScroll } from '../../utils/use-lock-scroll'
import { useSpringResumeOnVisible } from '../../utils/use-spring-resume-on-visible'
import { mergeProps } from '../../utils/with-default-props'
import { withStopPropagation } from '../../utils/with-stop-propagation'
import { useConfig } from '../config-provider'
Expand Down Expand Up @@ -42,6 +43,17 @@ export const CenterPopup: FC<CenterPopupProps> = props => {
const mergedProps = mergeProps(defaultProps, componentConfig, props)

const unmountedRef = useUnmountedRef()
const [active, setActive] = useState(mergedProps.visible)
const activeRef = useRef(active)
activeRef.current = active
const { shouldCallAfterClose } = useSpringResumeOnVisible({
visible: mergedProps.visible,
activeRef,
setActive,
afterClose: mergedProps.afterClose,
unmountedRef,
})

const style = useSpring({
scale: mergedProps.visible ? 1 : 0.8,
opacity: mergedProps.visible ? 1 : 0,
Expand All @@ -53,16 +65,16 @@ export const CenterPopup: FC<CenterPopupProps> = props => {
},
onRest: () => {
if (unmountedRef.current) return
setActive(mergedProps.visible)
if (mergedProps.visible) {
setActive(true)
mergedProps.afterShow?.()
} else {
} else if (shouldCallAfterClose()) {
setActive(false)
mergedProps.afterClose?.()
}
},
})

const [active, setActive] = useState(mergedProps.visible)
useIsomorphicLayoutEffect(() => {
if (mergedProps.visible) {
setActive(true)
Expand Down
16 changes: 14 additions & 2 deletions src/components/mask/mask.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
PropagationEvent,
withStopPropagation,
} from '../../utils/with-stop-propagation'
import { useSpringResumeOnVisible } from '../../utils/use-spring-resume-on-visible'

const classPrefix = `adm-mask`

Expand Down Expand Up @@ -69,8 +70,18 @@ export const Mask: FC<MaskProps> = p => {
}, [props.color, props.opacity])

const [active, setActive] = useState(props.visible)
const activeRef = useRef(active)
activeRef.current = active

const unmountedRef = useUnmountedRef()
const { shouldCallAfterClose } = useSpringResumeOnVisible({
visible: props.visible,
activeRef,
setActive,
afterClose: props.afterClose,
unmountedRef,
})

const { opacity } = useSpring({
opacity: props.visible ? 1 : 0,
config: {
Expand All @@ -85,10 +96,11 @@ export const Mask: FC<MaskProps> = p => {
},
onRest: () => {
if (unmountedRef.current) return
setActive(props.visible)
if (props.visible) {
setActive(true)
props.afterShow?.()
} else {
} else if (shouldCallAfterClose()) {
setActive(false)
props.afterClose?.()
}
},
Expand Down
18 changes: 15 additions & 3 deletions src/components/popup/popup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { defaultPopupBaseProps, PopupBaseProps } from './popup-base-props'
import { useInnerVisible } from '../../utils/use-inner-visible'
import { useConfig } from '../config-provider'
import { useDrag } from '@use-gesture/react'
import { useSpringResumeOnVisible } from '../../utils/use-spring-resume-on-visible'

const classPrefix = `adm-popup`

Expand Down Expand Up @@ -41,16 +42,26 @@ export const Popup: FC<PopupProps> = p => {
)

const [active, setActive] = useState(props.visible)
const activeRef = useRef(active)
activeRef.current = active
const ref = useRef<HTMLDivElement>(null)
useLockScroll(ref, props.disableBodyScroll && active ? 'strict' : false)

const unmountedRef = useUnmountedRef()
const { shouldCallAfterClose } = useSpringResumeOnVisible({
visible: props.visible,
activeRef,
setActive,
afterClose: props.afterClose,
unmountedRef,
})

useIsomorphicLayoutEffect(() => {
if (props.visible) {
setActive(true)
}
}, [props.visible])

const unmountedRef = useUnmountedRef()
const { percent } = useSpring({
percent: props.visible ? 0 : 100,
config: {
Expand All @@ -61,10 +72,11 @@ export const Popup: FC<PopupProps> = p => {
},
onRest: () => {
if (unmountedRef.current) return
setActive(props.visible)
if (props.visible) {
setActive(true)
props.afterShow?.()
} else {
} else if (shouldCallAfterClose()) {
setActive(false)
props.afterClose?.()
}
},
Expand Down
74 changes: 73 additions & 1 deletion src/components/popup/tests/popup.test.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,34 @@
import * as React from 'react'
import { mockDrag, render, screen, testA11y } from 'testing'
import { act, mockDrag, render, screen, testA11y } from 'testing'
import Popup from '..'
import ConfigProvider from '../../config-provider'

function setVisibilityState(state: 'visible' | 'hidden') {
Object.defineProperty(document, 'visibilityState', {
value: state,
writable: true,
configurable: true,
})
}

describe('Popup', () => {
const originalVisibilityState = Object.getOwnPropertyDescriptor(
document,
'visibilityState'
)

afterEach(() => {
if (originalVisibilityState) {
Object.defineProperty(
document,
'visibilityState',
originalVisibilityState
)
} else {
delete (document as any).visibilityState
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
})
Comment thread
coderabbitai[bot] marked this conversation as resolved.

test('a11y', async () => {
await testA11y(<Popup visible>foobar</Popup>)
})
Expand Down Expand Up @@ -91,4 +116,51 @@ describe('Popup', () => {
expect(screen.getByText('bamboo')).toBeVisible()
})
})

describe('visibilitychange', () => {
it('should call afterClose when page becomes visible after close while hidden', () => {
const afterClose = jest.fn()
const { rerender } = render(
<Popup visible afterClose={afterClose}>
foobar
</Popup>
)

// The popup is visible, now close it
rerender(
<Popup visible={false} afterClose={afterClose}>
foobar
</Popup>
)

setVisibilityState('hidden')
act(() => {
document.dispatchEvent(new Event('visibilitychange'))
})

// Simulate page becoming visible (e.g. user switches back to tab)
setVisibilityState('visible')
act(() => {
document.dispatchEvent(new Event('visibilitychange'))
})
Comment thread
coderabbitai[bot] marked this conversation as resolved.

expect(afterClose).toHaveBeenCalledTimes(1)
})

it('should not call afterClose when popup is still visible', () => {
const afterClose = jest.fn()
render(
<Popup visible afterClose={afterClose}>
foobar
</Popup>
)

setVisibilityState('visible')
act(() => {
document.dispatchEvent(new Event('visibilitychange'))
})

expect(afterClose).not.toHaveBeenCalled()
})
})
})
Loading
Loading