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 ( - -
-

{ planTitle }

-
-
- ); - } ); -}; - -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 ( - - - - - - - - - - - - - - - - - - - - { ( isStuck: boolean ) => ( - - ) } - - - - - - - - - - - -
- { translate( 'Available plans to choose from' ) } -
- ); -}; - -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 ( + +
+

{ planTitle }

+
+
+ ); + } ); +}; + +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 ( + + + + + + + + + + + + + + + + + + + + { ( isStuck: boolean ) => ( + + ) } + + + + + + + + + + + +
+ { translate( 'Available plans to choose from' ) } +
+ ); +}; + +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 ) => {