Skip to content

Commit

Permalink
fix(toast): add ability to add multiple toasts in a single call to ad…
Browse files Browse the repository at this point in the history
…dress #3698 (#3749)

* fix(toast): add ability to add multiple toasts in a single call to address #3698

* chore: typedocs and changeset

* chore: remove array option

* chore: typedocs
  • Loading branch information
SiTaggart committed Feb 2, 2024
1 parent 820074b commit 33116f9
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 13 deletions.
6 changes: 6 additions & 0 deletions .changeset/eighty-snails-watch.md
@@ -0,0 +1,6 @@
---
"@twilio-paste/toast": minor
"@twilio-paste/core": minor
---

[Toast]: adds ability to display multiple toasts from a single call
19 changes: 19 additions & 0 deletions packages/paste-core/components/toast/__tests__/useToaster.spec.tsx
Expand Up @@ -27,6 +27,25 @@ describe("useToaster", () => {
expect(result.current.toasts.length).toEqual(1);
});

it("should push multiple toasts by calling it twice", () => {
const { result } = renderHook(() => useToaster());
expect(result.current.toasts).toEqual([]);

act(() => {
result.current.push({
message: "hi",
variant: "error",
});
// eslint-disable-next-line unicorn/no-array-push-push
result.current.push({
message: "hi",
variant: "error",
});
});

expect(result.current.toasts.length).toEqual(2);
});

it("should generate an id when none is passed", () => {
const { result } = renderHook(() => useToaster());

Expand Down
29 changes: 16 additions & 13 deletions packages/paste-core/components/toast/src/useToaster.ts
Expand Up @@ -20,7 +20,7 @@ export const useToaster = (): UseToasterReturnedProps => {
}
});
};
}, []);
}, [toasts]);

const pop = (id: ToasterToast["id"]): void => {
if (!isMounted.current) {
Expand Down Expand Up @@ -50,26 +50,29 @@ export const useToaster = (): UseToasterReturnedProps => {
}

const generatedID = uid(newToast);
let timeOutId;
let timeOutId: number;
/*
* if you are setting a dismissAfter time, we need to grab a timeout id to use later if we need to clear the timeout
* for that particular toast. We also need to make sure the time is an integer to prevent locking the browser
*/
if (newToast.dismissAfter != null && Number.isInteger(newToast.dismissAfter)) {
timeOutId = window.setTimeout(pop, newToast.dismissAfter, newToast.id || generatedID);
}
/*
* We set a new toast to always setFocus. For all the existing toasts in the stack, we need to clear setFocus
* without creating a new state object. If you create a new state object, you cause react spring to rerun
* all the animations for the entire stack. So we mutate existing state instead.
*/
const existingToasts = toasts.map((toast) => {
const tmpToast = toast;
tmpToast.setFocus = false;
return tmpToast;
});

// add the new toast with a generatedID, timeoutid and setFocus to true. Allow for user to override
setToasts([{ id: generatedID, timeOutId, setFocus: true, ...newToast }, ...existingToasts]);
setToasts((state) => {
/*
* We set a new toast to always setFocus. For all the existing toasts in the stack, we need to clear setFocus
* without creating a new state object. If you create a new state object, you cause react spring to rerun
* all the animations for the entire stack. So we mutate existing state instead.
*/
const existingToasts = state.map((toast) => {
const tmpToast = toast;
tmpToast.setFocus = false;
return tmpToast;
});
return [{ id: generatedID, timeOutId, setFocus: true, ...newToast }, ...existingToasts];
});
};

return { toasts, push, pop };
Expand Down

0 comments on commit 33116f9

Please sign in to comment.