Skip to content

Commit 460faa1

Browse files
authored
Pricing grid redesign: implement variant4 (#112490)
* Add the bottom card for Variant 4 * Styling changes to match Figma design * Adjust the styling for mobile card * Fix the color for NASA logo * Display the logos for A4A card in India * Minor adjustments
1 parent 929fa2e commit 460faa1

5 files changed

Lines changed: 364 additions & 14 deletions

File tree

client/my-sites/plans-features-main/index.tsx

Lines changed: 70 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {
2929
isWooHostedPlan,
3030
isWooHostedFreePlan,
3131
isWpcomEnterpriseGridPlan,
32+
isEcommercePlan,
3233
} from '@automattic/calypso-products';
3334
import page from '@automattic/calypso-router';
3435
import { Button, Spinner } from '@automattic/components';
@@ -690,6 +691,8 @@ const PlansFeaturesMain = ( {
690691
const {
691692
isLoading: isPlansGridRedesignExperimentLoading,
692693
showDifferentiatorHeader: showPlansGridRedesignDifferentiatorHeader,
694+
showEnterpriseBottomCard,
695+
showWooCommerceBottomCard,
693696
usePlansGridRedesignFeatures,
694697
usePlansGridRedesign,
695698
usePlansGridRedesignNewDescription,
@@ -938,20 +941,74 @@ const PlansFeaturesMain = ( {
938941
[ isIndiaA4A, translate ]
939942
);
940943

944+
const gridPlansForFeaturesGridWithA4AIndiaCopy = useMemo(
945+
() => applyA4AIndiaCopy( gridPlansForFeaturesGridRaw ?? null ),
946+
[ gridPlansForFeaturesGridRaw, applyA4AIndiaCopy ]
947+
);
948+
949+
const bottomGridPlanForFeaturesGrid = useMemo( () => {
950+
if ( ! gridPlansForFeaturesGridWithA4AIndiaCopy ) {
951+
return undefined;
952+
}
953+
954+
let bottomGridPlan: GridPlan | undefined;
955+
956+
if ( showEnterpriseBottomCard ) {
957+
bottomGridPlan = gridPlansForFeaturesGridWithA4AIndiaCopy.find( ( { planSlug } ) =>
958+
isWpcomEnterpriseGridPlan( planSlug )
959+
);
960+
} else if ( showWooCommerceBottomCard ) {
961+
bottomGridPlan = gridPlansForFeaturesGridWithA4AIndiaCopy.find( ( { planSlug } ) =>
962+
isEcommercePlan( planSlug )
963+
);
964+
}
965+
966+
if (
967+
bottomGridPlan &&
968+
showWooCommerceBottomCard &&
969+
isEcommercePlan( bottomGridPlan.planSlug )
970+
) {
971+
return {
972+
...bottomGridPlan,
973+
planTitle: translate( 'WooCommerce' ),
974+
};
975+
}
976+
977+
return bottomGridPlan;
978+
}, [
979+
gridPlansForFeaturesGridWithA4AIndiaCopy,
980+
showEnterpriseBottomCard,
981+
showWooCommerceBottomCard,
982+
translate,
983+
] );
984+
941985
// when `deemphasizeFreePlan` is enabled, the Free plan will be presented as a CTA link instead of a plan card in the features grid.
942-
const gridPlansForFeaturesGrid = useMemo(
943-
() =>
944-
applyA4AIndiaCopy(
945-
gridPlansForFeaturesGridRaw?.filter( ( { planSlug } ) => {
946-
if ( deemphasizeFreePlan ) {
947-
return planSlug !== PLAN_FREE;
948-
}
986+
const gridPlansForFeaturesGrid = useMemo( () => {
987+
const bottomGridPlanSlug = bottomGridPlanForFeaturesGrid?.planSlug;
949988

950-
return true;
951-
} ) ?? null // optional chaining can result in `undefined`; we don't want to introduce it here.
952-
),
953-
[ gridPlansForFeaturesGridRaw, deemphasizeFreePlan, applyA4AIndiaCopy ]
954-
);
989+
return (
990+
gridPlansForFeaturesGridWithA4AIndiaCopy?.filter( ( { planSlug } ) => {
991+
if ( planSlug === bottomGridPlanSlug ) {
992+
return false;
993+
}
994+
995+
if ( showWooCommerceBottomCard && isWpcomEnterpriseGridPlan( planSlug ) ) {
996+
return false;
997+
}
998+
999+
if ( deemphasizeFreePlan ) {
1000+
return planSlug !== PLAN_FREE;
1001+
}
1002+
1003+
return true;
1004+
} ) ?? null
1005+
);
1006+
}, [
1007+
bottomGridPlanForFeaturesGrid,
1008+
gridPlansForFeaturesGridWithA4AIndiaCopy,
1009+
deemphasizeFreePlan,
1010+
showWooCommerceBottomCard,
1011+
] );
9551012

9561013
const gridPlansForComparisonGridFinal = useMemo(
9571014
() => applyA4AIndiaCopy( gridPlansForComparisonGrid ),
@@ -1421,6 +1478,7 @@ const PlansFeaturesMain = ( {
14211478
{ gridPlansForFeaturesGrid && (
14221479
<FeaturesGrid
14231480
allFeaturesList={ getFeaturesList() }
1481+
bottomGridPlan={ bottomGridPlanForFeaturesGrid }
14241482
className={ clsx( 'plans-features-main__features-grid', {
14251483
'is-plan-differentiators-experiment': isExperimentVariant,
14261484
'is-plans-grid-redesign-experiment': usePlansGridRedesign,
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import {
2+
getPlanClass,
3+
isEcommercePlan,
4+
isWpcomEnterpriseGridPlan,
5+
} from '@automattic/calypso-products';
6+
import { WooLogo } from '@automattic/components';
7+
import clsx from 'clsx';
8+
import { useTranslate } from 'i18n-calypso';
9+
import ActionButton from '../shared/action-button';
10+
import ClientLogoList from './client-logo-list';
11+
import type { GridPlan, PlanActionOverrides } from '../../types';
12+
13+
const ENTERPRISE_BOTTOM_CARD_LOGO_SLUGS = [
14+
'slack',
15+
'usa-today',
16+
'salesforce',
17+
'meta',
18+
'intuit',
19+
'capgemini',
20+
'news-corp',
21+
'samsung',
22+
'nasa',
23+
];
24+
25+
type BottomPlanCardProps = {
26+
currentSitePlanSlug?: string | null;
27+
gridPlan: GridPlan;
28+
isInSignup: boolean;
29+
planActionOverrides?: PlanActionOverrides;
30+
};
31+
32+
const BottomPlanCard = ( {
33+
currentSitePlanSlug,
34+
gridPlan,
35+
isInSignup,
36+
planActionOverrides,
37+
}: BottomPlanCardProps ) => {
38+
const translate = useTranslate();
39+
const isEnterprise = isWpcomEnterpriseGridPlan( gridPlan.planSlug );
40+
const isWooCommerce = isEcommercePlan( gridPlan.planSlug );
41+
42+
return (
43+
<div
44+
className={ clsx(
45+
'plans-grid-next-features-grid__bottom-plan-card',
46+
getPlanClass( gridPlan.planSlug ),
47+
{
48+
'is-enterprise-bottom-card': isEnterprise,
49+
'is-woocommerce-bottom-card': isWooCommerce,
50+
}
51+
) }
52+
>
53+
<div className="plans-grid-next-features-grid__bottom-plan-card-copy">
54+
<h4 className="plans-grid-next-features-grid__bottom-plan-card-title">
55+
{ gridPlan.planTitle }
56+
</h4>
57+
<p className="plans-grid-next-features-grid__bottom-plan-card-tagline">
58+
{ gridPlan.tagline }
59+
</p>
60+
<div className="plans-grid-next-features-grid__bottom-plan-card-action">
61+
<ActionButton
62+
availableForPurchase={ gridPlan.availableForPurchase }
63+
currentSitePlanSlug={ currentSitePlanSlug }
64+
isInSignup={ isInSignup }
65+
isMonthlyPlan={ gridPlan.isMonthlyPlan }
66+
isStuck={ false }
67+
planActionOverrides={ planActionOverrides }
68+
planSlug={ gridPlan.planSlug }
69+
showMonthlyPrice
70+
showPostButtonText={ false }
71+
visibleGridPlans={ [ gridPlan ] }
72+
/>
73+
</div>
74+
</div>
75+
<div
76+
className="plans-grid-next-features-grid__bottom-plan-card-media"
77+
aria-label={
78+
isEnterprise ? translate( 'Enterprise customer logos' ).toString() : undefined
79+
}
80+
>
81+
{ isEnterprise && <ClientLogoList slugs={ ENTERPRISE_BOTTOM_CARD_LOGO_SLUGS } /> }
82+
{ isWooCommerce && (
83+
<WooLogo className="plans-grid-next-features-grid__bottom-plan-card-woo-logo" />
84+
) }
85+
</div>
86+
</div>
87+
);
88+
};
89+
90+
export default BottomPlanCard;

packages/plans-grid-next/src/components/features-grid/index.tsx

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import useGridSize from '../../hooks/use-grid-size';
1515
import { PlanFeaturesItem } from '../item';
1616
import { PlanStorage } from '../shared/storage';
1717
import BillingTimeframes from './billing-timeframes';
18+
import BottomPlanCard from './bottom-plan-card';
1819
import EnterpriseFeatures from './enterprise-features';
1920
import PlanFeaturesList from './plan-features-list';
2021
import PlanHeaders from './plan-headers';
@@ -307,6 +308,7 @@ const TabletView = ( {
307308
// Now that everything under is functional component, we can deprecate this wrapper and only keep ComparisonGrid instead.
308309
// More details can be found in https://github.com/Automattic/wp-calypso/issues/87047
309310
const FeaturesGrid = ( {
311+
bottomGridPlan,
310312
currentSitePlanSlug,
311313
generatedWPComSubdomain,
312314
gridPlanForSpotlight,
@@ -348,7 +350,11 @@ const FeaturesGrid = ( {
348350
<div className="plans-grid-next-features-grid">
349351
{ 'small' !== gridSize && <SpotlightPlan { ...spotlightPlanProps } /> }
350352
<div className="plan-features">
351-
<div className="plan-features-2023-grid__content">
353+
<div
354+
className={ clsx( 'plan-features-2023-grid__content', {
355+
'has-bottom-plan-card': bottomGridPlan,
356+
} ) }
357+
>
352358
<div>
353359
{ 'large' === gridSize && (
354360
<div className="plan-features-2023-grid__desktop-view">
@@ -369,6 +375,14 @@ const FeaturesGrid = ( {
369375
</div>
370376
) }
371377
</div>
378+
{ bottomGridPlan && (
379+
<BottomPlanCard
380+
currentSitePlanSlug={ currentSitePlanSlug }
381+
gridPlan={ bottomGridPlan }
382+
isInSignup={ isInSignup }
383+
planActionOverrides={ planActionOverrides }
384+
/>
385+
) }
372386
</div>
373387
</div>
374388
</div>
@@ -379,6 +393,7 @@ const WrappedFeaturesGrid = ( props: FeaturesGridExternalProps ) => {
379393
const {
380394
siteId,
381395
intent,
396+
bottomGridPlan,
382397
gridPlans,
383398
useCheckPlanAvailabilityForPurchase,
384399
useAction,
@@ -439,12 +454,23 @@ const WrappedFeaturesGrid = ( props: FeaturesGridExternalProps ) => {
439454
'is-large': 'large' === gridSize,
440455
} );
441456

457+
const gridPlansForContext = useMemo( () => {
458+
if (
459+
! bottomGridPlan ||
460+
gridPlans.some( ( gridPlan ) => gridPlan.planSlug === bottomGridPlan.planSlug )
461+
) {
462+
return gridPlans;
463+
}
464+
465+
return [ ...gridPlans, bottomGridPlan ];
466+
}, [ bottomGridPlan, gridPlans ] );
467+
442468
return (
443469
<div ref={ gridContainerRef } className={ classNames }>
444470
<PlansGridContextProvider
445471
intent={ intent }
446472
siteId={ siteId }
447-
gridPlans={ gridPlans }
473+
gridPlans={ gridPlansForContext }
448474
coupon={ coupon }
449475
useCheckPlanAvailabilityForPurchase={ useCheckPlanAvailabilityForPurchase }
450476
useAction={ useAction }

0 commit comments

Comments
 (0)