diff --git a/client/me/purchases/add-new-payment-method/index.jsx b/client/me/purchases/add-new-payment-method/index.jsx index 148d81891d3b3b..7a5401923f5976 100644 --- a/client/me/purchases/add-new-payment-method/index.jsx +++ b/client/me/purchases/add-new-payment-method/index.jsx @@ -26,7 +26,7 @@ import Column from 'calypso/components/layout/column'; import PaymentMethodSidebar from 'calypso/me/purchases/components/payment-method-sidebar'; import { isEnabled } from '@automattic/calypso-config'; import PaymentMethodSelector from 'calypso/me/purchases/manage-purchase/payment-method-selector'; -import { useCreateCreditCard } from 'calypso/my-sites/checkout/composite-checkout/use-create-payment-methods'; +import { useCreateCreditCard } from 'calypso/my-sites/checkout/composite-checkout/hooks/use-create-payment-methods'; import PaymentMethodLoader from 'calypso/me/purchases/components/payment-method-loader'; function AddNewPaymentMethod() { diff --git a/client/me/purchases/manage-purchase/change-payment-method/use-create-assignable-payment-methods.ts b/client/me/purchases/manage-purchase/change-payment-method/use-create-assignable-payment-methods.ts index 352f312bee38c7..c88d33e4f84682 100644 --- a/client/me/purchases/manage-purchase/change-payment-method/use-create-assignable-payment-methods.ts +++ b/client/me/purchases/manage-purchase/change-payment-method/use-create-assignable-payment-methods.ts @@ -15,7 +15,7 @@ import { useCreateCreditCard, useCreateExistingCards, useCreatePayPal, -} from 'calypso/my-sites/checkout/composite-checkout/use-create-payment-methods'; +} from 'calypso/my-sites/checkout/composite-checkout/hooks/use-create-payment-methods'; import { translateCheckoutPaymentMethodToWpcomPaymentMethod } from 'calypso/my-sites/checkout/composite-checkout/lib/translate-payment-method-names'; import doesValueExist from 'calypso/my-sites/checkout/composite-checkout/lib/does-value-exist'; import useFetchAvailablePaymentMethods from './use-fetch-available-payment-methods'; @@ -52,7 +52,7 @@ export default function useCreateAssignablePaymentMethods( const storedCards = useSelector( getStoredCards ); const existingCardMethods = useCreateExistingCards( { storedCards, - activePayButtonText: translate( 'Use this card' ), + activePayButtonText: String( translate( 'Use this card' ) ), } ); const paymentMethods = useMemo( diff --git a/client/my-sites/checkout/composite-checkout/composite-checkout.tsx b/client/my-sites/checkout/composite-checkout/composite-checkout.tsx index a1988f0e6a1121..8c4b795aa795cd 100644 --- a/client/my-sites/checkout/composite-checkout/composite-checkout.tsx +++ b/client/my-sites/checkout/composite-checkout/composite-checkout.tsx @@ -50,7 +50,7 @@ import useIsApplePayAvailable from './hooks/use-is-apple-pay-available'; import filterAppropriatePaymentMethods from './lib/filter-appropriate-payment-methods'; import useStoredCards from './hooks/use-stored-cards'; import usePrepareProductsForCart from './hooks/use-prepare-products-for-cart'; -import useCreatePaymentMethods from './use-create-payment-methods'; +import useCreatePaymentMethods from './hooks/use-create-payment-methods'; import { applePayProcessor, freePurchaseProcessor, diff --git a/client/my-sites/checkout/composite-checkout/use-create-payment-methods.js b/client/my-sites/checkout/composite-checkout/hooks/use-create-payment-methods/index.js similarity index 81% rename from client/my-sites/checkout/composite-checkout/use-create-payment-methods.js rename to client/my-sites/checkout/composite-checkout/hooks/use-create-payment-methods/index.js index 99e9c5cf916453..4a68828d9d55bd 100644 --- a/client/my-sites/checkout/composite-checkout/use-create-payment-methods.js +++ b/client/my-sites/checkout/composite-checkout/hooks/use-create-payment-methods/index.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { useMemo, useRef, useEffect } from 'react'; +import { useMemo } from 'react'; import { createPayPalMethod, createAlipayMethod, @@ -19,33 +19,35 @@ import { createEpsMethod, createEpsPaymentMethodStore, createApplePayMethod, - createExistingCardMethod, } from '@automattic/composite-checkout'; import { useShoppingCart } from '@automattic/shopping-cart'; /** * Internal dependencies */ -import { createWeChatMethod, createWeChatPaymentMethodStore } from './payment-methods/wechat'; +import { createWeChatMethod, createWeChatPaymentMethodStore } from '../../payment-methods/wechat'; import { createCreditCardPaymentMethodStore, createCreditCardMethod, -} from './payment-methods/credit-card'; +} from '../../payment-methods/credit-card'; import { createEbanxTefPaymentMethodStore, createEbanxTefMethod, -} from './payment-methods/ebanx-tef'; +} from '../../payment-methods/ebanx-tef'; import { createIdWalletPaymentMethodStore, createIdWalletMethod, -} from './payment-methods/id-wallet'; +} from '../../payment-methods/id-wallet'; import { createNetBankingPaymentMethodStore, createNetBankingMethod, -} from './payment-methods/netbanking'; -import { createFullCreditsMethod } from './payment-methods/full-credits'; -import { createFreePaymentMethod } from './payment-methods/free-purchase'; +} from '../../payment-methods/netbanking'; +import { createFullCreditsMethod } from '../../payment-methods/full-credits'; +import { createFreePaymentMethod } from '../../payment-methods/free-purchase'; import { translateCheckoutPaymentMethodToWpcomPaymentMethod } from 'calypso/my-sites/checkout/composite-checkout/lib/translate-payment-method-names'; +import useCreateExistingCards from './use-create-existing-cards'; + +export { useCreateExistingCards }; export function useCreatePayPal( { labelText = null } = {} ) { const paypalMethod = useMemo( () => createPayPalMethod( { labelText } ), [ labelText ] ); @@ -288,59 +290,6 @@ function useCreateApplePay( { return applePayMethod; } -// See https://usehooks.com/useMemoCompare/ -function useMemoCompare( next, compare ) { - // Ref for storing previous value - const previousRef = useRef(); - const previous = previousRef.current; - - // Pass previous and next value to compare function - // to determine whether to consider them equal. - const isEqual = compare( previous, next ); - - // If not equal update previousRef to next value. - // We only update if not equal so that this hook continues to return - // the same old value if compare keeps returning true. - useEffect( () => { - if ( ! isEqual ) { - previousRef.current = next; - } - } ); - - // Finally, if equal then return the previous value - return isEqual ? previous : next; -} - -export function useCreateExistingCards( { storedCards, activePayButtonText = undefined } ) { - // Memoize the cards by comparing their stored_details_id values, in case the - // objects are recreated on each render. - const memoizedStoredCards = useMemoCompare( storedCards, ( prev, next ) => { - const prevIds = prev?.map( ( card ) => card.stored_details_id ) ?? []; - const nextIds = next?.map( ( card ) => card.stored_details_id ) ?? []; - return ( - prevIds.length === nextIds.length && prevIds.every( ( id, index ) => id === nextIds[ index ] ) - ); - } ); - const existingCardMethods = useMemo( () => { - return ( - memoizedStoredCards?.map( ( storedDetails ) => - createExistingCardMethod( { - id: `existingCard-${ storedDetails.stored_details_id }`, - cardholderName: storedDetails.name, - cardExpiry: storedDetails.expiry, - brand: storedDetails.card_type, - last4: storedDetails.card, - storedDetailsId: storedDetails.stored_details_id, - paymentMethodToken: storedDetails.mp_ref, - paymentPartnerProcessorId: storedDetails.payment_partner, - activePayButtonText, - } ) - ) ?? [] - ); - }, [ memoizedStoredCards, activePayButtonText ] ); - return existingCardMethods; -} - export default function useCreatePaymentMethods( { isStripeLoading, stripeLoadingError, diff --git a/client/my-sites/checkout/composite-checkout/hooks/use-create-payment-methods/use-create-existing-cards.ts b/client/my-sites/checkout/composite-checkout/hooks/use-create-payment-methods/use-create-existing-cards.ts new file mode 100644 index 00000000000000..1afb6049906b55 --- /dev/null +++ b/client/my-sites/checkout/composite-checkout/hooks/use-create-payment-methods/use-create-existing-cards.ts @@ -0,0 +1,53 @@ +/** + * External dependencies + */ +import { useMemo } from 'react'; +import { createExistingCardMethod } from '@automattic/composite-checkout'; +import type { PaymentMethod } from '@automattic/composite-checkout'; + +/** + * Internal dependencies + */ +import useMemoCompare from '../use-memo-compare'; +import type { StoredCard } from '../../types/stored-cards'; + +export default function useCreateExistingCards( { + storedCards, + activePayButtonText = undefined, +}: { + storedCards: StoredCard[]; + activePayButtonText?: string; +} ): PaymentMethod[] { + // Memoize the cards by comparing their stored_details_id values, in case the + // objects themselves are recreated on each render. + const memoizedStoredCards: StoredCard[] | undefined = useMemoCompare( + storedCards, + ( prev: undefined | StoredCard[], next: undefined | StoredCard[] ) => { + const prevIds = prev?.map( ( card ) => card.stored_details_id ) ?? []; + const nextIds = next?.map( ( card ) => card.stored_details_id ) ?? []; + return ( + prevIds.length === nextIds.length && + prevIds.every( ( id, index ) => id === nextIds[ index ] ) + ); + } + ); + + const existingCardMethods = useMemo( () => { + return ( + memoizedStoredCards?.map( ( storedDetails ) => + createExistingCardMethod( { + id: `existingCard-${ storedDetails.stored_details_id }`, + cardholderName: storedDetails.name, + cardExpiry: storedDetails.expiry, + brand: storedDetails.card_type, + last4: storedDetails.card, + storedDetailsId: storedDetails.stored_details_id, + paymentMethodToken: storedDetails.mp_ref, + paymentPartnerProcessorId: storedDetails.payment_partner, + activePayButtonText, + } ) + ) ?? [] + ); + }, [ memoizedStoredCards, activePayButtonText ] ); + return existingCardMethods; +} diff --git a/client/my-sites/checkout/composite-checkout/hooks/use-memo-compare.ts b/client/my-sites/checkout/composite-checkout/hooks/use-memo-compare.ts new file mode 100644 index 00000000000000..0b59c49c9e5d39 --- /dev/null +++ b/client/my-sites/checkout/composite-checkout/hooks/use-memo-compare.ts @@ -0,0 +1,30 @@ +/** + * External dependencies + */ +import { useRef, useEffect } from 'react'; + +// See https://usehooks.com/useMemoCompare/ +export default function useMemoCompare< A, B >( + next: B, + compare: ( previous: A | B | undefined, next: B ) => boolean +): A | B | undefined { + // Ref for storing previous value + const previousRef = useRef< undefined | A | B >(); + const previous = previousRef.current; + + // Pass previous and next value to compare function + // to determine whether to consider them equal. + const isEqual = compare( previous, next ); + + // If not equal update previousRef to next value. + // We only update if not equal so that this hook continues to return + // the same old value if compare keeps returning true. + useEffect( () => { + if ( ! isEqual ) { + previousRef.current = next; + } + } ); + + // Finally, if equal then return the previous value + return isEqual ? previous : next; +} diff --git a/client/my-sites/purchases/payment-methods/index.tsx b/client/my-sites/purchases/payment-methods/index.tsx index 5e5d0af524c1e1..98bb7762afe096 100644 --- a/client/my-sites/purchases/payment-methods/index.tsx +++ b/client/my-sites/purchases/payment-methods/index.tsx @@ -30,7 +30,7 @@ import Layout from 'calypso/components/layout'; import Column from 'calypso/components/layout/column'; import PaymentMethodSidebar from 'calypso/me/purchases/components/payment-method-sidebar'; import PaymentMethodSelector from 'calypso/me/purchases/manage-purchase/payment-method-selector'; -import { useCreateCreditCard } from 'calypso/my-sites/checkout/composite-checkout/use-create-payment-methods'; +import { useCreateCreditCard } from 'calypso/my-sites/checkout/composite-checkout/hooks/use-create-payment-methods'; import PaymentMethodLoader from 'calypso/me/purchases/components/payment-method-loader'; import doesValueExist from 'calypso/my-sites/checkout/composite-checkout/lib/does-value-exist'; diff --git a/packages/composite-checkout/src/types.ts b/packages/composite-checkout/src/types.ts index 60e21cc3e1c8e9..6bcf3256e9960e 100644 --- a/packages/composite-checkout/src/types.ts +++ b/packages/composite-checkout/src/types.ts @@ -33,9 +33,9 @@ export interface OrderSummaryData { export interface PaymentMethod { id: string; - label: React.ReactNode; - activeContent: React.ReactNode; - inactiveContent: React.ReactNode; + label?: React.ReactNode; + activeContent?: React.ReactNode; + inactiveContent?: React.ReactNode; submitButton: ReactElement; getAriaLabel: ( localize: ( value: string ) => string ) => string; }