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

Plans: Make storage selection calculations in plan prices optional #96520

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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
Expand Up @@ -2,32 +2,56 @@ import { isEnabled } from '@automattic/calypso-config';
import {
getPlanSlugForTermVariant,
PlanSlug,
TERMS_LIST,
URL_FRIENDLY_TERMS_MAPPING,
UrlFriendlyTermType,
} from '@automattic/calypso-products';
import { AddOns, Plans } from '@automattic/data-stores';
import { GridPlan } from '@automattic/plans-grid-next';
import {
HiddenPlans,
PlansIntent,
usePlansFromTypes,
usePlanTypesWithIntent,
} from '@automattic/plans-grid-next';
import useLongerPlanTermDefaultExperiment from './experiments/use-longer-plan-term-default-experiment';
import useCheckPlanAvailabilityForPurchase from './use-check-plan-availability-for-purchase';

const useEligibilityForTermSavingsPriceDisplay = ( {
gridPlans,
hiddenPlans,
intent,
isSubdomainNotGenerated,
selectedPlan,
term,
displayedIntervals,
coupon,
siteId,
storageAddOns,
isInSignup,
}: {
gridPlans: GridPlan[];
hiddenPlans?: HiddenPlans;
intent?: PlansIntent;
isSubdomainNotGenerated?: boolean;
selectedPlan?: PlanSlug;
term: ( typeof TERMS_LIST )[ number ];
displayedIntervals: UrlFriendlyTermType[];
storageAddOns: ( AddOns.AddOnMeta | null )[] | null;
coupon?: string;
siteId?: number | null;
isInSignup?: boolean;
} ) => {
const longerPlanTermDefaultExperiment = useLongerPlanTermDefaultExperiment();
const planSlugs = gridPlans.map( ( { planSlug } ) => planSlug );
Copy link
Contributor

Choose a reason for hiding this comment

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

p.s. NIT - if we bring back the original (prior to these changes), would it make sense to pass planSlugs instead? So do the mapping at consuming end perhaps.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh good question. I'll think this over 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we go back to the original, I prefer planSlugs. It more concisely describes what's needed for the hook, and it generalizes the interface more.

const planSlugsForAllDisplayedIntervals = planSlugs.flatMap(
const availablePlanSlugs = usePlansFromTypes( {
planTypes: usePlanTypesWithIntent( {
intent,
selectedPlan,
siteId,
hiddenPlans,
isSubdomainNotGenerated,
} ),
term,
intent,
} );
const planSlugsForAllDisplayedIntervals = availablePlanSlugs.flatMap(
( planSlug ) =>
displayedIntervals
.map( ( term ) =>
Expand Down
26 changes: 17 additions & 9 deletions client/my-sites/plans-features-main/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,19 @@ const PlansFeaturesMain = ( {
hideEnterprisePlan,
};

const enableTermSavingsPriceDisplay = useEligibilityForTermSavingsPriceDisplay( {
selectedPlan,
hiddenPlans,
isSubdomainNotGenerated: ! resolvedSubdomainName.result,
term,
intent,
displayedIntervals: filteredDisplayedIntervals,
storageAddOns,
coupon,
siteId,
isInSignup,
} );

// we need all the plans that are available to pick for comparison grid (these should extend into plans-ui data store selectors)
const gridPlansForComparisonGrid = useGridPlansForComparisonGrid( {
allFeaturesList: getFeaturesList(),
Expand All @@ -425,6 +438,7 @@ const PlansFeaturesMain = ( {
useCheckPlanAvailabilityForPurchase,
useFreeTrialPlanSlugs,
isDomainOnlySite,
reflectStorageSelectionInPlanPrices: ! enableTermSavingsPriceDisplay,
} );

// we need only the visible ones for features grid (these should extend into plans-ui data store selectors)
Expand All @@ -447,6 +461,7 @@ const PlansFeaturesMain = ( {
useFreeTrialPlanSlugs,
isDomainOnlySite,
term,
reflectStorageSelectionInPlanPrices: ! enableTermSavingsPriceDisplay,
} );

// when `deemphasizeFreePlan` is enabled, the Free plan will be presented as a CTA link instead of a plan card in the features grid.
Expand Down Expand Up @@ -755,15 +770,6 @@ const PlansFeaturesMain = ( {
</div>
);

const enableTermSavingsPriceDisplay = useEligibilityForTermSavingsPriceDisplay( {
gridPlans: gridPlansForFeaturesGrid ?? [],
displayedIntervals: filteredDisplayedIntervals,
storageAddOns,
coupon,
siteId,
isInSignup,
} );

return (
<>
<div className={ clsx( 'plans-features-main', 'is-pricing-grid-2023-plans-features-main' ) }>
Expand Down Expand Up @@ -863,6 +869,7 @@ const PlansFeaturesMain = ( {
onStorageAddOnClick={ handleStorageAddOnClick }
paidDomainName={ paidDomainName }
recordTracksEvent={ recordTracksEvent }
reflectStorageSelectionInPlanPrices={ ! enableTermSavingsPriceDisplay }
selectedFeature={ selectedFeature }
showLegacyStorageFeature={ showLegacyStorageFeature }
showRefundPeriod={ isAnyHostingFlow( flowName ) }
Expand Down Expand Up @@ -928,6 +935,7 @@ const PlansFeaturesMain = ( {
: undefined
}
recordTracksEvent={ recordTracksEvent }
reflectStorageSelectionInPlanPrices={ ! enableTermSavingsPriceDisplay }
selectedFeature={ selectedFeature }
selectedPlan={ selectedPlan }
showUpgradeableStorage={ showUpgradeableStorage }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@ describe( 'usePricingMetaForGridPlans', () => {

const pricingMeta = usePricingMetaForGridPlans( {
planSlugs: [ PLAN_BUSINESS ],
reflectStorageSelectionInPlanPrices: true,
storageAddOns: STORAGE_ADD_ONS_MOCK,
siteId,
coupon: undefined,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ interface Props {
* If true, the pricing includes discounts from upgrade credits.
*/
withProratedDiscounts?: boolean;

/**
* Storage add-on products ( e.g. increase site storage by 50GB ) can be purchased alongside plans. If true,
* storage add-on selections will be included in final plan price calculations. Otherwise, omit the add-ons
* from the final price.
*/
reflectStorageSelectionInPlanPrices?: boolean;
}

function getTotalPrice( planPrice: number | null | undefined, addOnPrice = 0 ): number | null {
Expand All @@ -79,6 +86,7 @@ const usePricingMetaForGridPlans = ( {
useCheckPlanAvailabilityForPurchase,
storageAddOns,
withProratedDiscounts,
reflectStorageSelectionInPlanPrices = false,
}: Props ): { [ planSlug: string ]: Plans.PricingMetaForGridPlan } | null => {
// plans - should have a definition for all plans, being the main source of API data
const plans = Plans.usePlans( { coupon } );
Expand Down Expand Up @@ -131,11 +139,12 @@ const usePricingMetaForGridPlans = ( {
const plan = plans.data?.[ planSlug ];
const sitePlan = sitePlans.data?.[ planSlug ];
const selectedStorageOption = selectedStorageOptions?.[ planSlug ];
const selectedStorageAddOn = selectedStorageOption
? storageAddOns?.find( ( addOn ) => {
return addOn?.addOnSlug === selectedStorageOption;
} )
: null;
const selectedStorageAddOn =
selectedStorageOption && reflectStorageSelectionInPlanPrices
? storageAddOns?.find( ( addOn ) => {
return addOn?.addOnSlug === selectedStorageOption;
} )
: null;
const storageAddOnPriceMonthly = selectedStorageAddOn?.prices?.monthlyPrice || 0;
const storageAddOnPriceYearly = selectedStorageAddOn?.prices?.yearlyPrice || 0;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1156,6 +1156,7 @@ const WrappedComparisonGrid = ( {
enableFeatureTooltips,
featureGroupMap,
enableTermSavingsPriceDisplay,
reflectStorageSelectionInPlanPrices,
...otherProps
}: ComparisonGridExternalProps ) => {
const gridContainerRef = useRef< HTMLDivElement >( null );
Expand Down Expand Up @@ -1190,6 +1191,7 @@ const WrappedComparisonGrid = ( {
featureGroupMap={ featureGroupMap }
hideUnsupportedFeatures={ hideUnsupportedFeatures }
enableTermSavingsPriceDisplay={ enableTermSavingsPriceDisplay }
reflectStorageSelectionInPlanPrices={ reflectStorageSelectionInPlanPrices }
>
<ComparisonGrid
intervalType={ intervalType }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,14 @@ const useTermVariantPlanSlugForSavings = ( {

const HeaderPrice = ( { planSlug, visibleGridPlans }: HeaderPriceProps ) => {
const translate = useTranslate();
const { gridPlansIndex, enableTermSavingsPriceDisplay, siteId, coupon, helpers } =
usePlansGridContext();
const {
gridPlansIndex,
enableTermSavingsPriceDisplay,
reflectStorageSelectionInPlanPrices,
siteId,
coupon,
helpers,
} = usePlansGridContext();
const { isAnyPlanPriceDiscounted, setIsAnyPlanPriceDiscounted } = useHeaderPriceContext();
const {
current,
Expand Down Expand Up @@ -76,6 +82,7 @@ const HeaderPrice = ( { planSlug, visibleGridPlans }: HeaderPriceProps ) => {
coupon,
siteId,
useCheckPlanAvailabilityForPurchase: helpers?.useCheckPlanAvailabilityForPurchase,
reflectStorageSelectionInPlanPrices,
} )?.[ termVariantPlanSlug ?? '' ];

const termVariantPrice =
Expand Down
3 changes: 3 additions & 0 deletions packages/plans-grid-next/src/grid-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ interface PlansGridContext {
hideUnsupportedFeatures?: boolean;
hideFeatureGroupTitles?: boolean;
enterpriseFeaturesList?: string[];
reflectStorageSelectionInPlanPrices?: boolean;
}

const PlansGridContext = createContext< PlansGridContext >( {} as PlansGridContext );
Expand All @@ -54,6 +55,7 @@ const PlansGridContextProvider = ( {
hideUnsupportedFeatures,
hideFeatureGroupTitles,
enterpriseFeaturesList,
reflectStorageSelectionInPlanPrices,
}: GridContextProps ) => {
const gridPlansIndex = gridPlans.reduce(
( acc, gridPlan ) => ( {
Expand Down Expand Up @@ -87,6 +89,7 @@ const PlansGridContextProvider = ( {
hideUnsupportedFeatures,
hideFeatureGroupTitles,
enterpriseFeaturesList,
reflectStorageSelectionInPlanPrices,
} }
>
{ children }
Expand Down
5 changes: 5 additions & 0 deletions packages/plans-grid-next/src/hooks/data-store/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ export interface UseGridPlansParams {
* Used to hide the "Your Plan" label for domain-only sites
*/
isDomainOnlySite?: boolean;
/**
* Determine if storage add-on products should be combined with plan costs when
* calculating prices.
*/
reflectStorageSelectionInPlanPrices?: boolean;
}

export type UseGridPlansType = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const useGridPlansForComparisonGrid = ( {
useCheckPlanAvailabilityForPurchase,
useFreeTrialPlanSlugs,
isDomainOnlySite,
reflectStorageSelectionInPlanPrices,
}: UseGridPlansParams ): GridPlan[] | null => {
const gridPlans = useGridPlans( {
allFeaturesList,
Expand All @@ -46,6 +47,7 @@ const useGridPlansForComparisonGrid = ( {
useCheckPlanAvailabilityForPurchase,
useFreeTrialPlanSlugs,
isDomainOnlySite,
reflectStorageSelectionInPlanPrices,
} );

const planFeaturesForComparisonGrid = useRestructuredPlanFeaturesForComparisonGrid( {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const useGridPlansForFeaturesGrid = ( {
useFreeTrialPlanSlugs,
highlightLabelOverrides,
isDomainOnlySite,
reflectStorageSelectionInPlanPrices,
}: UseGridPlansParams ): GridPlan[] | null => {
const gridPlans = useGridPlans( {
allFeaturesList,
Expand All @@ -44,6 +45,7 @@ const useGridPlansForFeaturesGrid = ( {
useFreeTrialPlanSlugs,
highlightLabelOverrides,
isDomainOnlySite,
reflectStorageSelectionInPlanPrices,
} );

const planFeaturesForFeaturesGrid = usePlanFeaturesForGridPlans( {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ const isGridPlanVisible = ( {
return isVisible;
};

const usePlanTypesWithIntent = ( {
export const usePlanTypesWithIntent = ( {
intent,
selectedPlan,
siteId,
Expand Down Expand Up @@ -234,6 +234,7 @@ const useGridPlans: UseGridPlansType = ( {
isDisplayingPlansNeededForFeature,
highlightLabelOverrides,
isDomainOnlySite,
reflectStorageSelectionInPlanPrices,
} ) => {
const freeTrialPlanSlugs = useFreeTrialPlanSlugs?.( {
intent: intent ?? 'default',
Expand Down Expand Up @@ -290,6 +291,7 @@ const useGridPlans: UseGridPlansType = ( {
coupon,
siteId,
useCheckPlanAvailabilityForPurchase,
reflectStorageSelectionInPlanPrices,
} );

// Null return would indicate that we are still loading the data. No grid without grid plans.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
import { AddOns, Plans } from '@automattic/data-stores';
import { formatCurrency } from '@automattic/format-currency';
import { useTranslate } from 'i18n-calypso';
import { usePlansGridContext } from '../../grid-context';
import type { GridPlan } from '../../types';

interface UsePlanBillingDescriptionProps {
Expand All @@ -35,7 +36,7 @@ export default function usePlanBillingDescription( {
}: UsePlanBillingDescriptionProps ) {
const translate = useTranslate();
const { currencyCode, originalPrice, discountedPrice, billingPeriod, introOffer } = pricing || {};

const { reflectStorageSelectionInPlanPrices } = usePlansGridContext();
const yearlyVariantPlanSlug = getPlanSlugForTermVariant( planSlug, TERM_ANNUALLY );

const yearlyVariantPricing = Plans.usePricingMetaForGridPlans( {
Expand All @@ -44,6 +45,7 @@ export default function usePlanBillingDescription( {
coupon,
siteId,
useCheckPlanAvailabilityForPurchase,
reflectStorageSelectionInPlanPrices,
} )?.[ yearlyVariantPlanSlug ?? '' ];

if ( ! pricing ) {
Expand Down
5 changes: 4 additions & 1 deletion packages/plans-grid-next/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import PlanTypeSelector from './components/plan-type-selector';
import { Plans2023Tooltip } from './components/plans-2023-tooltip';
import { EFFECTIVE_TERMS_LIST } from './constants';
import useGridPlanForSpotlight from './hooks/data-store/use-grid-plan-for-spotlight';
import useGridPlans from './hooks/data-store/use-grid-plans';
import useGridPlans, { usePlanTypesWithIntent } from './hooks/data-store/use-grid-plans';
import useGridPlansForComparisonGrid from './hooks/data-store/use-grid-plans-for-comparison-grid';
import useGridPlansForFeaturesGrid from './hooks/data-store/use-grid-plans-for-features-grid';
import usePlanBillingDescription from './hooks/data-store/use-plan-billing-description';
import usePlanFeaturesForGridPlans from './hooks/data-store/use-plan-features-for-grid-plans';
import usePlansFromTypes from './hooks/data-store/use-plans-from-types';
import useRestructuredPlanFeaturesForComparisonGrid from './hooks/data-store/use-restructured-plan-features-for-comparison-grid';
import { useManageTooltipToggle } from './hooks/use-manage-tooltip-toggle';

Expand All @@ -34,6 +35,8 @@ export {
useGridPlanForSpotlight,
usePlanBillingDescription,
usePlanFeaturesForGridPlans,
usePlansFromTypes,
usePlanTypesWithIntent,
useRestructuredPlanFeaturesForComparisonGrid,
};

Expand Down
6 changes: 6 additions & 0 deletions packages/plans-grid-next/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,12 @@ export type GridContextProps = {
* This carries lower precedence than promo/coupon and introductory pricing, irrespective of whether set or not.
*/
enableTermSavingsPriceDisplay?: boolean;

/**
* Determine if storage add-on products should be combined with plan costs when
* calculating prices.
*/
reflectStorageSelectionInPlanPrices?: boolean;
};

export type ComparisonGridExternalProps = Omit<
Expand Down
Loading