Skip to content

Commit

Permalink
Add migration cancellation error (#96293)
Browse files Browse the repository at this point in the history
* Add notice on error

* Extract hook with cancellation logic

* Fix tracking to pass string instead of nested object

Nested objects are not supported.

* Fix typo

* Extract useCancellation to another file

* Rename hook for more clarity

* Add test to validate the notice

* Test dismiss notice
  • Loading branch information
renatho authored Nov 13, 2024
1 parent 0303184 commit 0d42720
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 43 deletions.
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
import { recordTracksEvent } from '@automattic/calypso-analytics';
import { LoadingPlaceholder } from '@automattic/components';
import { Button } from '@wordpress/components';
import { translate } from 'i18n-calypso';
import { useCallback, useEffect, useState } from 'react';
import ConfirmModal from 'calypso/components/confirm-modal';
import { HostingHeroButton } from 'calypso/components/hosting-hero';
import { useMigrationCancellation } from 'calypso/data/site-migration/landing/use-migration-cancellation';
import { useSiteExcerptsQueryInvalidator } from 'calypso/data/sites/use-site-excerpts-query';
import Notice from 'calypso/components/notice';
import { addQueryArgs } from 'calypso/lib/url';
import { getMigrationType } from 'calypso/sites-dashboard/utils';
import { useDispatch } from 'calypso/state';
import { requestSite } from 'calypso/state/sites/actions';
import Cards from '../cards';
import { Container, Header } from '../layout';
import useCancelMigration from './use-cancel-migration';
import type { SiteDetails } from '@automattic/data-stores';

const getContinueMigrationUrl = ( site: SiteDetails ): string | null => {
Expand All @@ -36,47 +32,23 @@ const getContinueMigrationUrl = ( site: SiteDetails ): string | null => {

export const MigrationPending = ( { site }: { site: SiteDetails } ) => {
const continueMigrationUrl = getContinueMigrationUrl( site );
const [ isConfirmModalVisible, setIsConfirmModalVisible ] = useState( false );

const title = translate( 'Your WordPress site is ready to be migrated' );
const subTitle = translate(
'Start your migration today and get ready for unmatched WordPress hosting.'
);

const {
mutate: cancelMigration,
isSuccess: isCancellationSuccess,
isPending: isCancelling,
error: cancellationError,
} = useMigrationCancellation( site.ID );
const dispatch = useDispatch();
const invalidateSiteExcerptsQuery = useSiteExcerptsQueryInvalidator();
isModalVisible: isCancellationModalVisible,
isLoading: isCancelling,
cancelMigration,
openModal: openCancellationModal,
closeModal: closeCancellationModal,
showErrorNotice: showCancellationErrorNotice,
dismissErrorNotice: dismissCancellationErrorNotice,
} = useCancelMigration( site );

const reloadSite = useCallback( () => {
dispatch( requestSite( site.ID ) );
// invalidate the site excerpts query to refresh the /sites sidebar
invalidateSiteExcerptsQuery();
}, [ dispatch, invalidateSiteExcerptsQuery, site.ID ] );

useEffect( () => {
if ( isCancellationSuccess ) {
recordTracksEvent( 'calypso_pending_migration_canceled' );
reloadSite();
}
}, [ isCancellationSuccess, reloadSite ] );

useEffect( () => {
if ( cancellationError ) {
recordTracksEvent( 'calypso_pending_migration_cancel_error', { error: cancellationError } );
}
}, [ cancellationError ] );

const handleCancelButtonClick = useCallback( () => {
cancelMigration();
setIsConfirmModalVisible( false );
}, [ cancelMigration ] );

if ( isCancelling || isCancellationSuccess ) {
if ( isCancelling ) {
return (
<LoadingPlaceholder
aria-busy
Expand All @@ -89,16 +61,25 @@ export const MigrationPending = ( { site }: { site: SiteDetails } ) => {
return (
<Container>
<ConfirmModal
isVisible={ isConfirmModalVisible }
onCancel={ () => setIsConfirmModalVisible( false ) }
onConfirm={ handleCancelButtonClick }
isVisible={ isCancellationModalVisible }
onCancel={ closeCancellationModal }
onConfirm={ cancelMigration }
title={ translate( 'Cancel migration' ) }
text={ translate(
"When you cancel your migration your original site will stay as is. You can always restart the migration when you're ready."
) }
confirmButtonLabel={ translate( 'Cancel migration' ) }
cancelButtonLabel={ translate( "Don't cancel migration" ) }
/>

{ showCancellationErrorNotice && (
<Notice status="is-warning" onDismissClick={ dismissCancellationErrorNotice }>
{ translate(
'We ran into a problem cancelling your migration. Please try again shortly.'
) }
</Notice>
) }

<Header title={ title } subTitle={ subTitle }>
{ continueMigrationUrl && (
<div className="migration-pending__buttons">
Expand All @@ -108,7 +89,7 @@ export const MigrationPending = ( { site }: { site: SiteDetails } ) => {
<Button
variant="link"
className="migration-pending__cancel-button"
onClick={ () => setIsConfirmModalVisible( true ) }
onClick={ openCancellationModal }
>
{ translate( 'Cancel migration' ) }
</Button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,53 @@ describe( 'MigrationPending', () => {
expect( requestSite ).not.toHaveBeenCalled();
} );
} );

it( 'displays an error notice when the migration cancellation fails', async () => {
const mockedDispatch = jest.fn();
const invalidator = jest.fn();
jest.mocked( useDispatch ).mockReturnValue( mockedDispatch );
jest.mocked( useSiteExcerptsQueryInvalidator ).mockReturnValue( invalidator );
renderWithProvider( <MigrationPending site={ site } /> );

nock( 'https://public-api.wordpress.com' )
.delete( '/wpcom/v2/sites/123/site-migration-status-sticker' )
.reply( 500, { message: 'Test error' } );

await userEvent.click( screen.getByRole( 'button', { name: 'Cancel migration' } ) );

await userEvent.click( screen.getByRole( 'button', { name: 'Cancel migration' } ) );

await waitFor( () => {
const notice = screen.getByText(
'We ran into a problem cancelling your migration. Please try again shortly.'
);
expect( notice ).toBeInTheDocument();
} );
} );

it( 'dismisses the error notice', async () => {
const mockedDispatch = jest.fn();
const invalidator = jest.fn();
jest.mocked( useDispatch ).mockReturnValue( mockedDispatch );
jest.mocked( useSiteExcerptsQueryInvalidator ).mockReturnValue( invalidator );
renderWithProvider( <MigrationPending site={ site } /> );

nock( 'https://public-api.wordpress.com' )
.delete( '/wpcom/v2/sites/123/site-migration-status-sticker' )
.reply( 500, { message: 'Test error' } );

await userEvent.click( screen.getByRole( 'button', { name: 'Cancel migration' } ) );

await userEvent.click( screen.getByRole( 'button', { name: 'Cancel migration' } ) );

await userEvent.click( await screen.findByRole( 'button', { name: 'Dismiss' } ) );

await waitFor( () => {
expect(
screen.queryByText(
'We ran into a problem cancelling your migration. Please try again shortly.'
)
).not.toBeInTheDocument();
} );
} );
} );
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { recordTracksEvent } from '@automattic/calypso-analytics';
import { useCallback, useEffect, useState } from 'react';
import { useMigrationCancellation } from 'calypso/data/site-migration/landing/use-migration-cancellation';
import { useSiteExcerptsQueryInvalidator } from 'calypso/data/sites/use-site-excerpts-query';
import { useDispatch } from 'calypso/state';
import { requestSite } from 'calypso/state/sites/actions';
import type { SiteDetails } from '@automattic/data-stores';

const useCancelMigration = ( site: SiteDetails ) => {
const dispatch = useDispatch();
const invalidateSiteExcerptsQuery = useSiteExcerptsQueryInvalidator();

const {
mutate: mutateCancelMigration,
isSuccess: isCancellationSuccess,
isPending: isCancelling,
error: cancellationError,
} = useMigrationCancellation( site.ID );

const [ isModalVisible, setIsModalVisible ] = useState( false );
const [ errorNoticeDismissed, setErrorNoticeDismissed ] = useState( false );

const reloadSite = useCallback( () => {
dispatch( requestSite( site.ID ) );
// invalidate the site excerpts query to refresh the /sites sidebar
invalidateSiteExcerptsQuery();
}, [ dispatch, invalidateSiteExcerptsQuery, site.ID ] );

useEffect( () => {
if ( isCancellationSuccess ) {
recordTracksEvent( 'calypso_pending_migration_canceled' );
reloadSite();
}
}, [ isCancellationSuccess, reloadSite ] );

useEffect( () => {
if ( cancellationError ) {
recordTracksEvent( 'calypso_pending_migration_cancel_error', {
error: cancellationError.message,
} );
}
}, [ cancellationError ] );

const openModal = useCallback( () => {
setIsModalVisible( true );
}, [] );

const closeModal = useCallback( () => {
setIsModalVisible( false );
}, [] );

const dismissErrorNotice = useCallback( () => {
setErrorNoticeDismissed( true );
}, [] );

const cancelMigration = useCallback( () => {
setErrorNoticeDismissed( false );
mutateCancelMigration();
setIsModalVisible( false );
}, [ mutateCancelMigration ] );

return {
isModalVisible,
isLoading: isCancelling || isCancellationSuccess,
showErrorNotice: cancellationError && ! errorNoticeDismissed,
cancelMigration,
openModal,
closeModal,
dismissErrorNotice,
};
};

export default useCancelMigration;

0 comments on commit 0d42720

Please sign in to comment.