diff --git a/packages/hooks/__tests__/use-delayed-toggle.test.ts b/packages/hooks/__tests__/use-delayed-toggle.test.ts new file mode 100644 index 0000000000..26c24f11b3 --- /dev/null +++ b/packages/hooks/__tests__/use-delayed-toggle.test.ts @@ -0,0 +1,183 @@ +import { ref } from 'vue' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { useDelayedToggle } from '../use-delayed-toggle' + +describe('use-delayed-toggle', () => { + beforeEach(() => { + vi.useFakeTimers() + }) + + afterEach(() => { + vi.restoreAllMocks() + }) + + it('should can call open/close', () => { + const cbOpen = vi.fn() + const cbClose = vi.fn() + const { onOpen, onClose } = useDelayedToggle({ + open: cbOpen, + close: cbClose, + showAfter: ref(0), + hideAfter: ref(0), + autoClose: ref(0), + }) + + expect(cbOpen).not.toHaveBeenCalled() + expect(cbClose).not.toHaveBeenCalled() + + onOpen() + + vi.runAllTimers() + expect(cbOpen).toHaveBeenCalled() + expect(cbClose).not.toHaveBeenCalled() + + onClose() + vi.runAllTimers() + expect(cbOpen).toHaveBeenCalledTimes(1) + expect(cbClose).toHaveBeenCalledTimes(1) + }) + + it('should delay of appearance', () => { + const cbOpen = vi.fn() + const cbClose = vi.fn() + const { onOpen, onClose } = useDelayedToggle({ + open: cbOpen, + close: cbClose, + showAfter: ref(100), + hideAfter: ref(0), + autoClose: ref(0), + }) + + expect(cbOpen).not.toHaveBeenCalled() + expect(cbClose).not.toHaveBeenCalled() + + onOpen() + + vi.advanceTimersByTime(50) + expect(cbOpen).not.toHaveBeenCalled() + + vi.advanceTimersByTime(50) + expect(cbOpen).toHaveBeenCalled() + expect(cbClose).not.toHaveBeenCalled() + + onClose() + vi.runAllTimers() + expect(cbOpen).toHaveBeenCalledTimes(1) + expect(cbClose).toHaveBeenCalledTimes(1) + }) + + it('should delay of disappear', () => { + const cbOpen = vi.fn() + const cbClose = vi.fn() + const { onClose } = useDelayedToggle({ + open: cbOpen, + close: cbClose, + showAfter: ref(0), + hideAfter: ref(100), + autoClose: ref(0), + }) + + expect(cbOpen).not.toHaveBeenCalled() + expect(cbClose).not.toHaveBeenCalled() + + onClose() + vi.advanceTimersByTime(50) + expect(cbClose).not.toHaveBeenCalled() + vi.advanceTimersByTime(50) + expect(cbClose).toHaveBeenCalled() + + vi.runAllTimers() + expect(cbOpen).not.toHaveBeenCalled() + expect(cbClose).toHaveBeenCalledTimes(1) + }) + + it('should disappear automatically', () => { + const cbOpen = vi.fn() + const cbClose = vi.fn() + const { onOpen } = useDelayedToggle({ + open: cbOpen, + close: cbClose, + showAfter: ref(0), + hideAfter: ref(0), + autoClose: ref(100), + }) + + expect(cbOpen).not.toHaveBeenCalled() + expect(cbClose).not.toHaveBeenCalled() + + onOpen() + vi.advanceTimersByTime(0) + expect(cbOpen).toHaveBeenCalled() + vi.advanceTimersByTime(50) + expect(cbClose).not.toHaveBeenCalled() + vi.advanceTimersByTime(50) + expect(cbClose).toHaveBeenCalled() + + vi.runAllTimers() + expect(cbOpen).toHaveBeenCalledTimes(1) + expect(cbClose).toHaveBeenCalledTimes(1) + }) + + it('apply all time', () => { + const cbOpen = vi.fn() + const cbClose = vi.fn() + const { onOpen, onClose } = useDelayedToggle({ + open: cbOpen, + close: cbClose, + showAfter: ref(100), + hideAfter: ref(100), + autoClose: ref(100), + }) + + expect(cbOpen).not.toHaveBeenCalled() + expect(cbClose).not.toHaveBeenCalled() + + onOpen() + vi.advanceTimersByTime(0) + expect(cbOpen).not.toHaveBeenCalled() + vi.advanceTimersByTime(50) + expect(cbOpen).not.toHaveBeenCalled() + + onClose() + vi.advanceTimersByTime(50) + expect(cbOpen).not.toHaveBeenCalled() + expect(cbClose).not.toHaveBeenCalled() + + vi.runAllTimers() + expect(cbOpen).not.toHaveBeenCalled() + expect(cbClose).toHaveBeenCalledTimes(1) + }) + + it('the close function call once', () => { + const cbOpen = vi.fn() + const cbClose = vi.fn() + const { onOpen, onClose } = useDelayedToggle({ + open: cbOpen, + close: cbClose, + showAfter: ref(0), + hideAfter: ref(100), + autoClose: ref(50), + }) + + expect(cbOpen).not.toHaveBeenCalled() + expect(cbClose).not.toHaveBeenCalled() + + onOpen() + vi.advanceTimersByTime(0) + expect(cbOpen).toHaveBeenCalled() + expect(cbClose).not.toHaveBeenCalled() + + onClose() + + vi.advanceTimersByTime(50) + expect(cbClose).not.toHaveBeenCalled() + + vi.advanceTimersByTime(50) + expect(cbOpen).toHaveBeenCalledTimes(1) + expect(cbClose).toHaveBeenCalledTimes(1) + + vi.runAllTimers() + expect(cbOpen).toHaveBeenCalledTimes(1) + expect(cbClose).toHaveBeenCalledTimes(1) + }) +}) diff --git a/packages/hooks/use-delayed-toggle/index.ts b/packages/hooks/use-delayed-toggle/index.ts index 1feb7044c5..2994e2eb39 100644 --- a/packages/hooks/use-delayed-toggle/index.ts +++ b/packages/hooks/use-delayed-toggle/index.ts @@ -19,6 +19,9 @@ export const useDelayedToggleProps = buildProps({ type: Number, default: 200, }, + /** + * @description disappear automatically, in millisecond + */ autoClose: { type: Number, default: 0, @@ -38,7 +41,10 @@ export const useDelayedToggle = ({ close, }: UseDelayedToggleProps) => { const { registerTimeout } = useTimeout() - const { registerTimeout: registerTimeoutForAutoClose } = useTimeout() + const { + registerTimeout: registerTimeoutForAutoClose, + cancelTimeout: cancelTimeoutForAutoClose, + } = useTimeout() const onOpen = (event?: Event) => { registerTimeout(() => { @@ -54,6 +60,8 @@ export const useDelayedToggle = ({ } const onClose = (event?: Event) => { + cancelTimeoutForAutoClose() + registerTimeout(() => { close(event) }, unref(hideAfter))