Skip to content

Commit abfbe9e

Browse files
fix: make sure to clear all timers when unmount Popup (#5771)
1 parent 65fe1f7 commit abfbe9e

File tree

4 files changed

+47
-2
lines changed

4 files changed

+47
-2
lines changed

.changeset/shaggy-rats-share.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@ultraviolet/ui": patch
3+
---
4+
5+
`Popup`: Make sure to clear all timers when unmount

packages/ui/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"typecheck": "tsc --noEmit",
1818
"size": "size-limit",
1919
"test:unit": "LC_ALL=en_US.UTF-8 pnpm vitest --run --config vitest.config.ts",
20+
"test:watch": "LC_ALL=en_US.UTF-8 pnpm vitest watch --config vitest.config.ts",
2021
"test:unit:coverage": "pnpm test:unit --coverage",
2122
"lintpublish": "publint"
2223
},
@@ -107,4 +108,4 @@
107108
"react-toastify": "11.0.5",
108109
"react-use-clipboard": "1.0.9"
109110
}
110-
}
111+
}

packages/ui/src/components/Popup/__tests__/index.test.tsx

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
shouldMatchEmotionSnapshotWithPortal,
66
} from '@utils/test'
77
import type { ComponentProps } from 'react'
8-
import { describe, expect, test } from 'vitest'
8+
import { describe, expect, test, vi } from 'vitest'
99
import { Popup } from '../../index'
1010

1111
describe('popup', () => {
@@ -212,4 +212,28 @@ describe('popup', () => {
212212
expect(PopupPortal).not.toBeVisible()
213213
})
214214
})
215+
216+
test(`should handle unmount correctly`, async () => {
217+
const onClose = vi.fn()
218+
const { unmount } = renderWithTheme(
219+
<Popup onClose={onClose} text="test unmount!">
220+
<p data-testid="unmount-children">Hover me</p>
221+
</Popup>,
222+
)
223+
224+
const input = screen.getByTestId('unmount-children')
225+
await userEvent.hover(input)
226+
227+
await waitFor(
228+
() => {
229+
const PopupPortal = screen.getByText('test unmount!')
230+
expect(PopupPortal).toBeVisible()
231+
},
232+
{ timeout: 300 },
233+
)
234+
235+
await userEvent.unhover(input)
236+
unmount()
237+
expect(onClose).not.toHaveBeenCalled()
238+
})
215239
})

packages/ui/src/components/Popup/index.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,21 @@ export const Popup = forwardRef(
484484
}
485485
}, [])
486486

487+
/**
488+
* On unmount, clear all timers
489+
*/
490+
useEffect(
491+
() => () => {
492+
if (timer.current) {
493+
clearTimeout(timer.current)
494+
}
495+
if (debounceTimer.current) {
496+
clearTimeout(debounceTimer.current)
497+
}
498+
},
499+
[],
500+
)
501+
487502
/**
488503
* Will render children conditionally if children is a function or not.
489504
*/

0 commit comments

Comments
 (0)