diff --git a/packages/plans-grid-next/src/components/actions.tsx b/packages/plans-grid-next/src/components/actions.tsx
index c6fce0952e03e..f5cf92ab0cf6b 100644
--- a/packages/plans-grid-next/src/components/actions.tsx
+++ b/packages/plans-grid-next/src/components/actions.tsx
@@ -21,7 +21,7 @@ import { isMobile } from '@automattic/viewport';
import styled from '@emotion/styled';
import { useSelect } from '@wordpress/data';
import { useCallback } from '@wordpress/element';
-import { localize, TranslateResult, useTranslate } from 'i18n-calypso';
+import { TranslateResult, useTranslate } from 'i18n-calypso';
import { usePlansGridContext } from '../grid-context';
import useDefaultStorageOption from '../hooks/data-store/use-default-storage-option';
import useIsLargeCurrency from '../hooks/use-is-large-currency';
@@ -538,4 +538,4 @@ const PlanFeatures2023GridActions = ( props: PlanFeaturesActionsButtonProps ) =>
);
};
-export default localize( PlanFeatures2023GridActions );
+export default PlanFeatures2023GridActions;
diff --git a/packages/plans-grid-next/src/components/features-grid/billing-timeframes.tsx b/packages/plans-grid-next/src/components/features-grid/billing-timeframes.tsx
new file mode 100644
index 0000000000000..f531c22d414e6
--- /dev/null
+++ b/packages/plans-grid-next/src/components/features-grid/billing-timeframes.tsx
@@ -0,0 +1,37 @@
+import classNames from 'classnames';
+import { GridPlan } from '../../types';
+import PlanDivOrTdContainer from '../plan-div-td-container';
+import BillingTimeframe from '../shared/billing-timeframe';
+
+type BillingTimeframesProps = {
+ renderedGridPlans: GridPlan[];
+ showRefundPeriod?: boolean;
+ options?: {
+ isTableCell?: boolean;
+ };
+};
+
+const BillingTimeframes = ( {
+ options,
+ renderedGridPlans,
+ showRefundPeriod,
+}: BillingTimeframesProps ) => {
+ return renderedGridPlans.map( ( { planSlug } ) => {
+ const classes = classNames(
+ 'plan-features-2023-grid__table-item',
+ 'plan-features-2023-grid__header-billing-info'
+ );
+
+ return (
+
+
+
+ );
+ } );
+};
+
+export default BillingTimeframes;
diff --git a/packages/plans-grid-next/src/components/features-grid/index.tsx b/packages/plans-grid-next/src/components/features-grid/index.tsx
index e211ab0b67359..0f631fa05b2ec 100644
--- a/packages/plans-grid-next/src/components/features-grid/index.tsx
+++ b/packages/plans-grid-next/src/components/features-grid/index.tsx
@@ -1,667 +1,27 @@
import {
- FEATURE_CUSTOM_DOMAIN,
- getPlan,
getPlanClass,
- isBusinessTrial,
- isWooExpressMediumPlan,
- isWooExpressPlan,
- isWooExpressSmallPlan,
- isWpComFreePlan,
isWpcomEnterpriseGridPlan,
isFreePlan,
WPComStorageAddOnSlug,
PlanSlug,
} from '@automattic/calypso-products';
-import {
- BloombergLogo,
- CNNLogo,
- CondenastLogo,
- DisneyLogo,
- FacebookLogo,
- FoldableCard,
- SalesforceLogo,
- SlackLogo,
- TimeLogo,
-} from '@automattic/components';
+import { FoldableCard } from '@automattic/components';
import classNames from 'classnames';
import { useTranslate } from 'i18n-calypso';
-import { isStorageUpgradeableForPlan } from '../../lib/is-storage-upgradeable-for-plan';
-import { getStorageStringFromFeature } from '../../util';
-import PlanFeatures2023GridActions from '../actions';
-import PlanFeatures2023GridHeaderPrice from '../header-price';
-import { PlanFeaturesItem } from '../item';
-import PlanDivOrTdContainer from '../plan-div-td-container';
-import PlanFeaturesContainer from '../plan-features-container';
-import PlanLogo from '../plan-logo';
-import BillingTimeframe from '../shared/billing-timeframe';
-import { StickyContainer } from '../sticky-container';
-import StorageAddOnDropdown from '../storage-add-on-dropdown';
+import BillingTimeframes from './billing-timeframes';
+import MobileFreeDomain from './mobile-free-domain';
+import PlanFeaturesList from './plan-features-list';
+import PlanHeaders from './plan-headers';
+import PlanLogos from './plan-logos';
+import PlanPrice from './plan-price';
+import PlanStorageOptions from './plan-storage-options';
+import PlanTagline from './plan-tagline';
+import PreviousFeaturesIncludedTitle from './previous-features-included-title';
+import SpotlightPlan from './spotlight-plan';
+import Table from './table';
+import TopButtons from './top-buttons';
import type { DataResponse, FeaturesGridProps, GridPlan, PlanActionOverrides } from '../../types';
-type PlanLogosProps = {
- isInSignup: boolean;
- renderedGridPlans: GridPlan[];
- options?: {
- isTableCell?: boolean;
- };
-};
-
-const PlanLogos = ( { isInSignup, options, renderedGridPlans }: PlanLogosProps ) => {
- return renderedGridPlans.map( ( { planSlug }, index ) => {
- return (
-
- );
- } );
-};
-
-type PlanHeadersProps = {
- renderedGridPlans: GridPlan[];
- options?: {
- isTableCell?: boolean;
- };
-};
-
-const PlanHeaders = ( { options, renderedGridPlans }: PlanHeadersProps ) => {
- return renderedGridPlans.map( ( { planSlug, planTitle } ) => {
- const headerClasses = classNames( 'plan-features-2023-grid__header', getPlanClass( planSlug ) );
-
- return (
-
-
-
- );
- } );
-};
-
-type PlanTaglineProps = {
- renderedGridPlans: GridPlan[];
- options?: {
- isTableCell?: boolean;
- };
-};
-
-const PlanTagline = ( { options, renderedGridPlans }: PlanTaglineProps ) => {
- return renderedGridPlans.map( ( { planSlug, tagline } ) => {
- return (
-
- { tagline }
-
- );
- } );
-};
-
-type FeaturesGridPlanPriceProps = {
- currentSitePlanSlug?: string | null;
- planUpgradeCreditsApplicable?: number | null;
- renderedGridPlans: GridPlan[];
- options?: {
- isTableCell?: boolean;
- };
-};
-
-const FeaturesGridPlanPrice = ( {
- currentSitePlanSlug,
- options,
- planUpgradeCreditsApplicable,
- renderedGridPlans,
-}: FeaturesGridPlanPriceProps ) => {
- return renderedGridPlans.map( ( { planSlug } ) => {
- return (
-
-
-
- );
- } );
-};
-
-type BillingTimeframesProps = {
- renderedGridPlans: GridPlan[];
- showRefundPeriod?: boolean;
- options?: {
- isTableCell?: boolean;
- };
-};
-
-const BillingTimeframes = ( {
- options,
- renderedGridPlans,
- showRefundPeriod,
-}: BillingTimeframesProps ) => {
- return renderedGridPlans.map( ( { planSlug } ) => {
- const classes = classNames(
- 'plan-features-2023-grid__table-item',
- 'plan-features-2023-grid__header-billing-info'
- );
-
- return (
-
-
-
- );
- } );
-};
-
-type PlanStorageOptionsProps = {
- intervalType: string;
- onStorageAddOnClick?: ( addOnSlug: WPComStorageAddOnSlug ) => void;
- renderedGridPlans: GridPlan[];
- showUpgradeableStorage: boolean;
- options?: {
- isTableCell?: boolean;
- };
-};
-
-const PlanStorageOptions = ( {
- intervalType,
- onStorageAddOnClick,
- options,
- renderedGridPlans,
- showUpgradeableStorage,
-}: PlanStorageOptionsProps ) => {
- const translate = useTranslate();
-
- return renderedGridPlans.map( ( { planSlug, features: { storageOptions } } ) => {
- if ( ! options?.isTableCell && isWpcomEnterpriseGridPlan( planSlug ) ) {
- return null;
- }
-
- const shouldRenderStorageTitle =
- storageOptions.length > 0 &&
- ( storageOptions.length === 1 || intervalType !== 'yearly' || ! showUpgradeableStorage );
- const canUpgradeStorageForPlan = isStorageUpgradeableForPlan( {
- intervalType,
- showUpgradeableStorage,
- storageOptions,
- } );
- const storageJSX = canUpgradeStorageForPlan ? (
-
- ) : (
- storageOptions.map( ( storageOption ) => {
- if ( ! storageOption?.isAddOn ) {
- return (
-
- { getStorageStringFromFeature( storageOption?.slug ) }
-
- );
- }
- } )
- );
-
- return (
-
- { shouldRenderStorageTitle ? (
- { translate( 'Storage' ) }
- ) : null }
- { storageJSX }
-
- );
- } );
-};
-
-type TopButtonsProps = {
- currentSitePlanSlug?: string | null;
- isInSignup: boolean;
- isLaunchPage?: boolean | null;
- onUpgradeClick: ( planSlug: PlanSlug ) => void;
- planActionOverrides?: PlanActionOverrides;
- renderedGridPlans: GridPlan[];
- options?: {
- isTableCell?: boolean;
- isStuck?: boolean;
- };
-};
-
-const TopButtons = ( {
- currentSitePlanSlug,
- isInSignup,
- isLaunchPage,
- onUpgradeClick,
- options,
- planActionOverrides,
- renderedGridPlans,
-}: TopButtonsProps ) => {
- const translate = useTranslate();
-
- return renderedGridPlans.map(
- ( { planSlug, availableForPurchase, isMonthlyPlan, features: { storageOptions } } ) => {
- const classes = classNames( 'plan-features-2023-grid__table-item', 'is-top-buttons' );
-
- // Leaving it `undefined` makes it use the default label
- let buttonText;
-
- if (
- isWooExpressMediumPlan( planSlug ) &&
- ! isWooExpressMediumPlan( currentSitePlanSlug || '' )
- ) {
- buttonText = translate( 'Get Performance', { textOnly: true } );
- } else if (
- isWooExpressSmallPlan( planSlug ) &&
- ! isWooExpressSmallPlan( currentSitePlanSlug || '' )
- ) {
- buttonText = translate( 'Get Essential', { textOnly: true } );
- } else if ( isBusinessTrial( currentSitePlanSlug || '' ) ) {
- buttonText = translate( 'Get %(plan)s', {
- textOnly: true,
- args: {
- plan: getPlan( planSlug )?.getTitle() || '',
- },
- } );
- }
-
- return (
-
-
- onUpgradeClick( overridePlanSlug ?? planSlug )
- }
- planSlug={ planSlug }
- currentSitePlanSlug={ currentSitePlanSlug }
- buttonText={ buttonText }
- planActionOverrides={ planActionOverrides }
- showMonthlyPrice={ true }
- isStuck={ options?.isStuck || false }
- storageOptions={ storageOptions }
- visibleGridPlans={ renderedGridPlans }
- />
-
- );
- }
- );
-};
-
-type PreviousFeaturesIncludedTitleProps = {
- renderedGridPlans: GridPlan[];
- options?: {
- isTableCell?: boolean;
- };
-};
-
-const PreviousFeaturesIncludedTitle = ( {
- renderedGridPlans,
- options,
-}: PreviousFeaturesIncludedTitleProps ) => {
- const translate = useTranslate();
-
- return renderedGridPlans.map( ( { planSlug } ) => {
- const shouldRenderEnterpriseLogos = isWpcomEnterpriseGridPlan( planSlug );
- const shouldShowFeatureTitle = ! isWpComFreePlan( planSlug ) && ! shouldRenderEnterpriseLogos;
- const indexInGridPlansForFeaturesGrid = renderedGridPlans.findIndex(
- ( { planSlug: slug } ) => slug === planSlug
- );
- const previousProductName =
- indexInGridPlansForFeaturesGrid > 0
- ? renderedGridPlans[ indexInGridPlansForFeaturesGrid - 1 ].productNameShort
- : null;
- const title =
- previousProductName &&
- translate( 'Everything in %(planShortName)s, plus:', {
- args: { planShortName: previousProductName },
- } );
- const classes = classNames( 'plan-features-2023-grid__common-title', getPlanClass( planSlug ) );
- const rowspanProp = options?.isTableCell && shouldRenderEnterpriseLogos ? { rowSpan: '2' } : {};
- return (
-
- { shouldShowFeatureTitle && { title }
}
- { shouldRenderEnterpriseLogos && (
-
-
-
-
-
-
-
-
-
-
- ) }
-
- );
- } );
-};
-
-type PlanFeaturesListProps = {
- generatedWPComSubdomain: DataResponse< { domain_name: string } >;
- hideUnavailableFeatures?: boolean;
- isCustomDomainAllowedOnFreePlan: boolean;
- paidDomainName?: string;
- renderedGridPlans: GridPlan[];
- selectedFeature?: string;
- options?: {
- isTableCell?: boolean;
- };
-};
-
-const PlanFeaturesList = ( {
- generatedWPComSubdomain,
- hideUnavailableFeatures,
- isCustomDomainAllowedOnFreePlan,
- options,
- paidDomainName,
- renderedGridPlans,
- selectedFeature,
-}: PlanFeaturesListProps ) => {
- const translate = useTranslate();
- const plansWithFeatures = renderedGridPlans.filter(
- ( gridPlan ) => ! isWpcomEnterpriseGridPlan( gridPlan.planSlug )
- );
-
- return (
-
- );
-};
-
-type TableProps = {
- currentSitePlanSlug?: string | null;
- generatedWPComSubdomain: DataResponse< { domain_name: string } >;
- gridPlanForSpotlight?: GridPlan;
- hideUnavailableFeatures?: boolean;
- intervalType: string;
- isCustomDomainAllowedOnFreePlan: boolean;
- isInSignup: boolean;
- isLaunchPage?: boolean | null;
- onStorageAddOnClick?: ( addOnSlug: WPComStorageAddOnSlug ) => void;
- onUpgradeClick: ( planSlug: PlanSlug ) => void;
- paidDomainName?: string;
- planActionOverrides?: PlanActionOverrides;
- planUpgradeCreditsApplicable?: number | null;
- renderedGridPlans: GridPlan[];
- selectedFeature?: string;
- showUpgradeableStorage: boolean;
- stickyRowOffset: number;
- options?: {
- isTableCell?: boolean;
- };
-};
-
-const Table = ( {
- currentSitePlanSlug,
- generatedWPComSubdomain,
- gridPlanForSpotlight,
- hideUnavailableFeatures,
- intervalType,
- isCustomDomainAllowedOnFreePlan,
- isInSignup,
- isLaunchPage,
- onStorageAddOnClick,
- onUpgradeClick,
- paidDomainName,
- planActionOverrides,
- planUpgradeCreditsApplicable,
- renderedGridPlans,
- selectedFeature,
- showUpgradeableStorage,
- stickyRowOffset,
-}: TableProps ) => {
- // Do not render the spotlight plan if it exists
- const gridPlansWithoutSpotlight = ! gridPlanForSpotlight
- ? renderedGridPlans
- : renderedGridPlans.filter( ( { planSlug } ) => gridPlanForSpotlight.planSlug !== planSlug );
- const tableClasses = classNames(
- 'plan-features-2023-grid__table',
- `has-${ gridPlansWithoutSpotlight.length }-cols`
- );
- const translate = useTranslate();
-
- return (
-
-
- { translate( 'Available plans to choose from' ) }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- { ( isStuck: boolean ) => (
-
- ) }
-
-
-
-
-
-
-
-
-
-
-
-
- );
-};
-
-type SpotlightPlanProps = {
- currentSitePlanSlug?: string | null;
- gridPlanForSpotlight?: GridPlan;
- intervalType: string;
- isInSignup: boolean;
- isLaunchPage?: boolean | null;
- onStorageAddOnClick?: ( addOnSlug: WPComStorageAddOnSlug ) => void;
- onUpgradeClick: ( planSlug: PlanSlug ) => void;
- planActionOverrides?: PlanActionOverrides;
- planUpgradeCreditsApplicable?: number | null;
- showUpgradeableStorage: boolean;
- options?: {
- isTableCell?: boolean;
- };
-};
-
-const SpotlightPlan = ( {
- currentSitePlanSlug,
- gridPlanForSpotlight,
- intervalType,
- isInSignup,
- isLaunchPage,
- onStorageAddOnClick,
- onUpgradeClick,
- planActionOverrides,
- planUpgradeCreditsApplicable,
- showUpgradeableStorage,
-}: SpotlightPlanProps ) => {
- if ( ! gridPlanForSpotlight ) {
- return null;
- }
-
- const spotlightPlanClasses = classNames(
- 'plan-features-2023-grid__plan-spotlight',
- getPlanClass( gridPlanForSpotlight.planSlug )
- );
-
- const isNotFreePlan = ! isFreePlan( gridPlanForSpotlight.planSlug );
-
- return (
-
-
-
- { isNotFreePlan &&
}
- { isNotFreePlan && (
-
- ) }
- { isNotFreePlan &&
}
-
-
-
- );
-};
-
-type MobileFreeDomainProps = {
- gridPlan: GridPlan;
- paidDomainName?: string;
-};
-
-const MobileFreeDomain = ( { gridPlan, paidDomainName }: MobileFreeDomainProps ) => {
- const { planSlug, isMonthlyPlan } = gridPlan;
- const translate = useTranslate();
-
- if ( isMonthlyPlan || isWpComFreePlan( planSlug ) || isWpcomEnterpriseGridPlan( planSlug ) ) {
- return null;
- }
-
- // Remove the custom domain feature for Woo Express plans with introductory offer.
- if (
- isWooExpressPlan( planSlug ) &&
- ! gridPlan.features.wpcomFeatures.some(
- ( feature ) => feature.getSlug() === FEATURE_CUSTOM_DOMAIN
- )
- ) {
- return null;
- }
-
- const displayText = paidDomainName
- ? translate( '%(paidDomainName)s is included', {
- args: { paidDomainName },
- } )
- : translate( 'Free domain for one year' );
-
- return (
-
-
-
- { displayText }
-
-
-
- );
-};
-
type MobileViewProps = {
currentSitePlanSlug?: string | null;
generatedWPComSubdomain: DataResponse< { domain_name: string } >;
@@ -735,7 +95,7 @@ const MobileView = ( {
{ isNotFreePlan && isInSignup && }
{ isNotFreePlan && (
- {
+ const translate = useTranslate();
+
+ if ( isMonthlyPlan || isWpComFreePlan( planSlug ) || isWpcomEnterpriseGridPlan( planSlug ) ) {
+ return null;
+ }
+
+ // Remove the custom domain feature for Woo Express plans with introductory offer.
+ if (
+ isWooExpressPlan( planSlug ) &&
+ ! features.wpcomFeatures.some( ( feature ) => feature.getSlug() === FEATURE_CUSTOM_DOMAIN )
+ ) {
+ return null;
+ }
+
+ const displayText = paidDomainName
+ ? translate( '%(paidDomainName)s is included', {
+ args: { paidDomainName },
+ } )
+ : translate( 'Free domain for one year' );
+
+ return (
+
+
+
+ { displayText }
+
+
+
+ );
+};
+
+export default MobileFreeDomain;
diff --git a/packages/plans-grid-next/src/components/features-grid/plan-features-list.tsx b/packages/plans-grid-next/src/components/features-grid/plan-features-list.tsx
new file mode 100644
index 0000000000000..c700ad2d0aa35
--- /dev/null
+++ b/packages/plans-grid-next/src/components/features-grid/plan-features-list.tsx
@@ -0,0 +1,45 @@
+import { isWpcomEnterpriseGridPlan } from '@automattic/calypso-products';
+import { useMemo } from 'react';
+import { DataResponse, GridPlan } from '../../types';
+import PlanFeaturesContainer from '../plan-features-container';
+
+type PlanFeaturesListProps = {
+ generatedWPComSubdomain: DataResponse< { domain_name: string } >;
+ hideUnavailableFeatures?: boolean;
+ isCustomDomainAllowedOnFreePlan: boolean;
+ paidDomainName?: string;
+ renderedGridPlans: GridPlan[];
+ selectedFeature?: string;
+ options?: {
+ isTableCell?: boolean;
+ };
+};
+
+const PlanFeaturesList = ( {
+ generatedWPComSubdomain,
+ hideUnavailableFeatures,
+ isCustomDomainAllowedOnFreePlan,
+ options,
+ paidDomainName,
+ renderedGridPlans,
+ selectedFeature,
+}: PlanFeaturesListProps ) => {
+ const plansWithFeatures = useMemo( () => {
+ return renderedGridPlans.filter(
+ ( gridPlan ) => ! isWpcomEnterpriseGridPlan( gridPlan.planSlug )
+ );
+ }, [ renderedGridPlans ] );
+
+ return (
+
+ );
+};
+export default PlanFeaturesList;
diff --git a/packages/plans-grid-next/src/components/features-grid/plan-headers.tsx b/packages/plans-grid-next/src/components/features-grid/plan-headers.tsx
new file mode 100644
index 0000000000000..244bc37bdaf92
--- /dev/null
+++ b/packages/plans-grid-next/src/components/features-grid/plan-headers.tsx
@@ -0,0 +1,31 @@
+import { getPlanClass } from '@automattic/calypso-products';
+import classNames from 'classnames';
+import { GridPlan } from '../../types';
+import PlanDivOrTdContainer from '../plan-div-td-container';
+
+type PlanHeadersProps = {
+ renderedGridPlans: GridPlan[];
+ options?: {
+ isTableCell?: boolean;
+ };
+};
+
+const PlanHeaders = ( { options, renderedGridPlans }: PlanHeadersProps ) => {
+ return renderedGridPlans.map( ( { planSlug, planTitle } ) => {
+ const headerClasses = classNames( 'plan-features-2023-grid__header', getPlanClass( planSlug ) );
+
+ return (
+
+
+
+ );
+ } );
+};
+
+export default PlanHeaders;
diff --git a/packages/plans-grid-next/src/components/features-grid/plan-logos.tsx b/packages/plans-grid-next/src/components/features-grid/plan-logos.tsx
new file mode 100644
index 0000000000000..c26a57b20d927
--- /dev/null
+++ b/packages/plans-grid-next/src/components/features-grid/plan-logos.tsx
@@ -0,0 +1,27 @@
+import { GridPlan } from '../../types';
+import PlanLogo from '../plan-logo';
+
+type PlanLogosProps = {
+ isInSignup: boolean;
+ renderedGridPlans: GridPlan[];
+ options?: {
+ isTableCell?: boolean;
+ };
+};
+
+const PlanLogos = ( { isInSignup, options, renderedGridPlans }: PlanLogosProps ) => {
+ return renderedGridPlans.map( ( { planSlug }, index ) => {
+ return (
+
+ );
+ } );
+};
+
+export default PlanLogos;
diff --git a/packages/plans-grid-next/src/components/features-grid/plan-price.tsx b/packages/plans-grid-next/src/components/features-grid/plan-price.tsx
new file mode 100644
index 0000000000000..e8d268249a46f
--- /dev/null
+++ b/packages/plans-grid-next/src/components/features-grid/plan-price.tsx
@@ -0,0 +1,39 @@
+import { GridPlan } from '../../types';
+import PlanFeatures2023GridHeaderPrice from '../header-price';
+import PlanDivOrTdContainer from '../plan-div-td-container';
+
+type PlanPriceProps = {
+ currentSitePlanSlug?: string | null;
+ planUpgradeCreditsApplicable?: number | null;
+ renderedGridPlans: GridPlan[];
+ options?: {
+ isTableCell?: boolean;
+ };
+};
+
+const PlanPrice = ( {
+ currentSitePlanSlug,
+ options,
+ planUpgradeCreditsApplicable,
+ renderedGridPlans,
+}: PlanPriceProps ) => {
+ return renderedGridPlans.map( ( { planSlug } ) => {
+ return (
+
+
+
+ );
+ } );
+};
+
+export default PlanPrice;
diff --git a/packages/plans-grid-next/src/components/features-grid/plan-storage-options.tsx b/packages/plans-grid-next/src/components/features-grid/plan-storage-options.tsx
new file mode 100644
index 0000000000000..fd5b0dfe87b42
--- /dev/null
+++ b/packages/plans-grid-next/src/components/features-grid/plan-storage-options.tsx
@@ -0,0 +1,75 @@
+import { WPComStorageAddOnSlug, isWpcomEnterpriseGridPlan } from '@automattic/calypso-products';
+import { useTranslate } from 'i18n-calypso';
+import { isStorageUpgradeableForPlan } from '../../lib/is-storage-upgradeable-for-plan';
+import { GridPlan } from '../../types';
+import { getStorageStringFromFeature } from '../../util';
+import PlanDivOrTdContainer from '../plan-div-td-container';
+import StorageAddOnDropdown from '../storage-add-on-dropdown';
+
+type PlanStorageOptionsProps = {
+ intervalType: string;
+ onStorageAddOnClick?: ( addOnSlug: WPComStorageAddOnSlug ) => void;
+ renderedGridPlans: GridPlan[];
+ showUpgradeableStorage: boolean;
+ options?: {
+ isTableCell?: boolean;
+ };
+};
+
+const PlanStorageOptions = ( {
+ intervalType,
+ onStorageAddOnClick,
+ options,
+ renderedGridPlans,
+ showUpgradeableStorage,
+}: PlanStorageOptionsProps ) => {
+ const translate = useTranslate();
+
+ return renderedGridPlans.map( ( { planSlug, features: { storageOptions } } ) => {
+ if ( ! options?.isTableCell && isWpcomEnterpriseGridPlan( planSlug ) ) {
+ return null;
+ }
+
+ const shouldRenderStorageTitle =
+ storageOptions.length > 0 &&
+ ( storageOptions.length === 1 || intervalType !== 'yearly' || ! showUpgradeableStorage );
+ const canUpgradeStorageForPlan = isStorageUpgradeableForPlan( {
+ intervalType,
+ showUpgradeableStorage,
+ storageOptions,
+ } );
+ const storageJSX = canUpgradeStorageForPlan ? (
+
+ ) : (
+ storageOptions.map( ( storageOption ) => {
+ if ( ! storageOption?.isAddOn ) {
+ return (
+
+ { getStorageStringFromFeature( storageOption?.slug ) }
+
+ );
+ }
+ } )
+ );
+
+ return (
+
+ { shouldRenderStorageTitle ? (
+ { translate( 'Storage' ) }
+ ) : null }
+ { storageJSX }
+
+ );
+ } );
+};
+
+export default PlanStorageOptions;
diff --git a/packages/plans-grid-next/src/components/features-grid/plan-tagline.tsx b/packages/plans-grid-next/src/components/features-grid/plan-tagline.tsx
new file mode 100644
index 0000000000000..ca8794acc9865
--- /dev/null
+++ b/packages/plans-grid-next/src/components/features-grid/plan-tagline.tsx
@@ -0,0 +1,25 @@
+import { GridPlan } from '../../types';
+import PlanDivOrTdContainer from '../plan-div-td-container';
+
+type PlanTaglineProps = {
+ renderedGridPlans: GridPlan[];
+ options?: {
+ isTableCell?: boolean;
+ };
+};
+
+const PlanTagline = ( { options, renderedGridPlans }: PlanTaglineProps ) => {
+ return renderedGridPlans.map( ( { planSlug, tagline } ) => {
+ return (
+
+ { tagline }
+
+ );
+ } );
+};
+
+export default PlanTagline;
diff --git a/packages/plans-grid-next/src/components/features-grid/previous-features-included-title.tsx b/packages/plans-grid-next/src/components/features-grid/previous-features-included-title.tsx
new file mode 100644
index 0000000000000..bae587db6c900
--- /dev/null
+++ b/packages/plans-grid-next/src/components/features-grid/previous-features-included-title.tsx
@@ -0,0 +1,76 @@
+import {
+ getPlanClass,
+ isWpComFreePlan,
+ isWpcomEnterpriseGridPlan,
+} from '@automattic/calypso-products';
+import {
+ BloombergLogo,
+ CNNLogo,
+ CondenastLogo,
+ DisneyLogo,
+ FacebookLogo,
+ SalesforceLogo,
+ SlackLogo,
+ TimeLogo,
+} from '@automattic/components';
+import classNames from 'classnames';
+import { useTranslate } from 'i18n-calypso';
+import { GridPlan } from '../../types';
+import PlanDivOrTdContainer from '../plan-div-td-container';
+
+type PreviousFeaturesIncludedTitleProps = {
+ renderedGridPlans: GridPlan[];
+ options?: {
+ isTableCell?: boolean;
+ };
+};
+
+const PreviousFeaturesIncludedTitle = ( {
+ renderedGridPlans,
+ options,
+}: PreviousFeaturesIncludedTitleProps ) => {
+ const translate = useTranslate();
+
+ return renderedGridPlans.map( ( { planSlug } ) => {
+ const shouldRenderEnterpriseLogos = isWpcomEnterpriseGridPlan( planSlug );
+ const shouldShowFeatureTitle = ! isWpComFreePlan( planSlug ) && ! shouldRenderEnterpriseLogos;
+ const indexInGridPlansForFeaturesGrid = renderedGridPlans.findIndex(
+ ( { planSlug: slug } ) => slug === planSlug
+ );
+ const previousProductName =
+ indexInGridPlansForFeaturesGrid > 0
+ ? renderedGridPlans[ indexInGridPlansForFeaturesGrid - 1 ].productNameShort
+ : null;
+ const title =
+ previousProductName &&
+ translate( 'Everything in %(planShortName)s, plus:', {
+ args: { planShortName: previousProductName },
+ } );
+ const classes = classNames( 'plan-features-2023-grid__common-title', getPlanClass( planSlug ) );
+ const rowspanProp = options?.isTableCell && shouldRenderEnterpriseLogos ? { rowSpan: '2' } : {};
+ return (
+
+ { shouldShowFeatureTitle && { title }
}
+ { shouldRenderEnterpriseLogos && (
+
+
+
+
+
+
+
+
+
+
+ ) }
+
+ );
+ } );
+};
+
+export default PreviousFeaturesIncludedTitle;
diff --git a/packages/plans-grid-next/src/components/features-grid/spotlight-plan.tsx b/packages/plans-grid-next/src/components/features-grid/spotlight-plan.tsx
new file mode 100644
index 0000000000000..d7dbb17c3f095
--- /dev/null
+++ b/packages/plans-grid-next/src/components/features-grid/spotlight-plan.tsx
@@ -0,0 +1,87 @@
+import {
+ PlanSlug,
+ WPComStorageAddOnSlug,
+ getPlanClass,
+ isFreePlan,
+} from '@automattic/calypso-products';
+import classNames from 'classnames';
+import { GridPlan, PlanActionOverrides } from '../../types';
+import BillingTimeframes from './billing-timeframes';
+import PlanHeaders from './plan-headers';
+import PlanLogos from './plan-logos';
+import PlanPrice from './plan-price';
+import PlanStorageOptions from './plan-storage-options';
+import PlanTagline from './plan-tagline';
+import TopButtons from './top-buttons';
+
+type SpotlightPlanProps = {
+ currentSitePlanSlug?: string | null;
+ gridPlanForSpotlight?: GridPlan;
+ intervalType: string;
+ isInSignup: boolean;
+ isLaunchPage?: boolean | null;
+ onStorageAddOnClick?: ( addOnSlug: WPComStorageAddOnSlug ) => void;
+ onUpgradeClick: ( planSlug: PlanSlug ) => void;
+ planActionOverrides?: PlanActionOverrides;
+ planUpgradeCreditsApplicable?: number | null;
+ showUpgradeableStorage: boolean;
+ options?: {
+ isTableCell?: boolean;
+ };
+};
+
+const SpotlightPlan = ( {
+ currentSitePlanSlug,
+ gridPlanForSpotlight,
+ intervalType,
+ isInSignup,
+ isLaunchPage,
+ onStorageAddOnClick,
+ onUpgradeClick,
+ planActionOverrides,
+ planUpgradeCreditsApplicable,
+ showUpgradeableStorage,
+}: SpotlightPlanProps ) => {
+ if ( ! gridPlanForSpotlight ) {
+ return null;
+ }
+
+ const spotlightPlanClasses = classNames(
+ 'plan-features-2023-grid__plan-spotlight',
+ getPlanClass( gridPlanForSpotlight.planSlug )
+ );
+
+ const isNotFreePlan = ! isFreePlan( gridPlanForSpotlight.planSlug );
+
+ return (
+
+
+
+ { isNotFreePlan &&
}
+ { isNotFreePlan && (
+
+ ) }
+ { isNotFreePlan &&
}
+
+
+
+ );
+};
+
+export default SpotlightPlan;
diff --git a/packages/plans-grid-next/src/components/features-grid/table.tsx b/packages/plans-grid-next/src/components/features-grid/table.tsx
new file mode 100644
index 0000000000000..3d5ec58cb43b1
--- /dev/null
+++ b/packages/plans-grid-next/src/components/features-grid/table.tsx
@@ -0,0 +1,156 @@
+import { PlanSlug, WPComStorageAddOnSlug } from '@automattic/calypso-products';
+import classNames from 'classnames';
+import { useTranslate } from 'i18n-calypso';
+import { DataResponse, GridPlan, PlanActionOverrides } from '../../types';
+import { StickyContainer } from '../sticky-container';
+import BillingTimeframes from './billing-timeframes';
+import PlanFeaturesList from './plan-features-list';
+import PlanHeaders from './plan-headers';
+import PlanLogos from './plan-logos';
+import PlanPrice from './plan-price';
+import PlanStorageOptions from './plan-storage-options';
+import PlanTagline from './plan-tagline';
+import PreviousFeaturesIncludedTitle from './previous-features-included-title';
+import TopButtons from './top-buttons';
+
+type TableProps = {
+ currentSitePlanSlug?: string | null;
+ generatedWPComSubdomain: DataResponse< { domain_name: string } >;
+ gridPlanForSpotlight?: GridPlan;
+ hideUnavailableFeatures?: boolean;
+ intervalType: string;
+ isCustomDomainAllowedOnFreePlan: boolean;
+ isInSignup: boolean;
+ isLaunchPage?: boolean | null;
+ onStorageAddOnClick?: ( addOnSlug: WPComStorageAddOnSlug ) => void;
+ onUpgradeClick: ( planSlug: PlanSlug ) => void;
+ paidDomainName?: string;
+ planActionOverrides?: PlanActionOverrides;
+ planUpgradeCreditsApplicable?: number | null;
+ renderedGridPlans: GridPlan[];
+ selectedFeature?: string;
+ showUpgradeableStorage: boolean;
+ stickyRowOffset: number;
+ options?: {
+ isTableCell?: boolean;
+ };
+};
+
+const Table = ( {
+ currentSitePlanSlug,
+ generatedWPComSubdomain,
+ gridPlanForSpotlight,
+ hideUnavailableFeatures,
+ intervalType,
+ isCustomDomainAllowedOnFreePlan,
+ isInSignup,
+ isLaunchPage,
+ onStorageAddOnClick,
+ onUpgradeClick,
+ paidDomainName,
+ planActionOverrides,
+ planUpgradeCreditsApplicable,
+ renderedGridPlans,
+ selectedFeature,
+ showUpgradeableStorage,
+ stickyRowOffset,
+}: TableProps ) => {
+ // Do not render the spotlight plan if it exists
+ const gridPlansWithoutSpotlight = ! gridPlanForSpotlight
+ ? renderedGridPlans
+ : renderedGridPlans.filter( ( { planSlug } ) => gridPlanForSpotlight.planSlug !== planSlug );
+ const tableClasses = classNames(
+ 'plan-features-2023-grid__table',
+ `has-${ gridPlansWithoutSpotlight.length }-cols`
+ );
+ const translate = useTranslate();
+
+ return (
+
+
+ { translate( 'Available plans to choose from' ) }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ { ( isStuck: boolean ) => (
+
+ ) }
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default Table;
diff --git a/packages/plans-grid-next/src/components/features-grid/top-buttons.tsx b/packages/plans-grid-next/src/components/features-grid/top-buttons.tsx
new file mode 100644
index 0000000000000..fd2ba3c5640cc
--- /dev/null
+++ b/packages/plans-grid-next/src/components/features-grid/top-buttons.tsx
@@ -0,0 +1,93 @@
+import {
+ PlanSlug,
+ getPlan,
+ isBusinessTrial,
+ isWooExpressMediumPlan,
+ isWooExpressSmallPlan,
+} from '@automattic/calypso-products';
+import classNames from 'classnames';
+import { useTranslate } from 'i18n-calypso';
+import { GridPlan, PlanActionOverrides } from '../../types';
+import PlanFeatures2023GridActions from '../actions';
+import PlanDivOrTdContainer from '../plan-div-td-container';
+
+type TopButtonsProps = {
+ currentSitePlanSlug?: string | null;
+ isInSignup: boolean;
+ isLaunchPage?: boolean | null;
+ onUpgradeClick: ( planSlug: PlanSlug ) => void;
+ planActionOverrides?: PlanActionOverrides;
+ renderedGridPlans: GridPlan[];
+ options?: {
+ isTableCell?: boolean;
+ isStuck?: boolean;
+ };
+};
+
+const TopButtons = ( {
+ currentSitePlanSlug,
+ isInSignup,
+ isLaunchPage,
+ onUpgradeClick,
+ options,
+ planActionOverrides,
+ renderedGridPlans,
+}: TopButtonsProps ) => {
+ const translate = useTranslate();
+
+ return renderedGridPlans.map(
+ ( { planSlug, availableForPurchase, isMonthlyPlan, features: { storageOptions } } ) => {
+ const classes = classNames( 'plan-features-2023-grid__table-item', 'is-top-buttons' );
+
+ // Leaving it `undefined` makes it use the default label
+ let buttonText;
+
+ if (
+ isWooExpressMediumPlan( planSlug ) &&
+ ! isWooExpressMediumPlan( currentSitePlanSlug || '' )
+ ) {
+ buttonText = translate( 'Get Performance', { textOnly: true } );
+ } else if (
+ isWooExpressSmallPlan( planSlug ) &&
+ ! isWooExpressSmallPlan( currentSitePlanSlug || '' )
+ ) {
+ buttonText = translate( 'Get Essential', { textOnly: true } );
+ } else if ( isBusinessTrial( currentSitePlanSlug || '' ) ) {
+ buttonText = translate( 'Get %(plan)s', {
+ textOnly: true,
+ args: {
+ plan: getPlan( planSlug )?.getTitle() || '',
+ },
+ } );
+ }
+
+ return (
+
+
+ onUpgradeClick( overridePlanSlug ?? planSlug )
+ }
+ planSlug={ planSlug }
+ currentSitePlanSlug={ currentSitePlanSlug }
+ buttonText={ buttonText }
+ planActionOverrides={ planActionOverrides }
+ showMonthlyPrice={ true }
+ isStuck={ options?.isStuck || false }
+ storageOptions={ storageOptions }
+ visibleGridPlans={ renderedGridPlans }
+ />
+
+ );
+ }
+ );
+};
+
+export default TopButtons;
diff --git a/packages/plans-grid-next/src/components/plan-features-container.tsx b/packages/plans-grid-next/src/components/plan-features-container.tsx
index c945402ab3652..5284d7e308594 100644
--- a/packages/plans-grid-next/src/components/plan-features-container.tsx
+++ b/packages/plans-grid-next/src/components/plan-features-container.tsx
@@ -1,5 +1,5 @@
import { JetpackLogo } from '@automattic/components';
-import { LocalizeProps } from 'i18n-calypso';
+import { useTranslate } from 'i18n-calypso';
import { useManageTooltipToggle } from '../hooks/use-manage-tooltip-toggle';
import { DataResponse, GridPlan } from '../types';
import PlanFeatures2023GridFeatures from './features';
@@ -10,7 +10,6 @@ const PlanFeaturesContainer: React.FC< {
plansWithFeatures: GridPlan[];
paidDomainName?: string;
generatedWPComSubdomain: DataResponse< { domain_name: string } >; // used to show a wpcom free domain in the Free plan column when a paid domain is picked.
- translate: LocalizeProps[ 'translate' ];
hideUnavailableFeatures?: boolean; // used to hide features that are not available, instead of strike-through as explained in #76206
selectedFeature?: string;
isCustomDomainAllowedOnFreePlan: boolean; // indicate when a custom domain is allowed to be used with the Free plan.
@@ -19,13 +18,13 @@ const PlanFeaturesContainer: React.FC< {
plansWithFeatures,
paidDomainName,
generatedWPComSubdomain,
- translate,
hideUnavailableFeatures,
selectedFeature,
isCustomDomainAllowedOnFreePlan,
isTableCell,
} ) => {
const [ activeTooltipId, setActiveTooltipId ] = useManageTooltipToggle();
+ const translate = useTranslate();
return plansWithFeatures.map(
( { planSlug, features: { wpcomFeatures, jetpackFeatures } }, mapIndex ) => {