mirror of
https://github.com/nextui-org/nextui.git
synced 2025-12-08 19:26:11 +00:00
fix(toast): trigger onClose after timeout (#5771)
This commit is contained in:
parent
812f7939d9
commit
ed76faacd4
5
.changeset/strong-needles-kneel.md
Normal file
5
.changeset/strong-needles-kneel.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
"@heroui/toast": patch
|
||||
---
|
||||
|
||||
trigger onClose after toast timeout (#5609)
|
||||
@ -174,4 +174,69 @@ describe("Toast", () => {
|
||||
|
||||
expect(loadingIcon).toBeTruthy();
|
||||
});
|
||||
|
||||
it("should call onClose when toast times out", async () => {
|
||||
const onCloseMock = jest.fn();
|
||||
|
||||
const wrapper = render(
|
||||
<>
|
||||
<ToastProvider disableAnimation />
|
||||
<button
|
||||
data-testid="button"
|
||||
onClick={() => {
|
||||
addToast({
|
||||
title: title,
|
||||
description: description,
|
||||
timeout: 500,
|
||||
onClose: onCloseMock,
|
||||
});
|
||||
}}
|
||||
>
|
||||
Show Toast
|
||||
</button>
|
||||
</>,
|
||||
);
|
||||
|
||||
const button = wrapper.getByTestId("button");
|
||||
|
||||
await user.click(button);
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
|
||||
expect(onCloseMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("should call onClose when close button is clicked", async () => {
|
||||
const onCloseMock = jest.fn();
|
||||
|
||||
const wrapper = render(
|
||||
<>
|
||||
<ToastProvider disableAnimation maxVisibleToasts={1} />
|
||||
<button
|
||||
data-testid="button"
|
||||
onClick={() => {
|
||||
addToast({
|
||||
title: title,
|
||||
description: description,
|
||||
onClose: onCloseMock,
|
||||
});
|
||||
}}
|
||||
>
|
||||
Show Toast
|
||||
</button>
|
||||
</>,
|
||||
);
|
||||
|
||||
const button = wrapper.getByTestId("button");
|
||||
|
||||
await user.click(button);
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
|
||||
const closeButtons = wrapper.getAllByRole("button");
|
||||
|
||||
await user.click(closeButtons[0]);
|
||||
|
||||
expect(onCloseMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
@ -10,7 +10,7 @@ import type {HTMLHeroUIProps, PropGetter} from "@heroui/system";
|
||||
import {mapPropsVariants, useProviderContext} from "@heroui/system";
|
||||
import {toast as toastTheme} from "@heroui/theme";
|
||||
import {useDOMRef} from "@heroui/react-utils";
|
||||
import {clsx, dataAttr, isEmpty, objectToDeps, chain, mergeProps} from "@heroui/shared-utils";
|
||||
import {clsx, dataAttr, isEmpty, objectToDeps, mergeProps} from "@heroui/shared-utils";
|
||||
import {useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState} from "react";
|
||||
import {useToast as useToastAria} from "@react-aria/toast";
|
||||
import {useHover} from "@react-aria/interactions";
|
||||
@ -207,6 +207,7 @@ export function useToast<T extends ToastProps>(originalProps: UseToastProps<T>)
|
||||
|
||||
const [isLoading, setIsLoading] = useState<boolean>(!!promiseProp);
|
||||
const [isToastExiting, setIsToastExiting] = useState(false);
|
||||
const hasCalledOnCloseRef = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!promiseProp) return;
|
||||
@ -224,8 +225,12 @@ export function useToast<T extends ToastProps>(originalProps: UseToastProps<T>)
|
||||
useEffect(() => {
|
||||
if (isToastExiting && disableAnimation) {
|
||||
state.close(toast.key);
|
||||
if (!hasCalledOnCloseRef.current) {
|
||||
hasCalledOnCloseRef.current = true;
|
||||
onClose?.();
|
||||
}
|
||||
}
|
||||
}, [isToastExiting, disableAnimation, state, toast.key]);
|
||||
}, [isToastExiting, disableAnimation, state, toast.key, onClose]);
|
||||
|
||||
useEffect(() => {
|
||||
const updateProgress = (timestamp: number) => {
|
||||
@ -436,6 +441,10 @@ export function useToast<T extends ToastProps>(originalProps: UseToastProps<T>)
|
||||
: () => {
|
||||
if (!isToastExiting) return;
|
||||
state.close(toast.key);
|
||||
if (!hasCalledOnCloseRef.current) {
|
||||
hasCalledOnCloseRef.current = true;
|
||||
onClose?.();
|
||||
}
|
||||
},
|
||||
style: {
|
||||
opacity: opacityValue,
|
||||
@ -526,14 +535,17 @@ export function useToast<T extends ToastProps>(originalProps: UseToastProps<T>)
|
||||
"aria-label": "closeButton",
|
||||
"data-hidden": dataAttr(hideCloseButton),
|
||||
...mergeProps(props, {
|
||||
onPress: chain(() => {
|
||||
onPress: () => {
|
||||
setIsToastExiting(true);
|
||||
|
||||
if (!hasCalledOnCloseRef.current) {
|
||||
hasCalledOnCloseRef.current = true;
|
||||
onClose?.();
|
||||
}
|
||||
setTimeout(() => document.body.focus(), 0);
|
||||
}, onClose),
|
||||
},
|
||||
}),
|
||||
}),
|
||||
[setIsToastExiting, onClose, state, toast],
|
||||
[setIsToastExiting, onClose],
|
||||
);
|
||||
|
||||
const getCloseIconProps: PropGetter = useCallback(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user