From c393d88e2c8186c92cd34ddd1b14b8c3e84c1555 Mon Sep 17 00:00:00 2001
From: Rafael Agostini
Date: Fri, 6 Sep 2024 18:44:11 -0500
Subject: [PATCH] Backup: render BackupRealtimeMessage component for displaying
backup real-time info in restore page (#94218)
* feat: Add BackupRealtimeMessage component for displaying backup realtime information
* Render `BackupRealtimeMessage` on successful backup view
* Add unit tests for BackupRealtimeMessage component
* feat: Update BackupRealtimeMessage component to use baseBackupDate instead of backupDate
The BackupRealtimeMessage component has been updated to use the baseBackupDate prop instead of the backupDate prop. This change ensures that the correct backup date is displayed in the message. Also we are adding `selectedBackupDate` to use instead of `today` date.
* Refactor BackupSuccessful component to pass selectedBackupDate to BackupRealtimeMessage
* Remove redundant message scenario
* Replace `today` with `selectedBackupDate`
* Refactor BackupRealtimeMessage component to receive baseBackupDate as a Moment object
* Refactor BackupRealtimeMessage unit tests to consider latest changes
* Update test descriptions
* Improve unit test description grammar
* Improve showRealTimeMessage validation
* Remove `baseBackupDate` comment
* Refactor `BackupRealtimeMessage` conditions to improve readability and clarity
* Add real-time message on restore page
* Adjust CSS spacing on restore backup to properly fit realtime message
* BackupRealtimeMessage: add `Learn more` link
* BackupRealtimeMessage: update unit tests to consider the new Learn more link
* BackupRealtimeMessage: update unit tests comments
* Restore page: render real-time message under a feature flag
---
.../status-card/backup-realtime-message.tsx | 24 ++++++++++-
.../test/backup-realtime-message.jsx | 43 +++++++++++++++----
client/my-sites/backup/rewind-flow/index.tsx | 1 +
.../my-sites/backup/rewind-flow/restore.tsx | 16 +++++++
client/my-sites/backup/rewind-flow/style.scss | 6 ++-
5 files changed, 79 insertions(+), 11 deletions(-)
diff --git a/client/components/jetpack/daily-backup-status/status-card/backup-realtime-message.tsx b/client/components/jetpack/daily-backup-status/status-card/backup-realtime-message.tsx
index e433328d3eaa9..bd612c782af40 100644
--- a/client/components/jetpack/daily-backup-status/status-card/backup-realtime-message.tsx
+++ b/client/components/jetpack/daily-backup-status/status-card/backup-realtime-message.tsx
@@ -1,21 +1,31 @@
+import { useCallback } from '@wordpress/element';
import { useTranslate } from 'i18n-calypso';
import { FunctionComponent } from 'react';
import { useLocalizedMoment } from 'calypso/components/localized-moment';
+import { useDispatch } from 'calypso/state';
+import { recordTracksEvent } from 'calypso/state/analytics/actions/record';
import type { Moment } from 'moment';
type Props = {
baseBackupDate: Moment;
eventsCount: number;
selectedBackupDate: Moment;
+ learnMoreUrl?: string;
};
export const BackupRealtimeMessage: FunctionComponent< Props > = ( {
baseBackupDate,
eventsCount,
selectedBackupDate,
+ learnMoreUrl,
} ) => {
const translate = useTranslate();
const moment = useLocalizedMoment();
+ const dispatch = useDispatch();
+
+ const onLearnMoreClick = useCallback( () => {
+ dispatch( recordTracksEvent( 'calypso_jetpack_backup_realtime_message_learn_more_click' ) );
+ }, [ dispatch ] );
if (
! moment.isMoment( baseBackupDate ) ||
@@ -76,5 +86,17 @@ export const BackupRealtimeMessage: FunctionComponent< Props > = ( {
);
}
- return <>{ message }>;
+ return (
+
+ { message }
+ { learnMoreUrl && (
+ <>
+ { ' ' }
+
+ { translate( 'Learn more' ) }
+
+ >
+ ) }
+
+ );
};
diff --git a/client/components/jetpack/daily-backup-status/test/backup-realtime-message.jsx b/client/components/jetpack/daily-backup-status/test/backup-realtime-message.jsx
index b1d1488a38b12..5989d3db6ccb8 100644
--- a/client/components/jetpack/daily-backup-status/test/backup-realtime-message.jsx
+++ b/client/components/jetpack/daily-backup-status/test/backup-realtime-message.jsx
@@ -4,33 +4,40 @@
import { render } from '@testing-library/react';
import { useTranslate } from 'i18n-calypso';
import moment from 'moment';
+import { useDispatch } from 'calypso/state';
import { BackupRealtimeMessage } from '../status-card/backup-realtime-message';
-// Mock the useTranslate function
-jest.mock( 'i18n-calypso' );
+jest.mock( 'i18n-calypso' ); // Mock the useTranslate hook
+jest.mock( 'calypso/state' ); // Mock the useDispatch hook
describe( 'BackupRealtimeMessage', () => {
- const renderMessage = ( baseBackupDate, eventsCount, selectedDate ) => {
+ const renderMessage = ( baseBackupDate, eventsCount, selectedDate, learnMoreUrl ) => {
return render(
);
};
- const translateMock = jest.fn( ( singular, plural, { count, args } ) => {
- const translatedText = count === 1 ? singular : plural;
+ const translateMock = jest.fn( ( singular, plural, options ) => {
+ if ( options === undefined ) {
+ return singular;
+ }
+
+ const translatedText = options.count === 1 ? singular : plural;
return translatedText
- .replace( '%(baseBackupDate)s', args.baseBackupDate )
- .replace( '%(eventsCount)d', args.eventsCount )
- .replace( '%(daysAgo)d', args.daysAgo );
+ .replace( '%(baseBackupDate)s', options.args.baseBackupDate )
+ .replace( '%(eventsCount)d', options.args.eventsCount )
+ .replace( '%(daysAgo)d', options.args.daysAgo );
} );
beforeEach( () => {
jest.clearAllMocks();
useTranslate.mockImplementation( () => translateMock );
+ useDispatch.mockImplementation( () => jest.fn() );
} );
test( 'renders the correct message when the base backup date is the same as the selected backup date', () => {
@@ -77,4 +84,24 @@ describe( 'BackupRealtimeMessage', () => {
`We are using a full backup from this day (2024-08-26 12:00 PM) with 2 changes you have made since then until now.`
);
} );
+
+ test( 'renders the correct message and the "Learn more" link when learnMoreUrl is provided', () => {
+ useTranslate.mockImplementation( () => translateMock );
+
+ const selectedDate = moment( '2024-08-26T12:00:00Z' );
+ const baseBackupDate = moment( '2024-08-26T12:00:00Z' );
+ const learnMoreUrl = '/learn-more';
+
+ const { container, getByText } = renderMessage( baseBackupDate, 3, selectedDate, learnMoreUrl );
+
+ // Verify the message content
+ expect( container.textContent ).toContain(
+ 'We are using a full backup from this day (2024-08-26 12:00 PM) with 3 changes you have made since then until now.'
+ );
+
+ // Verify the `Learn more` link is rendered
+ const learnMoreLink = getByText( 'Learn more' );
+ expect( learnMoreLink ).toBeInTheDocument();
+ expect( learnMoreLink.getAttribute( 'href' ) ).toBe( learnMoreUrl );
+ } );
} );
diff --git a/client/my-sites/backup/rewind-flow/index.tsx b/client/my-sites/backup/rewind-flow/index.tsx
index fbf0a39ab60e3..418b3b47a83ec 100644
--- a/client/my-sites/backup/rewind-flow/index.tsx
+++ b/client/my-sites/backup/rewind-flow/index.tsx
@@ -99,6 +99,7 @@ const BackupRewindFlow: FunctionComponent< Props > = ( { rewindId, purpose } ) =
if ( purpose === RewindFlowPurpose.RESTORE ) {
return (
= ( {
+ backup,
backupDisplayDate,
rewindId,
siteId,
@@ -62,6 +66,7 @@ const BackupRestoreFlow: FunctionComponent< Props > = ( {
} ) => {
const dispatch = useDispatch();
const translate = useTranslate();
+ const moment = useLocalizedMoment();
const [ rewindConfig, setRewindConfig ] = useState< RewindConfig >( defaultRewindConfig );
@@ -223,6 +228,10 @@ const BackupRestoreFlow: FunctionComponent< Props > = ( {
( ! isAtomic && areCredentialsInvalid ) ||
Object.values( rewindConfig ).every( ( setting ) => ! setting );
+ const selectedDate = moment( rewindId, 'X' );
+ const baseBackupDate = backup.baseRewindId ? moment.unix( backup.baseRewindId ) : null;
+ const showRealTimeMessage = backup.baseRewindId && baseBackupDate && backup.rewindStepCount > 0;
+
const renderConfirm = () => (
<>
{ ! isAtomic && }
@@ -240,6 +249,13 @@ const BackupRestoreFlow: FunctionComponent< Props > = ( {
},
} ) }
+ { config.isEnabled( 'jetpack/backup-realtime-message' ) && showRealTimeMessage && (
+
+ ) }
{ translate( 'Choose the items you wish to restore:' ) }