Skip to content

Commit

Permalink
Plans: Use 2year and 3year default interval type (#96174)
Browse files Browse the repository at this point in the history
Co-authored-by: Jeremy Yip <jeremy.yip@automattic.com>
  • Loading branch information
chriskmnds and jeyip authored Nov 25, 2024
1 parent a2013c3 commit 1f562c3
Show file tree
Hide file tree
Showing 8 changed files with 126 additions and 39 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { useExperiment } from 'calypso/lib/explat';

const useTermFromExperimentVariant = (
experimentVariant: string | null | undefined
): string | null => {
switch ( experimentVariant ) {
case 'default_to_three_year_plans':
return '3yearly';
case 'default_to_two_year_plans':
return '2yearly';
default:
return null;
}
};

/**
* Returns:
* - the term as yearly, 2yearly, 3yearly (etc.) to be used as default in the plans
* page based on the experiment variant
* - OR null/undefined if control variant or if the experiment is still loading (Subject to change)
*
* This hook although used for the experiment, it can be refactored in the end to
* define the default term in the grid/plans page.
*
*/
const useLongerPlanTermDefaultExperiment = (): {
isLoadingExperiment: boolean;
term?: string | null;
// TODO: Consider removing this and always return concrete term values (where undefined/null would mean no term savings)
isEligibleForTermSavings: boolean;
} => {
// TODO: Figure out how to define explicit types for the experiment assignment
// variation names 'default_to_three_year_plans', 'default_to_two_year_plans'
// and 'emphasize_savings_only'.
const [ isLoadingExperimentAssignment, experimentAssignment ] = useExperiment(
'calypso_plans_page_emphasize_longer_plan_savings'
);
const term = useTermFromExperimentVariant( experimentAssignment?.variationName );

return {
isLoadingExperiment: isLoadingExperimentAssignment,
term: isLoadingExperimentAssignment ? undefined : term,
isEligibleForTermSavings: !! (
experimentAssignment?.variationName && experimentAssignment.variationName !== 'control'
),
};
};

export default useLongerPlanTermDefaultExperiment;
34 changes: 20 additions & 14 deletions client/my-sites/plans-features-main/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ import ComparisonGridToggle from './components/comparison-grid-toggle';
import PlanUpsellModal from './components/plan-upsell-modal';
import { useModalResolutionCallback } from './components/plan-upsell-modal/hooks/use-modal-resolution-callback';
import PlansPageSubheader from './components/plans-page-subheader';
import useLongerPlanTermDefaultExperiment from './hooks/experiments/use-longer-plan-term-default-experiment';
import useCheckPlanAvailabilityForPurchase from './hooks/use-check-plan-availability-for-purchase';
import useDefaultWpcomPlansIntent from './hooks/use-default-wpcom-plans-intent';
import useFilteredDisplayedIntervals from './hooks/use-filtered-displayed-intervals';
Expand Down Expand Up @@ -240,6 +241,8 @@ const PlansFeaturesMain = ( {
const showUpgradeableStorage = config.isEnabled( 'plans/upgradeable-storage' );
const getPlanTypeDestination = usePlanTypeDestinationCallback();

const longerPlanTermDefaultExperiment = useLongerPlanTermDefaultExperiment();

const resolveModal = useModalResolutionCallback( {
isCustomDomainAllowedOnFreePlan,
flowName,
Expand Down Expand Up @@ -282,6 +285,13 @@ const PlansFeaturesMain = ( {
! isPersonalPlan( selectedPlan ) &&
( 'interval' === planTypeSelector || ! previousRoute.startsWith( '/plans/' ) );

const filteredDisplayedIntervals = useFilteredDisplayedIntervals( {
productSlug: currentPlan?.productSlug,
displayedIntervals,
flowName,
paidDomainName,
} );

const term = usePlanBillingPeriod( {
intervalType,
...( selectedPlan ? { defaultValue: getPlan( selectedPlan )?.term } : {} ),
Expand Down Expand Up @@ -457,13 +467,6 @@ const PlansFeaturesMain = ( {
_customerType = 'business';
}

const filteredDisplayedIntervals = useFilteredDisplayedIntervals( {
productSlug: currentPlan?.productSlug,
displayedIntervals,
flowName,
paidDomainName,
} );

const planTypeSelectorProps = useMemo( () => {
const props = {
basePlansPath,
Expand Down Expand Up @@ -632,7 +635,8 @@ const PlansFeaturesMain = ( {
! intent ||
! defaultWpcomPlansIntent || // this may be unnecessary, but just in case
! gridPlansForFeaturesGrid ||
! gridPlansForComparisonGrid
! gridPlansForComparisonGrid ||
longerPlanTermDefaultExperiment.isLoadingExperiment
);

const isPlansGridReady = ! isLoadingGridPlans && ! resolvedSubdomainName.isLoading;
Expand Down Expand Up @@ -838,9 +842,10 @@ const PlansFeaturesMain = ( {
enableReducedFeatureGroupSpacing={ showSimplifiedFeatures }
enableLogosOnlyForEnterprisePlan={ showSimplifiedFeatures }
hideFeatureGroupTitles={ showSimplifiedFeatures }
enableTermSavingsPriceDisplay={ isEnabled(
'plans/term-savings-price-display'
) }
enableTermSavingsPriceDisplay={
isEnabled( 'plans/term-savings-price-display' ) ||
longerPlanTermDefaultExperiment.isEligibleForTermSavings
}
/>
) }
{ showEscapeHatch && hidePlansFeatureComparison && viewAllPlansButton }
Expand Down Expand Up @@ -900,9 +905,10 @@ const PlansFeaturesMain = ( {
}
enableFeatureTooltips
featureGroupMap={ featureGroupMapForComparisonGrid }
enableTermSavingsPriceDisplay={ isEnabled(
'plans/term-savings-price-display'
) }
enableTermSavingsPriceDisplay={
isEnabled( 'plans/term-savings-price-display' ) ||
longerPlanTermDefaultExperiment.isEligibleForTermSavings
}
/>
) }
<ComparisonGridToggle
Expand Down
4 changes: 4 additions & 0 deletions client/my-sites/plans-features-main/test/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ jest.mock( '@automattic/data-stores', () => ( {
jest.mock( 'calypso/components/data/query-active-promotions', () => jest.fn() );
jest.mock( 'calypso/components/data/query-products-list', () => jest.fn() );

jest.mock( '../hooks/experiments/use-longer-plan-term-default-experiment', () => () => ( {
isLoadingExperiment: false,
} ) );

import {
PLAN_FREE,
PLAN_BUSINESS_MONTHLY,
Expand Down
2 changes: 1 addition & 1 deletion client/my-sites/plans/controller.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { productSelect } from 'calypso/my-sites/plans/jetpack-plans/controller';
import setJetpackPlansHeader from 'calypso/my-sites/plans/jetpack-plans/plans-header';
import isSiteWpcom from 'calypso/state/selectors/is-site-wpcom';
import { getSelectedSite, getSelectedSiteId } from 'calypso/state/ui/selectors';
import Plans from './plans';
import Plans from './main';

function showJetpackPlans( context ) {
const state = context.store.getState();
Expand Down
28 changes: 27 additions & 1 deletion client/my-sites/plans/main.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {
PLAN_WOOEXPRESS_MEDIUM_MONTHLY,
PLAN_WOOEXPRESS_SMALL,
PLAN_WOOEXPRESS_SMALL_MONTHLY,
getBillingMonthsForTerm,
URL_FRIENDLY_TERMS_MAPPING,
} from '@automattic/calypso-products';
import page from '@automattic/calypso-router';
import { WpcomPlansUI, Plans } from '@automattic/data-stores';
Expand Down Expand Up @@ -39,10 +41,12 @@ import { PerformanceTrackerStop } from 'calypso/lib/performance-tracking';
import PlansNavigation from 'calypso/my-sites/plans/navigation';
import P2PlansMain from 'calypso/my-sites/plans/p2-plans-main';
import PlansFeaturesMain from 'calypso/my-sites/plans-features-main';
import useLongerPlanTermDefaultExperiment from 'calypso/my-sites/plans-features-main/hooks/experiments/use-longer-plan-term-default-experiment';
import { useSelector } from 'calypso/state';
import { getByPurchaseId } from 'calypso/state/purchases/selectors';
import { canCurrentUser } from 'calypso/state/selectors/can-current-user';
import getCurrentLocaleSlug from 'calypso/state/selectors/get-current-locale-slug';
import getCurrentPlanTerm from 'calypso/state/selectors/get-current-plan-term';
import getCurrentQueryArguments from 'calypso/state/selectors/get-current-query-arguments';
import getDomainFromHomeUpsellInQuery from 'calypso/state/selectors/get-domain-from-home-upsell-in-query';
import isEligibleForWpComMonthlyPlan from 'calypso/state/selectors/is-eligible-for-wpcom-monthly-plan';
Expand Down Expand Up @@ -548,12 +552,34 @@ const ConnectedPlans = connect(
)( withCartKey( withShoppingCart( localize( PlansComponent ) ) ) );

export default function PlansWrapper( props ) {
const { intervalType: intervalTypeFromProps } = props;
const selectedSiteId = useSelector( getSelectedSiteId );
const currentPlan = Plans.useCurrentPlan( { siteId: selectedSiteId } );
const longerPlanTermDefaultExperiment = useLongerPlanTermDefaultExperiment();
/**
* For WP.com plans page, if intervalType is not explicitly specified in the URL,
* we want to show plans of the same term as plan that is currently active
* We want to show the highest term between the current plan and the longer plan term default experiment
*/
const currentPlanTerm = useSelector( ( state ) =>
getIntervalTypeForTerm( getCurrentPlanTerm( state, selectedSiteId ) )
);
const intervalType =
longerPlanTermDefaultExperiment.term &&
currentPlanTerm &&
getBillingMonthsForTerm( URL_FRIENDLY_TERMS_MAPPING[ currentPlanTerm ] ) >
getBillingMonthsForTerm( URL_FRIENDLY_TERMS_MAPPING[ longerPlanTermDefaultExperiment.term ] )
? currentPlanTerm
: longerPlanTermDefaultExperiment.term;

return (
<CalypsoShoppingCartProvider>
<ConnectedPlans { ...props } currentPlan={ currentPlan } selectedSiteId={ selectedSiteId } />
<ConnectedPlans
{ ...props }
currentPlan={ currentPlan }
selectedSiteId={ selectedSiteId }
intervalType={ intervalTypeFromProps ?? intervalType ?? currentPlanTerm }
/>
</CalypsoShoppingCartProvider>
);
}
18 changes: 0 additions & 18 deletions client/my-sites/plans/plans.js

This file was deleted.

21 changes: 19 additions & 2 deletions client/signup/steps/plans/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { triggerGuidesForStep } from 'calypso/lib/guides/trigger-guides-for-step
import { buildUpgradeFunction } from 'calypso/lib/signup/step-actions';
import { getSegmentedIntent } from 'calypso/my-sites/plans/utils/get-segmented-intent';
import PlansFeaturesMain from 'calypso/my-sites/plans-features-main';
import useLongerPlanTermDefaultExperiment from 'calypso/my-sites/plans-features-main/hooks/experiments/use-longer-plan-term-default-experiment';
import { getStepUrl } from 'calypso/signup/utils';
import { getDomainFromUrl } from 'calypso/site-profiler/utils/get-valid-url';
import { recordTracksEvent } from 'calypso/state/analytics/actions';
Expand Down Expand Up @@ -114,9 +115,17 @@ export class PlansStep extends Component {
initialContext,
intervalType,
isDomainOnlySite,
longerPlanTermDefaultExperiment,
} = this.props;

const intervalTypeValue = intervalType || getIntervalType( this.props.path );
const intervalTypeValue =
intervalType ||
getIntervalType(
this.props.path,
flowName === 'onboarding' && longerPlanTermDefaultExperiment.term
? longerPlanTermDefaultExperiment.term
: undefined
);

let errorDisplay;

Expand Down Expand Up @@ -431,6 +440,14 @@ export const isDotBlogDomainRegistration = ( domainItem ) => {
return is_domain_registration && getTld( meta ) === 'blog';
};

const WrappedPlansStep = ( props ) => {
const longerPlanTermDefaultExperiment = useLongerPlanTermDefaultExperiment();

return (
<PlansStep { ...props } longerPlanTermDefaultExperiment={ longerPlanTermDefaultExperiment } />
);
};

export default connect(
( state, { path, signupDependencies: { siteSlug, siteId, domainItem } } ) => ( {
// Blogger plan is only available if user chose either a free domain or a .blog domain registration
Expand All @@ -446,4 +463,4 @@ export default connect(
hasInitializedSitesBackUrl: getCurrentUserSiteCount( state ) ? '/sites/' : false,
} ),
{ recordTracksEvent, saveSignupStep, submitSignupStep, errorNotice }
)( localize( PlansStep ) );
)( localize( WrappedPlansStep ) );
9 changes: 6 additions & 3 deletions client/signup/steps/plans/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,17 @@ const supportedIntervalTypes: SupportedIntervalTypes[] = [
'3yearly',
];

export const getIntervalType = ( path?: string ): SupportedIntervalTypes => {
export const getIntervalType = (
path?: string,
defaultType = 'yearly'
): SupportedIntervalTypes => {
const url = path ?? window?.location?.href ?? '';
const intervalType = getUrlParts( url ).searchParams.get( 'intervalType' ) || 'yearly';
const intervalType = getUrlParts( url ).searchParams.get( 'intervalType' ) || defaultType;

return (
supportedIntervalTypes.includes( intervalType as SupportedIntervalTypes )
? intervalType
: 'yearly'
: defaultType
) as SupportedIntervalTypes;
};

Expand Down

0 comments on commit 1f562c3

Please sign in to comment.