From c097d9f31380c77145399f1237c72189f667f97a Mon Sep 17 00:00:00 2001 From: Andrew Holloway Date: Fri, 7 Jun 2024 15:48:42 -0500 Subject: [PATCH] feat(ToastNotification): add dismissType with automated dismissing - add in new feature with default - add tests and snapshots --- .../ToastNotification.stories.ts | 14 +++++++++- .../ToastNotification.test.ts | 8 ------ .../ToastNotification.test.tsx | 26 ++++++++++++++++++ .../ToastNotification/ToastNotification.tsx | 27 ++++++++++++++++++- ...s.snap => ToastNotification.test.tsx.snap} | 0 5 files changed, 65 insertions(+), 10 deletions(-) delete mode 100644 src/components/ToastNotification/ToastNotification.test.ts create mode 100644 src/components/ToastNotification/ToastNotification.test.tsx rename src/components/ToastNotification/__snapshots__/{ToastNotification.test.ts.snap => ToastNotification.test.tsx.snap} (100%) diff --git a/src/components/ToastNotification/ToastNotification.stories.ts b/src/components/ToastNotification/ToastNotification.stories.ts index 77651c06a..0cef1e471 100644 --- a/src/components/ToastNotification/ToastNotification.stories.ts +++ b/src/components/ToastNotification/ToastNotification.stories.ts @@ -10,7 +10,10 @@ export default { layout: 'centered', badges: ['intro-1.0', 'current-2.0'], }, - argTypes: { onDismiss: { action: 'dismissed' } }, + argTypes: { + onDismiss: { action: 'dismissed' }, + timeout: { table: { disable: true } }, + }, args: { title: "You've got a temporary notification!", className: 'w-96', @@ -39,3 +42,12 @@ export const NotDismissable: StoryObj = { onDismiss: undefined, }, }; + +export const AutoDismiss: StoryObj = { + args: { + ...Default.args, + dissmissType: 'auto', + timeout: 500, + onDismiss: () => console.log('trigger onDismiss'), + }, +}; diff --git a/src/components/ToastNotification/ToastNotification.test.ts b/src/components/ToastNotification/ToastNotification.test.ts deleted file mode 100644 index eb0826f2f..000000000 --- a/src/components/ToastNotification/ToastNotification.test.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { generateSnapshots } from '@chanzuckerberg/story-utils'; -import type { StoryFile } from '@storybook/testing-react'; - -import * as stories from './ToastNotification.stories'; - -describe(' (v2)', () => { - generateSnapshots(stories as StoryFile); -}); diff --git a/src/components/ToastNotification/ToastNotification.test.tsx b/src/components/ToastNotification/ToastNotification.test.tsx new file mode 100644 index 000000000..d4fb19a81 --- /dev/null +++ b/src/components/ToastNotification/ToastNotification.test.tsx @@ -0,0 +1,26 @@ +import { generateSnapshots } from '@chanzuckerberg/story-utils'; +import { composeStories, type StoryFile } from '@storybook/testing-react'; +import { render, waitFor } from '@testing-library/react'; + +import React from 'react'; +import * as stories from './ToastNotification.stories'; + +const { AutoDismiss } = composeStories(stories); + +const { AutoDismiss: skip, ...staticStories } = stories; + +describe(' (v2)', () => { + afterEach(() => { + jest.resetAllMocks(); + }); + + generateSnapshots(staticStories as StoryFile); + + it('triggers the onDissmiss after a delay', async () => { + const consoleSpy = jest.spyOn(console, 'log').mockImplementation(); + + render(); + + await waitFor(() => expect(consoleSpy).toHaveBeenCalledTimes(1)); + }); +}); diff --git a/src/components/ToastNotification/ToastNotification.tsx b/src/components/ToastNotification/ToastNotification.tsx index e865acee9..a96925f00 100644 --- a/src/components/ToastNotification/ToastNotification.tsx +++ b/src/components/ToastNotification/ToastNotification.tsx @@ -1,5 +1,5 @@ import clsx from 'clsx'; -import React from 'react'; +import React, { useEffect } from 'react'; import getIconNameFromStatus from '../../util/getIconNameFromStatus-v2'; import type { Status } from '../../util/variant-types'; @@ -19,7 +19,18 @@ export type ToastNotificationProps = { * Callback when notification is dismissed. When passed in, renders banner with a close icon in the top right. */ onDismiss?: () => void; + /** + * Length of time to wait until `onDismiss` is called + */ + timeout?: number; // Design API + /** + * Determines whether the toast notification will dismiss on its own, or due to user action. When set to `"auto"`, + * it will dismiss after 8 seconds. + * + * **Default is `"manual"`**. + */ + dissmissType?: 'manual' | 'auto'; /** * Keyword to characterize the state of the notification */ @@ -37,8 +48,10 @@ export type ToastNotificationProps = { */ export const ToastNotification = ({ className, + dissmissType = 'manual', onDismiss, status = 'favorable', + timeout = 8000, title, ...other }: ToastNotificationProps) => { @@ -47,6 +60,18 @@ export const ToastNotification = ({ status && styles[`toast--status-${status}`], className, ); + + useEffect(() => { + const expireId = + dissmissType === 'auto' + ? setTimeout(() => { + onDismiss && onDismiss(); + }, timeout) + : undefined; + + return () => clearTimeout(expireId); + }, [onDismiss, dissmissType, timeout]); + return (