diff --git a/packages/hooks/src/useHover/__tests__/index.test.tsx b/packages/hooks/src/useHover/__tests__/index.test.tsx index a9a6e652e8..c21ba3b697 100644 --- a/packages/hooks/src/useHover/__tests__/index.test.tsx +++ b/packages/hooks/src/useHover/__tests__/index.test.tsx @@ -30,3 +30,56 @@ describe('useHover', () => { expect(trigger).toBe(2); }); }); + +describe('useHover - onLongHover', () => { + beforeEach(() => { + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it('should call onLongHover(true) after longHoverDuration', () => { + const onLongHover = jest.fn(); + const { getByText } = render(); + renderHook(() => useHover(getByText('Hover'), { onLongHover, longHoverDuration: 300 })); + + act(() => { + fireEvent.mouseEnter(getByText('Hover')); + }); + expect(onLongHover).not.toBeCalled(); + act(() => { + jest.advanceTimersByTime(300); + }); + expect(onLongHover).toHaveBeenCalledWith(true); + }); + + it('should call onLongHover(false) on mouseleave if timer exists', () => { + const onLongHover = jest.fn(); + const { getByText } = render(); + renderHook(() => useHover(getByText('Hover'), { onLongHover, longHoverDuration: 300 })); + act(() => { + fireEvent.mouseEnter(getByText('Hover')); + }); + act(() => { + fireEvent.mouseLeave(getByText('Hover')); + }); + expect(onLongHover).toHaveBeenCalledWith(false); + }); + + it('should not call onLongHover(true) if mouse leaves before duration', () => { + const onLongHover = jest.fn(); + const { getByText } = render(); + renderHook(() => useHover(getByText('Hover'), { onLongHover, longHoverDuration: 300 })); + act(() => { + fireEvent.mouseEnter(getByText('Hover')); + }); + act(() => { + jest.advanceTimersByTime(200); + fireEvent.mouseLeave(getByText('Hover')); + }); + expect(onLongHover).toHaveBeenCalledTimes(1); + expect(onLongHover).toHaveBeenCalledWith(false); + }); +}); diff --git a/packages/hooks/src/useHover/demo/demo3.tsx b/packages/hooks/src/useHover/demo/demo3.tsx new file mode 100644 index 0000000000..a602a6e921 --- /dev/null +++ b/packages/hooks/src/useHover/demo/demo3.tsx @@ -0,0 +1,28 @@ +/** + * title: Basic usage + * desc: Use ref or Pass in DOM element. + * + * title.zh-CN: 基础用法 + * desc.zh-CN: 使用 ref 或者传入 DOM 元素。 + */ + +import React, { useRef, useState } from 'react'; +import { useHover } from 'ahooks'; + +export default () => { + const ref = useRef(null); + const [isLongHovering, setIsLongHovering] = useState(false); + const isHovering = useHover(ref, { + longHoverDuration: 1000, + onLongHover: (value) => { + setIsLongHovering(value); + }, + }); + return ( +
+### Long term hover event
+
+
+
## API
```javascript
diff --git a/packages/hooks/src/useHover/index.ts b/packages/hooks/src/useHover/index.ts
index 0e319680d7..53a42d122c 100644
--- a/packages/hooks/src/useHover/index.ts
+++ b/packages/hooks/src/useHover/index.ts
@@ -1,24 +1,34 @@
import useBoolean from '../useBoolean';
import useEventListener from '../useEventListener';
import type { BasicTarget } from '../utils/domTarget';
+import { useRef } from 'react';
export interface Options {
onEnter?: () => void;
onLeave?: () => void;
onChange?: (isHovering: boolean) => void;
+ onLongHover?: (isLongHovering: boolean) => void;
+ longHoverDuration?: number;
}
export default (target: BasicTarget, options?: Options): boolean => {
- const { onEnter, onLeave, onChange } = options || {};
+ const { onEnter, onLeave, onChange, onLongHover, longHoverDuration = 500 } = options || {};
const [state, { setTrue, setFalse }] = useBoolean(false);
+ const timerRef = useRef
+### 长时间悬停事件
+
+
+
## API
```javascript