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 (Storage): Enable storage selection for current plan, and other fixes/refactors #91050

Merged
merged 5 commits into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/data-stores/src/add-ons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export { default as useAddOns } from './hooks/use-add-ons';
export { default as useAddOnCheckoutLink } from './hooks/use-add-on-checkout-link';
export { default as useAddOnPurchaseStatus } from './hooks/use-add-on-purchase-status';
export { default as useStorageAddOns } from './hooks/use-storage-add-ons';
export * from './constants';

/** Types */
export * from './types';
Original file line number Diff line number Diff line change
Expand Up @@ -98,20 +98,16 @@ const usePricingMetaForGridPlans = ( {
} else {
planPrices = Object.fromEntries(
planSlugs.map( ( planSlug ) => {
const availableForPurchase = planAvailabilityForPurchase[ planSlug ];
const selectedStorageOption = selectedStorageOptions?.[ planSlug ];
const selectedStorageAddOn = storageAddOns?.find( ( addOn ) => {
return selectedStorageOption && addOn?.featureSlugs?.includes( selectedStorageOption );
} );
const storageAddOnPrices =
selectedStorageAddOn?.purchased || selectedStorageAddOn?.exceedsSiteStorageLimits
? null
: selectedStorageAddOn?.prices;
const storageAddOnPriceMonthly = storageAddOnPrices?.monthlyPrice || 0;
const storageAddOnPriceYearly = storageAddOnPrices?.yearlyPrice || 0;

const plan = plans.data?.[ planSlug ];
const sitePlan = sitePlans.data?.[ planSlug ];
const selectedStorageOption = selectedStorageOptions?.[ planSlug ];
const selectedStorageAddOn = selectedStorageOption
? storageAddOns?.find( ( addOn ) => {
return addOn?.featureSlugs?.includes( selectedStorageOption );
} )
: null;
const storageAddOnPriceMonthly = selectedStorageAddOn?.prices?.monthlyPrice || 0;
const storageAddOnPriceYearly = selectedStorageAddOn?.prices?.yearlyPrice || 0;

/**
* 0. No plan or sitePlan (when selected site exists): planSlug is for a priceless plan.
Expand Down Expand Up @@ -176,7 +172,7 @@ const usePricingMetaForGridPlans = ( {
/**
* 2. Original and Discounted prices for plan available for purchase.
*/
if ( availableForPurchase ) {
if ( planAvailabilityForPurchase[ planSlug ] ) {
const originalPrice = {
monthly: getTotalPrice( plan.pricing.originalPrice.monthly, storageAddOnPriceMonthly ),
full: getTotalPrice( plan.pricing.originalPrice.full, storageAddOnPriceYearly ),
Expand Down
2 changes: 2 additions & 0 deletions packages/plans-grid-next/src/components/actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,12 @@ const PlanFeatures2023GridActions = ( {
const canPurchaseStorageAddOns = storageAddOnsForPlan?.some(
( storageAddOn ) => ! storageAddOn?.purchased && ! storageAddOn?.exceedsSiteStorageLimits
);

const storageAddOnCheckoutHref = storageAddOnsForPlan?.find(
( addOn ) =>
selectedStorageOptionForPlan && addOn?.featureSlugs?.includes( selectedStorageOptionForPlan )
)?.checkoutLink;

const nonDefaultStorageOptionSelected = defaultStorageOption !== selectedStorageOptionForPlan;

let actionButton = (
Expand Down
73 changes: 34 additions & 39 deletions packages/plans-grid-next/src/components/comparison-grid/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,14 @@ import filterUnusedFeaturesObject from '../../lib/filter-unused-features-object'
import getPlanFeaturesObject from '../../lib/get-plan-features-object';
import { isStorageUpgradeableForPlan } from '../../lib/is-storage-upgradeable-for-plan';
import { sortPlans } from '../../lib/sort-plan-properties';
import { getStorageStringFromFeature } from '../../util';
import PlanFeatures2023GridActions from '../actions';
import PlanFeatures2023GridHeaderPrice from '../header-price';
import PlanTypeSelector from '../plan-type-selector';
import { Plans2023Tooltip } from '../plans-2023-tooltip';
import PopularBadge from '../popular-badge';
import BillingTimeframe from '../shared/billing-timeframe';
import { StickyContainer } from '../sticky-container';
import { PlanStorageLabel, useGetAvailableStorageOptions } from '../storage';
import StorageAddOnDropdown from '../storage-add-on-dropdown';
import type {
GridPlan,
Expand Down Expand Up @@ -299,25 +299,6 @@ const PlanSelector = styled.header`
}
`;

const StorageButton = styled.div`
background: #f2f2f2;
border-radius: 5px;
padding: 4px 0;
width: -moz-fit-content;
width: fit-content;
text-align: center;
font-size: 0.75rem;
font-weight: 400;
line-height: 20px;
color: var( --studio-gray-90 );
min-width: 64px;
margin-top: 10px;

${ plansGridMediumLarge( css`
margin-top: 0;
` ) }
`;

const FeatureFootnotes = styled.div`
ol {
margin: 2em 0 0 1em;
Expand Down Expand Up @@ -584,6 +565,38 @@ const ComparisonGridFeatureGroupRowCell: React.FunctionComponent< {
renderedGridPlans: visibleGridPlans,
} );

/**
* TODO: Consider centralising `canUpgradeStorageForPlan` behind `availableStorageOptions`
*/
const availableStorageOptions = useGetAvailableStorageOptions()( {
storageOptions: gridPlan.features.storageOptions,
} );
/**
* The current plan is not marked as `availableForPurchase`, hence check on `current`.
*/
const canUpgradeStorageForPlan =
( gridPlan.current || gridPlan.availableForPurchase ) &&
isStorageUpgradeableForPlan( {
intervalType,
showUpgradeableStorage,
storageOptions: availableStorageOptions,
} );

const storageJSX = canUpgradeStorageForPlan ? (
<StorageAddOnDropdown
planSlug={ planSlug }
onStorageAddOnClick={ onStorageAddOnClick }
storageOptions={ availableStorageOptions }
priceOnSeparateLine
/>
) : (
gridPlan.features.storageOptions.map( ( storageOption ) => {
if ( ! storageOption?.isAddOn ) {
return <PlanStorageLabel storageOption={ storageOption } planSlug={ planSlug } />;
}
} )
);

if ( ! gridPlan ) {
return null;
}
Expand All @@ -605,13 +618,6 @@ const ComparisonGridFeatureGroupRowCell: React.FunctionComponent< {
( feature ) => feature.getSlug() === featureSlug
)
: false;
const storageOptions = gridPlan.features.storageOptions;
const defaultStorageOption = storageOptions.find( ( option ) => ! option.isAddOn );
const canUpgradeStorageForPlan = isStorageUpgradeableForPlan( {
intervalType,
showUpgradeableStorage,
storageOptions,
} );

const cellClasses = classNames(
'plan-comparison-grid__feature-group-row-cell',
Expand All @@ -638,18 +644,7 @@ const ComparisonGridFeatureGroupRowCell: React.FunctionComponent< {
{ isStorageFeature ? (
<>
<span className="plan-comparison-grid__plan-title">{ translate( 'Storage' ) }</span>
{ canUpgradeStorageForPlan ? (
<StorageAddOnDropdown
planSlug={ planSlug }
storageOptions={ gridPlan.features.storageOptions }
onStorageAddOnClick={ onStorageAddOnClick }
priceOnSeparateLine
/>
) : (
<StorageButton className="plan-features-2023-grid__storage-button" key={ planSlug }>
{ getStorageStringFromFeature( defaultStorageOption?.slug || '' ) }
</StorageButton>
) }
{ storageJSX }
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for making the nested conditionals more readable 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

doing a lot more of this in #91196 and likely others on top

</>
) : (
<>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import {
} from '@automattic/calypso-products';
import { usePlansGridContext } from '../../grid-context';
import { isStorageUpgradeableForPlan } from '../../lib/is-storage-upgradeable-for-plan';
import { getStorageStringFromFeature } from '../../util';
import { PlanFeaturesItem } from '../item';
import { PlanStorageLabel, useGetAvailableStorageOptions } from '../storage';
import StorageAddOnDropdown from '../storage-add-on-dropdown';

type PlanStorageOptionsProps = {
Expand All @@ -30,36 +30,43 @@ const PlanStorageOptions = ( {
const {
availableForPurchase,
features: { storageOptions },
current,
} = gridPlansIndex[ planSlug ];
const canUpgradeStorageForPlan = isStorageUpgradeableForPlan( {
intervalType,
showUpgradeableStorage,
storageOptions,
} );

const storageJSX =
canUpgradeStorageForPlan && availableForPurchase ? (
<StorageAddOnDropdown
planSlug={ planSlug }
onStorageAddOnClick={ onStorageAddOnClick }
storageOptions={ storageOptions }
/>
) : (
storageOptions.map( ( storageOption ) => {
if ( ! storageOption?.isAddOn ) {
return (
<div className="plan-features-2023-grid__storage-buttons" key={ planSlug }>
{ getStorageStringFromFeature( storageOption?.slug ) }
</div>
);
}
} )
);
const getAvailableStorageOptions = useGetAvailableStorageOptions();

if ( ! options?.isTableCell && isWpcomEnterpriseGridPlan( planSlug ) ) {
return null;
}

/**
* TODO: Consider centralising `canUpgradeStorageForPlan` behind `availableStorageOptions`
*/
const availableStorageOptions = getAvailableStorageOptions( { storageOptions } );
/**
* The current plan is not marked as `availableForPurchase`, hence check on `current`.
*/
const canUpgradeStorageForPlan =
( current || availableForPurchase ) &&
isStorageUpgradeableForPlan( {
intervalType,
showUpgradeableStorage,
storageOptions: availableStorageOptions,
} );

const storageJSX = canUpgradeStorageForPlan ? (
<StorageAddOnDropdown
planSlug={ planSlug }
onStorageAddOnClick={ onStorageAddOnClick }
storageOptions={ availableStorageOptions }
/>
) : (
storageOptions.map( ( storageOption ) => {
if ( ! storageOption?.isAddOn ) {
return <PlanStorageLabel storageOption={ storageOption } planSlug={ planSlug } />;
}
} )
);

return (
<div className="plan-features-2023-grid__storage">
<PlanFeaturesItem>{ storageJSX }</PlanFeaturesItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { 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';
import { getStorageStringFromFeature } from '../util';
import DropdownOption from './dropdown-option';
import { useStorageStringFromFeature } from './storage';
import type { PlanSlug, StorageOption, WPComStorageAddOnSlug } from '@automattic/calypso-products';
import type { AddOnMeta } from '@automattic/data-stores';

Expand All @@ -19,8 +19,9 @@ type StorageAddOnDropdownProps = {
};

type StorageAddOnOptionProps = {
title?: string;
planSlug: PlanSlug;
price?: string;
storageFeature: string;
isLargeCurrency?: boolean;
priceOnSeparateLine?: boolean;
};
Expand All @@ -35,16 +36,15 @@ const getStorageOptionPrice = (
};

const StorageAddOnOption = ( {
title,
planSlug,
price,
storageFeature,
isLargeCurrency = false,
priceOnSeparateLine,
}: StorageAddOnOptionProps ) => {
const translate = useTranslate();

if ( ! title ) {
return null;
}
const { siteId } = usePlansGridContext();
const title = useStorageStringFromFeature( { storageFeature, siteId, planSlug } ) ?? '';

return (
<>
Expand Down Expand Up @@ -114,28 +114,30 @@ export const StorageAddOnDropdown = ( {
}, [] );

const selectControlOptions = storageOptions.map( ( storageOption ) => {
const title = getStorageStringFromFeature( storageOption.slug ) || '';
const price = getStorageOptionPrice( storageAddOnsForPlan, storageOption.slug );
return {
key: storageOption.slug,
name: <StorageAddOnOption title={ title } price={ price } />,
name: (
<StorageAddOnOption
planSlug={ planSlug }
price={ getStorageOptionPrice( storageAddOnsForPlan, storageOption.slug ) }
storageFeature={ storageOption.slug }
/>
),
};
} );

const selectedOptionKey = selectedStorageOptionForPlan
? selectedStorageOptionForPlan
: defaultStorageOption;
const selectedOptionPrice =
selectedOptionKey && getStorageOptionPrice( storageAddOnsForPlan, selectedOptionKey );
const selectedOptionTitle =
( selectedOptionKey && getStorageStringFromFeature( selectedOptionKey ) ) || '';
const selectedOptionPrice = getStorageOptionPrice( storageAddOnsForPlan, selectedOptionKey );

const selectedOption = {
key: selectedOptionKey,
name: (
<StorageAddOnOption
title={ selectedOptionTitle }
planSlug={ planSlug }
price={ selectedOptionPrice }
storageFeature={ selectedOptionKey }
isLargeCurrency={ isLargeCurrency }
priceOnSeparateLine={ priceOnSeparateLine }
/>
Expand Down