Skip to content

Commit 8ab3315

Browse files
authored
feat(ToastNotification): keep toast notification open when user hovers it (#1288)
Signed-off-by: David Edler <david.edler@canonical.com>
1 parent 32c2262 commit 8ab3315

3 files changed

Lines changed: 54 additions & 2 deletions

File tree

src/components/Notifications/ToastNotification/ToastNotification.test.tsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,4 +187,28 @@ describe("ToastNotification", () => {
187187

188188
expect(onDismiss).toHaveBeenCalledWith(notifications);
189189
});
190+
191+
it("calls onHoverStart on mouse enter and onHoverEnd on mouse leave", async () => {
192+
const onHoverStart = jest.fn();
193+
const onHoverEnd = jest.fn();
194+
195+
render(
196+
<ToastNotification
197+
notification={baseNotification}
198+
show={true}
199+
onDismiss={jest.fn()}
200+
onHoverStart={onHoverStart}
201+
onHoverEnd={onHoverEnd}
202+
/>,
203+
);
204+
205+
const toast = screen.getByRole("alert");
206+
const user = userEvent.setup();
207+
208+
await user.hover(toast);
209+
expect(onHoverStart).toHaveBeenCalledTimes(1);
210+
211+
await user.unhover(toast);
212+
expect(onHoverEnd).toHaveBeenCalledTimes(1);
213+
});
190214
});

src/components/Notifications/ToastNotification/ToastNotification.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,17 @@ interface Props {
1111
notification: ToastNotificationType;
1212
onDismiss: (notification?: ToastNotificationType[]) => void;
1313
show: boolean;
14+
onHoverStart?: () => void;
15+
onHoverEnd?: () => void;
1416
}
1517

16-
const ToastNotification: FC<Props> = ({ notification, onDismiss, show }) => {
18+
const ToastNotification: FC<Props> = ({
19+
notification,
20+
onDismiss,
21+
show,
22+
onHoverStart,
23+
onHoverEnd,
24+
}) => {
1725
if (!notification) {
1826
return null;
1927
}
@@ -36,7 +44,11 @@ const ToastNotification: FC<Props> = ({ notification, onDismiss, show }) => {
3644
options={{ duration: 200 }}
3745
className="toast-animate"
3846
>
39-
<div className="toast-notification">
47+
<div
48+
className="toast-notification"
49+
onMouseEnter={onHoverStart}
50+
onMouseLeave={onHoverEnd}
51+
>
4052
<Notification
4153
title={notification.title ?? DefaultTitles[notification.type]}
4254
actions={notification.actions}

src/components/Notifications/ToastNotification/ToastNotificationProvider.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,20 @@ const ToastNotificationProvider: FC<PropsWithChildren<Props>> = ({
217217
});
218218
};
219219

220+
const pauseTimer = () => {
221+
if (notificationTimer && typeof notificationTimer !== "boolean") {
222+
clearTimeout(notificationTimer);
223+
setNotificationTimer(true); // mark as paused
224+
}
225+
};
226+
227+
const resumeTimer = () => {
228+
if (notificationTimer === true) {
229+
// only resume if previously paused
230+
showNotificationWithDelay();
231+
}
232+
};
233+
220234
const addNotification = (
221235
notification: NotificationType & { error?: unknown } & {
222236
id?: ToastNotificationType["id"];
@@ -311,6 +325,8 @@ const ToastNotificationProvider: FC<PropsWithChildren<Props>> = ({
311325
notification={latestNotification}
312326
onDismiss={clear}
313327
show={!!showNotification}
328+
onHoverStart={pauseTimer}
329+
onHoverEnd={resumeTimer}
314330
/>
315331
<ToastNotificationList
316332
notifications={notifications}

0 commit comments

Comments
 (0)