Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Top tier copy changes #1366

Merged
merged 5 commits into from
Aug 1, 2024
Merged
Show file tree
Hide file tree
Changes from 4 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
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import type { CurrencyIso } from '@/client/utilities/currencyIso';
import { PRODUCT_TYPES } from '../../../../shared/productTypes';
import { UpdateAmount } from '../../../components/mma/accountoverview/updateAmount/UpdateAmount';

Expand All @@ -9,7 +10,7 @@ const mainPlan = (billingPeriod: string) => ({
name: '',
shouldBeVisible: false,
currency: '£',
currencyISO: 'GBP',
currencyISO: 'GBP' as CurrencyIso,
billingPeriod,
features: '',
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { fireEvent, render, screen } from '@testing-library/react';
import type { CurrencyIso } from '@/client/utilities/currencyIso';
import { PRODUCT_TYPES } from '../../../../shared/productTypes';
import { UpdateAmount } from '../../../components/mma/accountoverview/updateAmount/UpdateAmount';

Expand All @@ -8,7 +9,7 @@ const mainPlan = (billingPeriod: string) => ({
name: '',
shouldBeVisible: false,
currency: '£',
currencyISO: 'GBP',
currencyISO: 'GBP' as CurrencyIso,
billingPeriod,
price: 500,
features: '',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import {
import { useEffect, useState } from 'react';
import type { PaidSubscriptionPlan } from '../../../../../shared/productResponse';
import { getBillingPeriodAdjective } from '../../../../../shared/productTypes';
import type { CurrencyIso } from '../../../../utilities/currencyIso';
import { fetchWithDefaultParameters } from '../../../../utilities/fetch';
import { getSupporterPlusSuggestedAmountsFromMainPlan } from '../../../../utilities/pricingConfig/suggestedAmounts';
import { supporterPlusPriceConfigByCountryGroup } from '../../../../utilities/pricingConfig/supporterPlusPricing';
Expand Down Expand Up @@ -106,7 +105,7 @@ export const SupporterPlusUpdateAmountForm = (
props: SupporterPlusUpdateAmountFormProps,
) => {
const priceConfig = (supporterPlusPriceConfigByCountryGroup[
props.mainPlan.currencyISO as CurrencyIso
props.mainPlan.currencyISO
] || supporterPlusPriceConfigByCountryGroup.international)[
props.mainPlan.billingPeriod
];
Expand Down
10 changes: 5 additions & 5 deletions client/components/mma/cancel/Cancellation.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Meta, StoryFn, StoryObj } from '@storybook/react';
import {http, HttpResponse} from 'msw';
import { http, HttpResponse } from 'msw';
import { ReactRouterDecorator } from '@/.storybook/ReactRouterDecorator';
import { PRODUCT_TYPES } from '@/shared/productTypes';
import {
Expand Down Expand Up @@ -66,7 +66,7 @@ export const Review: StoryObj<typeof CancellationContainer> = {
parameters: {
msw: [
http.post('/api/case', () => {
return HttpResponse.json({ id: 'caseId' })
return HttpResponse.json({ id: 'caseId' });
}),
],
reactRouter: {
Expand All @@ -87,7 +87,7 @@ export const Offer: StoryObj<typeof CancellationContainer> = {
parameters: {
msw: [
http.post('/api/case', () => {
return HttpResponse.json({ id: 'caseId' })
return HttpResponse.json({ id: 'caseId' });
}),
],
reactRouter: {
Expand Down Expand Up @@ -122,7 +122,7 @@ export const OfferReview: StoryObj<typeof CancellationContainer> = {
http.post('/api/discounts/apply-discount', () => {
return new HttpResponse(null, {
status: 201,
})
});
}),
],
},
Expand Down Expand Up @@ -168,5 +168,5 @@ export const Confirmation: StoryFn<typeof CancellationContainer> = () => {
return getCancellationSummary(
PRODUCT_TYPES.contributions,
contributionCancelled(),
)(contributionCancelled());
);
};
71 changes: 39 additions & 32 deletions client/components/mma/cancel/CancellationSummary.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import { css } from '@emotion/react';
import { palette, space } from '@guardian/source/foundations';
import {
palette,
space,
textEgyptianBold17,
} from '@guardian/source/foundations';
import { Link } from 'react-router-dom';
import { cancellationFormatDate } from '../../../../shared/dates';
import type {
PaidSubscriptionPlan,
ProductDetail,
Subscription,
} from '../../../../shared/productResponse';
import { getMainPlan } from '../../../../shared/productResponse';
import type { ProductType } from '../../../../shared/productTypes';
import { measure } from '../../../styles/typography';
import { hasDeliveryRecordsFlow } from '../../../utilities/productUtils';
Expand All @@ -21,16 +27,16 @@ import { ResubscribeThrasher } from './ResubscribeThrasher';
const actuallyCancelled = (
productType: ProductType,
productDetail: ProductDetail,
cancelledProductDetail: ProductDetail,
) => {
const deliveryRecordsLink: string = `/delivery/${productType.urlPart}/records`;
const subscription = productDetail.subscription;
const mainPlan = getMainPlan(
productDetail.subscription,
) as PaidSubscriptionPlan;
const headingCopy =
productType.productType === 'supporterplus'
? 'Your subscription has been cancelled'
: `Your ${productType.friendlyName(
cancelledProductDetail,
)} is cancelled`;
: `Your ${productType.friendlyName(productDetail)} is cancelled`;
return (
<>
<WithStandardTopMargin>
Expand All @@ -54,12 +60,13 @@ const actuallyCancelled = (
You will continue to receive the
benefits of your{' '}
{productType.friendlyName(
cancelledProductDetail,
productDetail,
)}{' '}
until{' '}
<b>
{cancellationFormatDate(
subscription.cancellationEffectiveDate,
productDetail.subscription
.cancellationEffectiveDate,
)}
</b>
. You will not be charged again. If you
Expand Down Expand Up @@ -130,18 +137,21 @@ const actuallyCancelled = (
reason,
)) && (
<>
<h4
css={css`
${textEgyptianBold17};
margin-bottom: ${space[3]}px;
`}
>
Support us another way
</h4>
<p>
{productType.cancellation &&
productType.cancellation
.summaryReasonSpecificPara &&
productType.cancellation.summaryReasonSpecificPara(
{productType?.cancellation?.summaryReasonSpecificPara(
reason,
)
? productType.cancellation.summaryReasonSpecificPara(
reason,
)
: 'If you are interested in supporting our journalism in other ways, ' +
'please consider either a contribution or a subscription.'}
mainPlan.currencyISO,
) ||
'If you are interested in supporting our journalism in other ways, ' +
'please consider either a contribution or a subscription.'}
</p>
<div css={{ marginBottom: '30px' }}>
<SupportTheGuardianButton
Expand Down Expand Up @@ -183,19 +193,16 @@ const actuallyCancelled = (
export const isCancelled = (subscription: Subscription) =>
Object.keys(subscription).length === 0 || subscription.cancelledAt;

export const getCancellationSummary =
(productType: ProductType, cancelledProductDetail: ProductDetail) =>
(productDetail: ProductDetail) =>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work getting rid of the extra layer of function application here 👍 It seems like it's definitely not needed - all of the call sites I can see immediately call the returned function, passing the same value as was passed in the initial call.

isCancelled(productDetail.subscription) ? (
actuallyCancelled(
productType,
export const getCancellationSummary = (
productType: ProductType,
productDetail: ProductDetail,
) =>
isCancelled(productDetail.subscription) ? (
actuallyCancelled(productType, productDetail)
) : (
<GenericErrorScreen
loggingMessage={`${productType.friendlyName(
productDetail,
cancelledProductDetail,
)
) : (
<GenericErrorScreen
loggingMessage={`${productType.friendlyName(
cancelledProductDetail,
)} cancellation call succeeded but subsequent product detail doesn't show as cancelled`}
/>
);
)} cancellation call succeeded but subsequent product detail doesn't show as cancelled`}
/>
);
12 changes: 2 additions & 10 deletions client/components/mma/cancel/stages/ExecuteCancellation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,21 +115,14 @@ const getCancellationSummaryWithReturnButton = (body: ReactNode) => () =>
);

const getCaseUpdatingCancellationSummary =
(
caseId: string,
productType: ProductTypeWithCancellationFlow,
cancelledProductDetail: ProductDetail,
) =>
(caseId: string, productType: ProductTypeWithCancellationFlow) =>
(mdapiResponse: MembersDataApiResponse) => {
const productDetail = (mdapiResponse.products[0] as ProductDetail) || {
subscription: {},
};

const render = getCancellationSummaryWithReturnButton(
getCancellationSummary(
productType,
cancelledProductDetail,
)(productDetail),
getCancellationSummary(productType, productDetail),
);
return caseId ? (
<CaseUpdateAsyncLoader
Expand Down Expand Up @@ -232,7 +225,6 @@ export const ExecuteCancellation = () => {
render={getCaseUpdatingCancellationSummary(
caseId,
productType,
productDetail,
)}
loadingMessage="Performing your cancellation..."
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Stack } from '@guardian/source/react-components';
import { PRODUCT_TYPES } from '@/shared/productTypes';
import { measure } from '../../../../styles/typography';
import { trackEvent } from '../../../../utilities/analytics';
import { Heading } from '../../shared/Heading';
import { hrefStyle } from '../cancellationConstants';

const trackCancellationClickEvent = (eventLabel: string) => () =>
trackEvent({
eventCategory: 'cancellation',
eventAction: 'click',
eventLabel,
});

export const tierThreeCancellationFlowStart = () => (
<Stack space={4}>
<Heading cssOverrides={measure.heading}>
We’re sorry to hear you’re thinking of cancelling your{' '}
{PRODUCT_TYPES.tierthree.friendlyName()}
</Heading>

<p>
With your vital support, the Guardian can remain editorially
independent, free from the influence of billionaire owners and
politicians. This enables us to challenge and hold the powerful to
account, and to fearlessly pursue the truth. The support from our
readers helps us keep our journalism without a paywall, open and
accessible to all.
</p>

<p>
If you’re looking to take a break, it’s possible to suspend your
subscription to the Guardian Weekly – reducing the cost of your
total subscription. You can suspend up to six issues per year. This
pauses delivery and you will receive the refund for any suspended
issues off your next bill.{' '}
<a
css={hrefStyle}
href="/suspend/digital+print"
onClick={trackCancellationClickEvent(
'tierThree_holiday_suspension',
)}
>
Suspend your subscription here
</a>
.
</p>

<p>Could you please take a moment to tell us why you want to cancel?</p>
</Stack>
);
2 changes: 1 addition & 1 deletion client/components/mma/holiday/HolidayQuestionsModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { InfoIcon } from '../shared/assets/InfoIcon';
import { Modal } from './Modal';

export const creditExplainerSentence = (issueKeyword: string) =>
`You will be credited for each suspended ${issueKeyword} on the next bill after the ${issueKeyword} date.`;
`You will be credited for each suspended ${issueKeyword} on your next bill after the ${issueKeyword} date.`;

interface HolidayQuestionsModalProps {
annualIssueLimit: number;
Expand Down
2 changes: 1 addition & 1 deletion client/components/mma/holiday/HolidaysOverview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ export const HolidaysOverview = () => {
{holidayStopResponse.annualIssueLimit}{' '}
{productType.holidayStops.issueKeyword}s
</strong>{' '}
per year of your subscription. <br />
per year on your subscription. <br />
</div>
{productType.holidayStops.alternateNoticeString && (
<div>
Expand Down
8 changes: 2 additions & 6 deletions client/components/mma/switch/SwitchContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
getBillingPeriodAdjective,
PRODUCT_TYPES,
} from '../../../../shared/productTypes';
import type { CurrencyIso } from '../../../utilities/currencyIso';
import {
LoadingState,
useAsyncLoader,
Expand Down Expand Up @@ -183,13 +182,10 @@ function getThresholds(
monthly: boolean,
): Thresholds {
const monthlyThreshold = getBenefitsThreshold(
mainPlan.currencyISO as CurrencyIso,
mainPlan.currencyISO,
'month',
);
const annualThreshold = getBenefitsThreshold(
mainPlan.currencyISO as CurrencyIso,
'year',
);
const annualThreshold = getBenefitsThreshold(mainPlan.currencyISO, 'year');
const thresholdForBillingPeriod = monthly
? monthlyThreshold
: annualThreshold;
Expand Down
3 changes: 1 addition & 2 deletions client/components/mma/upgrade/UpgradeSupport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { Stack } from '@guardian/source/react-components';
import { useContext, useState } from 'react';
import { formatAmount } from '@/client/utilities/utils';
import type { PreviewResponse } from '../../../../shared/productSwitchTypes';
import type { CurrencyIso } from '../../../utilities/currencyIso';
import { useAsyncLoader } from '../../../utilities/hooks/useAsyncLoader';
import { getContributionSuggestedAmounts } from '../../../utilities/pricingConfig/suggestedAmounts';
import { getBenefitsThreshold } from '../../../utilities/pricingConfig/supporterPlusPricing';
Expand All @@ -36,7 +35,7 @@ export const UpgradeSupport = () => {

const currentAmount = mainPlan.price / 100;
const threshold = getBenefitsThreshold(
mainPlan.currencyISO as CurrencyIso,
mainPlan.currencyISO,
mainPlan.billingPeriod as 'month' | 'year',
);

Expand Down
14 changes: 7 additions & 7 deletions client/fixtures/productBuilder/baseProducts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,7 @@ export function baseTierThree(): ProductDetail {
phoneRegionsToDisplay: ['UK & ROW', 'US', 'AUS'],
},
billingCountry: 'United Kingdom',
joinDate: '2024-06-13',
joinDate: '2021-11-29',
optIn: true,
subscription: {
paymentMethod: 'Card',
Expand All @@ -532,15 +532,15 @@ export function baseTierThree(): ProductDetail {
country: 'United Kingdom',
},
safeToUpdatePaymentMethod: true,
start: '2024-06-28',
end: '2025-06-13',
start: '2021-12-24',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity, why are these set to dates in the past?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved them into the past to line up with the mocked data for the potential holiday stop api response. I could have setup some new holiday stop dates specifically for top tier, or updated the dates for all products everywhere, but I thought this was the simplest thing to do.

end: '2022-12-15',
nextPaymentPrice: 2500,
nextPaymentDate: '2024-06-28',
lastPaymentDate: null,
potentialCancellationDate: null,
chargedThroughDate: null,
renewalDate: '2025-06-13',
anniversaryDate: '2025-06-28',
renewalDate: '2022-12-15',
anniversaryDate: '2022-12-24',
cancelledAt: false,
subscriptionId: 'A-S00897035',
trialLength: 4,
Expand All @@ -560,8 +560,8 @@ export function baseTierThree(): ProductDetail {
futurePlans: [
{
name: null,
start: '2024-06-28',
end: '2025-06-13',
start: '2021-12-10',
end: '2022-11-29',
shouldBeVisible: true,
chargedThrough: null,
price: 2500,
Expand Down
Loading
Loading