From 0687389d8fd98697aef087cf70014eea54f5cef1 Mon Sep 17 00:00:00 2001 From: pmakode-akamai Date: Tue, 27 Aug 2024 18:05:29 +0530 Subject: [PATCH 01/17] Refactor useToastNotification async toasts --- .../src/features/Events/asyncToasts.tsx | 136 ++++++++++ .../src/features/Events/factories/backup.tsx | 2 +- .../src/features/Events/factories/disk.tsx | 12 +- .../src/hooks/useToastNotifications.tsx | 235 +----------------- 4 files changed, 158 insertions(+), 227 deletions(-) create mode 100644 packages/manager/src/features/Events/asyncToasts.tsx diff --git a/packages/manager/src/features/Events/asyncToasts.tsx b/packages/manager/src/features/Events/asyncToasts.tsx new file mode 100644 index 00000000000..f827d2f7e99 --- /dev/null +++ b/packages/manager/src/features/Events/asyncToasts.tsx @@ -0,0 +1,136 @@ +import { getEventMessage } from './utils'; + +import type { Event, EventAction } from '@linode/api-v4'; + +interface ToastMessage { + message: ((event: Event) => JSX.Element | null | string | undefined) | string; + persist?: boolean; +} + +interface Toast { + failure?: ToastMessage; + /** + * If true, the toast will be displayed with an error variant. + */ + invertVariant?: boolean; + success?: ToastMessage; +} + +type Toasts = { + [key in EventAction]?: Toast; +}; + +/** + * This constant defines toast notifications that will be displayed + * when our events polling system gets a new event. + * + * Use this feature to notify users of *asynchronous tasks* such as migrating a Linode. + * + * DO NOT use this feature to notify the user of tasks like changing the label of a Linode. + * Toasts for that can be handled at the time of making the PUT request. + */ +export const toasts: Toasts = { + backups_restore: { + failure: { + message: (e) => getEventMessage(e), + persist: true, + }, + }, + disk_delete: { + failure: { + message: (e) => getEventMessage(e), + }, + success: { + message: (e) => getEventMessage(e), + }, + }, + disk_imagize: { + failure: { + message: (e) => getEventMessage(e), + persist: true, + }, + success: { + message: (e) => getEventMessage(e), + }, + }, + disk_resize: { + failure: { + message: (e) => getEventMessage(e), + persist: true, + }, + success: { + message: (e) => getEventMessage(e), + }, + }, + image_delete: { + failure: { message: (e) => getEventMessage(e) }, + success: { message: (e) => getEventMessage(e) }, + }, + image_upload: { + failure: { + message: (e) => getEventMessage(e), + persist: true, + }, + success: { message: (e) => getEventMessage(e) }, + }, + linode_clone: { + failure: { message: (e) => getEventMessage(e) }, + success: { + message: (e) => getEventMessage(e), + }, + }, + linode_migrate: { + failure: { message: (e) => getEventMessage(e) }, + success: { message: (e) => getEventMessage(e) }, + }, + linode_migrate_datacenter: { + failure: { message: (e) => getEventMessage(e) }, + success: { message: (e) => getEventMessage(e) }, + }, + linode_resize: { + failure: { message: (e) => getEventMessage(e) }, + success: { message: (e) => getEventMessage(e) }, + }, + linode_snapshot: { + failure: { + message: (e) => getEventMessage(e), + persist: true, + }, + }, + longviewclient_create: { + failure: { + message: (e) => getEventMessage(e), + }, + success: { + message: (e) => getEventMessage(e), + }, + }, + tax_id_invalid: { + failure: { message: (e) => getEventMessage(e) }, + invertVariant: true, + success: { + message: (e) => getEventMessage(e), + persist: true, + }, + }, + volume_attach: { + failure: { message: (e) => getEventMessage(e) }, + success: { message: (e) => getEventMessage(e) }, + }, + volume_create: { + failure: { message: (e) => getEventMessage(e) }, + success: { message: (e) => getEventMessage(e) }, + }, + volume_delete: { + failure: { message: (e) => getEventMessage(e) }, + success: { message: (e) => getEventMessage(e) }, + }, + volume_detach: { + failure: { message: (e) => getEventMessage(e) }, + success: { message: (e) => getEventMessage(e) }, + }, + volume_migrate: { + failure: { message: (e) => getEventMessage(e) }, + success: { message: (e) => getEventMessage(e) }, + }, +}; diff --git a/packages/manager/src/features/Events/factories/backup.tsx b/packages/manager/src/features/Events/factories/backup.tsx index 31344a45197..3e1bee1af85 100644 --- a/packages/manager/src/features/Events/factories/backup.tsx +++ b/packages/manager/src/features/Events/factories/backup.tsx @@ -22,7 +22,7 @@ export const backup: PartialEventMap<'backups'> = { backups_restore: { failed: (e) => ( <> - Backup could not be restored for + Backup could not be restored for{' '} {e.entity!.label}.{' '} Learn more about limits and considerations. diff --git a/packages/manager/src/features/Events/factories/disk.tsx b/packages/manager/src/features/Events/factories/disk.tsx index 8168cdd76e4..f75d88dffcc 100644 --- a/packages/manager/src/features/Events/factories/disk.tsx +++ b/packages/manager/src/features/Events/factories/disk.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { Link } from 'src/components/Link'; +import { sendLinodeDiskEvent } from 'src/utilities/analytics/customEventAnalytics'; import { EventLink } from '../EventLink'; @@ -140,7 +141,16 @@ export const disk: PartialEventMap<'disk'> = { <> A disk on Linode could{' '} not be resized.{' '} - + { + sendLinodeDiskEvent( + 'Resize', + 'Click:link', + 'Disk resize failed toast' + ); + }} + to="https://www.linode.com/docs/products/compute/compute-instances/guides/disks-and-storage/" + > Learn more diff --git a/packages/manager/src/hooks/useToastNotifications.tsx b/packages/manager/src/hooks/useToastNotifications.tsx index 51633c484b3..932d650f852 100644 --- a/packages/manager/src/hooks/useToastNotifications.tsx +++ b/packages/manager/src/hooks/useToastNotifications.tsx @@ -1,220 +1,21 @@ import { useSnackbar } from 'notistack'; -import * as React from 'react'; -import { Link } from 'src/components/Link'; -import { SupportLink } from 'src/components/SupportLink'; -import { Typography } from 'src/components/Typography'; -import { sendLinodeDiskEvent } from 'src/utilities/analytics/customEventAnalytics'; +import { toasts } from 'src/features/Events/asyncToasts'; -import type { Event, EventAction } from '@linode/api-v4'; +import type { Event } from '@linode/api-v4'; export const getLabel = (event: Event) => event.entity?.label ?? ''; export const getSecondaryLabel = (event: Event) => event.secondary_entity?.label ?? ''; -const formatLink = (text: string, link: string, handleClick?: () => void) => { - return ( - - {text} - - ); -}; - -interface ToastMessage { - link?: JSX.Element; - message: ((event: Event) => string | undefined) | string; - persist?: boolean; -} - -interface Toast { - failure?: ToastMessage; - /** - * If true, the toast will be displayed with an error variant. - */ - invertVariant?: boolean; - success?: ToastMessage; -} - -type Toasts = { - [key in EventAction]?: Toast; -}; - -/** - * This constant defines toast notifications that will be displayed - * when our events polling system gets a new event. - * - * Use this feature to notify users of *asynchronous tasks* such as migrating a Linode. - * - * DO NOT use this feature to notify the user of tasks like changing the label of a Linode. - * Toasts for that can be handled at the time of making the PUT request. - */ -const toasts: Toasts = { - backups_restore: { - failure: { - link: formatLink( - 'Learn more about limits and considerations.', - 'https://www.linode.com/docs/products/storage/backups/#limits-and-considerations' - ), - message: (e) => `Backup restoration failed for ${getLabel(e)}.`, - persist: true, - }, - }, - disk_delete: { - failure: { - message: (e) => - `Unable to delete disk ${getSecondaryLabel(e)} ${ - getLabel(e) ? ` on ${getLabel(e)}` : '' - }. Is it attached to a configuration profile that is in use?`, - }, - success: { - message: (e) => `Disk ${getSecondaryLabel(e)} successfully deleted.`, - }, - }, - disk_imagize: { - failure: { - link: formatLink( - 'Learn more about image technical specifications.', - 'https://www.linode.com/docs/products/tools/images/#technical-specifications' - ), - message: (e) => - `There was a problem creating Image ${getSecondaryLabel(e)}.`, - persist: true, - }, - - success: { - message: (e) => `Image ${getSecondaryLabel(e)} successfully created.`, - }, - }, - disk_resize: { - failure: { - link: formatLink( - 'Learn more about resizing restrictions.', - 'https://www.linode.com/docs/products/compute/compute-instances/guides/disks-and-storage/', - () => - sendLinodeDiskEvent( - 'Resize', - 'Click:link', - 'Disk resize failed toast' - ) - ), - message: `Disk resize failed.`, - persist: true, - }, - success: { - message: (e) => `Disk ${getSecondaryLabel(e)} successfully resized.`, - }, - }, - image_delete: { - failure: { message: (e) => `Error deleting Image ${getLabel(e)}.` }, - success: { message: (e) => `Image ${getLabel(e)} successfully deleted.` }, - }, - image_upload: { - failure: { - message: (e) => { - const isDeletion = e.message === 'Upload canceled.'; - - if (isDeletion) { - return undefined; - } - - return `There was a problem uploading image ${getLabel( - e - )}: ${e.message?.replace(/(\d+)/g, '$1 MB')}`; - }, - persist: true, - }, - success: { message: (e) => `Image ${getLabel(e)} is now available.` }, - }, - linode_clone: { - failure: { message: (e) => `Error cloning Linode ${getLabel(e)}.` }, - success: { - message: (e) => - `Linode ${getLabel(e)} successfully cloned to ${getSecondaryLabel(e)}.`, - }, - }, - linode_migrate: { - failure: { message: (e) => `Error migrating Linode ${getLabel(e)}.` }, - success: { message: (e) => `Linode ${getLabel(e)} successfully migrated.` }, - }, - linode_migrate_datacenter: { - failure: { message: (e) => `Error migrating Linode ${getLabel(e)}.` }, - success: { message: (e) => `Linode ${getLabel(e)} successfully migrated.` }, - }, - linode_resize: { - failure: { message: (e) => `Error resizing Linode ${getLabel(e)}.` }, - success: { message: (e) => `Linode ${getLabel(e)} successfully resized.` }, - }, - linode_snapshot: { - failure: { - link: formatLink( - 'Learn more about limits and considerations.', - 'https://www.linode.com/docs/products/storage/backups/#limits-and-considerations' - ), - message: (e) => `Snapshot backup failed on Linode ${getLabel(e)}.`, - persist: true, - }, - }, - longviewclient_create: { - failure: { - message: (e) => `Error creating Longview Client ${getLabel(e)}.`, - }, - success: { - message: (e) => `Longview Client ${getLabel(e)} successfully created.`, - }, - }, - tax_id_invalid: { - failure: { message: 'Error validating Tax Identification Number.' }, - invertVariant: true, - success: { - message: - 'Tax Identification Number could not be verified. Please check your Tax ID for accuracy or contact support for assistance.', - persist: true, - }, - }, - volume_attach: { - failure: { message: (e) => `Error attaching Volume ${getLabel(e)}.` }, - success: { message: (e) => `Volume ${getLabel(e)} successfully attached.` }, - }, - volume_create: { - failure: { message: (e) => `Error creating Volume ${getLabel(e)}.` }, - success: { message: (e) => `Volume ${getLabel(e)} successfully created.` }, - }, - volume_delete: { - failure: { message: 'Error deleting Volume.' }, - success: { message: 'Volume successfully deleted.' }, - }, - volume_detach: { - failure: { message: (e) => `Error detaching Volume ${getLabel(e)}.` }, - success: { message: (e) => `Volume ${getLabel(e)} successfully detached.` }, - }, - volume_migrate: { - failure: { message: (e) => `Error upgrading Volume ${getLabel(e)}.` }, - success: { message: (e) => `Volume ${getLabel(e)} successfully upgraded.` }, - }, -}; const getToastMessage = ( - toastMessage: ((event: Event) => string | undefined) | string, + toastMessage: + | ((event: Event) => JSX.Element | null | string | undefined) + | string, event: Event -): string | undefined => +): JSX.Element | null | string | undefined => typeof toastMessage === 'function' ? toastMessage(event) : toastMessage; -const createFormattedMessage = ( - message: string | undefined, - link: JSX.Element | undefined, - hasSupportLink: boolean -) => ( - - {message?.replace(/ contact Support/i, '') ?? message} - {hasSupportLink && ( - <> -   - . - - )} - {link && <> {link}} - -); - export const useToastNotifications = (): { handleGlobalToast: (event: Event) => void; } => { @@ -229,17 +30,11 @@ export const useToastNotifications = (): { const isSuccessEvent = ['finished', 'notification'].includes(event.status); if (isSuccessEvent && toastInfo.success) { - const { link, message, persist } = toastInfo.success; + const { message, persist } = toastInfo.success; const successMessage = getToastMessage(message, event); if (successMessage) { - const formattedSuccessMessage = createFormattedMessage( - successMessage, - link, - false - ); - - enqueueSnackbar(formattedSuccessMessage, { + enqueueSnackbar(successMessage, { persist: persist ?? false, variant: toastInfo.invertVariant ? 'error' : 'success', }); @@ -247,21 +42,11 @@ export const useToastNotifications = (): { } if (event.status === 'failed' && toastInfo.failure) { - const { link, message, persist } = toastInfo.failure; + const { message, persist } = toastInfo.failure; const failureMessage = getToastMessage(message, event); if (failureMessage) { - const hasSupportLink = failureMessage - .toLowerCase() - .includes('contact support'); - - const formattedFailureMessage = createFormattedMessage( - failureMessage, - link, - hasSupportLink - ); - - enqueueSnackbar(formattedFailureMessage, { + enqueueSnackbar(failureMessage, { persist: persist ?? false, variant: toastInfo.invertVariant ? 'success' : 'error', }); From a3d0532fb073c218042f55546c71636038a0d085 Mon Sep 17 00:00:00 2001 From: pmakode-akamai Date: Tue, 27 Aug 2024 18:23:05 +0530 Subject: [PATCH 02/17] Added changeset: Refactor useToastNotification async toasts --- .../.changeset/pr-10841-tech-stories-1724763185078.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 packages/manager/.changeset/pr-10841-tech-stories-1724763185078.md diff --git a/packages/manager/.changeset/pr-10841-tech-stories-1724763185078.md b/packages/manager/.changeset/pr-10841-tech-stories-1724763185078.md new file mode 100644 index 00000000000..6173b8d24de --- /dev/null +++ b/packages/manager/.changeset/pr-10841-tech-stories-1724763185078.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Tech Stories +--- + +Refactor useToastNotification async toasts ([#10841](https://github.com/linode/manager/pull/10841)) From 512499d2e18e9d371511ca3777f037d9fa67450d Mon Sep 17 00:00:00 2001 From: pmakode-akamai Date: Tue, 27 Aug 2024 19:45:38 +0530 Subject: [PATCH 03/17] Clean up and refactor async toasts configuration --- .../src/features/Events/asyncToasts.tsx | 153 ++++++------------ .../src/features/Events/factories/backup.tsx | 3 +- .../src/features/Events/factories/disk.tsx | 2 +- 3 files changed, 54 insertions(+), 104 deletions(-) diff --git a/packages/manager/src/features/Events/asyncToasts.tsx b/packages/manager/src/features/Events/asyncToasts.tsx index f827d2f7e99..685ebdd1f57 100644 --- a/packages/manager/src/features/Events/asyncToasts.tsx +++ b/packages/manager/src/features/Events/asyncToasts.tsx @@ -20,6 +20,37 @@ type Toasts = { [key in EventAction]?: Toast; }; +const createToastBoth = ( + options: { + invertVariant?: boolean; + persistFailure?: boolean; + persistSuccess?: boolean; + } = {} +): Toast => ({ + failure: { + message: (e) => getEventMessage(e), + persist: options.persistFailure || false, + }, + invertVariant: options.invertVariant || false, + success: { + message: (e: any) => getEventMessage(e), + persist: options.persistSuccess || false, + }, +}); + +const createToastFailureOnly = ( + options: { + invertVariant?: boolean; + persistFailure?: boolean; + } = {} +): Toast => ({ + failure: { + message: (e) => getEventMessage(e), + persist: options.persistFailure || false, + }, + invertVariant: options.invertVariant || false, +}); + /** * This constant defines toast notifications that will be displayed * when our events polling system gets a new event. @@ -30,107 +61,25 @@ type Toasts = { * Toasts for that can be handled at the time of making the PUT request. */ export const toasts: Toasts = { - backups_restore: { - failure: { - message: (e) => getEventMessage(e), - persist: true, - }, - }, - disk_delete: { - failure: { - message: (e) => getEventMessage(e), - }, - success: { - message: (e) => getEventMessage(e), - }, - }, - disk_imagize: { - failure: { - message: (e) => getEventMessage(e), - persist: true, - }, - success: { - message: (e) => getEventMessage(e), - }, - }, - disk_resize: { - failure: { - message: (e) => getEventMessage(e), - persist: true, - }, - success: { - message: (e) => getEventMessage(e), - }, - }, - image_delete: { - failure: { message: (e) => getEventMessage(e) }, - success: { message: (e) => getEventMessage(e) }, - }, - image_upload: { - failure: { - message: (e) => getEventMessage(e), - persist: true, - }, - success: { message: (e) => getEventMessage(e) }, - }, - linode_clone: { - failure: { message: (e) => getEventMessage(e) }, - success: { - message: (e) => getEventMessage(e), - }, - }, - linode_migrate: { - failure: { message: (e) => getEventMessage(e) }, - success: { message: (e) => getEventMessage(e) }, - }, - linode_migrate_datacenter: { - failure: { message: (e) => getEventMessage(e) }, - success: { message: (e) => getEventMessage(e) }, - }, - linode_resize: { - failure: { message: (e) => getEventMessage(e) }, - success: { message: (e) => getEventMessage(e) }, - }, - linode_snapshot: { - failure: { - message: (e) => getEventMessage(e), - persist: true, - }, - }, - longviewclient_create: { - failure: { - message: (e) => getEventMessage(e), - }, - success: { - message: (e) => getEventMessage(e), - }, - }, - tax_id_invalid: { - failure: { message: (e) => getEventMessage(e) }, + backups_restore: createToastFailureOnly({ persistFailure: true }), + disk_delete: createToastBoth(), + disk_imagize: createToastBoth({ persistFailure: true }), + disk_resize: createToastBoth({ persistFailure: true }), + image_delete: createToastBoth(), + image_upload: createToastBoth({ persistFailure: true }), + linode_clone: createToastBoth(), + linode_migrate: createToastBoth(), + linode_migrate_datacenter: createToastBoth(), + linode_resize: createToastBoth(), + linode_snapshot: createToastFailureOnly({ persistFailure: true }), + longviewclient_create: createToastBoth(), + tax_id_invalid: createToastBoth({ invertVariant: true, - success: { - message: (e) => getEventMessage(e), - persist: true, - }, - }, - volume_attach: { - failure: { message: (e) => getEventMessage(e) }, - success: { message: (e) => getEventMessage(e) }, - }, - volume_create: { - failure: { message: (e) => getEventMessage(e) }, - success: { message: (e) => getEventMessage(e) }, - }, - volume_delete: { - failure: { message: (e) => getEventMessage(e) }, - success: { message: (e) => getEventMessage(e) }, - }, - volume_detach: { - failure: { message: (e) => getEventMessage(e) }, - success: { message: (e) => getEventMessage(e) }, - }, - volume_migrate: { - failure: { message: (e) => getEventMessage(e) }, - success: { message: (e) => getEventMessage(e) }, - }, + persistSuccess: true, + }), + volume_attach: createToastBoth(), + volume_create: createToastBoth(), + volume_delete: createToastBoth(), + volume_detach: createToastBoth(), + volume_migrate: createToastBoth(), }; diff --git a/packages/manager/src/features/Events/factories/backup.tsx b/packages/manager/src/features/Events/factories/backup.tsx index 3e1bee1af85..0e45776e06d 100644 --- a/packages/manager/src/features/Events/factories/backup.tsx +++ b/packages/manager/src/features/Events/factories/backup.tsx @@ -25,8 +25,9 @@ export const backup: PartialEventMap<'backups'> = { Backup could not be restored for{' '} {e.entity!.label}.{' '} - Learn more about limits and considerations. + Learn more about limits and considerations + . ), finished: (e) => ( diff --git a/packages/manager/src/features/Events/factories/disk.tsx b/packages/manager/src/features/Events/factories/disk.tsx index f75d88dffcc..20b3b6d8f6a 100644 --- a/packages/manager/src/features/Events/factories/disk.tsx +++ b/packages/manager/src/features/Events/factories/disk.tsx @@ -112,7 +112,7 @@ export const disk: PartialEventMap<'disk'> = { Image could{' '} not be created.{' '} - Learn more about image technical specifications. + Learn more about image technical specifications . From 5217887211e6c623a91a9f36e6d375369d05b7dd Mon Sep 17 00:00:00 2001 From: pmakode-akamai Date: Wed, 28 Aug 2024 16:39:46 +0530 Subject: [PATCH 04/17] Update e2e test for attach-volume.spec.ts --- .../manager/cypress/e2e/core/volumes/attach-volume.spec.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/manager/cypress/e2e/core/volumes/attach-volume.spec.ts b/packages/manager/cypress/e2e/core/volumes/attach-volume.spec.ts index 6e2835aa20b..76c2ec63a0f 100644 --- a/packages/manager/cypress/e2e/core/volumes/attach-volume.spec.ts +++ b/packages/manager/cypress/e2e/core/volumes/attach-volume.spec.ts @@ -114,7 +114,9 @@ describe('volume attach and detach flows', () => { // Confirm that volume has been attached to Linode. cy.wait('@attachVolume').its('response.statusCode').should('eq', 200); - ui.toast.assertMessage(`Volume ${volume.label} successfully attached.`); + ui.toast.assertMessage( + `Volume ${volume.label} has been attached to Linode ${linode.label}.` + ); cy.findByText(volume.label) .should('be.visible') .closest('tr') From b5a403ca30f2dddf3b107e7fdc9841739bec6b16 Mon Sep 17 00:00:00 2001 From: pmakode-akamai Date: Thu, 29 Aug 2024 12:56:58 +0530 Subject: [PATCH 05/17] Update async toasts e2e tests in clone-linode.spec.ts --- packages/manager/cypress/e2e/core/linodes/clone-linode.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/manager/cypress/e2e/core/linodes/clone-linode.spec.ts b/packages/manager/cypress/e2e/core/linodes/clone-linode.spec.ts index cd9a78f2258..c64e43abba6 100644 --- a/packages/manager/cypress/e2e/core/linodes/clone-linode.spec.ts +++ b/packages/manager/cypress/e2e/core/linodes/clone-linode.spec.ts @@ -106,7 +106,7 @@ describe('clone linode', () => { ui.toast.assertMessage(`Your Linode ${newLinodeLabel} is being created.`); ui.toast.assertMessage( - `Linode ${linode.label} successfully cloned to ${newLinodeLabel}.`, + `Linode ${linode.label} has been cloned to ${newLinodeLabel}.`, { timeout: CLONE_TIMEOUT } ); }); From 9a29d2ff78b6154c5431b2de52c8289e5f061a2f Mon Sep 17 00:00:00 2001 From: pmakode-akamai Date: Thu, 29 Aug 2024 13:50:10 +0530 Subject: [PATCH 06/17] Update async toast e2e tests in linode-storage.spec.ts --- .../cypress/e2e/core/linodes/linode-storage.spec.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/manager/cypress/e2e/core/linodes/linode-storage.spec.ts b/packages/manager/cypress/e2e/core/linodes/linode-storage.spec.ts index 6cd87caaf14..9859b83bbc8 100644 --- a/packages/manager/cypress/e2e/core/linodes/linode-storage.spec.ts +++ b/packages/manager/cypress/e2e/core/linodes/linode-storage.spec.ts @@ -148,7 +148,9 @@ describe('linode storage tab', () => { cy.wait('@deleteDisk').its('response.statusCode').should('eq', 200); cy.findByText('Deleting', { exact: false }).should('be.visible'); ui.button.findByTitle('Add a Disk').should('be.enabled'); - ui.toast.assertMessage(`Disk ${diskName} successfully deleted.`); + ui.toast.assertMessage( + `Disk ${diskName} on Linode ${linode.label} has been deleted.` + ); cy.findByLabelText('List of Disks').within(() => { cy.contains(diskName).should('not.exist'); }); @@ -209,7 +211,9 @@ describe('linode storage tab', () => { cy.wait('@resizeDisk').its('response.statusCode').should('eq', 200); ui.toast.assertMessage('Disk queued for resizing.'); // cy.findByText('Resizing', { exact: false }).should('be.visible'); - ui.toast.assertMessage(`Disk ${diskName} successfully resized.`); + ui.toast.assertMessage( + `A disk on Linode ${linode.label} has been resized.` + ); }); }); }); From 24eea0c75ea79b376940ca48f51d45452e1545bb Mon Sep 17 00:00:00 2001 From: pmakode-akamai Date: Thu, 29 Aug 2024 20:45:07 +0530 Subject: [PATCH 07/17] Update async toasts e2e tests in machine-image-upload.spec.ts --- .../cypress/e2e/core/images/machine-image-upload.spec.ts | 4 +--- packages/manager/src/features/Events/factories/image.tsx | 3 +-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/manager/cypress/e2e/core/images/machine-image-upload.spec.ts b/packages/manager/cypress/e2e/core/images/machine-image-upload.spec.ts index fbd28300d66..25339973135 100644 --- a/packages/manager/cypress/e2e/core/images/machine-image-upload.spec.ts +++ b/packages/manager/cypress/e2e/core/images/machine-image-upload.spec.ts @@ -81,9 +81,7 @@ const eventIntercept = ( * @param message - Expected failure message. */ const assertFailed = (label: string, id: string, message: string) => { - ui.toast.assertMessage( - `There was a problem uploading image ${label}: ${message}` - ); + ui.toast.assertMessage(`Image ${label} could not be uploaded: ${message}`); cy.get(`[data-qa-image-cell="${id}"]`).within(() => { fbtVisible(label); diff --git a/packages/manager/src/features/Events/factories/image.tsx b/packages/manager/src/features/Events/factories/image.tsx index dbe4ef39cf2..a9912b6ab87 100644 --- a/packages/manager/src/features/Events/factories/image.tsx +++ b/packages/manager/src/features/Events/factories/image.tsx @@ -53,8 +53,7 @@ export const image: PartialEventMap<'image'> = { finished: (e) => ( <> - Image has been{' '} - uploaded. + Image is now available. ), notification: (e) => ( From fdccc2c30cac4fdf316a833b220f624cb70f62a8 Mon Sep 17 00:00:00 2001 From: pmakode-akamai Date: Fri, 30 Aug 2024 22:38:42 +0530 Subject: [PATCH 08/17] Update e2e tests and disk event messages --- .../core/images/smoke-create-image.spec.ts | 2 +- .../e2e/core/linodes/linode-config.spec.ts | 2 +- .../e2e/core/linodes/resize-linode.spec.ts | 4 +- .../e2e/core/volumes/delete-volume.spec.ts | 2 +- .../src/features/Events/factories/disk.tsx | 44 +++++++++++-------- 5 files changed, 31 insertions(+), 23 deletions(-) diff --git a/packages/manager/cypress/e2e/core/images/smoke-create-image.spec.ts b/packages/manager/cypress/e2e/core/images/smoke-create-image.spec.ts index f6e0a0e1cdc..1ee83565388 100644 --- a/packages/manager/cypress/e2e/core/images/smoke-create-image.spec.ts +++ b/packages/manager/cypress/e2e/core/images/smoke-create-image.spec.ts @@ -107,6 +107,6 @@ describe('create image (using mocks)', () => { cy.wait('@getEvents'); // Verify a success toast shows - ui.toast.assertMessage('Image My Config successfully created.'); + ui.toast.assertMessage('Image My Config has been created.'); }); }); diff --git a/packages/manager/cypress/e2e/core/linodes/linode-config.spec.ts b/packages/manager/cypress/e2e/core/linodes/linode-config.spec.ts index a6ea27fbc80..2e73ca80de0 100644 --- a/packages/manager/cypress/e2e/core/linodes/linode-config.spec.ts +++ b/packages/manager/cypress/e2e/core/linodes/linode-config.spec.ts @@ -381,7 +381,7 @@ describe('Linode Config management', () => { // Confirm toast message and that UI updates to reflect clone in progress. ui.toast.assertMessage( - `Linode ${sourceLinode.label} successfully cloned to ${destLinode.label}.` + `Linode ${sourceLinode.label} has been cloned to ${destLinode.label}.` ); cy.findByText(/CLONING \(\d+%\)/).should('be.visible'); }); diff --git a/packages/manager/cypress/e2e/core/linodes/resize-linode.spec.ts b/packages/manager/cypress/e2e/core/linodes/resize-linode.spec.ts index 0d003ddd864..125d48a7c70 100644 --- a/packages/manager/cypress/e2e/core/linodes/resize-linode.spec.ts +++ b/packages/manager/cypress/e2e/core/linodes/resize-linode.spec.ts @@ -186,7 +186,9 @@ describe('resize linode', () => { }); // Wait until the disk resize is done. - ui.toast.assertMessage(`Disk ${diskName} successfully resized.`); + ui.toast.assertMessage( + `A disk ${diskName} on Linode ${linode.label} has been resized.` + ); interceptLinodeResize(linode.id).as('linodeResize'); cy.visitWithLogin(`/linodes/${linode.id}?resize=true`); diff --git a/packages/manager/cypress/e2e/core/volumes/delete-volume.spec.ts b/packages/manager/cypress/e2e/core/volumes/delete-volume.spec.ts index fe441dcf865..7897d0c7f2c 100644 --- a/packages/manager/cypress/e2e/core/volumes/delete-volume.spec.ts +++ b/packages/manager/cypress/e2e/core/volumes/delete-volume.spec.ts @@ -87,7 +87,7 @@ describe('volume delete flow', () => { // Confirm that volume is deleted. cy.wait('@deleteVolume').its('response.statusCode').should('eq', 200); cy.findByText(volume.label).should('not.exist'); - ui.toast.assertMessage('Volume successfully deleted.'); + ui.toast.assertMessage(`Volume ${volume.label} has been deleted.`); } ); }); diff --git a/packages/manager/src/features/Events/factories/disk.tsx b/packages/manager/src/features/Events/factories/disk.tsx index 20b3b6d8f6a..52fad9fac42 100644 --- a/packages/manager/src/features/Events/factories/disk.tsx +++ b/packages/manager/src/features/Events/factories/disk.tsx @@ -77,32 +77,37 @@ export const disk: PartialEventMap<'disk'> = { disk_duplicate: { failed: (e) => ( <> - Disk on Linode could{' '} - not be duplicated. + Disk on Linode{' '} + could not be{' '} + duplicated. ), finished: (e) => ( <> - Disk on Linode has been{' '} - duplicated. + Disk on Linode{' '} + has been duplicated + . ), notification: (e) => ( <> - Disk on Linode has been{' '} - duplicated. + Disk on Linode{' '} + has been duplicated + . ), scheduled: (e) => ( <> - Disk on Linode is scheduled to be{' '} + Disk on Linode{' '} + is scheduled to be{' '} duplicated. ), started: (e) => ( <> - Disk on Linode is being{' '} - duplicated. + Disk on Linode{' '} + is being duplicated + . ), }, @@ -139,8 +144,9 @@ export const disk: PartialEventMap<'disk'> = { disk_resize: { failed: (e) => ( <> - A disk on Linode could{' '} - not be resized.{' '} + A disk on Linode{' '} + could not be{' '} + resized.{' '} { sendLinodeDiskEvent( @@ -155,29 +161,29 @@ export const disk: PartialEventMap<'disk'> = { ), - finished: (e) => ( <> - A disk on Linode has been{' '} - resized. + A disk on Linode{' '} + has been resized. ), notification: (e) => ( <> - A disk on Linode has been{' '} - resized. + A disk on Linode{' '} + has been resized. ), scheduled: (e) => ( <> - A disk on Linode is scheduled to be{' '} + A disk on Linode{' '} + is scheduled to be{' '} resized. ), started: (e) => ( <> - A disk on Linode is being{' '} - resized. + A disk on Linode{' '} + is being resized. ), }, From 2c2361d9454b8190ca7d0614c2a824305f23d37f Mon Sep 17 00:00:00 2001 From: pmakode-akamai Date: Fri, 30 Aug 2024 23:09:07 +0530 Subject: [PATCH 09/17] Update other e2e tests --- .../manager/cypress/e2e/core/linodes/linode-storage.spec.ts | 2 +- .../manager/cypress/e2e/core/volumes/upgrade-volume.spec.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/manager/cypress/e2e/core/linodes/linode-storage.spec.ts b/packages/manager/cypress/e2e/core/linodes/linode-storage.spec.ts index 9859b83bbc8..bd8fec6ba7b 100644 --- a/packages/manager/cypress/e2e/core/linodes/linode-storage.spec.ts +++ b/packages/manager/cypress/e2e/core/linodes/linode-storage.spec.ts @@ -212,7 +212,7 @@ describe('linode storage tab', () => { ui.toast.assertMessage('Disk queued for resizing.'); // cy.findByText('Resizing', { exact: false }).should('be.visible'); ui.toast.assertMessage( - `A disk on Linode ${linode.label} has been resized.` + `A disk ${diskName} on Linode ${linode.label} has been resized.` ); }); }); diff --git a/packages/manager/cypress/e2e/core/volumes/upgrade-volume.spec.ts b/packages/manager/cypress/e2e/core/volumes/upgrade-volume.spec.ts index 59436b4e983..f6d807e4c15 100644 --- a/packages/manager/cypress/e2e/core/volumes/upgrade-volume.spec.ts +++ b/packages/manager/cypress/e2e/core/volumes/upgrade-volume.spec.ts @@ -87,7 +87,7 @@ describe('volume upgrade/migration', () => { cy.findByText('active').should('be.visible'); - ui.toast.assertMessage(`Volume ${volume.label} successfully upgraded.`); + ui.toast.assertMessage(`Volume ${volume.label} has been migrated to NVMe.`); }); it('can upgrade an attached volume from the volumes landing page', () => { @@ -178,7 +178,7 @@ describe('volume upgrade/migration', () => { cy.findByText('active').should('be.visible'); - ui.toast.assertMessage(`Volume ${volume.label} successfully upgraded.`); + ui.toast.assertMessage(`Volume ${volume.label} has been migrated to NVMe.`); }); it('can upgrade an attached volume from the linode details page', () => { @@ -265,6 +265,6 @@ describe('volume upgrade/migration', () => { cy.findByText('active').should('be.visible'); - ui.toast.assertMessage(`Volume ${volume.label} successfully upgraded.`); + ui.toast.assertMessage(`Volume ${volume.label} has been migrated to NVMe.`); }); }); From fdc963333ee428306634658d7edcf11a52a7a3c8 Mon Sep 17 00:00:00 2001 From: pmakode-akamai Date: Sat, 31 Aug 2024 01:11:33 +0530 Subject: [PATCH 10/17] Refactor asyncToasts and useToastNotifications --- .../src/features/Events/asyncToasts.tsx | 115 ++++++++++-------- .../src/hooks/useToastNotifications.tsx | 8 +- 2 files changed, 66 insertions(+), 57 deletions(-) diff --git a/packages/manager/src/features/Events/asyncToasts.tsx b/packages/manager/src/features/Events/asyncToasts.tsx index 685ebdd1f57..ffb1452fd7d 100644 --- a/packages/manager/src/features/Events/asyncToasts.tsx +++ b/packages/manager/src/features/Events/asyncToasts.tsx @@ -3,16 +3,16 @@ import { getEventMessage } from './utils'; import type { Event, EventAction } from '@linode/api-v4'; interface ToastMessage { + /** + * If true, the toast will be displayed with an error variant. + */ + invertVariant?: boolean; message: ((event: Event) => JSX.Element | null | string | undefined) | string; persist?: boolean; } interface Toast { failure?: ToastMessage; - /** - * If true, the toast will be displayed with an error variant. - */ - invertVariant?: boolean; success?: ToastMessage; } @@ -20,36 +20,45 @@ type Toasts = { [key in EventAction]?: Toast; }; -const createToastBoth = ( - options: { - invertVariant?: boolean; - persistFailure?: boolean; - persistSuccess?: boolean; - } = {} -): Toast => ({ - failure: { - message: (e) => getEventMessage(e), - persist: options.persistFailure || false, - }, - invertVariant: options.invertVariant || false, - success: { - message: (e: any) => getEventMessage(e), - persist: options.persistSuccess || false, - }, -}); +interface ToastOption { + invertVariant?: boolean; + persist?: boolean; +} + +interface ToastOptions { + failure?: ToastOption | boolean; + success?: ToastOption | boolean; +} -const createToastFailureOnly = ( - options: { - invertVariant?: boolean; - persistFailure?: boolean; - } = {} -): Toast => ({ - failure: { - message: (e) => getEventMessage(e), - persist: options.persistFailure || false, - }, - invertVariant: options.invertVariant || false, -}); +const createToast = (options: ToastOptions) => { + const toastConfig: Toast = {}; + + if (options.failure) { + toastConfig.failure = { + invertVariant: + typeof options.failure !== 'boolean' + ? options.failure.invertVariant + : false, + message: (e) => getEventMessage(e), + persist: + typeof options.failure !== 'boolean' ? options.failure.persist : false, + }; + } + + if (options.success) { + toastConfig.success = { + invertVariant: + typeof options.success !== 'boolean' + ? options.success.invertVariant + : false, + message: (e) => getEventMessage(e), + persist: + typeof options.success !== 'boolean' ? options.success.persist : false, + }; + } + + return toastConfig; +}; /** * This constant defines toast notifications that will be displayed @@ -61,25 +70,25 @@ const createToastFailureOnly = ( * Toasts for that can be handled at the time of making the PUT request. */ export const toasts: Toasts = { - backups_restore: createToastFailureOnly({ persistFailure: true }), - disk_delete: createToastBoth(), - disk_imagize: createToastBoth({ persistFailure: true }), - disk_resize: createToastBoth({ persistFailure: true }), - image_delete: createToastBoth(), - image_upload: createToastBoth({ persistFailure: true }), - linode_clone: createToastBoth(), - linode_migrate: createToastBoth(), - linode_migrate_datacenter: createToastBoth(), - linode_resize: createToastBoth(), - linode_snapshot: createToastFailureOnly({ persistFailure: true }), - longviewclient_create: createToastBoth(), - tax_id_invalid: createToastBoth({ - invertVariant: true, - persistSuccess: true, + backups_restore: createToast({ failure: { persist: true } }), + disk_delete: createToast({ failure: false, success: true }), + disk_imagize: createToast({ failure: { persist: true }, success: true }), + disk_resize: createToast({ failure: { persist: true }, success: true }), + image_delete: createToast({ failure: true, success: true }), + image_upload: createToast({ failure: { persist: true }, success: true }), + linode_clone: createToast({ failure: true, success: true }), + linode_migrate: createToast({ failure: true, success: true }), + linode_migrate_datacenter: createToast({ failure: true, success: true }), + linode_resize: createToast({ failure: true, success: true }), + linode_snapshot: createToast({ failure: { persist: true } }), + longviewclient_create: createToast({ failure: true, success: true }), + tax_id_invalid: createToast({ + failure: { invertVariant: true }, + success: { invertVariant: true, persist: true }, }), - volume_attach: createToastBoth(), - volume_create: createToastBoth(), - volume_delete: createToastBoth(), - volume_detach: createToastBoth(), - volume_migrate: createToastBoth(), + volume_attach: createToast({ failure: true, success: true }), + volume_create: createToast({ failure: true, success: true }), + volume_delete: createToast({ failure: true, success: true }), + volume_detach: createToast({ failure: true, success: true }), + volume_migrate: createToast({ failure: true, success: true }), }; diff --git a/packages/manager/src/hooks/useToastNotifications.tsx b/packages/manager/src/hooks/useToastNotifications.tsx index 932d650f852..8198aa0bb9c 100644 --- a/packages/manager/src/hooks/useToastNotifications.tsx +++ b/packages/manager/src/hooks/useToastNotifications.tsx @@ -30,25 +30,25 @@ export const useToastNotifications = (): { const isSuccessEvent = ['finished', 'notification'].includes(event.status); if (isSuccessEvent && toastInfo.success) { - const { message, persist } = toastInfo.success; + const { invertVariant, message, persist } = toastInfo.success; const successMessage = getToastMessage(message, event); if (successMessage) { enqueueSnackbar(successMessage, { persist: persist ?? false, - variant: toastInfo.invertVariant ? 'error' : 'success', + variant: invertVariant ? 'error' : 'success', }); } } if (event.status === 'failed' && toastInfo.failure) { - const { message, persist } = toastInfo.failure; + const { invertVariant, message, persist } = toastInfo.failure; const failureMessage = getToastMessage(message, event); if (failureMessage) { enqueueSnackbar(failureMessage, { persist: persist ?? false, - variant: toastInfo.invertVariant ? 'success' : 'error', + variant: invertVariant ? 'success' : 'error', }); } } From ae79481e445de23a30a7d847f8459d8828c0e55e Mon Sep 17 00:00:00 2001 From: pmakode-akamai Date: Mon, 2 Sep 2024 13:09:03 +0530 Subject: [PATCH 11/17] Update types and comments --- packages/manager/src/features/Events/asyncToasts.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/manager/src/features/Events/asyncToasts.tsx b/packages/manager/src/features/Events/asyncToasts.tsx index ffb1452fd7d..4fbbfedf255 100644 --- a/packages/manager/src/features/Events/asyncToasts.tsx +++ b/packages/manager/src/features/Events/asyncToasts.tsx @@ -4,7 +4,8 @@ import type { Event, EventAction } from '@linode/api-v4'; interface ToastMessage { /** - * If true, the toast will be displayed with an error variant. + * If true, the toast will be displayed with an error variant for success messages \ + * or a success variant for error messages. */ invertVariant?: boolean; message: ((event: Event) => JSX.Element | null | string | undefined) | string; @@ -25,11 +26,18 @@ interface ToastOption { persist?: boolean; } -interface ToastOptions { +interface ToastOptionsBase { failure?: ToastOption | boolean; success?: ToastOption | boolean; } +/** + * To ensure that at least one of failure or success is provided while keeping both optional. + */ +type ToastOptions = + | (ToastOptionsBase & { failure: ToastOption | boolean }) + | (ToastOptionsBase & { success: ToastOption | boolean }); + const createToast = (options: ToastOptions) => { const toastConfig: Toast = {}; From 1206becc75dd549df0eea67ad0f32d0faecf153c Mon Sep 17 00:00:00 2001 From: pmakode-akamai Date: Mon, 2 Sep 2024 17:46:51 +0530 Subject: [PATCH 12/17] Add unit test cases --- .../src/features/Events/asyncToasts.test.tsx | 160 ++++++++++++++++++ .../src/features/Events/asyncToasts.tsx | 25 ++- 2 files changed, 171 insertions(+), 14 deletions(-) create mode 100644 packages/manager/src/features/Events/asyncToasts.test.tsx diff --git a/packages/manager/src/features/Events/asyncToasts.test.tsx b/packages/manager/src/features/Events/asyncToasts.test.tsx new file mode 100644 index 00000000000..0053aec923d --- /dev/null +++ b/packages/manager/src/features/Events/asyncToasts.test.tsx @@ -0,0 +1,160 @@ +import { createToast } from './asyncToasts'; + +describe('createToast', () => { + it('should handle case with both failure and success options as true or empty objects', () => { + const trueOptions = { failure: true, success: true }; + const emptyObjectOptions = { failure: {}, success: {} }; + + [trueOptions, emptyObjectOptions].forEach((options) => { + const result = createToast(options); + const expected = { + failure: { + invertVariant: false, + message: expect.any(Function), + persist: false, + }, + success: { + invertVariant: false, + message: expect.any(Function), + persist: false, + }, + }; + + expect(result).toEqual(expected); + }); + }); + + it('should handle case with only failure option as true or empty object or with success as false', () => { + const scenarios = [ + { failure: true }, + { failure: {} }, + { failure: true, success: false }, + { failure: {}, success: false }, + ]; + + scenarios.forEach((options) => { + const result = createToast(options); + const expected = { + failure: { + invertVariant: false, + message: expect.any(Function), + persist: false, + }, + }; + + expect(result).toEqual(expected); + }); + }); + + it('should handle case with only success option as true or empty object or with failure as false', () => { + const scenarios = [ + { success: true }, + { success: {} }, + { failure: false, success: true }, + { failure: false, success: {} }, + ]; + + scenarios.forEach((options) => { + const result = createToast(options); + const expected = { + success: { + invertVariant: false, + message: expect.any(Function), + persist: false, + }, + }; + + expect(result).toEqual(expected); + }); + }); + + it('should return an empty object if both failure and success are false or not provided', () => { + const falseOptions = { failure: false, success: false }; + const emptyOptions = {}; + [falseOptions, emptyOptions].forEach((options) => { + const result = createToast(options); + + expect(result).toEqual({}); + }); + }); + + it('should handle case with failure as true and success as false', () => { + const options = { failure: true, success: false }; + const result = createToast(options); + const expected = { + failure: { + invertVariant: false, + message: expect.any(Function), + persist: false, + }, + }; + + expect(result).toEqual(expected); + }); + + it('should handle case with success as true and failure as false', () => { + const options = { failure: false, success: true }; + const result = createToast(options); + const expected = { + success: { + invertVariant: false, + message: expect.any(Function), + persist: false, + }, + }; + + expect(result).toEqual(expected); + }); + + it('should handle cases with specific values for only failure or success options', () => { + // Only the failure options with specific values + const failureOnlyOptions = { + failure: { persist: true }, + }; + const result1 = createToast(failureOnlyOptions); + const expected1 = { + failure: { + invertVariant: false, + message: expect.any(Function), + persist: true, + }, + }; + expect(result1).toEqual(expected1); + + // Only the success options with specific values + const successOnlyOptions = { + success: { invertVariant: true, persist: false }, + }; + const result2 = createToast(successOnlyOptions); + const expected2 = { + success: { + invertVariant: true, + message: expect.any(Function), + persist: false, + }, + }; + expect(result2).toEqual(expected2); + }); + + it('should handle case with both failure and success options with specific values', () => { + const options = { + failure: { invertVariant: true, persist: true }, + success: { invertVariant: true, persist: false }, + }; + const result = createToast(options); + const expected = { + failure: { + invertVariant: true, + message: expect.any(Function), + persist: true, + }, + success: { + invertVariant: true, + message: expect.any(Function), + persist: false, + }, + }; + + expect(result).toEqual(expected); + }); +}); diff --git a/packages/manager/src/features/Events/asyncToasts.tsx b/packages/manager/src/features/Events/asyncToasts.tsx index 4fbbfedf255..a8916a3b260 100644 --- a/packages/manager/src/features/Events/asyncToasts.tsx +++ b/packages/manager/src/features/Events/asyncToasts.tsx @@ -26,30 +26,25 @@ interface ToastOption { persist?: boolean; } -interface ToastOptionsBase { +interface ToastOptions { failure?: ToastOption | boolean; success?: ToastOption | boolean; } -/** - * To ensure that at least one of failure or success is provided while keeping both optional. - */ -type ToastOptions = - | (ToastOptionsBase & { failure: ToastOption | boolean }) - | (ToastOptionsBase & { success: ToastOption | boolean }); - -const createToast = (options: ToastOptions) => { +export const createToast = (options: ToastOptions) => { const toastConfig: Toast = {}; if (options.failure) { toastConfig.failure = { invertVariant: typeof options.failure !== 'boolean' - ? options.failure.invertVariant + ? Boolean(options.failure.invertVariant) : false, message: (e) => getEventMessage(e), persist: - typeof options.failure !== 'boolean' ? options.failure.persist : false, + typeof options.failure !== 'boolean' + ? Boolean(options.failure.persist) + : false, }; } @@ -57,11 +52,13 @@ const createToast = (options: ToastOptions) => { toastConfig.success = { invertVariant: typeof options.success !== 'boolean' - ? options.success.invertVariant + ? Boolean(options.success.invertVariant) : false, message: (e) => getEventMessage(e), persist: - typeof options.success !== 'boolean' ? options.success.persist : false, + typeof options.success !== 'boolean' + ? Boolean(options.success.persist) + : false, }; } @@ -91,7 +88,7 @@ export const toasts: Toasts = { linode_snapshot: createToast({ failure: { persist: true } }), longviewclient_create: createToast({ failure: true, success: true }), tax_id_invalid: createToast({ - failure: { invertVariant: true }, + failure: true, success: { invertVariant: true, persist: true }, }), volume_attach: createToast({ failure: true, success: true }), From 9e9b8840de68d6a731e4cf266e21b553eb977ca8 Mon Sep 17 00:00:00 2001 From: pmakode-akamai Date: Mon, 2 Sep 2024 19:33:52 +0530 Subject: [PATCH 13/17] Refactor createToast function --- .../src/features/Events/asyncToasts.tsx | 29 ++++++------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/packages/manager/src/features/Events/asyncToasts.tsx b/packages/manager/src/features/Events/asyncToasts.tsx index a8916a3b260..2edba0c3739 100644 --- a/packages/manager/src/features/Events/asyncToasts.tsx +++ b/packages/manager/src/features/Events/asyncToasts.tsx @@ -34,32 +34,21 @@ interface ToastOptions { export const createToast = (options: ToastOptions) => { const toastConfig: Toast = {}; - if (options.failure) { - toastConfig.failure = { + const getToastMessage = (option: ToastOption | boolean): ToastMessage => { + return { invertVariant: - typeof options.failure !== 'boolean' - ? Boolean(options.failure.invertVariant) - : false, + typeof option !== 'boolean' ? Boolean(option.invertVariant) : false, message: (e) => getEventMessage(e), - persist: - typeof options.failure !== 'boolean' - ? Boolean(options.failure.persist) - : false, + persist: typeof option !== 'boolean' ? Boolean(option.persist) : false, }; + }; + + if (options.failure) { + toastConfig.failure = getToastMessage(options.failure); } if (options.success) { - toastConfig.success = { - invertVariant: - typeof options.success !== 'boolean' - ? Boolean(options.success.invertVariant) - : false, - message: (e) => getEventMessage(e), - persist: - typeof options.success !== 'boolean' - ? Boolean(options.success.persist) - : false, - }; + toastConfig.success = getToastMessage(options.success); } return toastConfig; From 9e41b289c7295860b5f3a89e7ccffd168e473c24 Mon Sep 17 00:00:00 2001 From: pmakode-akamai Date: Wed, 4 Sep 2024 00:47:38 +0530 Subject: [PATCH 14/17] Update createToast to conditionally pass parameters and adjust unit tests --- .../src/features/Events/asyncToasts.test.tsx | 38 ++++++++++--------- .../src/features/Events/asyncToasts.tsx | 15 ++++++-- 2 files changed, 32 insertions(+), 21 deletions(-) diff --git a/packages/manager/src/features/Events/asyncToasts.test.tsx b/packages/manager/src/features/Events/asyncToasts.test.tsx index 0053aec923d..8147deeb76f 100644 --- a/packages/manager/src/features/Events/asyncToasts.test.tsx +++ b/packages/manager/src/features/Events/asyncToasts.test.tsx @@ -9,14 +9,10 @@ describe('createToast', () => { const result = createToast(options); const expected = { failure: { - invertVariant: false, message: expect.any(Function), - persist: false, }, success: { - invertVariant: false, message: expect.any(Function), - persist: false, }, }; @@ -36,9 +32,7 @@ describe('createToast', () => { const result = createToast(options); const expected = { failure: { - invertVariant: false, message: expect.any(Function), - persist: false, }, }; @@ -58,9 +52,7 @@ describe('createToast', () => { const result = createToast(options); const expected = { success: { - invertVariant: false, message: expect.any(Function), - persist: false, }, }; @@ -83,9 +75,7 @@ describe('createToast', () => { const result = createToast(options); const expected = { failure: { - invertVariant: false, message: expect.any(Function), - persist: false, }, }; @@ -97,9 +87,7 @@ describe('createToast', () => { const result = createToast(options); const expected = { success: { - invertVariant: false, message: expect.any(Function), - persist: false, }, }; @@ -114,7 +102,6 @@ describe('createToast', () => { const result1 = createToast(failureOnlyOptions); const expected1 = { failure: { - invertVariant: false, message: expect.any(Function), persist: true, }, @@ -137,12 +124,12 @@ describe('createToast', () => { }); it('should handle case with both failure and success options with specific values', () => { - const options = { + const options1 = { failure: { invertVariant: true, persist: true }, success: { invertVariant: true, persist: false }, }; - const result = createToast(options); - const expected = { + const result1 = createToast(options1); + const expected1 = { failure: { invertVariant: true, message: expect.any(Function), @@ -154,7 +141,24 @@ describe('createToast', () => { persist: false, }, }; + expect(result1).toEqual(expected1); - expect(result).toEqual(expected); + const options2 = { + failure: { persist: true }, + success: { invertVariant: true }, + }; + + const result2 = createToast(options2); + const expected2 = { + failure: { + message: expect.any(Function), + persist: true, + }, + success: { + invertVariant: true, + message: expect.any(Function), + }, + }; + expect(result2).toEqual(expected2); }); }); diff --git a/packages/manager/src/features/Events/asyncToasts.tsx b/packages/manager/src/features/Events/asyncToasts.tsx index 2edba0c3739..8e7bd13f30b 100644 --- a/packages/manager/src/features/Events/asyncToasts.tsx +++ b/packages/manager/src/features/Events/asyncToasts.tsx @@ -35,11 +35,18 @@ export const createToast = (options: ToastOptions) => { const toastConfig: Toast = {}; const getToastMessage = (option: ToastOption | boolean): ToastMessage => { + const message: ToastMessage['message'] = (e) => getEventMessage(e); + + if (typeof option === 'boolean') { + return { message }; + } + return { - invertVariant: - typeof option !== 'boolean' ? Boolean(option.invertVariant) : false, - message: (e) => getEventMessage(e), - persist: typeof option !== 'boolean' ? Boolean(option.persist) : false, + message, + ...(option.invertVariant != undefined && { + invertVariant: option.invertVariant, + }), + ...(option.persist != undefined && { persist: option.persist }), }; }; From c5cf849fa9f2b03cb7f9191d9ef9d001a579a6a8 Mon Sep 17 00:00:00 2001 From: pmakode-akamai Date: Wed, 4 Sep 2024 00:52:17 +0530 Subject: [PATCH 15/17] few fixes on asyncToasts.tsx --- packages/manager/src/features/Events/asyncToasts.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/manager/src/features/Events/asyncToasts.tsx b/packages/manager/src/features/Events/asyncToasts.tsx index 8e7bd13f30b..5c3cdc9553e 100644 --- a/packages/manager/src/features/Events/asyncToasts.tsx +++ b/packages/manager/src/features/Events/asyncToasts.tsx @@ -43,10 +43,10 @@ export const createToast = (options: ToastOptions) => { return { message, - ...(option.invertVariant != undefined && { + ...(option.invertVariant !== undefined && { invertVariant: option.invertVariant, }), - ...(option.persist != undefined && { persist: option.persist }), + ...(option.persist !== undefined && { persist: option.persist }), }; }; From 91df88bad6df059354b18cf58b1a06d4e03a0107 Mon Sep 17 00:00:00 2001 From: pmakode-akamai Date: Wed, 4 Sep 2024 01:11:58 +0530 Subject: [PATCH 16/17] Update event messages for disk --- .../cypress/e2e/core/linodes/linode-storage.spec.ts | 2 +- .../cypress/e2e/core/linodes/resize-linode.spec.ts | 2 +- .../manager/src/features/Events/factories/disk.tsx | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/manager/cypress/e2e/core/linodes/linode-storage.spec.ts b/packages/manager/cypress/e2e/core/linodes/linode-storage.spec.ts index bd8fec6ba7b..0e256bbbb41 100644 --- a/packages/manager/cypress/e2e/core/linodes/linode-storage.spec.ts +++ b/packages/manager/cypress/e2e/core/linodes/linode-storage.spec.ts @@ -212,7 +212,7 @@ describe('linode storage tab', () => { ui.toast.assertMessage('Disk queued for resizing.'); // cy.findByText('Resizing', { exact: false }).should('be.visible'); ui.toast.assertMessage( - `A disk ${diskName} on Linode ${linode.label} has been resized.` + `Disk ${diskName} on Linode ${linode.label} has been resized.` ); }); }); diff --git a/packages/manager/cypress/e2e/core/linodes/resize-linode.spec.ts b/packages/manager/cypress/e2e/core/linodes/resize-linode.spec.ts index 125d48a7c70..ba2107cd62a 100644 --- a/packages/manager/cypress/e2e/core/linodes/resize-linode.spec.ts +++ b/packages/manager/cypress/e2e/core/linodes/resize-linode.spec.ts @@ -187,7 +187,7 @@ describe('resize linode', () => { // Wait until the disk resize is done. ui.toast.assertMessage( - `A disk ${diskName} on Linode ${linode.label} has been resized.` + `Disk ${diskName} on Linode ${linode.label} has been resized.` ); interceptLinodeResize(linode.id).as('linodeResize'); diff --git a/packages/manager/src/features/Events/factories/disk.tsx b/packages/manager/src/features/Events/factories/disk.tsx index 52fad9fac42..761fceafb21 100644 --- a/packages/manager/src/features/Events/factories/disk.tsx +++ b/packages/manager/src/features/Events/factories/disk.tsx @@ -144,7 +144,7 @@ export const disk: PartialEventMap<'disk'> = { disk_resize: { failed: (e) => ( <> - A disk on Linode{' '} + Disk on Linode{' '} could not be{' '} resized.{' '} = { ), finished: (e) => ( <> - A disk on Linode{' '} + Disk on Linode{' '} has been resized. ), notification: (e) => ( <> - A disk on Linode{' '} + Disk on Linode{' '} has been resized. ), scheduled: (e) => ( <> - A disk on Linode{' '} + Disk on Linode{' '} is scheduled to be{' '} resized. ), started: (e) => ( <> - A disk on Linode{' '} + Disk on Linode{' '} is being resized. ), From 1e742060c59e346a95a1c9eba8e04fb90ed34269 Mon Sep 17 00:00:00 2001 From: pmakode-akamai Date: Thu, 5 Sep 2024 15:14:58 +0530 Subject: [PATCH 17/17] Clean up: Remove test cases which are already covered --- .../src/features/Events/asyncToasts.test.tsx | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/packages/manager/src/features/Events/asyncToasts.test.tsx b/packages/manager/src/features/Events/asyncToasts.test.tsx index 8147deeb76f..0ed1b44a26a 100644 --- a/packages/manager/src/features/Events/asyncToasts.test.tsx +++ b/packages/manager/src/features/Events/asyncToasts.test.tsx @@ -70,30 +70,6 @@ describe('createToast', () => { }); }); - it('should handle case with failure as true and success as false', () => { - const options = { failure: true, success: false }; - const result = createToast(options); - const expected = { - failure: { - message: expect.any(Function), - }, - }; - - expect(result).toEqual(expected); - }); - - it('should handle case with success as true and failure as false', () => { - const options = { failure: false, success: true }; - const result = createToast(options); - const expected = { - success: { - message: expect.any(Function), - }, - }; - - expect(result).toEqual(expected); - }); - it('should handle cases with specific values for only failure or success options', () => { // Only the failure options with specific values const failureOnlyOptions = {