Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions _inc/client/components/jetpack-notices/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { isDevVersion, userCanConnectSite, userIsSubscriber } from 'state/initia
import DismissableNotices from './dismissable';
import JetpackBanner from 'components/jetpack-banner';
import { JETPACK_CONTACT_BETA_SUPPORT } from 'constants/urls';
import PlanConflictWarning from './plan-conflict-warning';

export class DevVersionNotice extends React.Component {
static displayName = 'DevVersionNotice';
Expand Down Expand Up @@ -199,6 +200,7 @@ class JetpackNotices extends React.Component {
isStaging={ this.props.isStaging }
isInIdentityCrisis={ this.props.isInIdentityCrisis }
/>
<PlanConflictWarning />
<DismissableNotices />
<UserUnlinked
connectUrl={ this.props.connectUrl }
Expand Down
93 changes: 93 additions & 0 deletions _inc/client/components/jetpack-notices/plan-conflict-warning.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/**
* External dependencies
*/
import React from 'react';
import { connect } from 'react-redux';
import { translate as __ } from 'i18n-calypso';
import { withRouter } from 'react-router';

/**
* Internal dependencies
*/
import SimpleNotice from 'components/notice';
import { getActiveSitePurchases } from 'state/site';

export function PlanConflictWarning( {
activeSitePurchases,
router: {
location: { pathname },
},
} ) {
// Only show on plans page.
if ( '/plans' !== pathname ) {
return null;
}

// If we only have a single purchase, this isn't required.
if ( activeSitePurchases.length <= 1 ) {
return null;
}

// Strip "monthly" suffixes.
const activeSitePurchasesTrimmed = activeSitePurchases.map( ( { product_slug, ...props } ) => ( {
...props,
product_slug: product_slug.replace( '_monthly', '' ),
} ) );

// Gets the current backup, if any.
const backupPurchase = activeSitePurchasesTrimmed.find( ( { product_slug } ) =>
product_slug.includes( 'backup' )
);
if ( ! backupPurchase ) {
return null;
}

// Get the site plan purchase.
const sitePlanPurchase = activeSitePurchasesTrimmed.find(
( { product_slug } ) =>
'jetpack_personal' === product_slug ||
'jetpack_premium' === product_slug ||
'jetpack_business' === product_slug
);
if ( ! sitePlanPurchase ) {
return null;
}

// If the user purchased real-time backups and doesn't have Professional plan, it's not a conflict.
if (
'jetpack_backup_realtime' === backupPurchase.product_slug &&
'jetpack_business' !== sitePlanPurchase.product_slug
) {
return null;
}

let featureName = __( 'daily backups' );
if ( 'jetpack_business' === sitePlanPurchase.product_slug ) {
featureName = __( 'real-time backups' );
}

return (
<SimpleNotice
status="is-warning"
showDismiss={ false }
text={ __(
'Your %(planName)s Plan includes %(featureName)s. ' +
'Looks like you also purchased the %(productName)s product. ' +
'Consider removing %(productName)s.',
{
args: {
featureName,
planName: sitePlanPurchase.product_name,
productName: backupPurchase.product_name,
},
}
) }
/>
);
}

const PlanConflictWarningWithRouter = withRouter( PlanConflictWarning );

export default connect( state => ( {
activeSitePurchases: getActiveSitePurchases( state ),
} ) )( PlanConflictWarningWithRouter );
89 changes: 89 additions & 0 deletions _inc/client/components/jetpack-notices/test/component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/**
* External dependencies
*/
import React from 'react';
import { shallow } from 'enzyme';
import { expect } from 'chai';

/**
* Internal dependencies
*/
import { PlanConflictWarning } from '../plan-conflict-warning';

describe( 'PlanConflictWarning', () => {
const router = { location: { pathname: '/plans' } };

const personalPlan = {
product_slug: 'jetpack_personal',
product_name: 'Jetpack Personal',
};

const professionalPlan = {
product_slug: 'jetpack_business',
product_name: 'Jetpack Professional',
};

const dailyBackups = {
product_slug: 'jetpack_backup_daily',
product_name: 'Jetpack Backup (Daily)',
};

const realTimeBackups = {
product_slug: 'jetpack_backup_realtime',
product_name: 'Jetpack Backup (Real-time)',
};

it( 'should not render when not in correct path', () => {
const wrapper = shallow( <PlanConflictWarning router={ { location: { pathname: '/test' } } } /> );
expect( wrapper.isEmptyRender() ).to.equal( true );
} );

it( 'should not render when there are no purchases', () => {
const wrapper = shallow( <PlanConflictWarning router={ router } activeSitePurchases={ [] } /> );
expect( wrapper.isEmptyRender() ).to.equal( true );
} );

it( 'should not render when there is one purchase', () => {
const wrapper = shallow( <PlanConflictWarning router={ router } activeSitePurchases={ [ {} ] } /> );
expect( wrapper.isEmptyRender() ).to.equal( true );
} );

it( 'should not render when there is no backup purchase', () => {
const wrapper = shallow( <PlanConflictWarning router={ router } activeSitePurchases={ [ personalPlan ] } /> );
expect( wrapper.isEmptyRender() ).to.equal( true );
} );

it( 'should not render when there is no site plan purchase', () => {
const wrapper = shallow( <PlanConflictWarning router={ router } activeSitePurchases={ [ dailyBackups ] } /> );
expect( wrapper.isEmptyRender() ).to.equal( true );
} );

it( 'should not render with both real-time backups and a non-professional plan', () => {
const wrapper = shallow( <PlanConflictWarning router={ router } activeSitePurchases={ [ realTimeBackups, personalPlan ] } /> );
expect( wrapper.isEmptyRender() ).to.equal( true );
} );

it( 'should not render with both real-time monthly backups and a non-professional plan', () => {
const realTimeBackupsMontly = { product_slug: 'jetpack_backups_realtime_monthly', ...realTimeBackups };
const wrapper = shallow( <PlanConflictWarning router={ router } activeSitePurchases={ [ realTimeBackupsMontly, personalPlan ] } /> );
expect( wrapper.isEmptyRender() ).to.equal( true );
} );

it( 'should show warning with both daily backups and a plan', () => {
const wrapper = shallow( <PlanConflictWarning router={ router } activeSitePurchases={ [ dailyBackups, personalPlan ] } /> );
expect( wrapper.prop( 'text' ) ).to.equal(
'Your Jetpack Personal Plan includes daily backups. ' +
'Looks like you also purchased the Jetpack Backup (Daily) product. ' +
'Consider removing Jetpack Backup (Daily).'
);
} );

it( 'should show warning with both real-time backups and a Professional plan', () => {
const wrapper = shallow( <PlanConflictWarning router={ router } activeSitePurchases={ [ realTimeBackups, professionalPlan ] } /> );
expect( wrapper.prop( 'text' ) ).to.equal(
'Your Jetpack Professional Plan includes real-time backups. ' +
'Looks like you also purchased the Jetpack Backup (Real-time) product. ' +
'Consider removing Jetpack Backup (Real-time).'
);
} );
} );