Skip to content

fix(toast): toast should appear above overlay and adding regionProps to ToastProvider #5001

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Mar 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/popular-forks-sparkle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@heroui/toast": patch
"@heroui/theme": patch
---

Toast should be above the modal(#4898).
20 changes: 19 additions & 1 deletion apps/docs/content/docs/components/toast.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ Toast has the following slots:
## Accessibility

- Toast has role of `alert`
- All Toasts are present in ToastRegion.
- All Toasts are present in `ToastRegion`.
- Close button has aria-label="Close" by default
- When no toast are present, ToastRegion is removed from the DOM

Expand Down Expand Up @@ -331,9 +331,27 @@ Toast has the following slots:
description: "Props to be passed to all toasts",
default: "-"
},
{
attribute: "regionProps",
type: "ToastRegionProps",
description: "Props to be passed to toast region",
default: "-"
},
]}
/>

### ToastRegion Props

<APITable
data={[
{
attribute: "classNames",
type: "Partial<Record<\"base\", string>>",
description: "Allows to set custom class names for the toast region slots.",
default: "-"
}
]}
/>

### Toast Events

Expand Down
2 changes: 2 additions & 0 deletions packages/components/toast/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@
"@heroui/system": "workspace:*",
"@heroui/theme": "workspace:*",
"@heroui/button": "workspace:*",
"@heroui/modal": "workspace:*",
"@heroui/drawer": "workspace:*",
"clean-package": "2.2.0",
"react": "^18.0.0",
"react-dom": "^18.0.0"
Expand Down
6 changes: 5 additions & 1 deletion packages/components/toast/src/toast-provider.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {ToastOptions, ToastQueue, useToastQueue} from "@react-stately/toast";
import {useProviderContext} from "@heroui/system";

import {ToastRegion} from "./toast-region";
import {RegionProps, ToastRegion} from "./toast-region";
import {ToastProps, ToastPlacement} from "./use-toast";

let globalToastQueue: ToastQueue<ToastProps> | null = null;
Expand All @@ -12,6 +12,7 @@ interface ToastProviderProps {
disableAnimation?: boolean;
toastProps?: ToastProps;
toastOffset?: number;
regionProps?: RegionProps;
}

export const getToastQueue = () => {
Expand All @@ -31,6 +32,7 @@ export const ToastProvider = ({
maxVisibleToasts = 3,
toastOffset = 0,
toastProps = {},
regionProps,
}: ToastProviderProps) => {
const toastQueue = useToastQueue(getToastQueue());
const globalContext = useProviderContext();
Expand All @@ -48,6 +50,7 @@ export const ToastProvider = ({
toastOffset={toastOffset}
toastProps={toastProps}
toastQueue={toastQueue}
{...regionProps}
/>
);
};
Expand All @@ -73,5 +76,6 @@ export const closeAll = () => {

keys.map((key) => {
globalToastQueue?.close(key);
globalToastQueue?.remove(key);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this prop does not exist?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! @davidaragundy , fixed 41084ac

});
};
18 changes: 15 additions & 3 deletions packages/components/toast/src/toast-region.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
import type {SlotsToClasses, ToastRegionSlots, ToastRegionVariantProps} from "@heroui/theme";

import {useCallback, useEffect, useMemo, useRef, useState} from "react";
import {useToastRegion, AriaToastRegionProps} from "@react-aria/toast";
import {QueuedToast, ToastState} from "@react-stately/toast";
import {useHover} from "@react-aria/interactions";
import {mergeProps} from "@react-aria/utils";
import {toastRegion, ToastRegionVariantProps} from "@heroui/theme";
import {toastRegion} from "@heroui/theme";
import {clsx} from "@heroui/shared-utils";

import Toast from "./toast";
import {ToastProps, ToastPlacement} from "./use-toast";

interface ToastRegionProps<T> extends AriaToastRegionProps, ToastRegionVariantProps {
export interface RegionProps {
className?: string;
classNames?: SlotsToClasses<ToastRegionSlots>;
}

interface ToastRegionProps<T> extends AriaToastRegionProps, ToastRegionVariantProps, RegionProps {
toastQueue: ToastState<T>;
placement?: ToastPlacement;
maxVisibleToasts: number;
Expand All @@ -23,6 +31,8 @@ export function ToastRegion<T extends ToastProps>({
maxVisibleToasts,
toastOffset,
toastProps = {},
className,
classNames,
...props
}: ToastRegionProps<T>) {
const ref = useRef(null);
Expand All @@ -41,6 +51,8 @@ export function ToastRegion<T extends ToastProps>({
[disableAnimation],
);

const baseStyles = clsx(classNames?.base, className);

useEffect(() => {
function handleTouchOutside(event: TouchEvent) {
if (ref.current && !(ref.current as HTMLDivElement).contains(event.target as Node)) {
Expand All @@ -65,7 +77,7 @@ export function ToastRegion<T extends ToastProps>({
<div
{...mergeProps(regionProps, hoverProps)}
ref={ref}
className={slots.base()}
className={slots.base({class: baseStyles})}
data-placement={placement}
onTouchStart={handleTouchStart}
>
Expand Down
78 changes: 78 additions & 0 deletions packages/components/toast/stories/toast.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@ import React, {useEffect} from "react";
import {Meta} from "@storybook/react";
import {cn, toast} from "@heroui/theme";
import {Button} from "@heroui/button";
import {
Modal,
ModalBody,
ModalContent,
ModalFooter,
ModalHeader,
useDisclosure,
} from "@heroui/modal";
import {Drawer, DrawerContent} from "@heroui/drawer";

import {Toast, ToastProps, ToastProvider, addToast, closeAll} from "../src";

Expand Down Expand Up @@ -217,6 +226,68 @@ const PromiseToastTemplate = (args: ToastProps) => {
);
};

const WithToastFromOverlayTemplate = (args) => {
const {isOpen, onOpen, onOpenChange, onClose} = useDisclosure();
const {
isOpen: isDrawerOpen,
onOpen: onDrawerOpen,
onOpenChange: onDrawerOpenChange,
} = useDisclosure({defaultOpen: args.defaultOpen});

return (
<>
<ToastProvider maxVisibleToasts={args.maxVisibleToasts} placement={args.placement} />

<Modal isOpen={isOpen} scrollBehavior="outside" onOpenChange={onOpenChange}>
<ModalContent>
<ModalHeader>Toast from Modal</ModalHeader>
<ModalBody>
<div>Press &quot;Show Toast&quot; to launch a toast.</div>
</ModalBody>
<ModalFooter>
<div className="flex gap-4">
<Button
onPress={() => {
addToast({
title: "Toast from modal",
description: "Toast Displayed Successfully",
...args,
});
}}
>
Show Toast
</Button>
<Button onPress={onClose}>Close</Button>
</div>
</ModalFooter>
</ModalContent>
</Modal>

<Drawer isOpen={isDrawerOpen} onOpenChange={onDrawerOpenChange}>
<DrawerContent className="p-4">
<Button
className="w-fit"
onPress={() => {
addToast({
title: "Toast from drawer",
description: "Toast Displayed Successfully",
...args,
});
}}
>
Show Toast
</Button>
</DrawerContent>
</Drawer>

<div className="flex gap-x-2">
<Button onPress={onOpen}>Open Modal</Button>
<Button onPress={onDrawerOpen}>Open Drawer</Button>
</div>
</>
);
};

const CustomToastComponent = (args) => {
const color = args.color;
const colorMap = {
Expand Down Expand Up @@ -409,6 +480,13 @@ export const WithEndContent = {
},
};

export const ToastFromOverlay = {
render: WithToastFromOverlayTemplate,
args: {
...defaultProps,
},
};

export const CustomStyles = {
render: CustomToastTemplate,
args: {
Expand Down
2 changes: 1 addition & 1 deletion packages/core/theme/src/components/toast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {colorVariants} from "../utils";

const toastRegion = tv({
slots: {
base: "relative z-50",
base: "relative z-[100]",
},
variants: {
disableAnimation: {
Expand Down
Loading