From 5e0d9682146c9b31ba8d033dac52620c6d1dcc17 Mon Sep 17 00:00:00 2001 From: Eden Wang Date: Mon, 20 May 2024 14:26:25 +0800 Subject: [PATCH] feat(pauseOnHover): add `pauseOnHover` options --- README.md | 6 +++ docs/examples/showProgress.tsx | 10 +++++ src/Notice.tsx | 13 +++++-- src/hooks/useNotification.tsx | 1 + src/interface.ts | 1 + tests/index.test.tsx | 68 ++++++++++++++++++++++++++++++++++ 6 files changed, 95 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 43984c6..69d446b 100644 --- a/README.md +++ b/README.md @@ -136,6 +136,12 @@ props details: false show with progress bar for auto-closing notification + + pauseOnHover + boolean + true + keep the timer running or not on hover + style Object diff --git a/docs/examples/showProgress.tsx b/docs/examples/showProgress.tsx index 50e80eb..4d0ebbc 100644 --- a/docs/examples/showProgress.tsx +++ b/docs/examples/showProgress.tsx @@ -18,6 +18,16 @@ export default () => { > Show With Progress + {contextHolder} ); diff --git a/src/Notice.tsx b/src/Notice.tsx index 69bb940..38a9b9c 100644 --- a/src/Notice.tsx +++ b/src/Notice.tsx @@ -22,6 +22,7 @@ const Notify = React.forwardRef { - clearTimeout(timeout); + if (pauseOnHover) { + clearTimeout(timeout); + } setSpentTime(Date.now() - start); }; } @@ -71,7 +74,7 @@ const Notify = React.forwardRef { - if (!mergedHovering && mergedShowProgress) { + if (!mergedHovering && mergedShowProgress && (pauseOnHover || spentTime === 0)) { const start = performance.now(); let animationFrame: number; @@ -90,11 +93,13 @@ const Notify = React.forwardRef { - cancelAnimationFrame(animationFrame); + if (pauseOnHover) { + cancelAnimationFrame(animationFrame); + } }; } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [duration, mergedHovering, mergedShowProgress, times]); + }, [duration, spentTime, mergedHovering, mergedShowProgress, times]); // ======================== Closable ======================== const closableObj = React.useMemo(() => { diff --git a/src/hooks/useNotification.tsx b/src/hooks/useNotification.tsx index 31f5d81..783b72b 100644 --- a/src/hooks/useNotification.tsx +++ b/src/hooks/useNotification.tsx @@ -18,6 +18,7 @@ export interface NotificationConfig { maxCount?: number; duration?: number; showProgress?: boolean; + pauseOnHover?: boolean; /** @private. Config for notification holder style. Safe to remove if refactor */ className?: (placement: Placement) => string; /** @private. Config for notification holder style. Safe to remove if refactor */ diff --git a/src/interface.ts b/src/interface.ts index b5d48f1..52ccbe0 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -8,6 +8,7 @@ export interface NoticeConfig { content?: React.ReactNode; duration?: number | null; showProgress?: boolean; + pauseOnHover?: boolean; closeIcon?: React.ReactNode; closable?: boolean | ({ closeIcon?: React.ReactNode } & React.AriaAttributes); className?: string; diff --git a/tests/index.test.tsx b/tests/index.test.tsx index 5dbbeb5..4217e31 100644 --- a/tests/index.test.tsx +++ b/tests/index.test.tsx @@ -309,6 +309,74 @@ describe('Notification.Basic', () => { expect(document.querySelector('.test')).toBeFalsy(); }); + describe('pauseOnHover is false', () => { + it('does not freeze when pauseOnHover is false', () => { + const { instance } = renderDemo(); + + act(() => { + instance.open({ + content: ( +

+ not freeze +

+ ), + duration: 0.3, + pauseOnHover: false, + }); + }); + + expect(document.querySelectorAll('.not-freeze')).toHaveLength(1); + + // Mouse in should remove + fireEvent.mouseEnter(document.querySelector('.rc-notification-notice')); + act(() => { + vi.runAllTimers(); + }); + expect(document.querySelectorAll('.not-freeze')).toHaveLength(0); + }); + + it('continue timing after hover', () => { + const { instance } = renderDemo({ + duration: 1, + pauseOnHover: false, + }); + + act(() => { + instance.open({ + content:

1

, + }); + }); + + expect(document.querySelector('.test')).toBeTruthy(); + + // Wait for 500ms + act(() => { + vi.advanceTimersByTime(500); + }); + expect(document.querySelector('.test')).toBeTruthy(); + + // Mouse in should not remove + fireEvent.mouseEnter(document.querySelector('.rc-notification-notice')); + act(() => { + vi.advanceTimersByTime(200); + }); + expect(document.querySelector('.test')).toBeTruthy(); + + // Mouse out should not remove until 500ms later + fireEvent.mouseLeave(document.querySelector('.rc-notification-notice')); + act(() => { + vi.advanceTimersByTime(200); + }); + expect(document.querySelector('.test')).toBeTruthy(); + + // + act(() => { + vi.advanceTimersByTime(100); + }); + expect(document.querySelector('.test')).toBeFalsy(); + }); + }); + describe('maxCount', () => { it('remove work when maxCount set', () => { const { instance } = renderDemo({