Skip to content

Commit ef993f6

Browse files
committed
refactor(toast,tooltip): update components implementation and tests
1 parent 31cb61e commit ef993f6

File tree

4 files changed

+42
-24
lines changed

4 files changed

+42
-24
lines changed

packages/react/src/components/toast/toast.spec.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,10 @@ describe("Toast", () => {
8989
</ToastProvider>,
9090
);
9191

92-
const closeButton = container.querySelector("[toast-close]")!;
93-
await user.click(closeButton);
92+
const closeButton = container.querySelector("[toast-close]");
93+
if (closeButton) {
94+
await user.click(closeButton);
95+
}
9496

9597
expect(handleOpenChange).toHaveBeenCalledWith(false);
9698
});
@@ -211,8 +213,10 @@ describe("Toaster with useToast", () => {
211213
expect(screen.getByText("Dismissible Toast")).toBeInTheDocument();
212214
});
213215

214-
const closeButton = container.querySelector("[toast-close]")!;
215-
await user.click(closeButton);
216+
const closeButton = container.querySelector("[toast-close]");
217+
if (closeButton) {
218+
await user.click(closeButton);
219+
}
216220

217221
await waitFor(() => {
218222
expect(screen.queryByText("Dismissible Toast")).not.toBeInTheDocument();

packages/react/src/components/toast/toaster.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,18 @@ import { useToast } from "./use-toast";
4444
* }
4545
* ```
4646
*/
47-
export function Toaster() {
47+
export function Toaster(): React.ReactElement {
4848
const { toasts } = useToast();
4949

5050
return (
5151
<ToastProvider>
52-
{toasts.map(function ({ id, title, description, action, ...props }) {
52+
{toasts.map(function toastMapper({
53+
id,
54+
title,
55+
description,
56+
action,
57+
...props
58+
}): React.ReactElement {
5359
return (
5460
<Toast key={id} {...props}>
5561
<div className="grid gap-1">

packages/react/src/components/toast/use-toast.ts

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ let count = 0;
6363
* - In production, consider using UUID or nanoid for guaranteed uniqueness
6464
* - IDs are used to update/dismiss specific toasts
6565
*/
66-
function genId() {
66+
function genId(): string {
6767
count = (count + 1) % Number.MAX_SAFE_INTEGER;
6868
return count.toString();
6969
}
@@ -88,7 +88,7 @@ type Action =
8888
toastId?: ToasterToast["id"];
8989
};
9090

91-
interface State {
91+
interface ToastState {
9292
toasts: ToasterToast[];
9393
}
9494

@@ -110,7 +110,7 @@ const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>();
110110
* - Clears any existing timer for the same toast ID
111111
* - This prevents duplicate timers when updating a toast
112112
*/
113-
const addToRemoveQueue = (toastId: string) => {
113+
const addToRemoveQueue = (toastId: string): void => {
114114
if (toastTimeouts.has(toastId)) {
115115
return;
116116
}
@@ -135,7 +135,7 @@ const addToRemoveQueue = (toastId: string) => {
135135
* - Enforces TOAST_LIMIT by dismissing oldest toasts
136136
* - This is a common pattern in state management libraries
137137
*/
138-
export const reducer = (state: State, action: Action): State => {
138+
export const reducer = (state: ToastState, action: Action): ToastState => {
139139
switch (action.type) {
140140
case "ADD_TOAST":
141141
return {
@@ -157,8 +157,8 @@ export const reducer = (state: State, action: Action): State => {
157157
if (toastId) {
158158
addToRemoveQueue(toastId);
159159
} else {
160-
state.toasts.forEach((toast) => {
161-
addToRemoveQueue(toast.id);
160+
state.toasts.forEach((currentToast) => {
161+
addToRemoveQueue(currentToast.id);
162162
});
163163
}
164164

@@ -197,9 +197,9 @@ export const reducer = (state: State, action: Action): State => {
197197
* - This is a simple pub/sub pattern for cross-component communication
198198
* - The Toaster component subscribes to render toasts
199199
*/
200-
const listeners: ((state: State) => void)[] = [];
200+
const listeners: ((state: ToastState) => void)[] = [];
201201

202-
let memoryState: State = { toasts: [] };
202+
let memoryState: ToastState = { toasts: [] };
203203

204204
/**
205205
* Dispatches actions to update toast state
@@ -209,7 +209,7 @@ let memoryState: State = { toasts: [] };
209209
* - Notifies all subscribed listeners
210210
* - This allows the hook to work outside of React components
211211
*/
212-
function dispatch(action: Action) {
212+
function dispatch(action: Action): void {
213213
memoryState = reducer(memoryState, action);
214214
listeners.forEach((listener) => {
215215
listener(memoryState);
@@ -234,17 +234,21 @@ type Toast = Omit<ToasterToast, "id">;
234234
* - Returns the toast object for programmatic updates
235235
* - This is the primary API for showing toasts
236236
*/
237-
function toast({ ...props }: Toast) {
237+
function toast({ ...props }: Toast): {
238+
id: string;
239+
dismiss: () => void;
240+
update: (props: ToasterToast) => void;
241+
} {
238242
const id = genId();
239243

240-
const update = (props: ToasterToast) => {
244+
const update = (toastProps: ToasterToast): void => {
241245
dispatch({
242246
type: "UPDATE_TOAST",
243-
toast: { ...props, id },
247+
toast: { ...toastProps, id },
244248
});
245249
};
246250

247-
const dismiss = () => {
251+
const dismiss = (): void => {
248252
dispatch({ type: "DISMISS_TOAST", toastId: id });
249253
};
250254

@@ -254,7 +258,7 @@ function toast({ ...props }: Toast) {
254258
...props,
255259
id,
256260
open: true,
257-
onOpenChange: (open) => {
261+
onOpenChange: (open): void => {
258262
if (!open) dismiss();
259263
},
260264
},
@@ -276,8 +280,12 @@ function toast({ ...props }: Toast) {
276280
* - Returns toast function and current toasts array
277281
* - Cleans up listener on unmount to prevent memory leaks
278282
*/
279-
function useToast() {
280-
const [state, setState] = React.useState<State>(memoryState);
283+
function useToast(): {
284+
toasts: ToasterToast[];
285+
toast: typeof toast;
286+
dismiss: (toastId?: string) => void;
287+
} {
288+
const [state, setState] = React.useState<ToastState>(memoryState);
281289

282290
React.useEffect(() => {
283291
listeners.push(setState);
@@ -292,7 +300,7 @@ function useToast() {
292300
return {
293301
...state,
294302
toast,
295-
dismiss: (toastId?: string) => {
303+
dismiss: (toastId?: string): void => {
296304
dispatch({ type: "DISMISS_TOAST", toastId });
297305
},
298306
};

packages/react/src/components/tooltip/tooltip.spec.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ describe("Tooltip", () => {
111111
});
112112

113113
it("renders with controlled state", () => {
114-
function ControlledTooltip() {
114+
function ControlledTooltip(): React.ReactElement {
115115
const [open, setOpen] = React.useState(false);
116116

117117
return (

0 commit comments

Comments
 (0)