Toast
PortedA manager for code outside React (timers, fetch helpers). Pass it to <Toasty toastManager={…}>.
import { Button } from "@etched/monet/button";
import { useToastManager } from "@etched/monet/toast";
export function Preview() {
const toasts = useToastManager();
return (
<Button onClick={() => toasts.add({ variant: "success", title: "Run 1,284 finished", description: "All 96 chips passed." })}>
Finish run
</Button>
);
}Installation
import { createToastManager, Toasty, useToastManager } from "@etched/monet/toast";Examples
Variants
Error toasts stay until the user closes them. The default toast has an id, so a second click bumps the same toast.
import { Button } from "@etched/monet/button";
import { useToastManager } from "@etched/monet/toast";
export function Variants() {
const toasts = useToastManager();
return (
<div className="row">
{/* Orbit's intake toast. */}
<Button onClick={() => toasts.add({ id: "intake", title: "Candidate saved." })}>Default</Button>
<Button onClick={() => toasts.add({ variant: "success", title: "Run 1,284 finished", description: "All 96 chips passed." })}>
Success
</Button>
<Button onClick={() => toasts.add({ variant: "info", title: "New results", description: "Lot 7 has 12 new chips." })}>Info</Button>
<Button onClick={() => toasts.add({ variant: "warning", title: "Queue almost full", description: "New runs may start late." })}>
Warning
</Button>
<Button onClick={() => toasts.add({ variant: "error", title: "Owner not saved", description: "The server did not answer. Try again." })}>
Error
</Button>
</div>
);
}Loading, promise and retry
A loading toast shows a spinner and stays until update changes it. promise does the same for one promise. An error that is updated to success closes on the usual timer again.
import { Button } from "@etched/monet/button";
import { useToastManager } from "@etched/monet/toast";
const wait = (ms: number, fail = false) => new Promise<void>((resolve, reject) => setTimeout(fail ? reject : resolve, ms));
export function LoadingPromiseAndRetry() {
const toasts = useToastManager();
const save = () => {
const id = toasts.add({ variant: "loading", title: "Saving owner…" });
setTimeout(() => toasts.update(id, { variant: "success", title: "Owner saved" }), 1000);
};
const failThenRetry = () => {
const retry = () => {
toasts.update(id, { variant: "loading", title: "Saving notes…", description: undefined, actions: [] });
setTimeout(() => toasts.update(id, { variant: "success", title: "Notes saved" }), 1000);
};
const id = toasts.add({
variant: "error",
title: "Notes not saved",
description: "The server did not answer.",
actions: [{ children: "Retry", size: "sm", onClick: retry }],
});
};
const exportRuns = (fail: boolean) =>
toasts
.promise(wait(1500, fail), {
loading: { title: "Exporting 96 runs…" },
success: { title: "Runs exported", description: "runs-1284.csv is in your downloads." },
error: { title: "Export failed", description: "Lot L-7799 has no runs. Check the lot ID and try again." },
})
// The toast shows the failure.
.catch(() => {});
return (
<div className="row">
<Button onClick={save}>Saving, then saved</Button>
<Button onClick={() => exportRuns(false)}>Export runs</Button>
<Button onClick={() => exportRuns(true)}>Export that fails</Button>
<Button onClick={failThenRetry}>Fails, then retry</Button>
</div>
);
}Action and persistent
Actions do not close the toast; call close(id) for that. A timeout of 0 keeps a toast until the user closes it.
import { Button } from "@etched/monet/button";
import { useToastManager } from "@etched/monet/toast";
export function ActionAndPersistent() {
const toasts = useToastManager();
const deleteRun = () => {
const id = toasts.add({
title: "Run 1,284 deleted",
actions: [{ children: "Undo", size: "sm", onClick: () => toasts.close(id) }],
});
};
return (
<div className="row">
<Button onClick={deleteRun}>With action</Button>
<Button onClick={() => toasts.add({ variant: "info", title: "Firmware 2.4 is out", description: "Stays until you close it.", timeout: 0 })}>
Persistent
</Button>
</div>
);
}Accessibility
F6moves focus to the toasts;Tabthen reaches each toast and its buttons.Escapecloses the focused toast, andShift+Tabgoes back.- A polite
regionnamed "Notifications" holds them. Each toast is adialog, or analertdialogforerror;closeLabelnames its close button. - Screen readers hear new toasts politely. An
errortoast is read at once from analertregion. - Timers pause while the pointer or keyboard focus is on the toasts.
errorandloadingtoasts stay until closed. - Do not make a toast's action, such as Undo, the only way to do it: most toasts close after
timeout. Pass translated text incloseLabel.
API reference
| Prop | Type | Default | Description |
|---|---|---|---|
| children * | ReactNode | – | The app. Toasts render in a portal above it. |
| toastManager | ToastManager | – | A manager from createToastManager(), so code outside React can show toasts here. |
| limit | number | 3 | Most toasts shown at once. Older ones fade out when a new one comes. |
| timeout | number | 5000 | Milliseconds before a toast closes, when it gives no timeout. 0 keeps toasts open. Error and loading toasts stay open anyway. |
| closeLabel | string | "Close" | Names each toast's close button. Pass translated text. |
| container | BaseToast.Portal.Props["container"] | document.body | The element the toasts render into (a portal), for example a shadow root. |