From 62a41f92f45de93c84369b7a7c1c6eff3f93a0b2 Mon Sep 17 00:00:00 2001 From: Raouf Date: Fri, 29 Nov 2024 15:53:03 +0100 Subject: [PATCH] feat(pie-toast-provider): DSW-2222 add tests --- .../toast/overview/priority-queue.json | 4 +- .../stories/pie-toast-provider.stories.ts | 81 +++++----- .../components/pie-toast-provider/src/defs.ts | 20 ++- .../pie-toast-provider/src/index.ts | 32 +--- .../pie-toast-provider/src/toaster.ts | 30 ++++ .../test/component/pie-toast-provider.spec.ts | 148 +++++++++++++++++- 6 files changed, 238 insertions(+), 77 deletions(-) create mode 100644 packages/components/pie-toast-provider/src/toaster.ts diff --git a/apps/pie-docs/src/components/toast/overview/priority-queue.json b/apps/pie-docs/src/components/toast/overview/priority-queue.json index e47860064e..647d8a4838 100644 --- a/apps/pie-docs/src/components/toast/overview/priority-queue.json +++ b/apps/pie-docs/src/components/toast/overview/priority-queue.json @@ -18,7 +18,7 @@ ], [ "4", - "Positive - actionable" + "Success - actionable" ], [ "5", @@ -34,7 +34,7 @@ ], [ "8", - "Positive" + "Success" ], [ "9", diff --git a/apps/pie-storybook/stories/pie-toast-provider.stories.ts b/apps/pie-storybook/stories/pie-toast-provider.stories.ts index 2608058173..7455fbb86f 100644 --- a/apps/pie-storybook/stories/pie-toast-provider.stories.ts +++ b/apps/pie-storybook/stories/pie-toast-provider.stories.ts @@ -11,14 +11,6 @@ import { createStory } from '../utilities'; type ToastProviderStoryMeta = Meta; -const onQueueUpdate = (event: CustomEvent) => { - action('pie-toast-provider-queue-update')(event.detail); - - const queueLengthTag = document.querySelector('#queue-length-tag') as HTMLElement; - if (queueLengthTag) { - queueLengthTag.textContent = `Toast Queue Length: ${event.detail.length}`; - } -}; const defaultArgs: ToastProviderProps = { ...defaultProps, options: { @@ -35,7 +27,7 @@ const toastProviderStoryMeta: ToastProviderStoryMeta = { component: 'pie-toast-provider', argTypes: { options: { - description: 'Global options to configure default toast behavior.', + description: 'Default options for all toasts; accepts all toast props.', control: 'object', defaultValue: { summary: defaultProps.options, @@ -53,7 +45,15 @@ const toastProviderStoryMeta: ToastProviderStoryMeta = { export default toastProviderStoryMeta; -const Template = ({ options }: ToastProviderProps) => html` +const Template = ({ options }: ToastProviderProps) => { + const onQueueUpdate = (event: CustomEvent) => { + const queueLength = document.querySelector('#queue-length-tag') as HTMLElement; + if (queueLength) { + queueLength.textContent = `Toast Queue Length: ${event.detail.length}`; + } + }; + + return html` @@ -66,57 +66,64 @@ const Template = ({ options }: ToastProviderProps) => html`
{ - toaster.create({ - message: 'Low Priority Info Toast', - variant: 'info', - duration: null, - leadingAction: { - text: 'Confirm', - }, - }); -}}> + toaster.create({ + message: 'Low Priority Info', + variant: 'info', + }); + }}> Trigger Info Toast (Low Priority) { - toaster.create({ - message: 'Medium Priority Warning Toast', - variant: 'warning', - }); -}}> + toaster.create({ + message: 'Medium Priority Warning Toast', + variant: 'warning', + }); + }}> Trigger Warning Toast (Medium Priority) { - toaster.create({ - message: 'High Priority Error Toast', - variant: 'error', - }); -}}> + toaster.create({ + message: 'High Priority Error Toast', + variant: 'error', + }); + }}> Trigger Error Toast (High Priority) { - toaster.create({ - message: 'Actionable Info Toast', - variant: 'info', - leadingAction: { text: 'Retry' }, - }); -}}> + toaster.create({ + message: 'Actionable Info Toast', + variant: 'info', + leadingAction: { text: 'Retry' }, + }); + }}> Trigger Actionable Info Toast + { + toaster.create({ + message: 'Persistent Toast', + duration: null, + }); + }}> + Trigger Persistent Toast + + { - toaster.clearAll(); -}}> + toaster.clearAll(); + }}> Clear All Toasts
`; +}; export const Default = createStory(Template, defaultArgs)(); diff --git a/packages/components/pie-toast-provider/src/defs.ts b/packages/components/pie-toast-provider/src/defs.ts index 5989db056d..f4475c7b98 100644 --- a/packages/components/pie-toast-provider/src/defs.ts +++ b/packages/components/pie-toast-provider/src/defs.ts @@ -1,48 +1,52 @@ import { type ToastProps } from '@justeattakeaway/pie-toast'; +import { type ComponentDefaultProps } from '@justeattakeaway/pie-webc-core'; + export const PRIORITY_ORDER: { [x: string]: number } = { 'error-actionable': 1, error: 2, 'warning-actionable': 3, - 'positive-actionable': 4, + 'success-actionable': 4, 'info-actionable': 5, 'neutral-actionable': 6, warning: 7, - positive: 8, + success: 8, info: 9, neutral: 10, }; export interface ExtendedToastProps extends ToastProps { /** - * Callback for when the toast is closed. + * Triggered when the user interacts with the close icon or when the toast auto dismiss. */ onPieToastClose?: () => void; /** - * Callback for when the toast is opened. + * Triggered when the toast is opened. */ onPieToastOpen?: () => void; /** - * Callback for when the leading action is clicked. + * Triggered when the user interacts with the leading action. */ onPieToastLeadingActionClick?: (event: Event) => void; } export interface ToastProviderProps { /** - * Default options for all toasts. + * Default options for all toasts; accepts all toast props. */ options?: Partial; } -export const defaultProps: ToastProviderProps = { +export type DefaultProps = ComponentDefaultProps; + +export const defaultProps: DefaultProps = { options: {}, }; /** - * Event name for when the chip is closed. + * Event name for when the toast provider queue is updated. * * @constant */ diff --git a/packages/components/pie-toast-provider/src/index.ts b/packages/components/pie-toast-provider/src/index.ts index 00d1276d82..00fd7a065c 100644 --- a/packages/components/pie-toast-provider/src/index.ts +++ b/packages/components/pie-toast-provider/src/index.ts @@ -12,6 +12,7 @@ import { defineCustomElement, dispatchCustomEvent, } from '@justeattakeaway/pie-webc-core'; +import '@justeattakeaway/pie-toast'; import styles from './toast-provider.scss?inline'; import { defaultProps, @@ -23,6 +24,7 @@ import { // Valid values available to consumers export * from './defs'; +export { toaster } from './toaster'; const componentSelector = 'pie-toast-provider'; @@ -118,7 +120,7 @@ export class PieToastProvider extends RtlMixin(LitElement) implements ToastProvi const { _currentToast, _dismissToast } = this; return html` -
+
${_currentToast ? html` { const toastProvider = page.locator(componentSelector); // Assert - expect(toastProvider).toBeVisible(); + expect(toastProvider).toBeDefined(); + }); + + test.describe('Priority Order Tests', () => { + test('should handle toast priority correctly', async ({ page, mount }) => { + // Arrange + let toasts: ExtendedToastProps[] = []; + await mount(PieToastProvider, { + on: { + [ON_TOAST_PROVIDER_QUEUE_UPDATE_EVENT]: (queue: ExtendedToastProps[]) => { + toasts = queue; + }, + }, + }); + + // Act + await page.evaluate(() => { + const toastProvider = document.querySelector('pie-toast-provider') as PieToastProvider; + + toastProvider.createToast({ + message: 'Neutral toast (Priority 10)', + variant: 'neutral', + }); + + toastProvider.createToast({ + message: 'Success toast with action (Priority 4)', + variant: 'success', + leadingAction: { text: 'Action' }, + }); + + toastProvider.createToast({ + message: 'Info toast with action (5)', + variant: 'info', + leadingAction: { text: 'Action' }, + }); + + toastProvider.createToast({ + message: 'Error toast (Priority 2)', + variant: 'error', + }); + }); + + // Assert + const queueVariants = toasts.map((toast: ExtendedToastProps) => `${toast.variant}${toast.leadingAction ? '-actionable' : ''}`); + for (let i = 1; i < queueVariants.length; i++) { + const prevPriority = PRIORITY_ORDER[queueVariants[i - 1]]; + const currPriority = PRIORITY_ORDER[queueVariants[i]]; + expect(currPriority).toBeGreaterThanOrEqual(prevPriority); // Ensure the current has a higher priority + } + }); + + test('should clear all toasts when clearToasts is called', async ({ page, mount }) => { + // Arrange + let toasts: ExtendedToastProps[] = []; + await mount(PieToastProvider, { + on: { + [ON_TOAST_PROVIDER_QUEUE_UPDATE_EVENT]: (queue: ExtendedToastProps[]) => { + toasts = queue; + }, + }, + }); + + // Act + await page.evaluate(() => { + const toastProvider = document.querySelector('pie-toast-provider') as PieToastProvider; + + toastProvider.createToast({ + message: 'Toast 1', + variant: 'neutral', + }); + + toastProvider.createToast({ + message: 'Toast 2', + variant: 'success', + }); + }); + + // Act + await page.evaluate(() => { + const toastProvider = document.querySelector('pie-toast-provider') as PieToastProvider; + toastProvider.clearToasts(); + }); + + // Assert + expect(toasts.length).toBe(0); + }); + }); + + test.describe('Props', () => { + test.describe('options', () => { + test('should apply global options to all toasts when options are passed', async ({ page, mount }) => { + let toasts: ExtendedToastProps[] = []; + await mount(PieToastProvider, { + props: { + options: { + variant: 'neutral', + isDismissible: true, + }, + } as PieToastProvider, + on: { + [ON_TOAST_PROVIDER_QUEUE_UPDATE_EVENT]: (queue: ExtendedToastProps[]) => { + toasts = queue; + }, + }, + }); + + // Act + await page.evaluate(() => { + const toastProvider = document.querySelector('pie-toast-provider') as PieToastProvider; + toastProvider.createToast({ message: 'Toast 1' }); + toastProvider.createToast({ message: 'Toast 2' }); + }); + + // Assert + toasts.forEach((toast) => { + expect(toast.isDismissible).toBeTruthy(); + expect(toast.variant).toBe('neutral'); + }); + }); + + test('should respect individual toast overrides when provided', async ({ page, mount }) => { + const toasts: ExtendedToastProps[] = []; + await mount(PieToastProvider, { + props: { + options: { duration: null }, + } as PieToastProvider, + + }); + + // Act + await page.evaluate(() => { + const toastProvider = document.querySelector('pie-toast-provider') as PieToastProvider; + toastProvider.createToast({ message: 'Toast 1' }); + toastProvider.createToast({ message: 'Toast 2' }); + toastProvider.createToast({ message: 'Toast 3', isDismissible: false }); + }); + + // Assert + expect(toasts[0].isDismissible).toBeTruthy(); // Global option should apply + expect(toasts[1].isDismissible).toBeFalsy(); // Override should take precedence + }); + }); }); });