Skip to content

Commit a5036bd

Browse files
markbiekclaude
andauthored
Domain-only thank you: hide "Start a new site" when site has a paid plan (#112063)
* Domain-only thank you: hide "Start a new site" when site has a paid plan On the domain-only checkout thank-you page, the "Start a new site" option (which sends the user into the plan-selection flow via createSiteFromDomainOnly) was shown unconditionally. When the purchased domain's site already has a paid plan attached, offering to create a new site is confusing. Hide that option when isCurrentPlanPaid is true for the domain's blog, and load the site's plan data via QuerySites so the check is available on the page. Co-Authored-By: Claude <noreply@anthropic.com> * Checkout: hide start-site CTA while plan status loads * Domain-only thank you: test QuerySites is rendered for plan lookup Guards against silently removing <QuerySites>, which would leave the plan selector permanently null and hide the CTA in production while all other tests stayed green. Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent f4c748e commit a5036bd

2 files changed

Lines changed: 78 additions & 24 deletions

File tree

  • client/my-sites/checkout/checkout-thank-you/redesign-v2/pages/domain-only

client/my-sites/checkout/checkout-thank-you/redesign-v2/pages/domain-only/index.tsx

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { formatCurrency } from '@automattic/number-formatters';
44
import { Step } from '@automattic/onboarding';
55
import { addQueryArgs } from '@wordpress/url';
66
import { useTranslate } from 'i18n-calypso';
7+
import QuerySites from 'calypso/components/data/query-sites';
78
import { OptionContent } from 'calypso/components/option-content';
89
import { getDashboardFromQuery } from 'calypso/dashboard/app/routing';
910
import { dashboardLink } from 'calypso/dashboard/utils/link';
@@ -17,6 +18,7 @@ import { useDomainToPlanCreditsApplicable } from 'calypso/my-sites/plans-feature
1718
import { useSelector } from 'calypso/state';
1819
import { hasDashboardOptIn } from 'calypso/state/dashboard/selectors';
1920
import { canAnySiteConnectDomains } from 'calypso/state/selectors/can-any-site-connect-domains';
21+
import isCurrentPlanPaid from 'calypso/state/sites/selectors/is-current-plan-paid';
2022
import attachToSite from './icons/attach-to-site.svg';
2123
import useDomainOnly from './icons/domain-only.svg';
2224
import addMailbox from './icons/mailbox.svg';
@@ -43,6 +45,11 @@ export default function DomainOnly( {
4345
const hasConnectableSites = useSelector( canAnySiteConnectDomains );
4446
const dashboardOptIn = useSelector( hasDashboardOptIn );
4547

48+
// Don't offer to create a new site when the domain's site already has a paid plan attached.
49+
const siteHasPaidPlan = useSelector( ( state ) =>
50+
isCurrentPlanPaid( state, domainPurchase.blogId )
51+
);
52+
4653
const planUpgradeCreditsApplicable = useDomainToPlanCreditsApplicable( domainPurchase.blogId );
4754

4855
// translators: %(domain)s is a domain name, like example.com
@@ -63,6 +70,7 @@ export default function DomainOnly( {
6370

6471
return (
6572
<div className="checkout-thank-you__domain-only-container">
73+
<QuerySites siteId={ domainPurchase.blogId } />
6674
<Step.CenteredColumnLayout
6775
className="step-container-v2--domain-only"
6876
columnWidth={ 6 }
@@ -74,30 +82,32 @@ export default function DomainOnly( {
7482
}
7583
verticalAlign="center"
7684
>
77-
<OptionContent
78-
illustration={ <img src={ startSite } alt="" aria-hidden /> }
79-
titleText={ translate( 'Start a new site' ) }
80-
topText={ translate( 'Create and launch a site on WordPress.com.' ) }
81-
benefits={
82-
planUpgradeCreditsApplicable
83-
? [
84-
translate(
85-
'%(upgradeCredits)s in upgrade credits will be applied to new paid plan purchases.',
86-
{
87-
args: {
88-
upgradeCredits: formatCurrency( planUpgradeCreditsApplicable, currency, {
89-
stripZeros: true,
90-
isSmallestUnit: true,
91-
} ),
92-
},
93-
}
94-
),
95-
]
96-
: undefined
97-
}
98-
href={ createSiteFromDomainOnly( domainPurchase.meta, domainPurchase.blogId ) }
99-
onSelect={ getOnClickEventHandler( 'start_new_site' ) }
100-
/>
85+
{ false === siteHasPaidPlan && (
86+
<OptionContent
87+
illustration={ <img src={ startSite } alt="" aria-hidden /> }
88+
titleText={ translate( 'Start a new site' ) }
89+
topText={ translate( 'Create and launch a site on WordPress.com.' ) }
90+
benefits={
91+
planUpgradeCreditsApplicable
92+
? [
93+
translate(
94+
'%(upgradeCredits)s in upgrade credits will be applied to new paid plan purchases.',
95+
{
96+
args: {
97+
upgradeCredits: formatCurrency( planUpgradeCreditsApplicable, currency, {
98+
stripZeros: true,
99+
isSmallestUnit: true,
100+
} ),
101+
},
102+
}
103+
),
104+
]
105+
: undefined
106+
}
107+
href={ createSiteFromDomainOnly( domainPurchase.meta, domainPurchase.blogId ) }
108+
onSelect={ getOnClickEventHandler( 'start_new_site' ) }
109+
/>
110+
) }
101111
<OptionContent
102112
illustration={ <img src={ addMailbox } alt="" aria-hidden /> }
103113
titleText={ translate( 'Add a mailbox' ) }

client/my-sites/checkout/checkout-thank-you/redesign-v2/pages/domain-only/test/index.tsx

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,20 @@ import {
1616
import { useDomainToPlanCreditsApplicable } from 'calypso/my-sites/plans-features-main/hooks/use-domain-to-plan-credits-applicable';
1717
import { hasDashboardOptIn } from 'calypso/state/dashboard/selectors';
1818
import { canAnySiteConnectDomains } from 'calypso/state/selectors/can-any-site-connect-domains';
19+
import isCurrentPlanPaid from 'calypso/state/sites/selectors/is-current-plan-paid';
1920
import { renderWithProvider } from 'calypso/test-helpers/testing-library';
2021
import DomainOnly from '../index';
2122
import type { ReceiptPurchase } from 'calypso/state/receipts/types';
2223

2324
jest.mock( '@automattic/calypso-analytics' );
2425

26+
const mockQuerySites = jest.fn();
27+
28+
jest.mock( 'calypso/components/data/query-sites', () => ( props: { siteId: number } ) => {
29+
mockQuerySites( props );
30+
return null;
31+
} );
32+
2533
jest.mock( 'calypso/state/dashboard/selectors', () => ( {
2634
hasDashboardOptIn: jest.fn(),
2735
} ) );
@@ -30,6 +38,11 @@ jest.mock( 'calypso/state/selectors/can-any-site-connect-domains', () => ( {
3038
canAnySiteConnectDomains: jest.fn(),
3139
} ) );
3240

41+
jest.mock( 'calypso/state/sites/selectors/is-current-plan-paid', () => ( {
42+
__esModule: true,
43+
default: jest.fn(),
44+
} ) );
45+
3346
jest.mock(
3447
'calypso/my-sites/plans-features-main/hooks/use-domain-to-plan-credits-applicable',
3548
() => ( {
@@ -83,6 +96,8 @@ describe( 'DomainOnly', () => {
8396
jest.mocked( hasDashboardOptIn ).mockReturnValue( false );
8497
jest.mocked( canAnySiteConnectDomains ).mockReturnValue( false );
8598
jest.mocked( useDomainToPlanCreditsApplicable ).mockReturnValue( null );
99+
jest.mocked( isCurrentPlanPaid ).mockReturnValue( false );
100+
mockQuerySites.mockClear();
86101
} );
87102

88103
afterEach( () => {
@@ -99,6 +114,35 @@ describe( 'DomainOnly', () => {
99114
);
100115
} );
101116

117+
it( 'is visible when the domain’s site does not have a paid plan', () => {
118+
jest.mocked( isCurrentPlanPaid ).mockReturnValue( false );
119+
renderComponent();
120+
121+
expect( screen.getByRole( 'link', { name: /Start a new site/ } ) ).toBeVisible();
122+
} );
123+
124+
it( 'is not visible when the domain’s site already has a paid plan', () => {
125+
jest.mocked( isCurrentPlanPaid ).mockReturnValue( true );
126+
renderComponent();
127+
128+
expect( screen.queryByRole( 'link', { name: /Start a new site/ } ) ).not.toBeInTheDocument();
129+
} );
130+
131+
it( 'is not visible while the site plan status is loading', () => {
132+
jest.mocked( isCurrentPlanPaid ).mockReturnValue( null );
133+
renderComponent();
134+
135+
expect( screen.queryByRole( 'link', { name: /Start a new site/ } ) ).not.toBeInTheDocument();
136+
} );
137+
138+
it( 'queries the sites so the plan status can be resolved', () => {
139+
renderComponent();
140+
141+
expect( mockQuerySites ).toHaveBeenCalledWith(
142+
expect.objectContaining( { siteId: mockDomainPurchase.blogId } )
143+
);
144+
} );
145+
102146
it( 'records a tracks event when the user clicks the "Start a new site" link', async () => {
103147
const user = userEvent.setup();
104148
renderComponent();

0 commit comments

Comments
 (0)