diff --git a/packages/core/src/app/shipping/stripeUPE/StripeShipping.tsx b/packages/core/src/app/shipping/stripeUPE/StripeShipping.tsx index 1abfcb69e5..d540e55784 100644 --- a/packages/core/src/app/shipping/stripeUPE/StripeShipping.tsx +++ b/packages/core/src/app/shipping/stripeUPE/StripeShipping.tsx @@ -8,7 +8,6 @@ import { useCheckout } from '@bigcommerce/checkout/payment-integration-api'; import { AddressFormSkeleton } from '@bigcommerce/checkout/ui'; import type CheckoutStepStatus from '../../checkout/CheckoutStepStatus'; -import { EMPTY_ARRAY } from '../../common/utility'; import ShippingHeader from '../ShippingHeader'; import StripeShippingForm, { type SingleShippingFormValues } from './StripeShippingForm'; @@ -48,25 +47,17 @@ const StripeShipping = ({ isShippingMethodLoading, ...shippingFormProps }: StripeShippingProps): ReactNode => { - const { checkoutService, checkoutState } = useCheckout(); + const { checkoutState } = useCheckout(); const { data: { getCheckout, getCustomer, - getConsignments, getShippingAddressFields, - getShippingCountries, }, } = checkoutState; const checkout = getCheckout(); - const consignments = getConsignments() || []; const customer = getCustomer(); - - const initialize = checkoutService.initializeShipping; - const deinitialize = checkoutService.deinitializeShipping; - - const countries = getShippingCountries() || EMPTY_ARRAY; const getFields = getShippingAddressFields; const [isStripeLoading, setIsStripeLoading] = useState(true); @@ -101,13 +92,9 @@ const StripeShipping = ({ shouldShowMultiShipping={shouldShowMultiShipping} /> { const errorLogger = new ConsoleErrorLogger(); const { customFields, ...rest } = getShippingAddress(); const localeContext = createLocaleContext(getStoreConfig()); + let checkoutState: CheckoutSelectors; + + const initialize = jest.fn(); + checkoutService.initializeShipping = initialize; const defaultProps = { isShippingMethodLoading: false, @@ -42,7 +52,6 @@ describe('StripeShippingForm', () => { isBillingSameAsShipping: false, isInitialValueLoaded: false, isMultiShippingMode: false, - countries: [], countriesWithAutocomplete: [], shippingAddress: rest, customerMessage: '', @@ -55,9 +64,7 @@ describe('StripeShippingForm', () => { onSubmit: jest.fn(), getFields: jest.fn(() => addressFormFields), onUnhandledError: jest.fn(), - deinitialize: jest.fn(), signOut: jest.fn(), - initialize: jest.fn(), updateAddress: jest.fn(), deleteConsignments: jest.fn(), }; @@ -72,6 +79,12 @@ describe('StripeShippingForm', () => { ); + beforeEach(() => { + checkoutState = checkoutService.getState(); + jest.spyOn(checkoutState.data, 'getCustomer').mockReturnValue(getCustomer()); + jest.spyOn(checkoutState.data, 'getCheckout').mockReturnValue(getCheckout()); + }) + afterEach(() => { jest.clearAllMocks(); }) @@ -79,7 +92,7 @@ describe('StripeShippingForm', () => { it('renders form with a correct parameters', async () => { const { container } = renderContainer({ isLoading: false }); - expect(defaultProps.initialize).toHaveBeenCalled(); + expect(initialize).toHaveBeenCalled(); expect(defaultProps.getFields).toHaveBeenCalledTimes(2); expect(defaultProps.getFields).toHaveBeenCalledWith("US"); // eslint-disable-next-line testing-library/no-node-access,testing-library/no-container @@ -159,7 +172,7 @@ describe('StripeShippingForm', () => { renderContainer({ isLoading: false }); await act(async () => { - const { stripeupe } = defaultProps.initialize.mock.calls[0][0]; + const { stripeupe } = initialize.mock.calls[0][0]; await stripeupe.onChangeShipping(shippingChangeEvent); }); @@ -210,7 +223,7 @@ describe('StripeShippingForm', () => { renderContainer({ isLoading: false }); await act(async () => { - const { stripeupe } = defaultProps.initialize.mock.calls[0][0]; + const { stripeupe } = initialize.mock.calls[0][0]; await stripeupe.onChangeShipping(shippingChangeEvent); }); diff --git a/packages/core/src/app/shipping/stripeUPE/StripeShippingForm.tsx b/packages/core/src/app/shipping/stripeUPE/StripeShippingForm.tsx index 1b55418f84..9628db5df9 100644 --- a/packages/core/src/app/shipping/stripeUPE/StripeShippingForm.tsx +++ b/packages/core/src/app/shipping/stripeUPE/StripeShippingForm.tsx @@ -1,28 +1,23 @@ import { - type Address, - type CheckoutParams, - type CheckoutSelectors, - type Consignment, - type Country, - type FormField, - type RequestOptions, - type ShippingInitializeOptions, - type ShippingRequestOptions, + type Address, + type CheckoutParams, + type CheckoutSelectors, + type FormField, + type RequestOptions, } from '@bigcommerce/checkout-sdk'; import { type FormikProps } from 'formik'; import { noop } from 'lodash'; -import React, { PureComponent, type ReactNode } from 'react'; +import React, { useCallback, useState } from 'react'; import { lazy, object } from 'yup'; - import { withLanguage, type WithLanguageProps } from '@bigcommerce/checkout/locale'; -import { FormContext } from '@bigcommerce/checkout/ui'; import { - type AddressFormValues, - getAddressFormFieldsValidationSchema, - getTranslateAddressError, - mapAddressToFormValues, + type AddressFormValues, + getAddressFormFieldsValidationSchema, + getTranslateAddressError, + mapAddressToFormValues, } from '../../address'; + import type CheckoutStepStatus from '../../checkout/CheckoutStepStatus'; import { withFormikExtended } from '../../common/form'; import { getCustomFormFieldsValidationSchema } from '../../formFields'; @@ -32,192 +27,178 @@ import hasSelectedShippingOptions from '../hasSelectedShippingOptions'; import ShippingFormFooter from '../ShippingFormFooter'; import StripeShippingAddress from './StripeShippingAddress'; +import { useCheckout } from '@bigcommerce/checkout/payment-integration-api'; +import { EMPTY_ARRAY } from '../../common/utility'; export interface SingleShippingFormProps { - isBillingSameAsShipping: boolean; - cartHasChanged: boolean; - consignments: Consignment[]; - countries: Country[]; - customerMessage: string; - isLoading: boolean; - isShippingMethodLoading: boolean; - isMultiShippingMode: boolean; - methodId?: string; - shippingAddress?: Address; - shouldShowOrderComments: boolean; - step: CheckoutStepStatus; - isInitialValueLoaded: boolean; - isStripeLoading?(): void; - isStripeAutoStep?(): void; - deinitialize(options: ShippingRequestOptions): Promise; - getFields(countryCode?: string): FormField[]; - initialize(options: ShippingInitializeOptions): Promise; - onSubmit(values: SingleShippingFormValues): void; - onUnhandledError?(error: Error): void; - updateAddress( - address: Partial
, - options?: RequestOptions, - ): Promise; + isBillingSameAsShipping: boolean; + cartHasChanged: boolean; + customerMessage: string; + isLoading: boolean; + isShippingMethodLoading: boolean; + isMultiShippingMode: boolean; + methodId?: string; + shippingAddress?: Address; + shouldShowOrderComments: boolean; + step: CheckoutStepStatus; + isInitialValueLoaded: boolean; + isStripeLoading?(): void; + isStripeAutoStep?(): void; + getFields(countryCode?: string): FormField[]; + onSubmit(values: SingleShippingFormValues): void; + onUnhandledError?(error: Error): void; + updateAddress( + address: Partial
, + options?: RequestOptions, + ): Promise; } export interface SingleShippingFormValues { - billingSameAsShipping: boolean; - shippingAddress?: AddressFormValues; - orderComment: string; -} - -interface SingleShippingFormState { - isUpdatingShippingData: boolean; + billingSameAsShipping: boolean; + shippingAddress?: AddressFormValues; + orderComment: string; } -class StripeShippingForm extends PureComponent< - SingleShippingFormProps & WithLanguageProps & FormikProps - > { - static contextType = FormContext; - - state: SingleShippingFormState = { - isUpdatingShippingData: false, - }; - - render(): ReactNode { - const { - cartHasChanged, - isInitialValueLoaded, - isLoading, - countries, - isStripeLoading, - shippingAddress, - consignments, - shouldShowOrderComments, - initialize, - isValid, - deinitialize, - onSubmit, - isStripeAutoStep, - step, - isShippingMethodLoading, - } = this.props; - - const { isUpdatingShippingData } = this.state; - - return ( -
-
- -
- -
-
- - - - ); +const StripeShippingForm: React.FC> = (props) => { + const { checkoutService, checkoutState } = useCheckout(); + const { + data: { + getConsignments, + getShippingCountries, + }, + } = checkoutState; + + const consignments = getConsignments() || []; + const initialize = checkoutService.initializeShipping; + const deinitialize = checkoutService.deinitializeShipping; + const countries = getShippingCountries() || EMPTY_ARRAY; + + const [isUpdatingShippingData] = useState(false); + + const { + cartHasChanged, + isInitialValueLoaded, + isLoading, + isStripeLoading, + shippingAddress, + shouldShowOrderComments, + isValid, + onSubmit, + isStripeAutoStep, + step, + isShippingMethodLoading, + updateAddress, + onUnhandledError = noop, + values, + setValues, + getFields, + } = props; + + const shouldDisableSubmit = () => { + if (!isValid) { + return false; } - private shouldDisableSubmit: () => boolean = () => { - const { isLoading, consignments, isValid } = this.props; - - const { isUpdatingShippingData } = this.state; - - if (!isValid) { - return false; - } - - return isLoading || isUpdatingShippingData || !hasSelectedShippingOptions(consignments); - }; - - private handleAddressSelect: (address: Address) => void = async (address) => { - const { updateAddress, onUnhandledError = noop, values, setValues } = this.props; - - try { - await updateAddress(address); - - setValues({ - ...values, - shippingAddress: mapAddressToFormValues( - this.getFields(address.countryCode), - address, - ), - }); - } catch (error) { - onUnhandledError(error); - } - }; - - private getFields(countryCode: string | undefined): FormField[] { - const { getFields } = this.props; - - return getFields(countryCode); + return isLoading || isUpdatingShippingData || !hasSelectedShippingOptions(consignments); + }; + + const handleAddressSelect = useCallback(async (address: Address) => { + try { + await updateAddress(address); + + setValues({ + ...values, + shippingAddress: mapAddressToFormValues( + getFields(address.countryCode), + address, + ), + }); + } catch (error) { + onUnhandledError(error); } -} - + }, [values, setValues, onUnhandledError]); + + return ( +
+
+ + +
+ +
+
+ + + + ); +}; export default withLanguage( - withFormikExtended({ - handleSubmit: (values, { props: { onSubmit } }) => { - onSubmit(values); - }, - mapPropsToValues: ({ - getFields, - shippingAddress, - isBillingSameAsShipping, - customerMessage, - }) => ({ - billingSameAsShipping: isBillingSameAsShipping, - orderComment: customerMessage, - shippingAddress: mapAddressToFormValues( - getFields(shippingAddress && shippingAddress.countryCode), - shippingAddress, - ), - }), - isInitialValid: ({ shippingAddress, getFields, language }) => - !!shippingAddress && + withFormikExtended({ + handleSubmit: (values, { props: { onSubmit } }) => { + onSubmit(values); + }, + mapPropsToValues: ({ + getFields, + shippingAddress, + isBillingSameAsShipping, + customerMessage, + }) => ({ + billingSameAsShipping: isBillingSameAsShipping, + orderComment: customerMessage, + shippingAddress: mapAddressToFormValues( + getFields(shippingAddress && shippingAddress.countryCode), + shippingAddress, + ), + }), + isInitialValid: ({ shippingAddress, getFields, language }) => + !!shippingAddress && + getAddressFormFieldsValidationSchema({ + language, + formFields: getFields(shippingAddress.countryCode), + }).isValidSync(shippingAddress), + validationSchema: ({ + language, + getFields, + methodId, + }: SingleShippingFormProps & WithLanguageProps) => + methodId + ? object({ + shippingAddress: lazy>((formValues) => + getCustomFormFieldsValidationSchema({ + translate: getTranslateAddressError(language), + formFields: getFields(formValues && formValues.countryCode), + }), + ), + }) + : object({ + shippingAddress: lazy>((formValues) => getAddressFormFieldsValidationSchema({ - language, - formFields: getFields(shippingAddress.countryCode), - }).isValidSync(shippingAddress), - validationSchema: ({ - language, - getFields, - methodId, - }: SingleShippingFormProps & WithLanguageProps) => - methodId - ? object({ - shippingAddress: lazy>((formValues) => - getCustomFormFieldsValidationSchema({ - translate: getTranslateAddressError(language), - formFields: getFields(formValues && formValues.countryCode), - }), - ), - }) - : object({ - shippingAddress: lazy>((formValues) => - getAddressFormFieldsValidationSchema({ - language, - formFields: getFields(formValues && formValues.countryCode), - }), - ), - }), - enableReinitialize: false, - })(StripeShippingForm), + language, + formFields: getFields(formValues && formValues.countryCode), + }), + ), + }), + enableReinitialize: false, + })(StripeShippingForm), );