diff --git a/.secrets.baseline b/.secrets.baseline index df019452129..cca3e77612b 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -166,15 +166,6 @@ "line_number": 512 } ], - "src/Apps/Order/Routes/Shipping2/index.tsx": [ - { - "type": "Base64 High Entropy String", - "filename": "src/Apps/Order/Routes/Shipping2/index.tsx", - "hashed_secret": "a215ca0c1670592780526439f4e0642af430072d", - "is_verified": false, - "line_number": 23 - } - ], "src/Apps/ViewingRoom/Routes/Statement/__tests__/ViewingRoomStatementRoute.jest.tsx": [ { "type": "Base64 High Entropy String", diff --git a/src/Apps/Order/Routes/Shipping2/Components/AddressModal2.tsx b/src/Apps/Order/Routes/Shipping2/Components/AddressModal2.tsx index 823b8967aec..e84a0ed030e 100644 --- a/src/Apps/Order/Routes/Shipping2/Components/AddressModal2.tsx +++ b/src/Apps/Order/Routes/Shipping2/Components/AddressModal2.tsx @@ -1,5 +1,4 @@ -import { useState } from "react" -import * as React from "react" +import { useState, FC } from "react" import * as Yup from "yup" import { Button, @@ -11,287 +10,395 @@ import { Spacer, Text, Banner, + GridColumns, + Column, } from "@artsy/palette" -import { Formik, FormikHelpers, FormikProps } from "formik" -import { AddressModalFields } from "Components/Address/AddressModalFields" +import { Form, Formik, FormikHelpers, useFormikContext } from "formik" import { ADDRESS_VALIDATION_SHAPE, - SavedAddressType, -} from "Apps/Order/Utils/shippingUtils" -import { addressWithFallbackValues } from "Apps/Order/Routes/Shipping2/Utils/shippingUtils" -import { useCreateSavedAddressMutation$data } from "__generated__/useCreateSavedAddressMutation.graphql" -import { useUpdateSavedAddressMutation$data } from "__generated__/useUpdateSavedAddressMutation.graphql" + addressWithFallbackValues, +} from "Apps/Order/Routes/Shipping2/Utils/shippingUtils" import createLogger from "Utils/logger" -import { useCreateSavedAddress } from "Apps/Order/Routes/Shipping2/Mutations/useCreateSavedAddress" -import { useDeleteSavedAddress } from "Apps/Order/Routes/Shipping2/Mutations/useDeleteSavedAddress" -import { useUpdateSavedAddress } from "Apps/Order/Routes/Shipping2/Mutations/useUpdateSavedAddress" -import { useUpdateUserDefaultAddress } from "Apps/Order/Routes/Shipping2/Mutations/useUpdateUserDefaultAddress" import { useShippingContext } from "Apps/Order/Routes/Shipping2/Hooks/useShippingContext" +import { SavedAddressType } from "Apps/Order/Utils/shippingUtils" +import { + SavedAddressResult, + UserAddressAction, + useUserAddressUpdates, +} from "Apps/Order/Routes/Shipping2/Hooks/useUserAddressUpdates" +import { CountrySelect } from "Components/CountrySelect" + +const logger = createLogger("AddressModal2.tsx") + +/** + * Modal type to be rendered. the `address` property is used + * when the modal is in edit mode as the starting values. + */ +export type AddressModalAction = + | { type: "create" } + | { type: "edit"; address: SavedAddressType } export interface AddressModalProps { closeModal: () => void - onSuccess: (addressID: string) => void - modalAction: AddressModalAction | null + onSuccess: (address: SavedAddressType) => void + addressModalAction: AddressModalAction | null +} + +interface FormValues { + attributes: { + name: string + phoneNumber: string + isDefault?: boolean + addressLine1: string + addressLine2?: string + city: string + region?: string + postalCode?: string + country: string + } + setAsDefault: boolean } -export const AddressModal: React.FC = ({ +export const AddressModal: FC = ({ closeModal, + addressModalAction, onSuccess, - modalAction, }) => { const logger = createLogger("AddressModal2.tsx") - - const [createUpdateError, setCreateUpdateError] = useState( - null - ) - const [showDialog, setShowDialog] = useState(false) const shippingContext = useShippingContext() - const createSavedAddress = useCreateSavedAddress().submitMutation - const deleteSavedAddress = useDeleteSavedAddress().submitMutation - const updateSavedAddress = useUpdateSavedAddress().submitMutation - const updateUserDefaultAddress = useUpdateUserDefaultAddress().submitMutation - - if (!modalAction) { - return null - } - - const title = (modalAction && MODAL_TITLE_MAP[modalAction.type]) || "" - - const initialAddress = - modalAction.type === "editUserAddress" - ? modalAction.address - : { - // TODO: Instead of using ShippingContext, initialValues could be a shippingUtils function - country: shippingContext.orderData.shipsFrom, - internalID: undefined, - isDefault: false, - } - - const handleModalClose = () => { - closeModal() - setCreateUpdateError(null) - } - - const handleErrors = ( - errors: ReadonlyArray<{ message: string }>, - formikHelpers - ) => { - if (!errors?.length) return + const { executeUserAddressAction } = useUserAddressUpdates() - const userMessage: Record | null = - SERVER_ERROR_MAP[errors[0].message] + let initialValues: FormValues - if (userMessage) { - formikHelpers.setFieldError(userMessage.field, userMessage.message) - } else { - setCreateUpdateError(GENERIC_FAIL_MESSAGE) + if (!addressModalAction) { + initialValues = { + attributes: { + isDefault: false, + ...addressWithFallbackValues({}), + }, + setAsDefault: false, } - - formikHelpers?.setSubmitting(false) - logger.error(errors.map(error => error.message).join(", ")) - } - - const handleDeleteAddress = async (addressID: string) => { - try { - return deleteSavedAddress({ - variables: { - input: { userAddressID: addressID }, - }, - }) - } catch (error) { - logger.error(error) - } - } - - const handleMutationPayload = ( - payload: - | useUpdateSavedAddressMutation$data["updateUserAddress"] - | useCreateSavedAddressMutation$data["createUserAddress"] - ): - | { data: SavedAddressType; errors: null } - | { data: null; errors: ReadonlyArray<{ message: string }> } => { - const addressOrErrors = payload?.userAddressOrErrors - - if (addressOrErrors?.__typename === "Errors") { - return { - errors: addressOrErrors.errors, - data: null, - } - } - return { - errors: null, - data: addressOrErrors as SavedAddressType, + } else { + const incomingAddress = + addressModalAction.type === "edit" ? addressModalAction.address : null + + initialValues = { + attributes: { + isDefault: incomingAddress?.isDefault ?? false, + + ...addressWithFallbackValues( + addressModalAction.type === "edit" ? addressModalAction.address : {} + ), + }, + setAsDefault: false, } } const handleSubmit = async ( - values: SavedAddressType, - helpers: FormikHelpers + values: FormValues, + helpers: FormikHelpers ) => { - const addressInput = addressWithFallbackValues(values) - - let operation: () => Promise> - - try { - shippingContext.actions.setIsPerformingOperation(true) - - if (modalAction.type === "createUserAddress") { - operation = async () => { - const result = await createSavedAddress({ - variables: { - input: { attributes: addressInput }, - }, - }) + if (!addressModalAction) { + return + } - return handleMutationPayload(result.createUserAddress) + let userAddressAction: UserAddressAction + switch (addressModalAction.type) { + case "edit": + userAddressAction = { + type: "edit", + address: { + ...values.attributes, + internalID: addressModalAction.address.internalID, + }, + setAsDefault: values.setAsDefault, } - } else { - operation = async () => { - const result = await updateSavedAddress({ - variables: { - input: { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - userAddressID: initialAddress.internalID!, - attributes: addressInput, - }, - }, - }) - - return handleMutationPayload(result.updateUserAddress) + break + case "create": + userAddressAction = { + type: "create", + address: { + ...values.attributes, + }, + setAsDefault: values.setAsDefault, } - } + break + default: + throw new Error("Invalid address modal action") + } - const { data, errors } = await operation() + try { + shippingContext.actions.setIsPerformingOperation(true) + const result = await executeUserAddressAction(userAddressAction) - if (errors) { - handleErrors(errors, helpers) + if (result.errors) { + handleGravityErrors(result.errors, helpers) + closeModal() return } + onSuccess(result.data) + closeModal() + } catch (error) { + logger.error(error) - const savedAddressID = data?.internalID + helpers.setStatus(GENERIC_FAIL_MESSAGE) + } finally { + shippingContext.actions.setIsPerformingOperation(false) + } + } - if ( - !!savedAddressID && - values?.isDefault && - values?.isDefault !== initialAddress?.isDefault - ) { - const updateAddressResult = await updateUserDefaultAddress({ - variables: { - input: { userAddressID: savedAddressID }, - }, - }) + const handleDeleteAddress = async (address: SavedAddressType) => { + return await executeUserAddressAction({ + type: "delete", + address: address, + }) + } - const updateAddressPayload = - updateAddressResult.updateUserDefaultAddress?.userAddressOrErrors + return ( + <> + + validateOnMount + validationSchema={validationSchema} + enableReinitialize={true} + initialValues={initialValues} + onSubmit={handleSubmit} + > + { + + } + + + ) +} - if (updateAddressPayload?.__typename === "Errors") { - logger.error( - updateAddressPayload.errors.map(error => error.message).join(", ") - ) +const AddressModalForm: FC<{ + onClose: () => void + addressModalAction: AddressModalProps["addressModalAction"] + onDeleteAddress: (address: SavedAddressType) => Promise +}> = ({ addressModalAction, onClose, onDeleteAddress }) => { + const shippingContext = useShippingContext() + const formikContext = useFormikContext() + const [showDeleteDialog, setShowDeleteDialog] = useState(false) - return - } - } + if (!addressModalAction) { + return null + } - onSuccess(savedAddressID) - setCreateUpdateError(null) - closeModal() - } catch (error) { - handleErrors([error], helpers) - } finally { - shippingContext.actions.setIsPerformingOperation(false) + const handleDeleteAddress = async () => { + if (addressModalAction?.type === "edit") { + try { + shippingContext.actions.setIsPerformingOperation(true) + await onDeleteAddress(addressModalAction.address) + } catch (error) { + logger.error(error) + } finally { + shippingContext.actions.setIsPerformingOperation(false) + setShowDeleteDialog(false) + onClose() + } } } - if (createUpdateError) { - logger.log({ createUpdateError }) + const { + values, + touched, + errors, + handleChange, + handleBlur, + setFieldValue, + } = formikContext + + const handleModalClose = () => { + formikContext.resetForm() + onClose() } + const title = + addressModalAction.type === "create" ? "Add address" : "Edit address" + return ( <> - - {(formik: FormikProps) => ( -
- {createUpdateError && ( - - {createUpdateError} - - )} - - - - + + {formikContext.status && ( + + {formikContext.status} + + )} + + + + + + { + setFieldValue("attributes.country", countryCode) + }} + error={ + touched.attributes?.country && errors.attributes?.country + ? errors.attributes.country + : "" + } + /> + + + + + + + + + + + + + + + + - - - {!initialAddress?.isDefault && ( - { - formik.setFieldValue("isDefault", selected) - }} - selected={formik.values?.isDefault} - data-test="setAsDefault" - > - Set as default - - )} - - {modalAction.type === "editUserAddress" && ( - - setShowDialog(true)} - > - - Delete address - - - - )} - - - + + Delete address + + + )} -
-
- {showDialog && ( + + + + {showDeleteDialog && ( setShowDialog(false)} + onClose={() => setShowDeleteDialog(false)} width="350px" > @@ -304,23 +411,14 @@ export const AddressModal: React.FC = ({ - @@ -330,37 +428,45 @@ export const AddressModal: React.FC = ({ ) } -export enum AddressModalActionType { - EDIT_USER_ADDRESS = "editUserAddress", - CREATE_USER_ADDRESS = "createUserAddress", -} - -export type AddressModalAction = - | { - type: AddressModalActionType.CREATE_USER_ADDRESS - } - | { - type: AddressModalActionType.EDIT_USER_ADDRESS - address: SavedAddressType - } - -const MODAL_TITLE_MAP: Record = { - createUserAddress: "Add address", - editUserAddress: "Edit address", -} - +// two different error messages for the same error? const SERVER_ERROR_MAP: Record> = { "Validation failed for phone: not a valid phone number": { - field: "phoneNumber", + field: "attributes.phoneNumber", message: "Please enter a valid phone number", }, "Validation failed: Phone not a valid phone number": { - field: "phoneNumber", + field: "attributes.phoneNumber", message: "Please enter a valid phone number", }, } +const validationSchema = Yup.object().shape({ + attributes: Yup.object().shape(ADDRESS_VALIDATION_SHAPE), +}) + export const GENERIC_FAIL_MESSAGE = "Sorry, there has been an issue saving your address. Please try again." -const validationSchema = Yup.object().shape(ADDRESS_VALIDATION_SHAPE) +const handleGravityErrors = ( + errors: ReadonlyArray<{ message: string }>, + helpers: { + setFieldError: (field: string, message: string) => void + setStatus: (message: string) => void + } +) => { + if (!errors?.length) return + + const userMessage: Record | null = + SERVER_ERROR_MAP[errors[0].message] + + if (userMessage) { + helpers.setFieldError( + `attributes.${userMessage.field}`, + userMessage.message + ) + } else { + helpers.setStatus(GENERIC_FAIL_MESSAGE) + } + + logger.error(errors.map(error => error.message).join(", ")) +} diff --git a/src/Apps/Order/Routes/Shipping2/Components/FulfillmentDetails.tsx b/src/Apps/Order/Routes/Shipping2/Components/FulfillmentDetails.tsx index b4071e1efbd..8554fed61e0 100644 --- a/src/Apps/Order/Routes/Shipping2/Components/FulfillmentDetails.tsx +++ b/src/Apps/Order/Routes/Shipping2/Components/FulfillmentDetails.tsx @@ -1,12 +1,10 @@ import { FulfillmentDetailsForm_order$key } from "__generated__/FulfillmentDetailsForm_order.graphql" -import { FC, useEffect, useRef, useState } from "react" +import { FC, useEffect, useState } from "react" import { graphql, useFragment } from "react-relay" +import { FormikHelpers } from "formik" import { extractNodes } from "Utils/extractNodes" import { useFeatureFlag } from "System/useFeatureFlag" -import { - AddressFormMode, - FulfillmentDetailsForm, -} from "Apps/Order/Routes/Shipping2/Components/FulfillmentDetailsForm" +import { FulfillmentDetailsForm } from "Apps/Order/Routes/Shipping2/Components/FulfillmentDetailsForm" import { FulfillmentType, FulfillmentValues, @@ -19,13 +17,12 @@ import { FulfillmentDetailsForm_me$key, } from "__generated__/FulfillmentDetailsForm_me.graphql" import createLogger from "Utils/logger" -import { useSaveFulfillmentDetails } from "Apps/Order/Routes/Shipping2/Mutations/useSaveFulfillmentDetails" -import { CommerceSetShippingInput } from "__generated__/useSaveFulfillmentDetailsMutation.graphql" import { useShippingContext } from "Apps/Order/Routes/Shipping2/Hooks/useShippingContext" import { ShippingContextProps } from "Apps/Order/Routes/Shipping2/ShippingContext" -import { useHandleUserAddressUpdates } from "Apps/Order/Routes/Shipping2/Hooks/useHandleUserAddressUpdates" +import { useUserAddressUpdates } from "Apps/Order/Routes/Shipping2/Hooks/useUserAddressUpdates" import { useRouter } from "System/Router/useRouter" import { useOrderTracking } from "Apps/Order/Hooks/useOrderTracking" +import { useHandleSaveFulfillmentDetails } from "Apps/Order/Routes/Shipping2/Hooks/useHandleSaveFulfillmentDetails" const logger = createLogger("Routes/Shipping2/FulfillmentDetails.tsx") @@ -40,23 +37,11 @@ export const FulfillmentDetails: FC = ({ }) => { const meData = useFragment(ME_FRAGMENT, me) const orderData = useFragment(ORDER_FRAGMENT, order) - const { router } = useRouter() const shippingContext = useShippingContext() - const saveFulfillmentDetails = useSaveFulfillmentDetails() - const { handleUserAddressUpdates } = useHandleUserAddressUpdates() const orderTracking = useOrderTracking() - - const savedAddresses = extractNodes(meData.addressConnection) - const hasSavedAddresses = !!savedAddresses.length - - // Note: Trigger address verification by setting this to true - const [verifyAddressNow, setVerifyAddressNow] = useState(false) - - // Once the user sees the address form, they should always see it. - const [forceNewAddressFormMode, setForceNewAddressFormMode] = useState( - !hasSavedAddresses - ) + const { handleNewUserAddressUpdates } = useUserAddressUpdates() + const { handleSaveFulfillmentDetails } = useHandleSaveFulfillmentDetails() const addressVerificationUSEnabled = !!useFeatureFlag( "address_verification_us" @@ -65,48 +50,72 @@ export const FulfillmentDetails: FC = ({ "address_verification_intl" ) - const shippingMode: Exclude = - forceNewAddressFormMode || savedAddresses.length === 0 - ? "new_address" - : "saved_addresses" + // Trigger address verification by setting this to true + const [verifyAddressNow, setVerifyAddressNow] = useState(false) + const savedAddresses = extractNodes(meData.addressConnection) + const hasSavedAddresses = savedAddresses.length > 0 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const firstArtwork = extractNodes(orderData.lineItems)[0]!.artwork! + const initialValues = getInitialValues(meData, shippingContext.orderData) + const availableFulfillmentTypes: FulfillmentType[] = firstArtwork.pickupAvailable ? [FulfillmentType.PICKUP, FulfillmentType.SHIP] : [FulfillmentType.SHIP] - // Only process once on load - const initialValues = useRef( - getInitialValues(meData, shippingContext.orderData) - ).current - /** * Effects */ + /* + * If the view ever has no saved addresses, force new address form mode for + * the rest of its life + */ useEffect(() => { - if (!forceNewAddressFormMode && !hasSavedAddresses) { - setForceNewAddressFormMode(true) + if ( + shippingContext.state.shippingFormMode !== "new_address" && + !hasSavedAddresses + ) { + shippingContext.actions.setShippingFormMode("new_address") } - }, [forceNewAddressFormMode, hasSavedAddresses]) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [hasSavedAddresses]) - // Force-re-save fulfillment details with existing values to refresh shipping quotes + /* + * Re-save fulfillment details on load if they are already saved + * and shipping quotes need refreshing for new address mode only + */ useEffect(() => { const existingFulfillmentDetails = shippingContext.orderData.savedFulfillmentDetails if ( - shippingContext.state.stage === "refresh_shipping_quotes" && - existingFulfillmentDetails?.fulfillmentType === FulfillmentType.SHIP + existingFulfillmentDetails?.fulfillmentType === FulfillmentType.SHIP && + shippingContext.orderData.requiresArtsyShippingTo( + existingFulfillmentDetails.attributes.country + ) ) { - submitFulfillmentDetails({ - performUserAddressUpdates: false, - formValues: { - attributes: existingFulfillmentDetails.fulfillmentDetails as ShipValues["attributes"], - fulfillmentType: existingFulfillmentDetails?.fulfillmentType, - }, - }) + const refreshShippingQuotes = async () => { + // instead of handleSubmit, call the save fulfillment details function + // directly + const result = await handleSaveFulfillmentDetails({ + attributes: existingFulfillmentDetails.attributes, + fulfillmentType: FulfillmentType.SHIP, + meta: { + // FIXME: Will clobber previous address verification (but we can't + // know what the previous status was unless we read from server) + addressVerifiedBy: null, + }, + }) + + shippingContext.actions.setIsPerformingOperation(false) + + if (result) { + shippingContext.actions.setStage("shipping_quotes") + } + } + + refreshShippingQuotes() } // eslint-disable-next-line react-hooks/exhaustive-deps }, []) @@ -114,20 +123,6 @@ export const FulfillmentDetails: FC = ({ /** * Handlers */ - - const handleFulfillmentDetailsSaved = ({ - requiresArtsyShipping, - }: { - requiresArtsyShipping: boolean - }) => { - if (requiresArtsyShipping) { - shippingContext.actions.setStage("shipping_quotes") - } else { - // Advance to payment - router.push(`/orders/${orderData.internalID}/payment`) - } - } - const shouldVerifyAddressOnSubmit = (values: FulfillmentValues) => { const enabledForAddress = (values as ShipValues).attributes.country === "US" @@ -136,9 +131,9 @@ export const FulfillmentDetails: FC = ({ return ( values.fulfillmentType === FulfillmentType.SHIP && - !hasSavedAddresses && + shippingContext.state.shippingFormMode === "new_address" && enabledForAddress && - values.attributes.addressVerifiedBy === null + values.meta.addressVerifiedBy === null ) } @@ -146,82 +141,90 @@ export const FulfillmentDetails: FC = ({ setVerifyAddressNow(false) } - const submitFulfillmentDetails = async ({ - performUserAddressUpdates, - formValues, - }: { - performUserAddressUpdates: boolean - formValues: FulfillmentValues - }) => { + /* + * Handle form submission including address verification, saved address updates + * and saving fulfillment details to order + */ + const handleSubmit = async ( + values: FulfillmentValues, + _helpers: FormikHelpers + ) => { + // Trigger address verification and return early if appropriate + if (shouldVerifyAddressOnSubmit(values)) { + setVerifyAddressNow(true) + return + } + + const resetSelectedSavedAddress = () => { + if ( + shippingContext.state.shippingFormMode === "saved_addresses" && + shippingContext.state.newSavedAddressID + ) { + shippingContext.actions.setSelectedSavedAddressID( + shippingContext.state.selectedSavedAddressID + ) + } + } + try { - let fulfillmentMutationValues: CommerceSetShippingInput - let requiresArtsyShippingToDestination: boolean shippingContext.actions.setIsPerformingOperation(true) - if (formValues.fulfillmentType === FulfillmentType.SHIP) { - const { - saveAddress, - addressVerifiedBy, - phoneNumber, - ...addressValues - } = formValues.attributes - - requiresArtsyShippingToDestination = shippingContext.orderData.requiresArtsyShippingTo( - addressValues.country + if ( + values.fulfillmentType === FulfillmentType.SHIP && + shippingContext.state.shippingFormMode === "new_address" + ) { + const userAddressUpdateResult = await handleNewUserAddressUpdates( + values ) - fulfillmentMutationValues = { - id: orderData.internalID, - fulfillmentType: requiresArtsyShippingToDestination - ? "SHIP_ARTA" - : FulfillmentType.SHIP, - phoneNumber, - shipping: { ...addressValues, phoneNumber: "" }, + if (userAddressUpdateResult) { + if (userAddressUpdateResult.errors) { + logger.error("Aborting: User address updates failed") + shippingContext.actions.setIsPerformingOperation(false) + // TODO: handle errors array by setting field values, showing dialog, etc + return + } else { + if (userAddressUpdateResult.actionType === "create") { + shippingContext.actions.setNewSavedAddressID( + userAddressUpdateResult.data.internalID + ) + } else if (userAddressUpdateResult.actionType === "delete") { + shippingContext.actions.setNewSavedAddressID(null) + } + if ( + userAddressUpdateResult.data?.internalID && + shippingContext.state.shippingFormMode === "new_address" + ) { + shippingContext.actions.setNewSavedAddressID( + userAddressUpdateResult.data.internalID + ) + } + } } + } - if (addressVerifiedBy) { - fulfillmentMutationValues.addressVerifiedBy = addressVerifiedBy + const saveFulfillmentDetailsResult = await handleSaveFulfillmentDetails( + values + ) + + if (saveFulfillmentDetailsResult.data) { + if ( + saveFulfillmentDetailsResult.data.requiresArtsyShippingToDestination + ) { + shippingContext.actions.setStage("shipping_quotes") + } else if (shippingContext.state.shippingFormMode === "new_address") { + // Advance to payment + router.push(`/orders/${orderData.internalID}/payment`) + } else { + // Don't advance if we're using saved addresses; instead wait for click + shippingContext.actions.setStage("fulfillment_details_saved") } } else { - requiresArtsyShippingToDestination = false - - fulfillmentMutationValues = { - id: orderData.internalID, - fulfillmentType: FulfillmentType.PICKUP, - phoneNumber: formValues.attributes.phoneNumber, - shipping: { - addressLine1: "", - addressLine2: "", - country: "", - name: "", - city: "", - postalCode: "", - region: "", - phoneNumber: "", - }, - } - } - - const result = await saveFulfillmentDetails.submitMutation({ - variables: { input: fulfillmentMutationValues }, - }) - - const orderOrError = result.commerceSetShipping?.orderOrError - - if (orderOrError?.__typename === "CommerceOrderWithMutationFailure") { - shippingContext.actions.setIsPerformingOperation(false) - - shippingContext.actions.handleExchangeError(orderOrError.error, logger) - return - } - - if (performUserAddressUpdates) { - await handleUserAddressUpdates(formValues) + resetSelectedSavedAddress() + logger.error( + "No request for saveFulfillmentDetails - this should not happen" + ) } - - handleFulfillmentDetailsSaved({ - requiresArtsyShipping: requiresArtsyShippingToDestination, - }) } catch (error) { orderTracking.errorMessageViewed({ error_code: null, @@ -230,25 +233,13 @@ export const FulfillmentDetails: FC = ({ "Something went wrong. Please try again or contact orders@artsy.net.", flow: "user selects a shipping option", }) - + resetSelectedSavedAddress() shippingContext.actions.dialog.showErrorDialog() } finally { shippingContext.actions.setIsPerformingOperation(false) } } - const handleSubmit = values => { - if (shouldVerifyAddressOnSubmit(values)) { - setVerifyAddressNow(true) - return - } else { - return submitFulfillmentDetails({ - performUserAddressUpdates: forceNewAddressFormMode, - formValues: values, - }) - } - } - return ( = ({ verifyAddressNow={verifyAddressNow} onSubmit={handleSubmit} availableFulfillmentTypes={availableFulfillmentTypes} - shippingMode={shippingMode} /> ) } @@ -364,24 +354,31 @@ const ME_FRAGMENT = graphql` } ` +/** + * Get form values for initial data or suitable for resetting to. + */ const getInitialValues = ( me: FulfillmentDetailsForm_me$data, - orderData: ShippingContextProps["orderData"] + orderData: ShippingContextProps["orderData"], + forceNewAddressFormMode?: boolean ): FulfillmentValues => { if (orderData.savedFulfillmentDetails) { return { fulfillmentType: orderData.savedFulfillmentDetails.fulfillmentType, attributes: { ...addressWithFallbackValues( - orderData.savedFulfillmentDetails.fulfillmentDetails + orderData.savedFulfillmentDetails.attributes ), - saveAddress: false, + }, + meta: { + userAddressAction: null, addressVerifiedBy: null, }, } as FulfillmentValues } const savedAddresses = extractNodes(me.addressConnection) + // The default ship-to address should be the first one that // can be shipped-to, preferring the default @@ -390,15 +387,11 @@ const getInitialValues = ( orderData.availableShippingCountries ) - const shippableDefaultAddress = defaultUserAddress - ? addressWithFallbackValues(defaultUserAddress) - : null - - if (shippableDefaultAddress) { + if (defaultUserAddress) { return { fulfillmentType: FulfillmentType.SHIP, - attributes: { - ...shippableDefaultAddress, + attributes: addressWithFallbackValues(defaultUserAddress), + meta: { saveAddress: false, addressVerifiedBy: null, }, @@ -410,15 +403,16 @@ const getInitialValues = ( // (that is still in parsedOrderData). In addition the initial values // are less relevant if the user has saved addresses - Setting country // doesn't matter. - const initialFulfillmentValues: ShipValues["attributes"] = { - ...addressWithFallbackValues({ country: orderData.shipsFrom }), - - addressVerifiedBy: null, - saveAddress: savedAddresses.length === 0, - } + const initialFulfillmentValues: ShipValues["attributes"] = addressWithFallbackValues( + { country: orderData.shipsFrom } + ) return { fulfillmentType: FulfillmentType.SHIP, attributes: initialFulfillmentValues, + meta: { + addressVerifiedBy: null, + saveAddress: true, + }, } } diff --git a/src/Apps/Order/Routes/Shipping2/Components/FulfillmentDetailsForm.tsx b/src/Apps/Order/Routes/Shipping2/Components/FulfillmentDetailsForm.tsx index e9b2b208628..8554980971f 100644 --- a/src/Apps/Order/Routes/Shipping2/Components/FulfillmentDetailsForm.tsx +++ b/src/Apps/Order/Routes/Shipping2/Components/FulfillmentDetailsForm.tsx @@ -15,12 +15,13 @@ import { AddressVerificationFlowQueryRenderer, } from "Apps/Order/Components/AddressVerificationFlow" -import { SavedAddressesFragmentContainer } from "Apps/Order/Routes/Shipping2/Components/SavedAddresses2" +import { SavedAddresses2 } from "Apps/Order/Routes/Shipping2/Components/SavedAddresses2" import { + ADDRESS_VALIDATION_SHAPE, FulfillmentType, FulfillmentValues, ShipValues, - ShippingAddressFormValues, + addressWithFallbackValues, } from "Apps/Order/Routes/Shipping2/Utils/shippingUtils" import { CountrySelect } from "Components/CountrySelect" import { RouterLink } from "System/Router/RouterLink" @@ -32,8 +33,7 @@ import { Formik, } from "formik" import { pick } from "lodash" -import { useEffect, useState } from "react" -import { ADDRESS_VALIDATION_SHAPE } from "Apps/Order/Utils/shippingUtils" +import { useCallback, useEffect, useState } from "react" import { Collapse } from "Apps/Order/Components/Collapse" import { FulfillmentDetailsForm_me$data } from "__generated__/FulfillmentDetailsForm_me.graphql" import { @@ -43,6 +43,7 @@ import { import { ContextModule, OwnerType } from "@artsy/cohesion" import { useAnalyticsContext } from "System/Analytics/AnalyticsContext" import { useShippingContext } from "Apps/Order/Routes/Shipping2/Hooks/useShippingContext" +import { SavedAddressType } from "Apps/Order/Utils/shippingUtils" export interface FulfillmentDetailsFormProps extends FulfillmentDetailsFormLayoutProps { @@ -55,7 +56,6 @@ interface FulfillmentDetailsFormLayoutProps { verifyAddressNow: boolean onAddressVerificationComplete: () => void availableFulfillmentTypes: FulfillmentType[] - shippingMode: Exclude } export type AddressFormMode = "saved_addresses" | "new_address" | "pickup" @@ -103,23 +103,37 @@ const FulfillmentDetailsFormLayout = ( touched, handleChange, handleBlur, - submitForm, setFieldValue, setValues, - isValid, } = formikContext const addressFormMode: AddressFormMode = - values.fulfillmentType === "SHIP" ? props.shippingMode : "pickup" + values.fulfillmentType === "SHIP" + ? shippingContext.state.shippingFormMode + : "pickup" + /** + * Expose formik context to entire shipping route + * via `shippingContext.state.fulfillmentDetailsCtx` + */ useEffect(() => { - /** - * Pass some key formik bits up to the shipping route - * TODO: This could be accomplished with useImperativeHandle(ref, formikContext) - */ - shippingContext.actions.setFormHelpers(formikContext) + shippingContext.actions.setFulfillmentDetailsFormikContext(formikContext) // eslint-disable-next-line react-hooks/exhaustive-deps - }, [submitForm, isValid, values]) + }, [formikContext.values, formikContext.isValid]) + + // Wrapper for change handlers that sets the stage to fulfillment_details + // when the user edits an address field + const withBackToFulfillmentDetails = void>( + cb: F + ) => (...args: Parameters) => { + if ( + addressFormMode === "new_address" && + shippingContext.state.stage !== "fulfillment_details" + ) { + shippingContext.actions.setStage("fulfillment_details") + } + cb(...args) + } const trackAutoCompleteEdits = (fieldName: string, handleChange) => ( ...args @@ -132,60 +146,98 @@ const FulfillmentDetailsFormLayout = ( } const handleCloseVerification = async () => { - await setFieldValue("attributes.addressVerifiedBy", AddressVerifiedBy.USER) + await setFieldValue("meta.addressVerifiedBy", AddressVerifiedBy.USER) await props.onAddressVerificationComplete() } - const handleChooseAddress = async (verifiedBy, chosenAddress) => { + const serializedValues = JSON.stringify(formikContext.values) + + const handleSelectSavedAddress = useCallback( + async (address: SavedAddressType) => { + shippingContext.actions.setStage("fulfillment_details") + await formikContext.setValues({ + ...formikContext.values, + fulfillmentType: FulfillmentType.SHIP, + attributes: addressWithFallbackValues(address), + meta: { + ...formikContext.values.meta, + }, + }) + + await formikContext.submitForm() + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [formikContext.setValues, formikContext.submitForm, serializedValues] + ) + + const handleChooseAddressForVerification = async ( + verifiedBy, + chosenAddress + ) => { const newValues = { ...values, attributes: { ...values.attributes, ...chosenAddress, + }, + meta: { + ...values.meta, addressVerifiedBy: verifiedBy, }, } await setValues(newValues) await props.onAddressVerificationComplete() + formikContext.submitForm() } - // Reset form when switching between ship/pickup - // eslint-disable-next-line react-hooks/rules-of-hooks - const previousFulfillmentType = usePrevious(values.fulfillmentType) - // eslint-disable-next-line react-hooks/rules-of-hooks + /** + * Effects + */ + + // Reset form fields when switching between ship/pickup + const previousValues = usePrevious(values) + useEffect(() => { - if ( - values.fulfillmentType === FulfillmentType.PICKUP && - previousFulfillmentType !== FulfillmentType.PICKUP - ) { - setValues({ - fulfillmentType: FulfillmentType.PICKUP, - attributes: { - name: "", - phoneNumber: "", - }, - }) - return + const resetAttributesOnFulfillmentTypeChange = async () => { + if (values.fulfillmentType !== previousValues.fulfillmentType) { + if (values.fulfillmentType === FulfillmentType.PICKUP) { + await setValues({ + ...values, + attributes: { + name: "", + phoneNumber: "", + addressLine1: "", + addressLine2: "", + city: "", + region: "", + postalCode: "", + country: "", + }, + }) + return + } + + if (values.fulfillmentType === FulfillmentType.SHIP) { + // reset to current initialValues (based on calculation in FulfillmentDetails.tsx) + formikContext.resetForm() + } + } } - }, [setValues, previousFulfillmentType, values.fulfillmentType]) + resetAttributesOnFulfillmentTypeChange() + }, [ + setValues, + previousValues.fulfillmentType, + formikContext, + values, + shippingContext.actions, + ]) // When not showing the form/creating a new address, // inputs should not be tabbable const tabbableIf = (activeForm: AddressFormMode): 0 | -1 => addressFormMode === activeForm ? 0 : -1 - const handleSelectSavedAddress = (address: ShippingAddressFormValues) => { - setValues({ - fulfillmentType: FulfillmentType.SHIP, - attributes: { - ...address, - saveAddress: false, - addressVerifiedBy: null, - }, - }) - } - return (
{props.verifyAddressNow && ( @@ -202,16 +254,16 @@ const FulfillmentDetailsFormLayout = ( ]) as ShipValues["attributes"] } onClose={handleCloseVerification} - onChosenAddress={handleChooseAddress} + onChosenAddress={handleChooseAddressForVerification} /> )} - {props.availableFulfillmentTypes.length > 1 && ( + {props.availableFulfillmentTypes.includes(FulfillmentType.PICKUP) && ( <> { + onSelect={withBackToFulfillmentDetails(value => { setFieldValue("fulfillmentType", value) - }} + })} defaultValue={values.fulfillmentType} > @@ -249,12 +301,10 @@ const FulfillmentDetailsFormLayout = ( data-testid="savedAddressesCollapse" open={addressFormMode === "saved_addresses"} > - { - handleSelectSavedAddress(a) - }} + onSelect={handleSelectSavedAddress} /> {/* NEW ADDRESS */} @@ -271,7 +321,7 @@ const FulfillmentDetailsFormLayout = ( title={"Full name"} autoCorrect="off" value={values.attributes.name} - onChange={handleChange} + onChange={withBackToFulfillmentDetails(handleChange)} onBlur={handleBlur} error={touched.attributes?.name && errors.attributes?.name} data-testid="AddressForm_name" @@ -291,9 +341,11 @@ const FulfillmentDetailsFormLayout = ( aria-labelledby="country-select" tabIndex={tabbableIf("new_address")} selected={values.attributes.country} - onSelect={trackAutoCompleteEdits("country", selected => { - setFieldValue(`attributes.country`, selected) - })} + onSelect={withBackToFulfillmentDetails( + trackAutoCompleteEdits("country", selected => { + setFieldValue(`attributes.country`, selected) + }) + )} disabled={ !!shippingContext.orderData.lockShippingCountryTo && shippingContext.orderData.lockShippingCountryTo !== "EU" @@ -327,9 +379,8 @@ const FulfillmentDetailsFormLayout = ( placeholder="Street address" title="Address line 1" value={values.attributes.addressLine1} - onChange={trackAutoCompleteEdits( - "addressLine1", - handleChange + onChange={withBackToFulfillmentDetails( + trackAutoCompleteEdits("addressLine1", handleChange) )} onBlur={handleBlur} onSelect={option => { @@ -378,9 +429,8 @@ const FulfillmentDetailsFormLayout = ( placeholder="Apt, floor, suite, etc." title="Address line 2 (optional)" value={values.attributes.addressLine2} - onChange={trackAutoCompleteEdits( - "addressLine2", - handleChange + onChange={withBackToFulfillmentDetails( + trackAutoCompleteEdits("addressLine2", handleChange) )} onBlur={handleBlur} error={ @@ -399,7 +449,9 @@ const FulfillmentDetailsFormLayout = ( placeholder="City" title="City" value={values.attributes.city} - onChange={trackAutoCompleteEdits("city", handleChange)} + onChange={withBackToFulfillmentDetails( + trackAutoCompleteEdits("city", handleChange) + )} onBlur={handleBlur} error={ (touched as FormikTouched).attributes?.city && @@ -424,7 +476,9 @@ const FulfillmentDetailsFormLayout = ( } autoCorrect="off" value={values.attributes.region} - onChange={trackAutoCompleteEdits("region", handleChange)} + onChange={withBackToFulfillmentDetails( + trackAutoCompleteEdits("region", handleChange) + )} onBlur={handleBlur} error={ (touched as FormikTouched).attributes?.region && @@ -440,7 +494,7 @@ const FulfillmentDetailsFormLayout = ( placeholder={ values.attributes.country === "US" ? "ZIP code" - : "ZIP/postal code" + : "ZIP/Postal code" } title={ values.attributes.country === "US" @@ -450,7 +504,9 @@ const FulfillmentDetailsFormLayout = ( autoCapitalize="characters" autoCorrect="off" value={values.attributes.postalCode} - onChange={trackAutoCompleteEdits("postalCode", handleChange)} + onChange={withBackToFulfillmentDetails( + trackAutoCompleteEdits("postalCode", handleChange) + )} onBlur={handleBlur} error={ (touched as FormikTouched).attributes @@ -473,7 +529,7 @@ const FulfillmentDetailsFormLayout = ( placeholder="Add phone number including country code" pattern="[^a-z]+" value={values.attributes.phoneNumber} - onChange={handleChange} + onChange={withBackToFulfillmentDetails(handleChange)} onBlur={handleBlur} error={ touched.attributes?.phoneNumber && @@ -492,9 +548,9 @@ const FulfillmentDetailsFormLayout = ( data-testid="FulfillmentDetailsForm_saveAddress" tabIndex={tabbableIf("new_address")} onSelect={selected => { - setFieldValue("attributes.saveAddress", selected) + setFieldValue("meta.saveAddress", selected) }} - selected={values.attributes.saveAddress} + selected={values.meta?.saveAddress} > Save shipping address for later use @@ -502,7 +558,6 @@ const FulfillmentDetailsFormLayout = ( )} - {/* PHONE NUMBER */} { mb={2} bg="red10" color="red100" - data-test="artaErrorMessage" + data-testid="artaErrorMessage" > In order to provide a shipping quote, we need some more information from you. Please contact{" "} diff --git a/src/Apps/Order/Routes/Shipping2/Components/SaveAndContinueButton.tsx b/src/Apps/Order/Routes/Shipping2/Components/SaveAndContinueButton.tsx index d66865f2ac5..f48d5c82054 100644 --- a/src/Apps/Order/Routes/Shipping2/Components/SaveAndContinueButton.tsx +++ b/src/Apps/Order/Routes/Shipping2/Components/SaveAndContinueButton.tsx @@ -1,6 +1,7 @@ import { Button, ButtonProps } from "@artsy/palette" import { useSaveSelectedShippingQuote } from "Apps/Order/Routes/Shipping2/Hooks/useSaveSelectedShippingQuote" import { useShippingContext } from "Apps/Order/Routes/Shipping2/Hooks/useShippingContext" +import { useRouter } from "System/Router/useRouter" import { SaveAndContinueButton_order$key } from "__generated__/SaveAndContinueButton_order.graphql" import { graphql, useFragment } from "react-relay" @@ -22,36 +23,36 @@ export const SaveAndContinueButton: React.FC = ({ order ) + const { router } = useRouter() + const shippingContext = useShippingContext() + const { saveSelectedShippingQuote } = useSaveSelectedShippingQuote(data) - const disableSubmit = (() => { - if (shippingContext.state.isPerformingOperation) { - return true - } - if ( - shippingContext.state.stage === "fulfillment_details" && - !shippingContext.state.formHelpers.isValid - ) { - return true - } - if ( - shippingContext.state.stage === "shipping_quotes" && - !shippingContext.state.selectedShippingQuoteId - ) { - return true - } - return false - })() + let disableSubmit = false + + if (shippingContext.state.isPerformingOperation) { + disableSubmit = true + } else if (shippingContext.state.stage === "fulfillment_details") { + disableSubmit = + shippingContext.state.fulfillmentDetailsFormikContext.isSubmitting + } else if (shippingContext.state.stage === "shipping_quotes") { + disableSubmit = !shippingContext.state.selectedShippingQuoteID + } const onContinueButtonPressed = async () => { + // save and continue - stage", shippingContext.state.stage) if (shippingContext.state.stage === "fulfillment_details") { - return shippingContext.state.formHelpers.submitForm() + return shippingContext.state.fulfillmentDetailsFormikContext?.submitForm() } if (shippingContext.state.stage === "shipping_quotes") { await saveSelectedShippingQuote() } + + if (shippingContext.state.stage === "fulfillment_details_saved") { + router.push(`/orders/${data.internalID}/payment`) + } } return ( diff --git a/src/Apps/Order/Routes/Shipping2/Components/SavedAddressItem2.tsx b/src/Apps/Order/Routes/Shipping2/Components/SavedAddressItem2.tsx index 131715bdab1..fddb2285dc1 100644 --- a/src/Apps/Order/Routes/Shipping2/Components/SavedAddressItem2.tsx +++ b/src/Apps/Order/Routes/Shipping2/Components/SavedAddressItem2.tsx @@ -84,7 +84,7 @@ export const SavedAddressItem: React.FC = ( } handleClickEdit(event) }} - data-test="editAddressInShipping" + data-testid="editAddressInShipping" > Edit diff --git a/src/Apps/Order/Routes/Shipping2/Components/SavedAddresses2.tsx b/src/Apps/Order/Routes/Shipping2/Components/SavedAddresses2.tsx index 81a0e8343f7..2a2424afacd 100644 --- a/src/Apps/Order/Routes/Shipping2/Components/SavedAddresses2.tsx +++ b/src/Apps/Order/Routes/Shipping2/Components/SavedAddresses2.tsx @@ -1,148 +1,175 @@ -import * as React from "react" -import { useState } from "react" -import styled from "styled-components" +import { useEffect, useState, FC } from "react" import { compact } from "lodash" +import { graphql, useFragment } from "react-relay" +import { SavedAddresses2_me$key } from "__generated__/SavedAddresses2_me.graphql" +import styled from "styled-components" import { RadioGroup, BorderedRadio, Spacer, Clickable } from "@artsy/palette" -import { createRefetchContainer, graphql, RelayRefetchProp } from "react-relay" -import { SavedAddresses2_me$data } from "__generated__/SavedAddresses2_me.graphql" import { AddressModal, AddressModalAction, - AddressModalActionType, } from "Apps/Order/Routes/Shipping2/Components/AddressModal2" import createLogger from "Utils/logger" import { SavedAddressItem } from "Apps/Order/Routes/Shipping2/Components/SavedAddressItem2" -import { extractNodes } from "Utils/extractNodes" import { themeGet } from "@styled-system/theme-get" import { + FulfillmentValues, SavedAddressType, - ShippingAddressFormValues, - addressWithFallbackValues, + getAddressByID, getDefaultUserAddress, } from "Apps/Order/Routes/Shipping2/Utils/shippingUtils" import { useShippingContext } from "Apps/Order/Routes/Shipping2/Hooks/useShippingContext" import { useOrderTracking } from "Apps/Order/Hooks/useOrderTracking" - -export const NEW_ADDRESS = "NEW_ADDRESS" -const PAGE_SIZE = 30 +import { useFormikContext } from "formik" +import { extractNodes } from "Utils/extractNodes" export interface SavedAddressesProps { - relay: RelayRefetchProp - me: SavedAddresses2_me$data active: boolean - onSelect: (address: ShippingAddressFormValues) => void + me: SavedAddresses2_me$key | null + onSelect: (address: SavedAddressType) => void } -const SavedAddresses: React.FC = props => { +export const SavedAddresses2: FC = props => { const logger = createLogger("SavedAddresses.tsx") - const [activeModal, setActiveModal] = useState( - null - ) + const shippingContext = useShippingContext() + const orderTracking = useOrderTracking() + const formikContext = useFormikContext() - const { onSelect, me, relay } = props + const [ + addressModalAction, + setAddressModalAction, + ] = useState(null) - const addressList = compact( - extractNodes(me?.addressConnection) ?? [] + const data = useFragment( + graphql` + fragment SavedAddresses2_me on Me + @argumentDefinitions( + first: { type: "Int", defaultValue: 30 } + last: { type: "Int" } + after: { type: "String" } + before: { type: "String" } + ) { + addressConnection( + first: $first + last: $last + before: $before + after: $after + ) { + edges { + node { + internalID + name + addressLine1 + addressLine2 + city + region + postalCode + country + phoneNumber + isDefault + } + } + } + } + `, + props.me ) - const selectedSavedAddressId = - shippingContext.orderData.savedFulfillmentDetails?.selectedSavedAddressId - const [selectedAddressID, setSelectedAddressID] = useState< - string | undefined - >( - getBestAvailableAddress( - addressList, - selectedSavedAddressId, - shippingContext.orderData.availableShippingCountries - )?.internalID + const addressList = compact( + extractNodes(data?.addressConnection) ?? [] ) - const selectedAddress = - selectedAddressID && getAddressByID(addressList, selectedAddressID) - const selectedAddressPresent = !!selectedAddress - - const orderTracking = useOrderTracking() + const savedAddressOnOrder = shippingContext.orderData.savedFulfillmentDetails + ?.selectedSavedAddressID + ? getAddressByID( + addressList, + shippingContext.orderData.savedFulfillmentDetails + ?.selectedSavedAddressID + ) ?? null + : null + + const selectAndSubmitAddress = (address: SavedAddressType) => { + shippingContext.actions.setSelectedSavedAddressID(address.internalID) + props.onSelect(address) + } - React.useEffect(() => { - if (!selectedAddressPresent) { - setSelectedAddressID( - getBestAvailableAddress( + const addressSavedToOrderID = savedAddressOnOrder?.internalID + + // TODO: Make sure this can't create an infinite loop if submitting fails + useEffect(() => { + // Automatically select (save) best available address ID if it isn't present + const automaticallySelectBestAddress = async () => { + if ( + props.active && + !shippingContext.state.isPerformingOperation && + !formikContext.isSubmitting && + !shippingContext.state.selectedSavedAddressID && + addressList.length > 0 + ) { + const bestAddress = getBestAvailableAddress( addressList, - selectedSavedAddressId, + addressSavedToOrderID, shippingContext.orderData.availableShippingCountries - )?.internalID - ) - } - }, [ - selectedAddressPresent, - addressList, - selectedSavedAddressId, - shippingContext.orderData.availableShippingCountries, - ]) - - const handleSelectAddress = (id: string): void => { - setSelectedAddressID(id) - const selectedAddress = getAddressByID(addressList, id) - if (!selectedAddress) { - logger.warn("Address not found: ", id) + ) + if (bestAddress) { + selectAndSubmitAddress(bestAddress) + } + } } + automaticallySelectBestAddress() + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [props.active]) + + /* + * Select an address radio button and pass the address to the parent. + */ + const handleClickAddress = (id: string): void => { orderTracking.clickedShippingAddress() - // Set values on the fulfillment form context. - // Can these values be invalid? If so, maybe we could pop a form up for - // them to fix it. Seems unlikely. - onSelect(addressWithFallbackValues(selectedAddress)) - } - - const refetchAddresses = () => { - return new Promise((resolve, reject) => - relay.refetch( - { - first: PAGE_SIZE, - }, - null, - error => { - if (error) { - logger.error(error) - reject(error) - } else { - resolve() - } - } - ) - ) + const address = getAddressByID(addressList, id) + if (!address) { + logger.error("Address not found: ", id) + return + } + selectAndSubmitAddress(address) } - const handleClickEditAddress = (address: SavedAddressType) => { - setSelectedAddressID(address.id) - setActiveModal({ - type: AddressModalActionType.EDIT_USER_ADDRESS, + const handleClickEditAddress = async (address: SavedAddressType) => { + const addressModalAction: AddressModalAction = { + type: "edit", address: address, - }) + } + + setAddressModalAction(addressModalAction) } - const refetchAndSelectAddress = async (addressID: string) => { - await refetchAddresses() - setSelectedAddressID(addressID) + const handleAddressModalSuccess = (address: SavedAddressType) => { + selectAndSubmitAddress(address) + setAddressModalAction(null) } + /* Effects */ + return ( <> {addressList.map((address, index) => { return ( = props => { { orderTracking.clickedAddNewShippingAddress() - setActiveModal({ type: AddressModalActionType.CREATE_USER_ADDRESS }) + setAddressModalAction({ type: "create" }) }} > Add a new address )} { - setActiveModal(null) + setAddressModalAction(null) }} - onSuccess={refetchAndSelectAddress} + onSuccess={handleAddressModalSuccess} /> ) } -export const SavedAddressesFragmentContainer = createRefetchContainer( - SavedAddresses, - { - me: graphql` - fragment SavedAddresses2_me on Me - @argumentDefinitions( - first: { type: "Int", defaultValue: 30 } - last: { type: "Int" } - after: { type: "String" } - before: { type: "String" } - ) { - id - addressConnection( - first: $first - last: $last - before: $before - after: $after - ) @connection(key: "SavedAddresses_addressConnection") { - totalCount - edges { - node { - id - internalID - addressLine1 - addressLine2 - addressLine3 - city - country - isDefault - name - phoneNumber - postalCode - region - } - } - } - } - `, - }, - graphql` - query SavedAddresses2RefetchQuery { - me { - ...SavedAddresses2_me - } - } - ` -) - const AddAddressButton = styled(Clickable)` text-decoration: underline; &:hover { @@ -232,10 +211,6 @@ const AddAddressButton = styled(Clickable)` } ` -const getAddressByID = (addressList: SavedAddressType[], addressID: string) => { - return addressList.find(node => node.internalID === addressID) -} - const getBestAvailableAddress = ( addressList: SavedAddressType[], addressID?: string | null, diff --git a/src/Apps/Order/Routes/Shipping2/Components/ShippingQuotes2.tsx b/src/Apps/Order/Routes/Shipping2/Components/ShippingQuotes2.tsx index 1952bfdd790..28676e31e72 100644 --- a/src/Apps/Order/Routes/Shipping2/Components/ShippingQuotes2.tsx +++ b/src/Apps/Order/Routes/Shipping2/Components/ShippingQuotes2.tsx @@ -51,15 +51,15 @@ export const ShippingQuotes2: React.FC = ({ return null } - const handleShippingQuoteSelected = (newShippingQuoteId: string) => { - orderTracking.clickedSelectShippingOption(newShippingQuoteId) - shippingContext.actions.setSelectedShippingQuote(newShippingQuoteId) + const handleShippingQuoteSelected = (newShippingQuoteID: string) => { + orderTracking.clickedSelectShippingOption(newShippingQuoteID) + shippingContext.actions.setSelectedShippingQuote(newShippingQuoteID) } return ( {quotes.map(shippingQuote => { const description = @@ -69,7 +69,7 @@ export const ShippingQuotes2: React.FC = ({ return ( = ({ {description} - + {shippingQuote?.price} @@ -143,9 +143,9 @@ const useAutoSelectBestShippingQuote = ( * or that is already selected on the server, * or the first quote in the list */ - const bestArtsyShippingQuoteId = + const bestArtsyShippingQuoteID = quotes.find( - quote => quote.id === shippingContext.state.selectedShippingQuoteId + quote => quote.id === shippingContext.state.selectedShippingQuoteID )?.id || quotes.find(quote => quote.isSelected)?.id || quotes?.[0]?.id @@ -153,16 +153,16 @@ const useAutoSelectBestShippingQuote = ( useEffect(() => { if ( shippingContext.state.stage === "shipping_quotes" && - bestArtsyShippingQuoteId && - bestArtsyShippingQuoteId !== shippingContext.state.selectedShippingQuoteId + bestArtsyShippingQuoteID && + bestArtsyShippingQuoteID !== shippingContext.state.selectedShippingQuoteID ) { - shippingContext.actions.setSelectedShippingQuote(bestArtsyShippingQuoteId) + shippingContext.actions.setSelectedShippingQuote(bestArtsyShippingQuoteID) } }, [ - bestArtsyShippingQuoteId, - shippingContext.orderData.selectedShippingQuoteId, + bestArtsyShippingQuoteID, + shippingContext.orderData.selectedShippingQuoteID, shippingContext.state.stage, - shippingContext.state.selectedShippingQuoteId, + shippingContext.state.selectedShippingQuoteID, shippingContext.actions, ]) } diff --git a/src/Apps/Order/Routes/Shipping2/__tests__/AddressModal2.jest.tsx b/src/Apps/Order/Routes/Shipping2/Components/__tests__/AddressModal2.jest.tsx similarity index 82% rename from src/Apps/Order/Routes/Shipping2/__tests__/AddressModal2.jest.tsx rename to src/Apps/Order/Routes/Shipping2/Components/__tests__/AddressModal2.jest.tsx index caab26e041b..cfb57550a4c 100644 --- a/src/Apps/Order/Routes/Shipping2/__tests__/AddressModal2.jest.tsx +++ b/src/Apps/Order/Routes/Shipping2/Components/__tests__/AddressModal2.jest.tsx @@ -1,8 +1,6 @@ import { AddressModal, AddressModalProps, - GENERIC_FAIL_MESSAGE, - AddressModalActionType, } from "Apps/Order/Routes/Shipping2/Components/AddressModal2" import { validAddress } from "Components/__tests__/Utils/addressForm2" import { useSystemContext } from "System/useSystemContext" @@ -25,13 +23,6 @@ jest.unmock("react-relay") jest.mock("System/useSystemContext") const mockUseSystemContext = useSystemContext as jest.Mock -jest.mock("Utils/Hooks/useMatchMedia", () => ({ - __internal__useMatchMedia: () => ({}), -})) -jest.mock("Utils/user", () => ({ - userHasLabFeature: jest.fn(), -})) - const errorBoxQuery = "Banner[data-testid='form-banner-error']" const mockSavedAddress: SavedAddressType = { @@ -89,6 +80,7 @@ afterEach(() => { }) // FIXME: MockBoot interfering somehow... +// eslint-disable-next-line jest/no-disabled-tests describe.skip("AddressModal", () => { beforeEach(() => { mockRelayEnv = createMockEnvironment() @@ -102,8 +94,8 @@ describe.skip("AddressModal", () => { testAddressModalProps = { onSuccess: jest.fn(), - modalAction: { - type: AddressModalActionType.EDIT_USER_ADDRESS, + addressModalAction: { + type: "edit", address: mockSavedAddress, }, @@ -118,17 +110,17 @@ describe.skip("AddressModal", () => { expect(wrapper.find("input").length).toBe(7) expect(wrapper.find("select").length).toBe(1) - expect(wrapper.find("Checkbox[data-test='setAsDefault']").length).toBe(1) - expect(wrapper.find("Clickable[data-test='deleteButton']").length).toBe(1) - expect(wrapper.find("Button[data-test='saveButton']").length).toBe(1) + expect(wrapper.find("Checkbox[data-testid='setAsDefault']").length).toBe(1) + expect(wrapper.find("Clickable[data-testid='deleteButton']").length).toBe(1) + expect(wrapper.find("Button[data-testid='saveButton']").length).toBe(1) }) it("renders EditModal without checkbox when address is default", () => { const { wrapper } = getWrapper({ componentProps: { ...testAddressModalProps, - modalAction: { - type: AddressModalActionType.EDIT_USER_ADDRESS, + addressModalAction: { + type: "edit", address: { ...mockSavedAddress, isDefault: true, @@ -137,15 +129,15 @@ describe.skip("AddressModal", () => { }, }) expect(wrapper.text()).toContain("Edit address") - expect(wrapper.find("Checkbox[data-test='setAsDefault']").length).toBe(0) + expect(wrapper.find("Checkbox[data-testid='setAsDefault']").length).toBe(0) }) it("renders AddModal with the title, input fields, checkbox and button", () => { const { wrapper } = getWrapper({ componentProps: { ...testAddressModalProps, - modalAction: { - type: AddressModalActionType.CREATE_USER_ADDRESS, + addressModalAction: { + type: "create", }, }, }) @@ -153,17 +145,19 @@ describe.skip("AddressModal", () => { expect(wrapper.find("input").length).toBe(7) expect(wrapper.find("select").length).toBe(1) - expect(wrapper.find("Checkbox[data-test='setAsDefault']").length).toBe(1) - expect(wrapper.find("Clickable[data-test='deleteButton']").length).toBe(0) - expect(wrapper.find("Button[data-test='saveButton']").length).toBe(1) + expect(wrapper.find("Checkbox[data-testid='setAsDefault']").length).toBe(1) + expect(wrapper.find("Clickable[data-testid='deleteButton']").length).toBe(0) + expect(wrapper.find("Button[data-testid='saveButton']").length).toBe(1) }) it("clicking the delete button spawns a correct dialog", async () => { const { wrapper } = getWrapper() - const deleteButton = wrapper.find("Clickable[data-test='deleteButton']") + const deleteButton = wrapper.find("Clickable[data-testid='deleteButton']") deleteButton.simulate("click") await wrapper.update() - const dialog = wrapper.find("ModalDialog[data-test='deleteAddressDialog']") + const dialog = wrapper.find( + "ModalDialog[data-testid='deleteAddressDialog']" + ) expect(dialog).toHaveLength(1) expect(dialog.text()).toContain("Delete address?") @@ -179,9 +173,11 @@ describe.skip("AddressModal", () => { it("when the dialog is confirmed, the delete action happens", async () => { const { mockResolveLastOperation, wrapper } = getWrapper() - const deleteButton = wrapper.find("Clickable[data-test='deleteButton']") + const deleteButton = wrapper.find("Clickable[data-testid='deleteButton']") deleteButton.simulate("click") - const dialog = wrapper.find("ModalDialog[data-test='deleteAddressDialog']") + const dialog = wrapper.find( + "ModalDialog[data-testid='deleteAddressDialog']" + ) const dialogDelete = dialog.find("Button").at(1) dialogDelete.simulate("click") @@ -196,11 +192,15 @@ describe.skip("AddressModal", () => { input: { userAddressID: "internal-id" }, }) + await flushPromiseQueue() + expect(wrapper.find(AddressModal).props().closeModal).toHaveBeenCalled() }) describe("update mode", () => { - it("creates address when form is submitted with valid values", async () => { + // TODO: Migrate to RTL for easier address form filling? + // eslint-disable-next-line jest/no-disabled-tests + it.skip("updates address when form is submitted with valid values", async () => { const { mockResolveLastOperation, wrapper } = getWrapper() const formik = wrapper.find("Formik").first() @@ -246,10 +246,11 @@ describe.skip("AddressModal", () => { it("shows generic error when mutation returns error", async () => { const { mockResolveLastOperation, wrapper } = getWrapper() - const formik = wrapper.find("Formik").first() - formik.props().onSubmit!(validAddress as any) + const form = wrapper.find("Form") + form.simulate("submit") await flushPromiseQueue() + mockResolveLastOperation({ UpdateUserAddressPayload: () => ({ userAddressOrErrors: { @@ -263,21 +264,34 @@ describe.skip("AddressModal", () => { }, }), }) + await flushPromiseQueue() + await wrapper.update() - expect(wrapper.find(errorBoxQuery).text()).toContain(GENERIC_FAIL_MESSAGE) + + await flushPromiseQueue() + + expect(wrapper.find(errorBoxQuery).text()).toContain( + "Sorry, there has been an issue saving your address. Please try again." + ) }) it("shows generic error when mutation fails", async () => { const { wrapper, mockRejectLastOperation } = getWrapper() - const formik = wrapper.find("Formik").first() - formik.props().onSubmit!(validAddress as any) + const form = wrapper.find("Form") + form.simulate("submit") + await flushPromiseQueue() + mockRejectLastOperation(new TypeError("Network request failed")) + await flushPromiseQueue() + await wrapper.update() - expect(wrapper.find(errorBoxQuery).text()).toContain(GENERIC_FAIL_MESSAGE) + expect(wrapper.find(errorBoxQuery).text()).toContain( + "Sorry, there has been an issue saving your address. Please try again." + ) }) // FIXME: Flakey test diff --git a/src/Apps/Order/Routes/Shipping2/__tests__/FulfillmentDetailsForm.jest.tsx b/src/Apps/Order/Routes/Shipping2/Components/__tests__/FulfillmentDetailsForm.jest.tsx similarity index 94% rename from src/Apps/Order/Routes/Shipping2/__tests__/FulfillmentDetailsForm.jest.tsx rename to src/Apps/Order/Routes/Shipping2/Components/__tests__/FulfillmentDetailsForm.jest.tsx index 371dac99834..0b959b7b5b8 100644 --- a/src/Apps/Order/Routes/Shipping2/__tests__/FulfillmentDetailsForm.jest.tsx +++ b/src/Apps/Order/Routes/Shipping2/Components/__tests__/FulfillmentDetailsForm.jest.tsx @@ -41,7 +41,7 @@ jest.mock("react-tracking") const mockOnSubmit = jest.fn() const mockOnAddressVerificationComplete = jest.fn() let testProps: DeepPartial -let mockShippingContext: DeepPartial +let mockShippingContext: ShippingContextProps jest.mock("Apps/Order/Routes/Shipping2/Hooks/useShippingContext", () => ({ useShippingContext: () => mockShippingContext, @@ -49,7 +49,7 @@ jest.mock("Apps/Order/Routes/Shipping2/Hooks/useShippingContext", () => ({ // Mock relay-connected component jest.mock("Apps/Order/Routes/Shipping2/Components/SavedAddresses2", () => ({ - SavedAddressesFragmentContainer: () =>
, + SavedAddresses2: () =>
, })) const renderTree = testProps => { @@ -72,7 +72,6 @@ beforeEach(() => { })) mockOnAddressVerificationComplete.mockReset() testProps = { - shippingMode: "saved_addresses", availableFulfillmentTypes: [FulfillmentType.SHIP], initialValues: { fulfillmentType: FulfillmentType.SHIP, @@ -83,10 +82,9 @@ beforeEach(() => { city: "", region: "", postalCode: "", - saveAddress: false, - addressVerifiedBy: null, country: "US", }, + meta: {}, }, onSubmit: mockOnSubmit, onAddressVerificationComplete: mockOnAddressVerificationComplete, @@ -97,14 +95,17 @@ beforeEach(() => { }, }, } - mockShippingContext = { + mockShippingContext = ({ actions: { - setFormHelpers: jest.fn(), + setFulfillmentDetailsFormikContext: jest.fn(), }, orderData: { shippingQuotes: [], }, - } + state: { + shippingFormMode: "saved_addresses", + }, + } as unknown) as ShippingContextProps }) const addressFormErrors = { @@ -148,9 +149,11 @@ describe("FulfillmentDetailsForm", () => { it("has name and phone number fields", async () => { renderTree(testProps) + await userEvent.click( screen.getByRole("radio", { name: /Arrange for pickup/ }) ) + const fullNameField = await screen.findByPlaceholderText("Full name") const phoneNumberField = await screen.findByPlaceholderText( "Add phone number including country code" @@ -166,8 +169,20 @@ describe("FulfillmentDetailsForm", () => { it("will not submit without required fields", async () => { renderTree(testProps) + await userEvent.click( + screen.getByRole("radio", { name: /Arrange for pickup/ }) + ) + + expect( + screen.getByRole("radio", { name: /Arrange for pickup/ }) + ).toBeChecked() + + await flushPromiseQueue() + await submitForm() + await flushPromiseQueue() + await waitFor(() => { ;["Full name is required", "Phone number is required"].forEach( error => { @@ -201,7 +216,14 @@ describe("FulfillmentDetailsForm", () => { attributes: { name: "John Doe", phoneNumber: "1234567890", + city: "", + region: "", + postalCode: "", + country: "", + addressLine1: "", + addressLine2: "", }, + meta: {}, }, expect.anything() ) @@ -263,7 +285,7 @@ describe("FulfillmentDetailsForm", () => { }) it("shows the saved addresses or plain address form depending on the passed prop", async () => { - testProps.shippingMode = "saved_addresses" + mockShippingContext.state.shippingFormMode = "saved_addresses" renderTree(testProps) // Note - SavedAddresses is mocked out. await waitFor(() => { @@ -277,7 +299,7 @@ describe("FulfillmentDetailsForm", () => { cleanup() - testProps.shippingMode = "new_address" + mockShippingContext.state.shippingFormMode = "new_address" renderTree(testProps) await waitFor(() => { expect(screen.getByTestId("savedAddressesCollapse")).toHaveStyle({ @@ -362,9 +384,8 @@ describe("FulfillmentDetailsForm", () => { region: "", postalCode: "", phoneNumber: "5555937743", - addressVerifiedBy: null, - saveAddress: false, }, + meta: {}, }, expect.anything() ) @@ -435,9 +456,8 @@ describe("FulfillmentDetailsForm", () => { region: validAddress.region, postalCode: validAddress.postalCode, phoneNumber: validAddress.phoneNumber, - addressVerifiedBy: null, - saveAddress: false, }, + meta: {}, }, expect.anything() ) @@ -445,6 +465,7 @@ describe("FulfillmentDetailsForm", () => { }) }) }) + describe("Address autocomplete", () => { let mockFetch: jest.Mock beforeEach(() => { diff --git a/src/Apps/Order/Routes/Shipping2/Components/__tests__/SavedAddresses2.jest.tsx b/src/Apps/Order/Routes/Shipping2/Components/__tests__/SavedAddresses2.jest.tsx new file mode 100644 index 00000000000..4bddc1df369 --- /dev/null +++ b/src/Apps/Order/Routes/Shipping2/Components/__tests__/SavedAddresses2.jest.tsx @@ -0,0 +1,334 @@ +import { useTracking } from "react-tracking" +import { Analytics } from "System/Analytics/AnalyticsContext" +import { fireEvent, screen, within } from "@testing-library/react" +import { + SavedAddresses2, + SavedAddressesProps, +} from "Apps/Order/Routes/Shipping2/Components/SavedAddresses2" +import { ShippingContextProps } from "Apps/Order/Routes/Shipping2/ShippingContext" +import { flushPromiseQueue } from "DevTools/flushPromiseQueue" +import userEvent from "@testing-library/user-event" +import { Formik } from "formik" +import { + FulfillmentType, + ShipValues, +} from "Apps/Order/Routes/Shipping2/Utils/shippingUtils" +import { MockBoot } from "DevTools/MockBoot" +import { fillAddressForm } from "Components/__tests__/Utils/addressForm2" +import { setupTestWrapperTL } from "DevTools/setupTestWrapper" +import { graphql } from "react-relay" +import { SavedAddresses2TestQuery } from "__generated__/SavedAddresses2TestQuery.graphql" + +jest.unmock("react-relay") +jest.mock("react-tracking") +jest.mock("Utils/Hooks/useMatchMedia", () => ({ + __internal__useMatchMedia: () => ({}), +})) + +jest.setTimeout(10000) + +let testProps: Omit +let mockShippingContext: ShippingContextProps +const mockFormikSubmit = jest.fn() +const mockExecuteUserAddressAction = jest.fn() + +jest.mock("Apps/Order/Routes/Shipping2/Hooks/useShippingContext", () => { + return { + useShippingContext: jest.fn(() => mockShippingContext), + } +}) + +jest.mock("Apps/Order/Routes/Shipping2/Hooks/useUserAddressUpdates", () => { + return { + useUserAddressUpdates: () => ({ + executeUserAddressAction: mockExecuteUserAddressAction, + }), + } +}) + +const TestWrapper = ({ children }) => ( + + + + initialValues={{ + attributes: { + addressLine1: "", + addressLine2: "", + city: "", + country: "", + name: "", + phoneNumber: "", + postalCode: "", + region: "", + }, + fulfillmentType: FulfillmentType.SHIP, + meta: {}, + }} + onSubmit={mockFormikSubmit} + > + {children} + + + +) + +const { renderWithRelay } = setupTestWrapperTL({ + Component: props => { + return ( + + + + ) + }, + query: graphql` + query SavedAddresses2TestQuery { + me { + ...SavedAddresses2_me + } + } + `, +}) + +describe("Saved Addresses", () => { + const trackEvent = jest.fn() + beforeEach(() => { + jest.clearAllMocks() + + testProps = { + active: true, + onSelect: jest.fn(), + } + mockShippingContext = ({ + orderData: { + availableShippingCountries: ["US"], + savedFulfillmentDetails: { selectedSavedAddressID: "2" }, + }, + state: { + isPerformingOperation: false, + selectedSavedAddressID: "2", + shippingFormMode: "saved_addresses", + }, + actions: { + setIsPerformingOperation: jest.fn(), + setSelectedSavedAddressID: jest.fn(), + }, + } as unknown) as ShippingContextProps + ;(useTracking as jest.Mock).mockImplementation(() => ({ + trackEvent, + })) + }) + + it("renders the addresses on the page", async () => { + renderWithRelay({ + Me: () => ({ + addressConnection: basicAddressList, + }), + }) + const savedAddresses = screen.getAllByTestId("savedAddress") + expect(savedAddresses).toHaveLength(2) + expect(savedAddresses.map(address => address.textContent)).toEqual([ + "Test Name1 Main StMadrid, Spain, 28001555-555-5555Edit", + "Test Name401 BroadwayFloor 25New York, NY, US, 10013422-424-4242Edit", + ]) + }) + + it("calls the onSelect prop when the user clicks an address", async () => { + renderWithRelay({ + Me: () => ({ + addressConnection: basicAddressList, + }), + }) + const savedAddresses = screen.getAllByTestId("savedAddress") + savedAddresses[0].click() + expect(testProps.onSelect).toHaveBeenCalledTimes(1) + expect(testProps.onSelect).toHaveBeenCalledWith({ + addressLine1: "1 Main St", + addressLine2: "", + city: "Madrid", + country: "Spain", + internalID: "1", + isDefault: false, + name: "Test Name", + phoneNumber: "555-555-5555", + postalCode: "28001", + region: "", + }) + }) + + it("renders with an address pre-selected, but doesn't automatically save anything", async () => { + mockShippingContext.state.selectedSavedAddressID = "2" + renderWithRelay({ + Me: () => ({ + addressConnection: basicAddressList, + }), + }) + + const savedAddresses = screen.getAllByTestId("savedAddress") + + expect(savedAddresses[0]).not.toBeChecked() + expect(savedAddresses[1]).toBeChecked() + + await flushPromiseQueue() + expect(testProps.onSelect).not.toHaveBeenCalled() + }) + + describe("Creating a new address", () => { + it("loads the address modal in new address mode", async () => { + renderWithRelay({ + Me: () => ({ + addressConnection: basicAddressList, + }), + }) + + expect(screen.queryByText("Add address")).not.toBeInTheDocument() + + const addAddressButton = screen.getByText("Add a new address") + await userEvent.click(addAddressButton) + + expect(await screen.findByText("Add address")).toBeInTheDocument() + + const nameInput = screen.getByPlaceholderText("Full name") + expect(nameInput).toBeInTheDocument() + expect(nameInput).toHaveDisplayValue("") + + const streetInput = screen.getByPlaceholderText("Street address") + expect(streetInput).toBeInTheDocument() + expect(streetInput).toHaveDisplayValue("") + }) + + it("tracks an analytics event when the user clicks the add address button", async () => { + renderWithRelay({ + Me: () => ({ + addressConnection: basicAddressList, + }), + }) + const addAddressButton = screen.getByText("Add a new address") + await userEvent.click(addAddressButton) + + expect(trackEvent).toHaveBeenCalledWith({ + action: "clickedAddNewShippingAddress", + context_module: "ordersShipping", + context_page_owner_id: "order-id", + context_page_owner_type: "orders-shipping", + }) + }) + + // Test takes too long to run + // eslint-disable-next-line jest/no-disabled-tests + it.skip("calls the parent formik context onSubmit when the user saves a new address", async () => { + let startTime = Date.now() + const logTime = (label: string) => { + console.log(label, Date.now() - startTime) + startTime = Date.now() + } + logTime("start") + + renderWithRelay({ + Me: () => ({ + addressConnection: basicAddressList, + }), + }) + logTime("rendered") + + const validAddress = { + name: "Test Name", + addressLine1: "1 Main St", + addressLine2: "Basement", + city: "Madrid", + region: "NY", + postalCode: "28001", + country: "ES", + phoneNumber: "555-555-5555", + } + const addAddressButton = screen.getByText("Add a new address") + await userEvent.click(addAddressButton) + logTime("clicked button") + + screen.getByText("Add address") + logTime("found modal, filling") + + await fillAddressForm(validAddress) + + await flushPromiseQueue() + logTime("filled") + + mockExecuteUserAddressAction.mockResolvedValueOnce({ + data: { ...validAddress }, + }) + logTime("resolved") + + await userEvent.click(screen.getByText("Save")) + + await flushPromiseQueue() + logTime("clicked save") + + expect(testProps.onSelect).toHaveBeenCalledWith( + expect.objectContaining(validAddress) + ) + logTime("asserted") + + await flushPromiseQueue() + + expect(testProps.onSelect).toHaveBeenCalledTimes(1) + }) + }) + + describe("Editing an address", () => { + it("loads the address modal in edit mode", async () => { + renderWithRelay({ + Me: () => ({ + addressConnection: basicAddressList, + }), + }) + + expect(screen.queryByText("Edit address")).not.toBeInTheDocument() + + const savedAddresses = screen.getAllByTestId("savedAddress") + const editAddressButton = within(savedAddresses[0]).getByText("Edit") + + // have to use fireEvent because the button (a styled `Text`) does not appear as clickable + await fireEvent.click(editAddressButton) + + expect(await screen.findByText("Edit address")).toBeInTheDocument() + expect(screen.getByDisplayValue("Test Name")).toBeInTheDocument() + expect(screen.getByPlaceholderText("Street address")).toHaveValue( + "1 Main St" + ) + }) + }) +}) + +const basicAddressList = { + edges: [ + { + node: { + internalID: "1", + addressLine1: "1 Main St", + addressLine2: "", + addressLine3: "", + city: "Madrid", + country: "Spain", + isDefault: false, + name: "Test Name", + phoneNumber: "555-555-5555", + postalCode: "28001", + region: "", + }, + }, + { + node: { + internalID: "2", + addressLine1: "401 Broadway", + addressLine2: "Floor 25", + addressLine3: "", + city: "New York", + country: "US", + isDefault: true, + name: "Test Name", + phoneNumber: "422-424-4242", + postalCode: "10013", + region: "NY", + }, + }, + ], +} diff --git a/src/Apps/Order/Routes/Shipping2/Hooks/__tests__/useUserAddressUpdates.jest.ts b/src/Apps/Order/Routes/Shipping2/Hooks/__tests__/useUserAddressUpdates.jest.ts new file mode 100644 index 00000000000..2da060fb022 --- /dev/null +++ b/src/Apps/Order/Routes/Shipping2/Hooks/__tests__/useUserAddressUpdates.jest.ts @@ -0,0 +1,400 @@ +import { act, renderHook } from "@testing-library/react-hooks" +import { + MockEnvironment, + MockPayloadGenerator, + createMockEnvironment, +} from "relay-test-utils" +import { + UserAddressAction, + useUserAddressUpdates, +} from "Apps/Order/Routes/Shipping2/Hooks/useUserAddressUpdates" +import { + FulfillmentType, + FulfillmentValues, +} from "Apps/Order/Routes/Shipping2/Utils/shippingUtils" +import { ShippingContextProps } from "Apps/Order/Routes/Shipping2/ShippingContext" +import { flushPromiseQueue } from "DevTools/flushPromiseQueue" + +let mockRelayEnv: MockEnvironment +let values: FulfillmentValues +let mockShippingContext: ShippingContextProps + +jest.unmock("react-relay") +jest.mock("System/useSystemContext", () => ({ + useSystemContext: () => ({ relayEnvironment: mockRelayEnv }), +})) +jest.mock("Apps/Order/Routes/Shipping2/Hooks/useShippingContext", () => ({ + useShippingContext: () => mockShippingContext, +})) + +const setupHook = () => { + const hookResult = renderHook(() => useUserAddressUpdates()) + return hookResult +} + +beforeEach(() => { + mockRelayEnv = createMockEnvironment() + + values = { + fulfillmentType: FulfillmentType.SHIP, + meta: {}, + attributes: { + addressLine1: "401 Broadway", + addressLine2: "", + city: "New York", + country: "US", + name: "Artsy UK Ltd", + phoneNumber: "1234567890", + postalCode: "10013", + region: "NY", + }, + } + + mockShippingContext = ({ + state: { + mode: "saved_addresses", + }, + actions: { + setIsPerformingOperation: jest.fn(), + }, + orderData: { + savedFulfillmentDetails: null, + }, + meData: { + addressList: [], + }, + } as unknown) as ShippingContextProps +}) + +const resolveMostRecentOperation = async (resolvers: any) => { + const operation = mockRelayEnv.mock.getMostRecentOperation() + + await act(() => { + mockRelayEnv.mock.resolve( + operation, + MockPayloadGenerator.generate(operation, resolvers) + ) + }) + + const operationName = operation.request.node.operation.name + const operationVariables = operation.request.variables + + return { operation, operationName, operationVariables } +} + +let userAddressAction: UserAddressAction + +describe("useUserAddressUpdates", () => { + describe("handleNewUserAddressUpdates", () => { + it("returns a function", () => { + const { result } = setupHook() + expect(result.current.handleNewUserAddressUpdates).toBeInstanceOf( + Function + ) + }) + + it("does not call a mutation and returns an empty result and errors if fulfillment type is pickup", async () => { + const { result } = setupHook() + const { handleNewUserAddressUpdates } = result.current + + values.fulfillmentType = FulfillmentType.PICKUP + + const response = await handleNewUserAddressUpdates(values) + + expect(response).toBeNull() + expect(mockRelayEnv.mock.getAllOperations().length).toBe(0) + }) + + describe("fulfillment type is ship", () => { + beforeEach(() => { + values.fulfillmentType = FulfillmentType.SHIP + }) + + describe("mode is new_address", () => { + beforeEach(() => { + mockShippingContext.state.shippingFormMode = "new_address" + }) + + it("calls the create mutation and returns the result if the save address box is checked and there is no *new* saved address ID", async () => { + values.meta.saveAddress = true + mockShippingContext.state.newSavedAddressID = null + + const { result } = setupHook() + const { handleNewUserAddressUpdates } = result.current + + const request = handleNewUserAddressUpdates(values) + + await flushPromiseQueue() + + const operation = await resolveMostRecentOperation({ + CreateUserAddressPayload: () => ({ + userAddressOrErrors: { + __typename: "UserAddress", + internalID: "1234", + }, + }), + }) + + expect(operation.operationName).toBe("useCreateSavedAddressMutation") + + const response = await request + + expect(response?.data?.internalID).toEqual("1234") + }) + + it("calls the update mutation if the save address box is checked and there is *new* saved address ID", async () => { + values.meta.saveAddress = true + mockShippingContext.meData.addressList = [ + { internalID: "1234" } as any, + ] + mockShippingContext.state.newSavedAddressID = "1234" + mockShippingContext.orderData.savedFulfillmentDetails = { + fulfillmentType: FulfillmentType.SHIP, + fulfillmentDetails: {}, + } as any + + const { result } = setupHook() + const { handleNewUserAddressUpdates } = result.current + + const request = handleNewUserAddressUpdates(values) + + await flushPromiseQueue() + + const operation = await resolveMostRecentOperation({ + UpdateUserAddressPayload: () => ({ + userAddressOrErrors: { + __typename: "UserAddress", + internalID: "1234", + }, + }), + }) + + expect(operation.operationName).toBe("useUpdateSavedAddressMutation") + expect(operation.operationVariables.input).toMatchObject({ + userAddressID: "1234", + attributes: expect.any(Object), + }) + + const response = await request + + expect(response?.data?.internalID).toEqual("1234") + }) + + it("calls the delete mutation if a new address is saved and the save address box is not checked", async () => { + // TODO: form values or dispatch to state? Form value updates can be awaited... + mockShippingContext.state.newSavedAddressID = "1234" + values.meta.saveAddress = false + + const { result } = setupHook() + const { handleNewUserAddressUpdates } = result.current + + const request = handleNewUserAddressUpdates(values) + + await flushPromiseQueue() + + const operation = await resolveMostRecentOperation({ + DeleteUserAddressPayload: () => ({ + userAddressOrErrors: { + __typename: "UserAddress", + internalID: "1234", + }, + }), + }) + + expect(operation.operationName).toBe("useDeleteSavedAddressMutation") + + const response = await request + + expect(response?.data).toEqual({ __typename: "UserAddress" }) + }) + }) + describe("executeUserAddressAction", () => { + beforeEach(() => { + mockShippingContext.state.shippingFormMode = "saved_addresses" + }) + + it("calls the create mutation and returns the result if the user address action is create", async () => { + userAddressAction = { + type: "create", + address: { + name: "Erik Example", + phoneNumber: "1234567890", + addressLine1: "401 Broadway", + addressLine2: "", + city: "New York", + region: "NY", + country: "US", + postalCode: "10013", + }, + } + const { result } = setupHook() + const { executeUserAddressAction } = result.current + + const request = executeUserAddressAction(userAddressAction) + + await flushPromiseQueue() + + const operation = await resolveMostRecentOperation({ + CreateUserAddressPayload: () => ({ + userAddressOrErrors: { + __typename: "UserAddress", + internalID: "1234", + }, + }), + }) + + expect(operation.operationName).toBe("useCreateSavedAddressMutation") + + const response = await request + + expect(response.data?.internalID).toEqual("1234") + }) + + it("calls the create mutation and returns the result if the user address action is edit", async () => { + userAddressAction = { + type: "edit", + address: { + internalID: "1234", + name: "Erik Example", + phoneNumber: "1234567890", + addressLine1: "401 Broadway", + addressLine2: "", + city: "New York", + region: "NY", + country: "US", + postalCode: "10013", + }, + } + const { result } = setupHook() + const { executeUserAddressAction } = result.current + + const request = executeUserAddressAction(userAddressAction) + + await flushPromiseQueue() + + const operation = await resolveMostRecentOperation({ + UpdateUserAddressPayload: () => ({ + userAddressOrErrors: { + __typename: "UserAddress", + internalID: "1234", + }, + }), + }) + + expect(operation.operationName).toBe("useUpdateSavedAddressMutation") + + const response = await request + + expect(response.data?.internalID).toEqual("1234") + }) + + it("calls the set as default mutation if a create/update mutation is successful and setAsDefault is true", async () => { + userAddressAction = { + type: "create", + address: { + name: "Erik Example", + phoneNumber: "1234567890", + addressLine1: "401 Broadway", + addressLine2: "", + city: "New York", + region: "NY", + country: "US", + postalCode: "10013", + }, + setAsDefault: true, + } + const { result } = setupHook() + const { executeUserAddressAction } = result.current + + const request = executeUserAddressAction(userAddressAction) + + await flushPromiseQueue() + + await resolveMostRecentOperation({ + CreateUserAddressPayload: () => ({ + userAddressOrErrors: { + __typename: "UserAddress", + internalID: "1234", + }, + }), + }) + + const secondOperation = await resolveMostRecentOperation({ + UpdateUserDefaultAddressPayload: () => ({ + updateUserDefaultAddress: { + userAddressOrErrors: { + __typename: "UserAddress", + }, + }, + }), + }) + + expect(secondOperation.operationName).toBe( + "useUpdateUserDefaultAddressMutation" + ) + const response = await request + + expect(response.data?.internalID).toEqual("1234") + }) + }) + + describe("Error handling", () => { + beforeEach(() => { + userAddressAction = { + type: "create", + address: { + name: "Erik Example", + phoneNumber: "1234567890", + addressLine1: "401 Broadway", + addressLine2: "", + city: "New York", + region: "NY", + country: "US", + postalCode: "10013", + }, + } + }) + + it("returns a list with a single error object if the mutation fails", async () => { + const { result } = setupHook() + const { executeUserAddressAction } = result.current + + const request = executeUserAddressAction(userAddressAction) + + const graphqlError = new Error("mutation failed") + mockRelayEnv.mock.rejectMostRecentOperation(graphqlError) + + const response = await request + expect(response.errors).toEqual([graphqlError]) + }) + + it("returns a list of errors if gravity returns an error type and sets correct field errors", async () => { + const { result } = setupHook() + const { executeUserAddressAction } = result.current + + const request = executeUserAddressAction(userAddressAction) + const error = { + message: "Validation failed for phone: not a valid phone number", + } + + await resolveMostRecentOperation({ + CreateUserAddressPayload: () => ({ + userAddressOrErrors: { + __typename: "Errors", + errors: [error], + }, + }), + }) + + let response = await request + + expect(response.errors).toEqual([ + { + message: "Validation failed for phone: not a valid phone number", + }, + ]) + }) + }) + }) + }) +}) diff --git a/src/Apps/Order/Routes/Shipping2/Hooks/useBackToFulfillmentDetails.tsx b/src/Apps/Order/Routes/Shipping2/Hooks/useBackToFulfillmentDetails.tsx deleted file mode 100644 index da925a924b7..00000000000 --- a/src/Apps/Order/Routes/Shipping2/Hooks/useBackToFulfillmentDetails.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { usePrevious } from "@artsy/palette" -import { useShippingContext } from "Apps/Order/Routes/Shipping2/Hooks/useShippingContext" -import { matchAddressFields } from "Apps/Order/Routes/Shipping2/Utils/shippingUtils" -import { extractNodes } from "Utils/extractNodes" -import { Shipping2_me$data } from "__generated__/Shipping2_me.graphql" -import { useEffect } from "react" - -/** - * Go back to fulfillment details stage if the user edits the address or - * deletes a saved address. - */ -export const useBackToFullfillmentDetails = (me: Shipping2_me$data) => { - const shippingContext = useShippingContext() - - const formValues = shippingContext.state.formHelpers.values - const previousFormValues = usePrevious(formValues) - - const savedAddresses = extractNodes(me.addressConnection) - const previousSavedAddresses = usePrevious(savedAddresses) - - /** - * Go back to fulfillment details stage if the user edits the address or - * deletes a saved address. - */ - useEffect(() => { - if ( - shippingContext.state.stage === "fulfillment_details" || - !shippingContext.orderData.savedFulfillmentDetails - ) { - return - } - - const addressValuesChanged = !matchAddressFields( - formValues.attributes, - previousFormValues.attributes - ) - - const deletedNewSavedAddress = - shippingContext.state.newSavedAddressId && - previousSavedAddresses.length > savedAddresses.length && - !savedAddresses.find( - a => a.internalID === shippingContext.state.newSavedAddressId - ) - - if (addressValuesChanged || deletedNewSavedAddress) { - shippingContext.actions.setStage("fulfillment_details") - - if (deletedNewSavedAddress) { - shippingContext.actions.setNewSavedAddressId(null) - } - } - }, [ - formValues.attributes, - shippingContext.state.formHelpers.values, - shippingContext.orderData.savedFulfillmentDetails, - previousFormValues.attributes, - previousSavedAddresses.length, - savedAddresses, - savedAddresses.length, - shippingContext.state.newSavedAddressId, - shippingContext.state.stage, - shippingContext.actions, - ]) -} diff --git a/src/Apps/Order/Routes/Shipping2/Hooks/useHandleExchangeError.tsx b/src/Apps/Order/Routes/Shipping2/Hooks/useHandleExchangeError.tsx index b76eec6c89f..3342f132783 100644 --- a/src/Apps/Order/Routes/Shipping2/Hooks/useHandleExchangeError.tsx +++ b/src/Apps/Order/Routes/Shipping2/Hooks/useHandleExchangeError.tsx @@ -78,7 +78,7 @@ export const useHandleExchangeError = ({ title, message: formattedMessage, }) - } else if (isArtsyShipping && !orderData.selectedShippingQuoteId) { + } else if (isArtsyShipping && !orderData.selectedShippingQuoteID) { orderTracking.errorMessageViewed({ error_code: null, title: "An error occurred", diff --git a/src/Apps/Order/Routes/Shipping2/Hooks/useHandleSaveFulfillmentDetails.ts b/src/Apps/Order/Routes/Shipping2/Hooks/useHandleSaveFulfillmentDetails.ts new file mode 100644 index 00000000000..34d584061dd --- /dev/null +++ b/src/Apps/Order/Routes/Shipping2/Hooks/useHandleSaveFulfillmentDetails.ts @@ -0,0 +1,129 @@ +import { useOrderTracking } from "Apps/Order/Hooks/useOrderTracking" +import { useShippingContext } from "Apps/Order/Routes/Shipping2/Hooks/useShippingContext" +import { useSaveFulfillmentDetails } from "Apps/Order/Routes/Shipping2/Mutations/useSaveFulfillmentDetails" +import { + FulfillmentType, + FulfillmentValues, +} from "Apps/Order/Routes/Shipping2/Utils/shippingUtils" +import createLogger from "Utils/logger" +import { CommerceSetShippingInput } from "__generated__/useSaveFulfillmentDetailsMutation.graphql" + +const logger = createLogger( + "Order/Routes/Shipping2/Hooks/useHandleSaveFulfillmentDetails" +) + +type ExchangeOrGravityError = + | Error + | { + code: string + data: string | null | undefined + type: string + } + +export const useHandleSaveFulfillmentDetails = () => { + const shippingContext = useShippingContext() + const saveFulfillmentDetails = useSaveFulfillmentDetails() + const orderTracking = useOrderTracking() + + const handleSaveFulfillmentDetails = async ( + values: FulfillmentValues + ): Promise< + | { + error: ExchangeOrGravityError + data?: undefined + } + | { + error?: undefined + data: { + internalID: string + requiresArtsyShippingToDestination: boolean + } + } + > => { + try { + let fulfillmentMutationValues: CommerceSetShippingInput + let requiresArtsyShippingToDestination: boolean + + shippingContext.actions.setIsPerformingOperation(true) + + if (values.fulfillmentType === FulfillmentType.PICKUP) { + requiresArtsyShippingToDestination = false + + fulfillmentMutationValues = { + id: shippingContext.orderData.internalID, + fulfillmentType: FulfillmentType.PICKUP, + phoneNumber: values.attributes.phoneNumber, + shipping: { + addressLine1: "", + addressLine2: "", + country: "", + name: "", + city: "", + postalCode: "", + region: "", + phoneNumber: "", + }, + } + } else { + requiresArtsyShippingToDestination = shippingContext.orderData.requiresArtsyShippingTo( + values.attributes.country + ) + + const { phoneNumber, ...addressValues } = values.attributes + + fulfillmentMutationValues = { + id: shippingContext.orderData.internalID, + fulfillmentType: requiresArtsyShippingToDestination + ? "SHIP_ARTA" + : FulfillmentType.SHIP, + phoneNumber, + shipping: { ...addressValues, phoneNumber: "" }, + } + + if (values.meta.addressVerifiedBy) { + fulfillmentMutationValues.addressVerifiedBy = + values.meta.addressVerifiedBy + } + } + + const result = await saveFulfillmentDetails.submitMutation({ + variables: { input: fulfillmentMutationValues }, + }) + + const orderOrError = result.commerceSetShipping?.orderOrError + + switch (orderOrError?.__typename) { + case "CommerceOrderWithMutationSuccess": + return { + data: { ...orderOrError.order, requiresArtsyShippingToDestination }, + } + case "CommerceOrderWithMutationFailure": + shippingContext.actions.handleExchangeError( + orderOrError.error, + logger + ) + return { error: orderOrError.error } + default: + logger.error("Unexpected mutation result", orderOrError) + return { + error: new Error( + "Unexpected mutation result: " + orderOrError?.__typename + ), + } + } + } catch (error) { + orderTracking.errorMessageViewed({ + error_code: null, + title: "An error occurred", + message: + "Something went wrong. Please try again or contact orders@artsy.net.", + flow: "user selects a shipping option", + }) + + shippingContext.actions.dialog.showErrorDialog() + return { error } + } + } + + return { handleSaveFulfillmentDetails } +} diff --git a/src/Apps/Order/Routes/Shipping2/Hooks/useHandleUserAddressUpdates.ts b/src/Apps/Order/Routes/Shipping2/Hooks/useHandleUserAddressUpdates.ts deleted file mode 100644 index 6516096c9d9..00000000000 --- a/src/Apps/Order/Routes/Shipping2/Hooks/useHandleUserAddressUpdates.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { useShippingContext } from "Apps/Order/Routes/Shipping2/Hooks/useShippingContext" -import { useCreateSavedAddress } from "Apps/Order/Routes/Shipping2/Mutations/useCreateSavedAddress" -import { useDeleteSavedAddress } from "Apps/Order/Routes/Shipping2/Mutations/useDeleteSavedAddress" -import { useUpdateSavedAddress } from "Apps/Order/Routes/Shipping2/Mutations/useUpdateSavedAddress" -import { - FulfillmentType, - FulfillmentValues, - addressWithFallbackValues, - matchAddressFields, -} from "Apps/Order/Routes/Shipping2/Utils/shippingUtils" -import createLogger from "Utils/logger" - -const logger = createLogger( - "Order/Routes/Shipping/Hooks/useHandleUserAddressUpdates.tsx" -) - -export const useHandleUserAddressUpdates = () => { - const shippingContext = useShippingContext() - const createSavedAddress = useCreateSavedAddress() - const updateSavedAddress = useUpdateSavedAddress() - const deleteSavedAddress = useDeleteSavedAddress() - - const handleUserAddressUpdates = async (formValues: FulfillmentValues) => { - if (formValues.fulfillmentType !== FulfillmentType.SHIP) { - return - } - - const formAddressAttributes = formValues.attributes - - const addressShouldBeSaved = !!formAddressAttributes.saveAddress - - const current = { - newSavedAddressId: shippingContext.state.newSavedAddressId, - savedFulfillmentDetails: - shippingContext.orderData.savedFulfillmentDetails?.fulfillmentDetails, - } - - try { - if (addressShouldBeSaved) { - // Address not saved, create it - if (!current.newSavedAddressId) { - shippingContext.actions.setIsPerformingOperation(true) - - const response = await createSavedAddress.submitMutation({ - variables: { - input: { - attributes: addressWithFallbackValues(formAddressAttributes), - }, - }, - }) - - const newAddress = response?.createUserAddress?.userAddressOrErrors - - if (newAddress?.__typename === "UserAddress") { - shippingContext.actions.setNewSavedAddressId(newAddress.internalID) - return - } - - // Address create failed - // const errorMessage = newAddress?.__typename === "Errors" - // ? newAddress.errors.map(e => e.message).join(", ") - // : "Something went wrong." - // throw new Error(errorMessage) - return - } else if ( - current.newSavedAddressId && - current.savedFulfillmentDetails && - !matchAddressFields( - current.savedFulfillmentDetails, - formAddressAttributes - ) - ) { - shippingContext.actions.setIsPerformingOperation(true) - - await updateSavedAddress.submitMutation({ - variables: { - input: { - userAddressID: current.newSavedAddressId, - attributes: addressWithFallbackValues(formAddressAttributes), - }, - }, - }) - } - // Address should not be saved, delete it if it exists - } else { - if (shippingContext.state.newSavedAddressId) { - shippingContext.actions.setIsPerformingOperation(true) - - await deleteSavedAddress.submitMutation({ - variables: { - input: { - userAddressID: shippingContext.state.newSavedAddressId, - }, - }, - }) - - shippingContext.actions.setNewSavedAddressId(null) - } - } - } catch (error) { - logger.error(error) - } finally { - shippingContext.actions.setIsPerformingOperation(false) - } - } - - return { handleUserAddressUpdates } -} diff --git a/src/Apps/Order/Routes/Shipping2/Hooks/useSaveSelectedShippingQuote.tsx b/src/Apps/Order/Routes/Shipping2/Hooks/useSaveSelectedShippingQuote.tsx index 6b35b80d3a9..a19db6eeb01 100644 --- a/src/Apps/Order/Routes/Shipping2/Hooks/useSaveSelectedShippingQuote.tsx +++ b/src/Apps/Order/Routes/Shipping2/Hooks/useSaveSelectedShippingQuote.tsx @@ -1,5 +1,5 @@ import { useOrderTracking } from "Apps/Order/Hooks/useOrderTracking" -import { useHandleUserAddressUpdates } from "Apps/Order/Routes/Shipping2/Hooks/useHandleUserAddressUpdates" +import { useUserAddressUpdates } from "Apps/Order/Routes/Shipping2/Hooks/useUserAddressUpdates" import { useShippingContext } from "Apps/Order/Routes/Shipping2/Hooks/useShippingContext" import { useSelectShippingQuote } from "Apps/Order/Routes/Shipping2/Mutations/useSelectShippingQuote" import { FulfillmentType } from "Apps/Order/Routes/Shipping2/Utils/shippingUtils" @@ -19,10 +19,10 @@ export const useSaveSelectedShippingQuote = ( const orderTracking = useOrderTracking() const shippingContext = useShippingContext() const selectShippingQuote = useSelectShippingQuote() - const { handleUserAddressUpdates } = useHandleUserAddressUpdates() + const { handleNewUserAddressUpdates } = useUserAddressUpdates() const saveSelectedShippingQuote = async () => { - if (!shippingContext.state.selectedShippingQuoteId) { + if (!shippingContext.state.selectedShippingQuoteID) { logger.error("No shipping quote selected") return } @@ -36,12 +36,18 @@ export const useSaveSelectedShippingQuote = ( try { shippingContext.actions.setIsPerformingOperation(true) + if (shippingContext.state.shippingFormMode === "new_address") { + await handleNewUserAddressUpdates( + shippingContext.state.fulfillmentDetailsFormikContext.values + ) + } + const result = await selectShippingQuote.submitMutation({ variables: { input: { id: order.internalID, selectedShippingQuoteId: - shippingContext.state.selectedShippingQuoteId, + shippingContext.state.selectedShippingQuoteID, }, }, }) @@ -53,8 +59,6 @@ export const useSaveSelectedShippingQuote = ( return } - await handleUserAddressUpdates(shippingContext.state.formHelpers.values) - // Advance to payment router.push(`/orders/${order.internalID}/payment`) } catch (error) { diff --git a/src/Apps/Order/Routes/Shipping2/Hooks/useSelectFirstShippingQuote.tsx b/src/Apps/Order/Routes/Shipping2/Hooks/useSelectFirstShippingQuote.tsx index 0c04bfd9330..2fa322a4ae8 100644 --- a/src/Apps/Order/Routes/Shipping2/Hooks/useSelectFirstShippingQuote.tsx +++ b/src/Apps/Order/Routes/Shipping2/Hooks/useSelectFirstShippingQuote.tsx @@ -7,26 +7,27 @@ import { useEffect } from "react" export const useSelectFirstShippingQuote = () => { const shippingContext = useShippingContext() - const defaultShippingQuoteId = + const defaultShippingQuoteID = shippingContext.orderData.shippingQuotes?.[0]?.id - const bestArtsyShippingQuote = shippingContext.state.isArtsyShipping + const bestArtsyShippingQuote = shippingContext.orderData + .savedFulfillmentDetails?.isArtsyShipping ? shippingContext.orderData.shippingQuotes?.find(quote => quote.isSelected) - ?.id || defaultShippingQuoteId + ?.id || defaultShippingQuoteID : null useEffect(() => { if ( bestArtsyShippingQuote && bestArtsyShippingQuote !== - shippingContext.orderData.selectedShippingQuoteId + shippingContext.orderData.selectedShippingQuoteID ) { shippingContext.actions.setSelectedShippingQuote(bestArtsyShippingQuote) } // eslint-disable-next-line react-hooks/exhaustive-deps }, [ - shippingContext.orderData.selectedShippingQuoteId, - defaultShippingQuoteId, + shippingContext.orderData.selectedShippingQuoteID, + defaultShippingQuoteID, bestArtsyShippingQuote, ]) } diff --git a/src/Apps/Order/Routes/Shipping2/Hooks/useUserAddressUpdates.ts b/src/Apps/Order/Routes/Shipping2/Hooks/useUserAddressUpdates.ts new file mode 100644 index 00000000000..5aa5f8fc37d --- /dev/null +++ b/src/Apps/Order/Routes/Shipping2/Hooks/useUserAddressUpdates.ts @@ -0,0 +1,282 @@ +import { useShippingContext } from "Apps/Order/Routes/Shipping2/Hooks/useShippingContext" +import { useCreateSavedAddress } from "Apps/Order/Routes/Shipping2/Mutations/useCreateSavedAddress" +import { useDeleteSavedAddress } from "Apps/Order/Routes/Shipping2/Mutations/useDeleteSavedAddress" +import { useUpdateSavedAddress } from "Apps/Order/Routes/Shipping2/Mutations/useUpdateSavedAddress" +import { useUpdateUserDefaultAddress } from "Apps/Order/Routes/Shipping2/Mutations/useUpdateUserDefaultAddress" +import { ShippingContextProps } from "Apps/Order/Routes/Shipping2/ShippingContext" +import { + FulfillmentType, + FulfillmentValues, + SavedAddressType, + ShipValues, + addressWithFallbackValues, + getAddressByID, + matchAddressFields, +} from "Apps/Order/Routes/Shipping2/Utils/shippingUtils" +import createLogger from "Utils/logger" +import { useCreateSavedAddressMutation$data } from "__generated__/useCreateSavedAddressMutation.graphql" +import { useDeleteSavedAddressMutation$data } from "__generated__/useDeleteSavedAddressMutation.graphql" +import { useUpdateSavedAddressMutation$data } from "__generated__/useUpdateSavedAddressMutation.graphql" + +const logger = createLogger( + "Order/Routes/Shipping/Hooks/useHandleUserAddressUpdates.tsx" +) + +type Result = + /* An error occurred */ + | { + actionType: UserAddressAction["type"] + errors: ReadonlyArray<{ message: string }> + data: null + } + /* An operation was performed successfully */ + | (Success & { errors: null; actionType: UserAddressAction["type"] }) + +export type SavedAddressResult = Result<{ + data: SavedAddressType +}> + +interface AddressInput { + addressLine1: string + addressLine2?: string + city: string + country: string + postalCode?: string + region?: string + name: string + phoneNumber: string +} + +export type UserAddressAction = + | { + type: "edit" + address: AddressInput & { internalID: string } + setAsDefault?: boolean + } + | { + type: "delete" + address: { internalID: string } + } + | { + type: "create" + address: AddressInput + setAsDefault?: boolean + } + +export const useUserAddressUpdates = () => { + const createSavedAddress = useCreateSavedAddress() + const updateSavedAddress = useUpdateSavedAddress() + const deleteSavedAddress = useDeleteSavedAddress() + const updateDefaultAddress = useUpdateUserDefaultAddress() + const shippingContext = useShippingContext() + + const handleNewUserAddressUpdates = async ( + values: FulfillmentValues + ): Promise => { + if (values.fulfillmentType !== FulfillmentType.SHIP) { + return null + } + + let userAddressAction: UserAddressAction | null = null + + userAddressAction = getUserAddressActionForAddressFormValues( + values, + shippingContext + ) + + if (!userAddressAction) { + return null + } + + const result = await executeUserAddressAction(userAddressAction) + + if (!result) { + return null + } + + if (result.errors) { + return result + } + + return { + ...result, + actionType: userAddressAction.type, + } + } + + const executeUserAddressAction = async ( + userAddressAction: UserAddressAction + ): Promise => { + let processedPayload: Omit | null = null + try { + switch (userAddressAction.type) { + case "create": { + shippingContext.actions.setIsPerformingOperation(true) + + const response = await createSavedAddress.submitMutation({ + variables: { + input: { + attributes: addressWithFallbackValues( + userAddressAction.address + ), + }, + }, + }) + processedPayload = handleMutationPayload(response.createUserAddress) + + break + } + + case "edit": { + shippingContext.actions.setIsPerformingOperation(true) + + const input = { + userAddressID: userAddressAction.address.internalID, + attributes: addressWithFallbackValues(userAddressAction.address), + } + + const response = await updateSavedAddress.submitMutation({ + variables: { + input, + }, + }) + + processedPayload = handleMutationPayload(response.updateUserAddress) + + break + } + + case "delete": { + shippingContext.actions.setIsPerformingOperation(true) + + const response = await deleteSavedAddress.submitMutation({ + variables: { + input: { + userAddressID: userAddressAction.address.internalID, + }, + }, + }) + processedPayload = handleMutationPayload(response.deleteUserAddress) + break + } + } + + const result = { + ...processedPayload, + actionType: userAddressAction.type, + } as SavedAddressResult + + if (result.errors) { + return result + } + + const setDefaultID = + userAddressAction.type !== "delete" && userAddressAction.setAsDefault + ? result.data.internalID + : undefined + let setDefaultResult: Omit | null = null + if (setDefaultID) { + const setDefaultResponse = await updateDefaultAddress.submitMutation({ + variables: { + input: { + userAddressID: setDefaultID, + }, + }, + }) + setDefaultResult = handleMutationPayload( + setDefaultResponse.updateUserDefaultAddress + ) + if (setDefaultResult.errors) { + // Todo: Handle an error in setting the default address + logger.error(setDefaultResult.errors.map(e => e.message).join(", ")) + } + } + + return { ...result, actionType: userAddressAction.type } + } catch (error) { + const result: SavedAddressResult = { + actionType: userAddressAction.type, + errors: [error], + data: null, + } + return result + } + } + + return { handleNewUserAddressUpdates, executeUserAddressAction } +} + +const getUserAddressActionForAddressFormValues = ( + values: ShipValues, + shippingContext: ShippingContextProps +): UserAddressAction | null => { + const savedFulfillmentDetails = + shippingContext.orderData.savedFulfillmentDetails + const formAddressAttributes = values.attributes + + if (values.meta.saveAddress) { + // The address form doesn't include this input, and is only used for the + // user's first address (not when they already have saved addresses). + if (!shippingContext.state.newSavedAddressID) { + return { type: "create", address: values.attributes, setAsDefault: false } + } else if ( + savedFulfillmentDetails?.fulfillmentType === FulfillmentType.SHIP && + !matchAddressFields( + formAddressAttributes, + savedFulfillmentDetails.attributes + ) + ) { + const newSavedAddress = getAddressByID( + shippingContext.meData.addressList, + shippingContext.state.newSavedAddressID + ) + + if (!newSavedAddress) { + logger.error("Expected a new saved address to exist") + } + + if ( + newSavedAddress && + !matchAddressFields(newSavedAddress, formAddressAttributes) + ) { + return { + type: "edit", + address: { + ...formAddressAttributes, + internalID: shippingContext.state.newSavedAddressID, + }, + setAsDefault: false, + } + } + } + } + + if (!values.meta.saveAddress && shippingContext.state.newSavedAddressID) { + return { + type: "delete", + address: { internalID: shippingContext.state.newSavedAddressID }, + } + } + + return null +} + +const handleMutationPayload = ( + payload: + | useUpdateSavedAddressMutation$data["updateUserAddress"] + | useCreateSavedAddressMutation$data["createUserAddress"] + | useDeleteSavedAddressMutation$data["deleteUserAddress"] +): Omit => { + const addressOrErrors = payload?.userAddressOrErrors + + if (addressOrErrors?.__typename === "Errors") { + return { + errors: addressOrErrors.errors, + data: null, + } + } + return { + errors: null, + data: addressOrErrors as SavedAddressType, + } +} diff --git a/src/Apps/Order/Routes/Shipping2/Mutations/useSaveFulfillmentDetails.ts b/src/Apps/Order/Routes/Shipping2/Mutations/useSaveFulfillmentDetails.ts index 947cbb40966..9bf66aa55f5 100644 --- a/src/Apps/Order/Routes/Shipping2/Mutations/useSaveFulfillmentDetails.ts +++ b/src/Apps/Order/Routes/Shipping2/Mutations/useSaveFulfillmentDetails.ts @@ -13,7 +13,7 @@ export const useSaveFulfillmentDetails = () => { __typename ... on CommerceOrderWithMutationSuccess { order { - id + internalID __typename ... on CommerceOrder { ...Shipping2_order diff --git a/src/Apps/Order/Routes/Shipping2/ShippingContext.tsx b/src/Apps/Order/Routes/Shipping2/ShippingContext.tsx index c710a734419..1052857a5fa 100644 --- a/src/Apps/Order/Routes/Shipping2/ShippingContext.tsx +++ b/src/Apps/Order/Routes/Shipping2/ShippingContext.tsx @@ -1,23 +1,35 @@ -import { createContext, FC } from "react" +import React, { createContext, FC, useMemo, useReducer, useRef } from "react" import { ComputedOrderData, computeOrderData, } from "Apps/Order/Routes/Shipping2/Utils/computeOrderData" -import { useReducer } from "react" +import { compact } from "lodash" +import { extractNodes } from "Utils/extractNodes" import { ShippingProps, ShippingStage } from "Apps/Order/Routes/Shipping2" import { FormikProps } from "formik" -import { FulfillmentValues } from "Apps/Order/Routes/Shipping2/Utils/shippingUtils" +import { + FulfillmentValues, + SavedAddressType, +} from "Apps/Order/Routes/Shipping2/Utils/shippingUtils" import createLogger from "Utils/logger" import { Dialog } from "Apps/Order/Dialogs" import { useHandleExchangeError } from "Apps/Order/Routes/Shipping2/Hooks/useHandleExchangeError" export interface State { - formHelpers: FormHelpers - newSavedAddressId: string | null - selectedShippingQuoteId: string | null + // Form state for fulfillment details + fulfillmentDetailsFormikContext: FormikProps + // Whether to show the new address form or the saved addresses radio buttons + shippingFormMode: "new_address" | "saved_addresses" + // Presence of this value indicates that the user has saved their first address + newSavedAddressID: string | null + // Form state for saved address radio buttons + selectedSavedAddressID: string | null + // Form state for shipping quote radio buttons + selectedShippingQuoteID: string | null + // Stage of the shipping flow the user is in stage: ShippingStage + // Manually set & unset when performing mutations isPerformingOperation: boolean - isArtsyShipping: boolean } interface Actions { @@ -29,9 +41,13 @@ interface Actions { }, logger: ReturnType ) => void - setFormHelpers: (payload: FormHelpers) => void + setFulfillmentDetailsFormikContext: ( + payload: FormikProps + ) => void setSelectedShippingQuote: (payload: string | null) => void - setNewSavedAddressId: (payload: string | null) => void + setNewSavedAddressID: (payload: string | null) => void + setSelectedSavedAddressID: (payload: string | null) => void + setShippingFormMode: (payload: "new_address" | "saved_addresses") => void setIsPerformingOperation: (payload: boolean) => void setStage: (payload: ShippingStage) => void } @@ -40,6 +56,9 @@ export interface ShippingContextProps { actions: Actions state: State orderData: ComputedOrderData + meData: { + addressList: SavedAddressType[] + } } export const ShippingContext = createContext({} as any) @@ -48,68 +67,90 @@ export const ShippingContextProvider: FC> = props => { - const orderData = computeOrderData(props.order, props.me) + const meData = useMemo( + () => ({ + addressList: compact( + extractNodes(props.me?.addressConnection) ?? [] + ), + }), + [props.me.addressConnection] + ) + const orderData = computeOrderData(props.order, meData) - const isArtsyShipping = !!orderData.savedFulfillmentDetails?.isArtsyShipping + /* + * Because there is a single button for both fulfillment details and + * shipping quote steps (and duplicated in the sidebar) + * we need to hack some formik values UP from the fulfillment details form. + */ + const fulfillmentFormikRef = useRef>(({ + // Used to submit the form + submitForm: () => Promise.reject(new Error("form not loaded")), + // Used to disable the button + isValid: false, + // Used to get the form values for un-saving the address if the user + // unchecks it after saving it in the fulfillment details step. + values: { + attributes: { + saveAddress: false, + }, + }, + } as unknown) as FormikProps) const initialState: State = { - /** - * Because there is a single button for both fulfillment details and - * shipping quote steps (and duplicated in the sidebar) - * we need to hack some formik values UP from the fulfillment details form. - */ - formHelpers: ({ - // Used to submit the form - submitForm: () => Promise.reject(new Error("form not loaded")), - // Used to disable the button - isValid: false, - // Used to get the form values for un-saving the address if the user - // unchecks it after saving it in the fulfillment details step. - values: { - attributes: { - saveAddress: false, - }, - }, - } as unknown) as FormHelpers, - isArtsyShipping, + fulfillmentDetailsFormikContext: fulfillmentFormikRef.current, + shippingFormMode: + meData.addressList.length > 0 ? "saved_addresses" : "new_address", isPerformingOperation: false, - newSavedAddressId: null, - selectedShippingQuoteId: orderData.selectedShippingQuoteId ?? null, - stage: isArtsyShipping ? "refresh_shipping_quotes" : "fulfillment_details", + newSavedAddressID: null, + selectedShippingQuoteID: orderData.selectedShippingQuoteID ?? null, + stage: "fulfillment_details", + selectedSavedAddressID: + orderData.savedFulfillmentDetails?.selectedSavedAddressID ?? null, } const [state, dispatch] = useReducer(shippingStateReducer, initialState) const { handleExchangeError } = useHandleExchangeError({ dialog: props.dialog, - isArtsyShipping, + isArtsyShipping: + orderData.savedFulfillmentDetails?.isArtsyShipping ?? false, orderData, }) const actions = { - setFormHelpers: (formHelpers: FormHelpers) => { - dispatch({ type: "SET_FORM_HELPERS", payload: formHelpers }) + setFulfillmentDetailsFormikContext: ( + formHelpers: FormikProps + ) => { + dispatch({ type: "SET_FULFILLMENT_DETAILS_CTX", payload: formHelpers }) + }, + setIsPerformingOperation: (payload: boolean) => { + dispatch({ type: "SET_IS_PERFORMING_OPERATION", payload }) + }, + setNewSavedAddressID: (payload: string | null) => { + dispatch({ type: "SET_NEW_SAVED_ADDRESS_ID", payload }) }, setSelectedShippingQuote: (payload: string | null) => { dispatch({ type: "SET_SELECTED_SHIPPING_QUOTE", payload }) }, - setNewSavedAddressId: (payload: string | null) => { - dispatch({ type: "SET_NEW_SAVED_ADDRESS_ID", payload }) + setSelectedSavedAddressID: (payload: string | null) => { + dispatch({ type: "SET_SELECTED_SAVED_ADDRESS_ID", payload }) + }, + setShippingFormMode: (payload: "new_address" | "saved_addresses") => { + dispatch({ type: "SET_SHIPPING_FORM_MODE", payload }) }, setStage: (payload: ShippingStage) => { dispatch({ type: "SET_STAGE", payload }) }, - setIsPerformingOperation: (payload: boolean) => { - dispatch({ type: "SET_IS_PERFORMING_OPERATION", payload }) - }, + handleExchangeError, dialog: props.dialog, } const contextProps = { - state, - orderData, actions, + meData, + orderData, + state, } return ( @@ -119,48 +160,63 @@ export const ShippingContextProvider: FC - export type Action = | { - type: "SET_FORM_HELPERS" - payload: FormHelpers + type: "SET_FULFILLMENT_DETAILS_CTX" + payload: FormikProps } - | { type: "SET_SELECTED_SHIPPING_QUOTE"; payload: string | null } + | { type: "SET_IS_PERFORMING_OPERATION"; payload: boolean } | { type: "SET_NEW_SAVED_ADDRESS_ID"; payload: string | null } + | { type: "SET_SELECTED_SHIPPING_QUOTE"; payload: string | null } + | { type: "SET_SELECTED_SAVED_ADDRESS_ID"; payload: string | null } + | { + type: "SET_SHIPPING_FORM_MODE" + payload: "new_address" | "saved_addresses" + } | { type: "SET_STAGE"; payload: ShippingStage } - | { type: "SET_IS_PERFORMING_OPERATION"; payload: boolean } const shippingStateReducer = (state: State, action: Action): State => { switch (action.type) { - case "SET_FORM_HELPERS": { + case "SET_FULFILLMENT_DETAILS_CTX": { return { ...state, - formHelpers: action.payload, + fulfillmentDetailsFormikContext: action.payload, } } - case "SET_SELECTED_SHIPPING_QUOTE": { + case "SET_IS_PERFORMING_OPERATION": { return { ...state, - selectedShippingQuoteId: action.payload, + isPerformingOperation: action.payload, } } case "SET_NEW_SAVED_ADDRESS_ID": { return { ...state, - newSavedAddressId: action.payload, + newSavedAddressID: action.payload, } } - case "SET_STAGE": { + case "SET_SELECTED_SAVED_ADDRESS_ID": { return { ...state, - stage: action.payload, + selectedSavedAddressID: action.payload, } } - case "SET_IS_PERFORMING_OPERATION": { + case "SET_SELECTED_SHIPPING_QUOTE": { return { ...state, - isPerformingOperation: action.payload, + selectedShippingQuoteID: action.payload, + } + } + case "SET_SHIPPING_FORM_MODE": { + return { + ...state, + shippingFormMode: action.payload, + } + } + case "SET_STAGE": { + return { + ...state, + stage: action.payload, } } default: { diff --git a/src/Apps/Order/Routes/Shipping2/Utils/computeOrderData.ts b/src/Apps/Order/Routes/Shipping2/Utils/computeOrderData.ts index c84d4fac253..ea60115791f 100644 --- a/src/Apps/Order/Routes/Shipping2/Utils/computeOrderData.ts +++ b/src/Apps/Order/Routes/Shipping2/Utils/computeOrderData.ts @@ -1,4 +1,5 @@ import { ShippingProps } from "Apps/Order/Routes/Shipping2" +import { ShippingContextProps } from "Apps/Order/Routes/Shipping2/ShippingContext" import { FulfillmentType, PickupValues, @@ -10,12 +11,13 @@ import { ALL_COUNTRY_CODES, EU_COUNTRY_CODES } from "Components/CountrySelect" import { extractNodes } from "Utils/extractNodes" export interface ComputedOrderData { + internalID: string availableShippingCountries: string[] lockShippingCountryTo: "EU" | string | null requiresArtsyShippingTo: (shipTo: string) => boolean savedFulfillmentDetails: SavedFulfillmentData savedShippingQuoteData: SavedShippingQuoteData - selectedShippingQuoteId?: string + selectedShippingQuoteID?: string shippingQuotes?: Array<{ id: string; isSelected: boolean }> shipsFrom: string } @@ -24,25 +26,25 @@ type SavedFulfillmentData = | { fulfillmentType: FulfillmentType.PICKUP isArtsyShipping: false - fulfillmentDetails: PickupValues["attributes"] - selectedSavedAddressId: null + attributes: PickupValues["attributes"] + selectedSavedAddressID: null } | { fulfillmentType: FulfillmentType.SHIP isArtsyShipping: boolean - fulfillmentDetails: ShippingAddressFormValues - selectedSavedAddressId: string | null + attributes: ShippingAddressFormValues + selectedSavedAddressID: string | null } | null type SavedShippingQuoteData = { - selectedShippingQuoteId: string | null + selectedShippingQuoteID: string | null shippingQuotes: Array<{ id: string; isSelected: boolean }> } | null export const computeOrderData = ( order: ShippingProps["order"], - me: ShippingProps["me"] + meData: ShippingContextProps["meData"] ): ComputedOrderData => { // FIXME: Non-null assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion @@ -53,7 +55,7 @@ export const computeOrderData = ( // FIXME: Non-null assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const artworkCountry = firstArtwork?.shippingCountry! - const savedFulfillmentDetails = getSavedFulfillmentDetails(order, me) + const savedFulfillmentDetails = getSavedFulfillmentDetails(order, meData) // FIXME: Non-null assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const shipsFrom = firstArtwork.shippingCountry! @@ -89,13 +91,14 @@ export const computeOrderData = ( const shippingQuotes = extractNodes(firstLineItem.shippingQuoteOptions) ?? [] - const selectedShippingQuoteId = + const selectedShippingQuoteID = shippingQuotes.find(quote => quote.isSelected)?.id ?? null return { + internalID: order.internalID, savedFulfillmentDetails, savedShippingQuoteData: { - selectedShippingQuoteId, + selectedShippingQuoteID, shippingQuotes, }, shippingQuotes, @@ -108,7 +111,7 @@ export const computeOrderData = ( const getSavedFulfillmentDetails = ( order: ShippingProps["order"], - me: ShippingProps["me"] + meData: ShippingContextProps["meData"] ): SavedFulfillmentData => { const fulfillmentTypeName = order.requestedFulfillment?.__typename @@ -117,11 +120,17 @@ const getSavedFulfillmentDetails = ( return { fulfillmentType: FulfillmentType.PICKUP, isArtsyShipping: false, - fulfillmentDetails: { + attributes: { phoneNumber: order.requestedFulfillment.phoneNumber ?? "", name: "", + addressLine1: "", + addressLine2: "", + city: "", + region: "", + country: "", + postalCode: "", }, - selectedSavedAddressId: null, + selectedSavedAddressID: null, } } else if ( ["CommerceShip", "CommerceShipArta"].includes(fulfillmentTypeName) @@ -129,11 +138,10 @@ const getSavedFulfillmentDetails = ( const fulfillmentDetails = addressWithFallbackValues( order.requestedFulfillment ) - const savedAddresses = extractNodes(me.addressConnection) - - const selectedSavedAddressId = + // TODO: can this logic be colocated with other areas, like FulfillmentDetails' getInitialValues? + const selectedSavedAddressID = (fulfillmentDetails && - savedAddresses.find(node => + meData.addressList.find(node => matchAddressFields(node, fulfillmentDetails) )?.internalID) ?? null @@ -141,10 +149,8 @@ const getSavedFulfillmentDetails = ( return { fulfillmentType: FulfillmentType.SHIP, isArtsyShipping: fulfillmentTypeName === "CommerceShipArta", - fulfillmentDetails: addressWithFallbackValues( - order.requestedFulfillment - ), - selectedSavedAddressId: selectedSavedAddressId, + attributes: addressWithFallbackValues(order.requestedFulfillment), + selectedSavedAddressID: selectedSavedAddressID, } } } diff --git a/src/Apps/Order/Routes/Shipping2/Utils/shippingUtils.ts b/src/Apps/Order/Routes/Shipping2/Utils/shippingUtils.ts index ca538c51032..14c543c9aab 100644 --- a/src/Apps/Order/Routes/Shipping2/Utils/shippingUtils.ts +++ b/src/Apps/Order/Routes/Shipping2/Utils/shippingUtils.ts @@ -1,6 +1,8 @@ +import * as Yup from "yup" import { AddressVerifiedBy } from "Apps/Order/Components/AddressVerificationFlow" import { ShippingProps } from "Apps/Order/Routes/Shipping2" import { pick, omitBy, isNil, isEqual } from "lodash" +import { postalCodeValidator } from "Components/Address/utils" export enum FulfillmentType { SHIP = "SHIP", @@ -12,15 +14,34 @@ export interface PickupValues { attributes: { name: string phoneNumber: string + // Even though these don't appear on the form + // we want values to not mess with the controlled/ + // uncontrolled component state + addressLine1: "" + addressLine2: "" + city: "" + region: "" + country: "" + postalCode: "" } + meta: FormMetaValues } export interface ShipValues { fulfillmentType: FulfillmentType.SHIP - attributes: ShippingAddressFormValues & { - saveAddress: boolean - addressVerifiedBy: AddressVerifiedBy | null - } + attributes: ShippingAddressFormValues + meta: FormMetaValues +} + +interface FormMetaValues { + // Address was verified (in this flow instance) + addressVerifiedBy?: AddressVerifiedBy | null + // User saved an address within the lifecycle of this form + newSavedAddressID?: string + // Address should be saved (create/update) to user's address book + saveAddress?: boolean + // Address should be set as default in user's address book + setAddressAsDefault?: boolean } export type FulfillmentValues = ShipValues | PickupValues @@ -29,13 +50,26 @@ export interface ShippingAddressFormValues { name: string phoneNumber: string addressLine1: string - addressLine2?: string + addressLine2: string city: string region: string country: string postalCode: string } +export const ADDRESS_VALIDATION_SHAPE = { + addressLine1: Yup.string().required("Street address is required"), + addressLine2: Yup.string().nullable(), + city: Yup.string().required("City is required"), + postalCode: postalCodeValidator, + region: Yup.string().when("country", { + is: country => ["US", "CA"].includes(country), + then: Yup.string().required("State is required"), + otherwise: Yup.string(), + }), + country: Yup.string().required("Country is required"), +} + const ORDER_EMPTY_ADDRESS: ShippingAddressFormValues = { name: "", phoneNumber: "", @@ -54,6 +88,11 @@ export const onlyAddressValues = (values: any) => { ) } +/** + * Takes an address object and returns a new address object with all the + * non-null values from the original address object. Useful for converting + * a SavedAddress from relay to a ShippingAddressFormValues object. + */ export const addressWithFallbackValues = ( address: any ): ShippingAddressFormValues => ({ @@ -69,6 +108,13 @@ export type SavedAddressType = NonNullable< >["node"] > +export const getAddressByID = ( + addressList: SavedAddressType[], + addressID: string +) => { + return addressList.find(node => node.internalID === addressID) +} + // Get the user's default address, optionally filtering by a list of countries. export const getDefaultUserAddress = ( savedAddresses: SavedAddressType[], diff --git a/src/Apps/Order/Routes/Shipping2/__tests__/SavedAddresses2.jest.tsx b/src/Apps/Order/Routes/Shipping2/__tests__/SavedAddresses2.jest.tsx deleted file mode 100644 index e6c3a303eef..00000000000 --- a/src/Apps/Order/Routes/Shipping2/__tests__/SavedAddresses2.jest.tsx +++ /dev/null @@ -1,215 +0,0 @@ -import { graphql } from "react-relay" -import { setupTestWrapper } from "DevTools/setupTestWrapper" -import { AddressModal } from "Apps/Order/Routes/Shipping2/Components/AddressModal2" -import { RootTestPage } from "DevTools/RootTestPage" -import { userAddressMutation } from "Apps/__tests__/Fixtures/Order/MutationResults" -import { SavedAddressItem } from "Apps/Order/Routes/Shipping2/Components/SavedAddressItem2" -import { useTracking } from "react-tracking" -import { AnalyticsCombinedContextProvider } from "System/Analytics/AnalyticsContext" -import { waitFor } from "@testing-library/react" -import { SavedAddressesFragmentContainer } from "Apps/Order/Routes/Shipping2/Components/SavedAddresses2" -import { DeepPartial } from "Utils/typeSupport" -import { ShippingContextProps } from "Apps/Order/Routes/Shipping2/ShippingContext" - -jest.unmock("react-relay") -jest.mock("react-tracking") -jest.mock("Utils/Hooks/useMatchMedia", () => ({ - __internal__useMatchMedia: () => ({}), -})) - -const mockContext: DeepPartial = { - orderData: { - availableShippingCountries: ["US"], - savedFulfillmentDetails: { selectedSavedAddressId: "2" }, - }, -} -jest.mock("Apps/Order/Routes/Shipping2/Hooks/useShippingContext", () => { - return { - useShippingContext: jest.fn(() => mockContext), - } -}) - -class SavedAddressesTestPage extends RootTestPage { - async selectEdit() { - this.find(`[data-test="editAddressInShipping"]`) - .first() - .simulate("click", { preventDefault: () => {} }) - await this.update() - } -} - -describe("Saved Addresses", () => { - const trackEvent = jest.fn() - - beforeAll(() => { - ;(useTracking as jest.Mock).mockImplementation(() => ({ - trackEvent, - })) - }) - - const { getWrapper } = setupTestWrapper({ - Component: (props: any) => { - return ( - - - - ) - }, - query: graphql` - query SavedAddresses2Mutation_Test_Query @relay_test_operation { - me { - ...SavedAddresses2_me - } - } - `, - }) - - describe("Saved Addresses mutations", () => { - it("edits the saved addresses after calling edit address mutation", async () => { - const { wrapper } = getWrapper( - { Me: () => userAddressMutation.me }, - { active: true } - ) - const page = new SavedAddressesTestPage(wrapper) - page.selectEdit() - const addresses = page.find(SavedAddressItem).first().text() - expect(addresses).toBe( - "Test Name1 Main StMadrid, Spain, 28001555-555-5555Edit" - ) - }) - }) - - describe("Saved Addresses", () => { - it("add address modal with expected props", async () => { - const { wrapper } = getWrapper( - { - Me: () => ({ - addressConnection: mockAddressConnection, - }), - }, - { active: true } - ) - const button = wrapper.find("[data-test='shippingButton']").first() - expect(wrapper.find(AddressModal).props().modalAction).toBeNull() - button.simulate("click") - await wrapper.update() - - expect(wrapper.find(AddressModal).props().modalAction).toEqual({ - type: "createUserAddress", - }) - }) - it("edit address modal with expected props", async () => { - const { wrapper } = getWrapper( - { - Me: () => ({ - addressConnection: mockAddressConnection, - }), - }, - { active: true } - ) - - const savedAddressItem = wrapper.find(SavedAddressItem).first() - expect(wrapper.find(AddressModal).props().modalAction).toBeNull() - - savedAddressItem.props().handleClickEdit() - await wrapper.update() - - expect(wrapper.find(AddressModal).props().modalAction).toEqual({ - type: "editUserAddress", - address: expect.objectContaining({ - internalID: "1", - }), - }) - }) - - it("render an add address button", () => { - const { wrapper } = getWrapper({ - Me: () => ({ - addressConnection: mockAddressConnection, - }), - }) - expect(wrapper.find("[data-test='shippingButton']").first()).toHaveLength( - 1 - ) - }) - - describe("when clicking on the add address button", () => { - it("tracks an analytics event", async () => { - const { wrapper } = getWrapper({ - Me: () => ({ - addressConnection: mockAddressConnection, - }), - }) - - await wrapper.update() - wrapper.find("[data-test='shippingButton']").first().simulate("click") - - expect(trackEvent).toHaveBeenCalledWith({ - action: "clickedAddNewShippingAddress", - context_module: "ordersShipping", - context_page_owner_id: "example-order-id", - context_page_owner_type: "orders-shipping", - }) - }) - }) - - it("renders radio buttons with addresses", async () => { - const { wrapper } = getWrapper({ - Me: () => ({ - addressConnection: mockAddressConnection, - }), - }) - const radios = wrapper.find("Radio") - - expect(radios.length).toBe(2) - await waitFor(() => { - expect(radios.map(radio => radio.props().value)).toEqual(["1", "2"]) - expect(radios.map(radio => radio.props().selected)).toEqual([ - false, - true, - ]) - expect(radios.map(radio => radio.text())).toEqual([ - "Test Name1 Main StMadrid, Spain, 28001555-555-5555Edit", - "Test Name401 BroadwayFloor 25New York, NY, US, 10013422-424-4242Edit", - ]) - }) - }) - }) -}) - -const mockAddressConnection = { - edges: [ - { - cursor: "aaaabbbb", - node: { - internalID: "1", - addressLine1: "1 Main St", - addressLine2: "", - addressLine3: "", - city: "Madrid", - country: "Spain", - isDefault: false, - name: "Test Name", - phoneNumber: "555-555-5555", - postalCode: "28001", - region: "", - }, - }, - { - cursor: "bbbbbcccc", - node: { - internalID: "2", - addressLine1: "401 Broadway", - addressLine2: "Floor 25", - addressLine3: "", - city: "New York", - country: "US", - isDefault: true, - name: "Test Name", - phoneNumber: "422-424-4242", - postalCode: "10013", - region: "NY", - }, - }, - ], -} diff --git a/src/Apps/Order/Routes/Shipping2/__tests__/Shipping2.jest.tsx b/src/Apps/Order/Routes/Shipping2/__tests__/Shipping2.jest.tsx index 52fa09871e9..d1eab4af15f 100644 --- a/src/Apps/Order/Routes/Shipping2/__tests__/Shipping2.jest.tsx +++ b/src/Apps/Order/Routes/Shipping2/__tests__/Shipping2.jest.tsx @@ -1,5 +1,4 @@ /* eslint-disable jest/no-disabled-tests */ -// TODO: Re-enable tests import { cloneDeep, merge } from "lodash" import { setupTestWrapperTL } from "DevTools/setupTestWrapper" import { MockBoot } from "DevTools/MockBoot" @@ -28,10 +27,11 @@ import { Shipping2TestQuery, Shipping2TestQuery$rawResponse, } from "__generated__/Shipping2TestQuery.graphql" -import { act, screen, waitFor } from "@testing-library/react" +import { screen, waitFor } from "@testing-library/react" import { useTracking } from "react-tracking" import { useFeatureFlag } from "System/useFeatureFlag" import { + clickSaveAddress, fillAddressForm, validAddress, } from "Components/__tests__/Utils/addressForm2" @@ -40,12 +40,7 @@ import { flushPromiseQueue } from "DevTools/flushPromiseQueue" import { queryByAttribute } from "@testing-library/dom" import { ErrorDialogMessage } from "Apps/Order/Utils/getErrorDialogCopy" import { within } from "@testing-library/dom" -import { - createMockEnvironment, - MockEnvironment, - MockPayloadGenerator, -} from "relay-test-utils" -import { RelayMockEnvironment } from "relay-test-utils/lib/RelayModernMockEnvironment" +import { MockEnvironment, createMockEnvironment } from "relay-test-utils" import { useRouter } from "System/Router/useRouter" jest.setTimeout(10000) @@ -110,9 +105,7 @@ const meWithoutAddress: Shipping2TestQuery$rawResponse["me"] = { country: "United States", }, addressConnection: { - totalCount: 0, edges: [], - pageInfo, }, } @@ -245,16 +238,17 @@ const verifyAddressWithSuggestions = async ( const resolveSaveFulfillmentDetails = async ( mockResolveLastOperation, + /* A mockResolver for the CommerceSetShipping payload */ commerceSetShipping, /* if you submitted the form with a different address than validAddress, pass it here to override the address in the mutation response. */ - requestedFulfillmentOverride?: any + responseAddress?: any ) => { await flushPromiseQueue() - const payload = requestedFulfillmentOverride + const payload = responseAddress ? merge({}, commerceSetShipping, { orderOrError: { - order: { requestedFulfillment: requestedFulfillmentOverride }, + order: { requestedFulfillment: responseAddress }, }, }) : commerceSetShipping @@ -264,38 +258,21 @@ const resolveSaveFulfillmentDetails = async ( }) } -const getAllPendingOperationNames = (env: RelayMockEnvironment) => { +const getAllPendingOperationNames = (env: MockEnvironment) => { return env.mock.getAllOperations().map(op => op.request.node.operation.name) } -let realConsoleError: typeof console.error +let mockTrackEvent: jest.Mock // FIXME: MockBoot interfering somehow... describe.skip("Shipping", () => { const mockUseRouter = useRouter as jest.Mock - let isCommittingMutation: boolean - let relayEnv: MockEnvironment - const mockPush = jest.fn(() => { - act(() => - relayEnv.mock.getAllOperations().forEach(op => { - relayEnv.mock.resolve(op, MockPayloadGenerator.generate(op)) - }) - ) - }) + const mockPush = jest.fn() beforeEach(() => { - realConsoleError = console.error - console.error = jest.fn((...args) => { - // Swallow errors thrown intentionally from tests - if (args[0].includes("##TEST_ERROR##")) { - return - } - realConsoleError(...args) - }) - isCommittingMutation = false - relayEnv = createMockEnvironment() + mockTrackEvent = jest.fn() ;(useTracking as jest.Mock).mockImplementation(() => ({ - trackEvent: jest.fn(), + trackEvent: mockTrackEvent, })) ;(useFeatureFlag as jest.Mock).mockImplementation(() => false) @@ -306,22 +283,28 @@ describe.skip("Shipping", () => { }) }) - afterEach(() => { - console.error = realConsoleError - jest.restoreAllMocks() - }) + let mockRelayEnv + // create the mock environment here so we can pass it into our MockBoot + // component + const renderWithRelay: typeof renderWithRelayRaw = ( + resolvers, + componentProps = {}, + relayEnv = createMockEnvironment() + ) => { + mockRelayEnv = relayEnv + return renderWithRelayRaw(resolvers, componentProps, relayEnv) + } - const { renderWithRelay } = setupTestWrapperTL({ - Component: props => ( - - - - ), + const { renderWithRelay: renderWithRelayRaw } = setupTestWrapperTL< + Shipping2TestQuery + >({ + Component: props => { + return ( + + + + ) + }, query: graphql` query Shipping2TestQuery @relay_test_operation @raw_response_type { order: commerceOrder(id: "unused") { @@ -337,27 +320,19 @@ describe.skip("Shipping", () => { describe("with partner shipping", () => { describe("with no saved address", () => { it("shows an active offer stepper if it's an offer order", async () => { - renderWithRelay( - { - CommerceOrder: () => UntouchedOfferOrder, - Me: () => meWithoutAddress, - }, - undefined, - relayEnv - ) + renderWithRelay({ + CommerceOrder: () => UntouchedOfferOrder, + Me: () => meWithoutAddress, + }) expect(screen.getAllByRole("button", { name: "Offer" }).length).toBe(1) }) it("renders fulfillment selection if artwork is available for pickup", async () => { - renderWithRelay( - { - CommerceOrder: () => order, - Me: () => meWithoutAddress, - }, - undefined, - relayEnv - ) + renderWithRelay({ + CommerceOrder: () => order, + Me: () => meWithoutAddress, + }) expect(screen.getByText("Delivery method")).toBeVisible() expect(screen.getByRole("radio", { name: "Shipping" })).toBeVisible() @@ -370,14 +345,10 @@ describe.skip("Shipping", () => { const shippingOnlyOrder = cloneDeep(order) as any shippingOnlyOrder.lineItems.edges[0].node.artwork.pickup_available = false - renderWithRelay( - { - CommerceOrder: () => shippingOnlyOrder, - Me: () => meWithoutAddress, - }, - undefined, - relayEnv - ) + renderWithRelay({ + CommerceOrder: () => shippingOnlyOrder, + Me: () => meWithoutAddress, + }) expect(screen.queryByText("Delivery method")).not.toBeInTheDocument() expect( @@ -399,14 +370,10 @@ describe.skip("Shipping", () => { shippingCountry: "US", }) - renderWithRelay( - { - CommerceOrder: () => domesticShippingNonEUOrder, - Me: () => meWithoutAddress, - }, - undefined, - relayEnv - ) + renderWithRelay({ + CommerceOrder: () => domesticShippingNonEUOrder, + Me: () => meWithoutAddress, + }) expect(screen.getByTestId("AddressForm_country")).toHaveValue("US") expect(screen.getByTestId("AddressForm_country")).toBeDisabled() @@ -422,32 +389,54 @@ describe.skip("Shipping", () => { shippingCountry: "IT", }) - renderWithRelay( - { - CommerceOrder: () => domesticShippingEUOrder, - Me: () => meWithoutAddress, - }, - undefined, - relayEnv - ) + renderWithRelay({ + CommerceOrder: () => domesticShippingEUOrder, + Me: () => meWithoutAddress, + }) expect(screen.getByTestId("AddressForm_country")).toHaveValue("IT") expect(screen.getByTestId("AddressForm_country")).toBeEnabled() }) it("sets shipping on order, saves address on user and advances to payment", async () => { - const { mockResolveLastOperation } = renderWithRelay( - { - CommerceOrder: () => ({ ...order, __id: order.id }), - Me: () => meWithoutAddress, - }, - undefined, - relayEnv - ) + const { mockResolveLastOperation, env } = renderWithRelay({ + CommerceOrder: () => ({ ...order, __id: order.id }), + Me: () => meWithoutAddress, + }) await fillAddressForm(validAddress) + await saveAndContinue() + const createAddressOperation = await mockResolveLastOperation({ + CreateUserAddressPayload: () => saveAddressSuccess.createUserAddress, + Me: () => ({ + ...meWithoutAddress, + addressConnection: { + totalCount: 0, + edges: [ + { + node: { + ...saveAddressSuccess.createUserAddress + ?.userAddressOrErrors, + }, + }, + ], + }, + }), + }) + + expect(createAddressOperation.operationName).toBe( + "useCreateSavedAddressMutation" + ) + expect(createAddressOperation.operationVariables).toMatchObject({ + input: { + attributes: validAddress, + }, + }) + + await flushPromiseQueue() + const fulfillmentRequest = await resolveSaveFulfillmentDetails( mockResolveLastOperation, settingOrderShipmentSuccess.commerceSetShipping @@ -468,38 +457,21 @@ describe.skip("Shipping", () => { }, }) - await flushPromiseQueue() - const createAddressOperation = await mockResolveLastOperation({ - CreateUserAddressPayload: () => saveAddressSuccess.createUserAddress, - }) - expect(createAddressOperation.operationName).toBe( - "useCreateSavedAddressMutation" - ) - expect(createAddressOperation.operationVariables).toMatchObject({ - input: { - attributes: validAddress, - }, - }) await waitFor(() => { - expect(getAllPendingOperationNames(relayEnv)).toEqual([]) + expect(getAllPendingOperationNames(env)).toEqual([]) expect(mockPush).toHaveBeenCalledWith("/orders/2939023/payment") }) }) it("save address not checked: sets shipping on order and advances to payment without saving address", async () => { - const { env, mockResolveLastOperation } = renderWithRelay( - { - CommerceOrder: () => order, - Me: () => meWithoutAddress, - }, - undefined, - relayEnv - ) + const { env, mockResolveLastOperation } = renderWithRelay({ + CommerceOrder: () => order, + Me: () => meWithoutAddress, + }) await fillAddressForm(validAddress) - await userEvent.click( - screen.getByRole("checkbox", { name: /Save shipping address/ }) - ) + await clickSaveAddress() + await saveAndContinue() const fulfillmentRequest = await resolveSaveFulfillmentDetails( @@ -511,62 +483,65 @@ describe.skip("Shipping", () => { "useSaveFulfillmentDetailsMutation" ) await waitFor(() => { - expect(getAllPendingOperationNames(relayEnv)).toEqual([]) + expect(getAllPendingOperationNames(env)).toEqual([]) expect(mockPush).toHaveBeenCalledWith("/orders/2939023/payment") }) expect(() => env.mock.getMostRecentOperation()).toThrow() }) it("shows a loading spinner while operations are pending", async () => { - const { mockResolveLastOperation } = renderWithRelay( - { - CommerceOrder: () => ({ ...order, __id: order.id }), - Me: () => meWithoutAddress, - }, - undefined, - relayEnv - ) + const { mockResolveLastOperation, env } = renderWithRelay({ + CommerceOrder: () => ({ ...order, __id: order.id }), + Me: () => meWithoutAddress, + }) await fillAddressForm(validAddress) await saveAndContinue() - const button = screen.getByRole("button", { name: "Save and Continue" }) - expect(queryByAttribute("class", button, /Spinner/)).toBeInTheDocument() - - await resolveSaveFulfillmentDetails( - mockResolveLastOperation, - settingOrderShipmentSuccess.commerceSetShipping - ) - - await flushPromiseQueue() + const getSpinner = () => { + const button = screen.getByRole("button", { + name: "Save and Continue", + }) + return queryByAttribute("class", button, /Spinner/) + } - expect(queryByAttribute("class", button, /Spinner/)).toBeInTheDocument() + await waitFor(() => { + expect(getSpinner()).toBeInTheDocument() + }) await mockResolveLastOperation({ CreateUserAddressPayload: () => saveAddressSuccess.createUserAddress, }) + await resolveSaveFulfillmentDetails( + mockResolveLastOperation, + settingOrderShipmentSuccess.commerceSetShipping + ) + await waitFor(() => { - expect( - queryByAttribute("class", button, /Spinner/) - ).not.toBeInTheDocument() - expect(getAllPendingOperationNames(relayEnv)).toEqual([]) + expect(getSpinner()).not.toBeInTheDocument() + expect(getAllPendingOperationNames(env)).toEqual([]) expect(mockPush).toHaveBeenCalledWith("/orders/2939023/payment") }) }) - it("shows a generic error when there is an unrecognized error code in the result", async () => { - const { mockResolveLastOperation } = renderWithRelay( - { - CommerceOrder: () => order, - Me: () => meWithoutAddress, - }, - undefined, - relayEnv - ) + it("shows a generic error when there is an unrecognized error code in the exchange result", async () => { + const { mockResolveLastOperation } = renderWithRelay({ + CommerceOrder: () => order, + Me: () => meWithoutAddress, + }) await fillAddressForm(validAddress) await saveAndContinue() + + await flushPromiseQueue() + + await mockResolveLastOperation({ + CreateUserAddressPayload: () => saveAddressSuccess.createUserAddress, + }) + + await flushPromiseQueue() + await resolveSaveFulfillmentDetails(mockResolveLastOperation, { orderOrError: { __typename: "CommerceOrderWithMutationFailure", @@ -577,23 +552,23 @@ describe.skip("Shipping", () => { }, }, }) + await waitFor(() => { expect(mockShowErrorDialog).toHaveBeenCalledWith() }) }) - it("shows an error when the mutation throws an error", async () => { - const { mockRejectLastOperation } = renderWithRelay( - { - CommerceOrder: () => order, - Me: () => meWithoutAddress, - }, - undefined, - relayEnv - ) + it("shows an error when an exchange mutation throws an error", async () => { + const { mockRejectLastOperation } = renderWithRelay({ + CommerceOrder: () => order, + Me: () => meWithoutAddress, + }) await fillAddressForm(validAddress) + await clickSaveAddress() + await saveAndContinue() + await waitFor(() => mockRejectLastOperation(new Error("##TEST_ERROR## wrong number")) ) @@ -601,16 +576,14 @@ describe.skip("Shipping", () => { }) it("shows an error when there is a missing_country error from the server", async () => { - const { mockResolveLastOperation } = renderWithRelay( - { - CommerceOrder: () => order, - Me: () => meWithoutAddress, - }, - undefined, - relayEnv - ) + const { mockResolveLastOperation } = renderWithRelay({ + CommerceOrder: () => order, + Me: () => meWithoutAddress, + }) await fillAddressForm(validAddress) + await clickSaveAddress() + await saveAndContinue() await resolveSaveFulfillmentDetails(mockResolveLastOperation, { @@ -633,16 +606,14 @@ describe.skip("Shipping", () => { }) it("shows an error when there is a missing_region error from the server", async () => { - const { mockResolveLastOperation } = renderWithRelay( - { - CommerceOrder: () => order, - Me: () => meWithoutAddress, - }, - undefined, - relayEnv - ) + const { mockResolveLastOperation } = renderWithRelay({ + CommerceOrder: () => order, + Me: () => meWithoutAddress, + }) await fillAddressForm(validAddress) + await clickSaveAddress() + await saveAndContinue() await resolveSaveFulfillmentDetails(mockResolveLastOperation, { @@ -665,16 +636,14 @@ describe.skip("Shipping", () => { }) it("shows an error when there is a destination_could_not_be_geocodederror from the server", async () => { - const { mockResolveLastOperation } = renderWithRelay( - { - CommerceOrder: () => order, - Me: () => meWithoutAddress, - }, - undefined, - relayEnv - ) + const { mockResolveLastOperation } = renderWithRelay({ + CommerceOrder: () => order, + Me: () => meWithoutAddress, + }) await fillAddressForm(validAddress) + await clickSaveAddress() + await saveAndContinue() await resolveSaveFulfillmentDetails(mockResolveLastOperation, { @@ -697,61 +666,23 @@ describe.skip("Shipping", () => { }) it("pre-populates address form for order with already persisted shipping info", async () => { - renderWithRelay( - { - CommerceOrder: () => ({ - ...order, - requestedFulfillment: { - ...validAddress, - __typename: "CommerceShip", - name: "Dr Collector", - }, - }), - Me: () => meWithoutAddress, - }, - undefined, - relayEnv - ) + renderWithRelay({ + CommerceOrder: () => ({ + ...order, + requestedFulfillment: { + ...validAddress, + __typename: "CommerceShip", + name: "Dr Collector", + }, + }), + Me: () => meWithoutAddress, + }) expect(screen.getByPlaceholderText("Full name")).toHaveValue( "Dr Collector" ) }) - it("resets shipping for order with already persisted shipping info", async () => { - const { env, mockResolveLastOperation } = renderWithRelay( - { - CommerceOrder: () => ({ - ...order, - requestedFulfillment: { - ...validAddress, - __typename: "CommerceShip", - name: "Dr Collector", - }, - }), - Me: () => meWithoutAddress, - }, - undefined, - relayEnv - ) - - await saveAndContinue() - - const fulfillmentRequest = await resolveSaveFulfillmentDetails( - mockResolveLastOperation, - settingOrderShipmentSuccess.commerceSetShipping - ) - - expect(fulfillmentRequest.operationName).toBe( - "useSaveFulfillmentDetailsMutation" - ) - expect(fulfillmentRequest.operationVariables).toMatchObject({ - // Fill in when test is fixed - }) - await flushPromiseQueue() - expect(() => env.mock.getMostRecentOperation()).toThrow() - }) - describe("address verification", () => { describe("with US enabled and international disabled", () => { beforeEach(() => { @@ -761,21 +692,21 @@ describe.skip("Shipping", () => { }) it("triggers basic form validation before address verification", async () => { - const { env } = renderWithRelay( - { - CommerceOrder: () => order, - Me: () => meWithoutAddress, - }, - undefined, - relayEnv - ) + const { env } = renderWithRelay({ + CommerceOrder: () => order, + Me: () => meWithoutAddress, + }) expect( screen.queryByText(/[\w\s]is required/) ).not.toBeInTheDocument() - await fillAddressForm(validAddress) - userEvent.clear(screen.getByPlaceholderText("Street address")) + await fillAddressForm({ ...validAddress, addressLine1: undefined }) + + await flushPromiseQueue() + expect( + screen.queryByText(/[\w\s]is required/) + ).not.toBeInTheDocument() await userEvent.click(screen.getByText("Save and Continue")) @@ -784,18 +715,14 @@ describe.skip("Shipping", () => { screen.getByText("Street address is required") ).toBeVisible() }) - expect(() => env.mock.getMostRecentOperation()).toThrow() + expect(env.mock.getAllOperations().length).toEqual(0) }) it("triggers the flow for US address after clicking continue", async () => { - const { env, mockResolveLastOperation } = renderWithRelay( - { - CommerceOrder: () => order, - Me: () => meWithoutAddress, - }, - undefined, - relayEnv - ) + const { env, mockResolveLastOperation } = renderWithRelay({ + CommerceOrder: () => order, + Me: () => meWithoutAddress, + }) await fillAddressForm(validAddressBeforeVerification) @@ -807,18 +734,14 @@ describe.skip("Shipping", () => { recommendedAddress ) - expect(() => env.mock.getMostRecentOperation()).toThrow() + expect(env.mock.getAllOperations().length).toEqual(0) }) it("uses recommended address", async () => { - const { mockResolveLastOperation } = renderWithRelay( - { - CommerceOrder: () => order, - Me: () => meWithoutAddress, - }, - undefined, - relayEnv - ) + const { mockResolveLastOperation } = renderWithRelay({ + CommerceOrder: () => order, + Me: () => meWithoutAddress, + }) await fillAddressForm(validAddressBeforeVerification) @@ -831,17 +754,23 @@ describe.skip("Shipping", () => { ) await userEvent.click(screen.getByText("Use This Address")) - await flushPromiseQueue() - const fulfillmentOperation = await resolveSaveFulfillmentDetails( - mockResolveLastOperation, - settingOrderShipmentSuccess.commerceSetShipping - ) + await flushPromiseQueue() const createAddressOperation = await mockResolveLastOperation({ CreateUserAddressPayload: () => saveAddressSuccess.createUserAddress, }) + expect(createAddressOperation.operationName).toBe( + "useCreateSavedAddressMutation" + ) + + await flushPromiseQueue() + const fulfillmentOperation = await resolveSaveFulfillmentDetails( + mockResolveLastOperation, + settingOrderShipmentSuccess.commerceSetShipping + ) + expect(fulfillmentOperation.operationName).toBe( "useSaveFulfillmentDetailsMutation" ) @@ -858,22 +787,16 @@ describe.skip("Shipping", () => { }, }, }) - expect(createAddressOperation.operationName).toBe( - "useCreateSavedAddressMutation" - ) }) it("goes back and edits address after verification", async () => { - const { mockResolveLastOperation } = renderWithRelay( - { - CommerceOrder: () => order, - Me: () => meWithoutAddress, - }, - undefined, - relayEnv - ) + const { mockResolveLastOperation } = renderWithRelay({ + CommerceOrder: () => order, + Me: () => meWithoutAddress, + }) await fillAddressForm(validAddressBeforeVerification) + await clickSaveAddress() await saveAndContinue() @@ -890,8 +813,11 @@ describe.skip("Shipping", () => { screen.getByPlaceholderText("City"), ": the big apple" ) + await flushPromiseQueue() + await saveAndContinue() + const fulfillmentOperation = await resolveSaveFulfillmentDetails( mockResolveLastOperation, settingOrderShipmentSuccess.commerceSetShipping @@ -918,23 +844,28 @@ describe.skip("Shipping", () => { }) it("does not triggers the flow for international address after clicking continue", async () => { - const { env } = renderWithRelay( - { - CommerceOrder: () => order, - Me: () => meWithoutAddress, - }, - undefined, - relayEnv - ) + const { mockResolveLastOperation } = renderWithRelay({ + CommerceOrder: () => order, + Me: () => meWithoutAddress, + }) await fillAddressForm(validAddress) - userEvent.selectOptions(screen.getByTestId("AddressForm_country"), [ - "TW", - ]) + await clickSaveAddress() + + await userEvent.selectOptions( + screen.getByTestId("AddressForm_country"), + ["TW"] + ) + + await saveAndContinue() + await flushPromiseQueue() - await userEvent.click(screen.getByText("Save and Continue")) - expect(() => env.mock.getMostRecentOperation()).toThrow() + const operation = mockResolveLastOperation({}) + + expect(operation.operationName).toBe( + "useSaveFulfillmentDetailsMutation" + ) }) }) @@ -949,43 +880,43 @@ describe.skip("Shipping", () => { }) it("does not trigger the flow for US address after clicking continue", async () => { - const { mockResolveLastOperation } = renderWithRelay( - { - CommerceOrder: () => order, - Me: () => meWithoutAddress, - }, - undefined, - relayEnv - ) + const { mockResolveLastOperation } = renderWithRelay({ + CommerceOrder: () => order, + Me: () => meWithoutAddress, + }) await fillAddressForm(validAddress) + await clickSaveAddress() + await saveAndContinue() + await flushPromiseQueue() - const { operationName } = await waitFor(() => - mockResolveLastOperation({}) + const operation = mockResolveLastOperation({}) + expect(operation.operationName).toBe( + "useSaveFulfillmentDetailsMutation" ) - expect(operationName).toBe("useSaveFulfillmentDetailsMutation") }) it("triggers the flow for international address after clicking continue", async () => { - const { mockResolveLastOperation } = renderWithRelay( - { - CommerceOrder: () => order, - Me: () => meWithoutAddress, - }, - undefined, - relayEnv - ) + const { mockResolveLastOperation } = renderWithRelay({ + CommerceOrder: () => order, + Me: () => meWithoutAddress, + }) await fillAddressForm(inputAddress) + await clickSaveAddress() + await userEvent.selectOptions( screen.getByTestId("AddressForm_country"), ["TW"] ) - await userEvent.click(screen.getByText("Save and Continue")) + await flushPromiseQueue() - await verifyAddressWithSuggestions( - mockResolveLastOperation, + await saveAndContinue() + + await flushPromiseQueue() + await verifyAddressWithSuggestions( + mockResolveLastOperation, inputAddress, { ...inputAddress, @@ -1021,19 +952,18 @@ describe.skip("Shipping", () => { }) }) - // TODO: Reproducing saved address behavior ticketed in EMI-1520 https://artsyproduct.atlassian.net/browse/EMI-1520 // e.g.: Should valid saved address be automatically saved to show shipping quotes? describe("with saved addresses", () => { it("does not show the new address form", async () => { - renderWithRelay( - { - CommerceOrder: () => order, - Me: () => meWithAddresses, - }, - undefined, - relayEnv - ) + const { mockResolveLastOperation } = renderWithRelay({ + CommerceOrder: () => order, + Me: () => meWithAddresses, + }) + await resolveSaveFulfillmentDetails( + mockResolveLastOperation, + settingOrderShipmentSuccess.commerceSetShipping + ) // TODO: need a better way to check if the form is collapsed (height 0). // Zero height is not considered invisible. // https://github.com/testing-library/jest-dom/issues/450 @@ -1047,11 +977,16 @@ describe.skip("Shipping", () => { }) it("lists the addresses and renders the add address option", async () => { - renderWithRelay({ + const { mockResolveLastOperation } = renderWithRelay({ CommerceOrder: () => order, Me: () => meWithAddresses, }) + await resolveSaveFulfillmentDetails( + mockResolveLastOperation, + settingOrderShipmentSuccess.commerceSetShipping + ) + expect( screen.getByRole("radio", { name: /401 Broadway/ }) ).toBeVisible() @@ -1061,37 +996,21 @@ describe.skip("Shipping", () => { ).toBeVisible() }) - // TODO: EMI-1520 - // TODO it("automatically saves the default saved address on load if valid", async () => {}) - // TODO it("automatically re-saves the existing already-saved address if present", async () => {}) - - it("sets shipping with the first saved address and phone number when user submits the form directly", async () => { - const { mockResolveLastOperation } = renderWithRelay( - { - CommerceOrder: () => order, - Me: () => meWithAddresses, - }, - undefined, - relayEnv - ) - - // This test may be in conflict with the above, but silently passing. Confirm behavior - // TODO: Confirm whether this behavior is desired for EMI-1520 - // (Or only when shipping quotes are required) - // await resolveFulfillmentDetails(mockResolveLastOperation, - // settingOrderShipmentSuccess.commerceSetShipping, - // ) - await saveAndContinue() + it("automatically saves the default saved address on load if valid (without tracking)", async () => { + const { mockResolveLastOperation } = renderWithRelay({ + CommerceOrder: () => order, + Me: () => meWithAddresses, + }) - const fulfillmentMutation = await resolveSaveFulfillmentDetails( + const automaticFulfillmentMutation = await resolveSaveFulfillmentDetails( mockResolveLastOperation, settingOrderShipmentSuccess.commerceSetShipping ) - expect(fulfillmentMutation.operationName).toEqual( + expect(automaticFulfillmentMutation.operationName).toEqual( "useSaveFulfillmentDetailsMutation" ) - expect(fulfillmentMutation.operationVariables).toEqual({ + expect(automaticFulfillmentMutation.operationVariables).toEqual({ input: { id: "2939023", fulfillmentType: "SHIP", @@ -1108,27 +1027,29 @@ describe.skip("Shipping", () => { }, }, }) + + expect(mockTrackEvent).not.toHaveBeenCalled() }) - it("sets shipping with the selected saved address and phone number", async () => { - const { mockResolveLastOperation } = renderWithRelay( - { - CommerceOrder: () => order, - Me: () => meWithAddresses, - }, - undefined, - relayEnv + it("sets shipping again when the user clicks to select address", async () => { + const { mockResolveLastOperation } = renderWithRelay({ + CommerceOrder: () => order, + Me: () => meWithAddresses, + }) + + await resolveSaveFulfillmentDetails( + mockResolveLastOperation, + settingOrderShipmentSuccess.commerceSetShipping ) - // Set shipping on load for the default address - // TODO: Confirm whether this behavior is desired for EMI-1520 - // (Or only when shipping quotes are required) and if so add this initial - // operation resolver - // await resolveFulfillmentDetails(mockResolveLastOperation, - // settingOrderShipmentSuccess.commerceSetShipping, - // ) + await userEvent.click(screen.getByRole("radio", { name: /1 Main St/ })) - // This test may be in conflict with the above, but silently passing. Confirm behavior - await saveAndContinue() + + expect(mockTrackEvent).toHaveBeenCalledWith({ + action: "clickedShippingAddress", + context_module: "ordersShipping", + context_page_owner_id: "2939023", + context_page_owner_type: "orders-shipping", + }) const fulfillmentMutation = await resolveSaveFulfillmentDetails( mockResolveLastOperation, @@ -1165,108 +1086,117 @@ describe.skip("Shipping", () => { }) it("does not trigger the flow", async () => { - const { mockResolveLastOperation } = renderWithRelay( - { - CommerceOrder: () => order, - Me: () => meWithAddresses, - }, - undefined, - relayEnv + const { mockResolveLastOperation } = renderWithRelay({ + CommerceOrder: () => order, + Me: () => meWithAddresses, + }) + + const automaticFulfillmentMutation = await resolveSaveFulfillmentDetails( + mockResolveLastOperation, + settingOrderShipmentSuccess.commerceSetShipping ) + + expect(automaticFulfillmentMutation.operationName).toEqual( + "useSaveFulfillmentDetailsMutation" + ) + await userEvent.click( screen.getByRole("radio", { name: /1 Main St/ }) ) - await saveAndContinue() - - const { operationName } = await waitFor(() => + const manualFulfillmentMutation = await waitFor(() => mockResolveLastOperation({}) ) - expect(operationName).toBe("useSaveFulfillmentDetailsMutation") + expect(manualFulfillmentMutation.operationName).toBe( + "useSaveFulfillmentDetailsMutation" + ) }) }) }) describe("editing address", () => { - // Failing: Double elements on screen - it.skip("opens a modal with the address prepopulated", async () => { - renderWithRelay( - { - CommerceOrder: () => UntouchedBuyOrderWithShippingQuotes, - Me: () => meWithAddresses, - }, - undefined, - relayEnv + it("opens a modal with the address prepopulated", async () => { + const { mockResolveLastOperation } = renderWithRelay({ + CommerceOrder: () => order, + Me: () => meWithAddresses, + }) + + await resolveSaveFulfillmentDetails( + mockResolveLastOperation, + settingOrderShipmentSuccess.commerceSetShipping ) - // Set shipping on load for the default address - // TODO: Confirm whether this behavior is desired for EMI-1520 - // (or only when shipping quotes are required) - // await resolveFulfillmentDetails(mockResolveLastOperation, - // settingOrderShipmentSuccess.commerceSetShipping, - // ) + const selectedAddress = screen.getAllByTestId("savedAddress")[1] + expect(selectedAddress).toHaveTextContent("401 Broadway") - const selectedAddress = screen.getByRole("radio", { - name: /401 Broadway/, - checked: true, - }) await userEvent.click(within(selectedAddress).getByText("Edit")) await waitFor(async () => { + const addressModal = screen.getByTestId("AddressModal") expect(screen.getByText("Edit address")).toBeVisible() - expect(screen.getByDisplayValue("401 Broadway")).toBeInTheDocument() - expect(screen.getByDisplayValue("Floor 25")).toBeInTheDocument() - expect(screen.getByDisplayValue("New York")).toBeInTheDocument() - expect(screen.getByDisplayValue("NY")).toBeInTheDocument() - expect(screen.getByDisplayValue("10013")).toBeInTheDocument() + expect( + within(addressModal).getByDisplayValue("401 Broadway") + ).toBeVisible() + expect( + within(addressModal).getByDisplayValue("Floor 25") + ).toBeVisible() + expect( + within(addressModal).getByDisplayValue("New York") + ).toBeVisible() + expect(within(addressModal).getByDisplayValue("NY")).toBeVisible() + expect( + within(addressModal).getByDisplayValue("10013") + ).toBeVisible() }) }) - // TODO: Address should be saved to order on update - it.skip("updates the address after submitting the modal form", async () => { - const { mockResolveLastOperation } = renderWithRelay( - { - CommerceOrder: () => UntouchedBuyOrderWithShippingQuotes, - Me: () => meWithAddresses, - }, - undefined, - relayEnv - ) - // Set shipping on load for the default address - // TODO: Confirm whether this behavior is desired for EMI-1520 - // (Or only when shipping quotes are required) - // await resolveFulfillmentDetails(mockResolveLastOperation, - // settingOrderShipmentSuccess.commerceSetShipping, - // ) - - const selectedAddress = screen.getByRole("radio", { - name: /401 Broadway/, - checked: true, + it("updates the address after submitting the modal form, then saves the address to the order", async () => { + const { mockResolveLastOperation } = renderWithRelay({ + CommerceOrder: () => order, + Me: () => meWithAddresses, }) + + await resolveSaveFulfillmentDetails( + mockResolveLastOperation, + settingOrderShipmentSuccess.commerceSetShipping + ) + + const selectedAddress = screen.getAllByTestId("savedAddress")[1] + expect(selectedAddress).toHaveTextContent("401 Broadway") + await userEvent.click(within(selectedAddress).getByText("Edit")) - const modalTitle = screen.getByText("Edit address") + const modalTitle = await screen.findByText("Edit address") expect(modalTitle).toBeVisible() // TODO: need a better way to get a specific input field from multiple forms - const addressLine2 = screen.getAllByPlaceholderText( - /Apt, floor, suite/ - )[0] - userEvent.clear(addressLine2) - userEvent.paste(addressLine2, "25th fl.") + await waitFor(async () => { + const addressModal = screen.getByTestId("AddressModal") + const addressLine2 = within(addressModal).getByPlaceholderText( + /Apt, floor, suite/ + ) + await userEvent.clear(addressLine2) + await userEvent.paste(addressLine2, "25th fl.") + }) + userEvent.click(screen.getByRole("button", { name: "Save" })) + await flushPromiseQueue() - const createAddressOperation = await mockResolveLastOperation({ - CreateUserAddressPayload: () => - saveAddressSuccess.createUserAddress, + + const updateAddressResult = { + userAddressOrErrors: { + ...saveAddressSuccess.createUserAddress!.userAddressOrErrors, + addressLine2: "25th fl.", + }, + } + const updateAddressOperation = await mockResolveLastOperation({ + UpdateUserAddressPayload: () => updateAddressResult, }) - // const saveFulfillmentOperation = await resolveFulfillmentDetails(mockResolveLastOperation, - // settingOrderShipmentSuccess.commerceSetShipping, - // ) - expect(createAddressOperation.operationName).toEqual( + + expect(updateAddressOperation.operationName).toEqual( "useUpdateSavedAddressMutation" ) - expect(createAddressOperation.operationVariables).toMatchObject({ + expect(updateAddressOperation.operationVariables).toMatchObject({ input: { attributes: { addressLine1: "401 Broadway", @@ -1281,6 +1211,23 @@ describe.skip("Shipping", () => { userAddressID: "2", }, }) + + await flushPromiseQueue() + + const saveFulfillmentOperation = await resolveSaveFulfillmentDetails( + mockResolveLastOperation, + settingOrderShipmentSuccess.commerceSetShipping + ) + + expect(saveFulfillmentOperation.operationName).toEqual( + "useSaveFulfillmentDetailsMutation" + ) + + // Fixme: Save address with correct new value + expect( + saveFulfillmentOperation.operationVariables.input.shipping + .addressLine2 + ).toEqual("25th fl.") }) }) }) @@ -1297,15 +1244,11 @@ describe.skip("Shipping", () => { }) it("uses recommended address", async () => { - const { mockResolveLastOperation } = renderWithRelay( - { - CommerceOrder: () => - UntouchedBuyOrderWithArtsyShippingDomesticFromUS, - Me: () => meWithoutAddress, - }, - undefined, - relayEnv - ) + const { mockResolveLastOperation, env } = renderWithRelay({ + CommerceOrder: () => + UntouchedBuyOrderWithArtsyShippingDomesticFromUS, + Me: () => meWithoutAddress, + }) await fillAddressForm(validAddress) await saveAndContinue() @@ -1317,6 +1260,22 @@ describe.skip("Shipping", () => { ) await userEvent.click(screen.getByText("Use This Address")) + + await flushPromiseQueue() + + const createAddressOperation = await mockResolveLastOperation({ + CreateUserAddressPayload: () => + merge({}, saveAddressSuccess.createUserAddress, { + userAddressOrErrors: { + ...recommendedAddress, + __typename: "UserAddress", + }, + }), + }) + expect(createAddressOperation.operationName).toBe( + "useCreateSavedAddressMutation" + ) + await flushPromiseQueue() const fulfillmentOperation = await resolveSaveFulfillmentDetails( @@ -1343,19 +1302,11 @@ describe.skip("Shipping", () => { }) await flushPromiseQueue() - const createAddressOperation = await mockResolveLastOperation({ - CreateUserAddressPayload: () => - merge({}, saveAddressSuccess.createUserAddress, { - userAddressOrErrors: recommendedAddress, - }), - }) - expect(createAddressOperation.operationName).toBe( - "useCreateSavedAddressMutation" - ) await waitFor(() => userEvent.click(screen.getByText(/^Premium/))) await flushPromiseQueue() + await saveAndContinue() await flushPromiseQueue() const selectNewShippingOptionOperation = await mockResolveLastOperation( @@ -1369,20 +1320,16 @@ describe.skip("Shipping", () => { ) await flushPromiseQueue() - expect(getAllPendingOperationNames(relayEnv)).toEqual([]) + expect(getAllPendingOperationNames(env)).toEqual([]) expect(mockPush).toHaveBeenCalledWith("/orders/2939023/payment") }) it("goes back and edits address after verification", async () => { - const { mockResolveLastOperation } = renderWithRelay( - { - CommerceOrder: () => - UntouchedBuyOrderWithArtsyShippingDomesticFromUS, - Me: () => meWithoutAddress, - }, - undefined, - relayEnv - ) + const { mockResolveLastOperation, env } = renderWithRelay({ + CommerceOrder: () => + UntouchedBuyOrderWithArtsyShippingDomesticFromUS, + Me: () => meWithoutAddress, + }) await fillAddressForm(validAddressBeforeVerification) @@ -1401,6 +1348,16 @@ describe.skip("Shipping", () => { await saveAndContinue() await flushPromiseQueue() + const createAddressOperation = await mockResolveLastOperation({ + CreateUserAddressPayload: () => + saveAddressSuccess.createUserAddress, + }) + expect(createAddressOperation.operationName).toBe( + "useCreateSavedAddressMutation" + ) + + await flushPromiseQueue() + const fulfillmentOperation = await resolveSaveFulfillmentDetails( mockResolveLastOperation, settingOrderArtaShipmentSuccess.commerceSetShipping @@ -1422,14 +1379,6 @@ describe.skip("Shipping", () => { }, }, }) - await flushPromiseQueue() - const createAddressOperation = await mockResolveLastOperation({ - CreateUserAddressPayload: () => - saveAddressSuccess.createUserAddress, - }) - expect(createAddressOperation.operationName).toBe( - "useCreateSavedAddressMutation" - ) await flushPromiseQueue() await saveAndContinue() @@ -1444,7 +1393,7 @@ describe.skip("Shipping", () => { ) await flushPromiseQueue() - expect(getAllPendingOperationNames(relayEnv)).toEqual([]) + expect(getAllPendingOperationNames(env)).toEqual([]) expect(mockPush).toHaveBeenCalledWith("/orders/2939023/payment") }) }) @@ -1456,17 +1405,14 @@ describe.skip("Shipping", () => { ) as any settingOrderArtaShipmentSuccessWithoutQuotes.commerceSetShipping.orderOrError.order.lineItems.edges[0].node.shippingQuoteOptions.edges = [] - const { mockResolveLastOperation } = renderWithRelay( - { - CommerceOrder: () => - UntouchedBuyOrderWithArtsyShippingDomesticFromUS, - Me: () => meWithoutAddress, - }, - undefined, - relayEnv - ) + const { mockResolveLastOperation } = renderWithRelay({ + CommerceOrder: () => UntouchedBuyOrderWithArtsyShippingDomesticFromUS, + Me: () => meWithoutAddress, + }) await fillAddressForm(validAddress) + await clickSaveAddress() + await saveAndContinue() await resolveSaveFulfillmentDetails( @@ -1485,27 +1431,31 @@ describe.skip("Shipping", () => { }) it("hides shipping quotes and updates shipping address plus saved address if user edits address fields", async () => { - const { mockResolveLastOperation } = renderWithRelay( - { - CommerceOrder: () => - UntouchedBuyOrderWithArtsyShippingDomesticFromUS, - Me: () => meWithoutAddress, - }, - undefined, - relayEnv - ) + const { mockResolveLastOperation, env } = renderWithRelay({ + CommerceOrder: () => UntouchedBuyOrderWithArtsyShippingDomesticFromUS, + Me: () => meWithoutAddress, + }) await fillAddressForm(validAddress) await saveAndContinue() - await resolveSaveFulfillmentDetails( - mockResolveLastOperation, - settingOrderArtaShipmentSuccess.commerceSetShipping - ) - await flushPromiseQueue() const saveAddressOperation = await mockResolveLastOperation({ CreateUserAddressPayload: () => saveAddressSuccess.createUserAddress, + Me: () => ({ + ...meWithoutAddress, + addressConnection: { + totalCount: 0, + edges: [ + { + node: { + ...saveAddressSuccess.createUserAddress + ?.userAddressOrErrors, + }, + }, + ], + }, + }), }) expect(saveAddressOperation.operationName).toBe( @@ -1514,6 +1464,13 @@ describe.skip("Shipping", () => { await flushPromiseQueue() + await resolveSaveFulfillmentDetails( + mockResolveLastOperation, + settingOrderArtaShipmentSuccess.commerceSetShipping + ) + + await flushPromiseQueue() + const addressLine1 = screen.getByPlaceholderText("Street address") const addressLine2 = screen.getByPlaceholderText( "Apt, floor, suite, etc." @@ -1531,24 +1488,34 @@ describe.skip("Shipping", () => { await flushPromiseQueue() await saveAndContinue() - await flushPromiseQueue() - await resolveSaveFulfillmentDetails( - mockResolveLastOperation, - settingOrderArtaShipmentSuccess.commerceSetShipping, - { addressLine1: "401 Broadway Suite 25", addressLine2: "" } - ) + + const updatedUserAddress = { + ...updateAddressSuccess?.updateUserAddress?.userAddressOrErrors, + __typename: "UserAddress", + addressLine1: "401 Broadway Suite 25", + addressLine2: "", + } await flushPromiseQueue() const updateAddressOperation = await mockResolveLastOperation({ - UpdateUserAddressPayload: () => - merge({}, updateAddressSuccess.updateUserAddress, { - userAddressOrErrors: { - addressLine1: "401 Broadway Suite 25", - addressLine2: "", - }, - }), + UpdateUserAddressPayload: () => ({ + userAddressOrErrors: updatedUserAddress, + }), + Me: () => ({ + ...meWithoutAddress, + addressConnection: { + totalCount: 1, + edges: [ + { + node: updatedUserAddress, + }, + ], + }, + }), }) + await flushPromiseQueue() + expect(updateAddressOperation.operationName).toBe( "useUpdateSavedAddressMutation" ) @@ -1568,16 +1535,29 @@ describe.skip("Shipping", () => { }, }) + await resolveSaveFulfillmentDetails( + mockResolveLastOperation, + settingOrderArtaShipmentSuccess.commerceSetShipping, + { addressLine1: "401 Broadway Suite 25", addressLine2: "" } + ) + + await flushPromiseQueue() + const premiumShipping = await screen.findByRole("radio", { name: /Premium/, }) + await userEvent.click(premiumShipping) + await saveAndContinue() + await flushPromiseQueue() + const selectShippingOptionOperation = await mockResolveLastOperation({ CommerceSelectShippingOptionPayload: () => selectShippingQuoteSuccess.commerceSelectShippingOption, }) + expect(selectShippingOptionOperation.operationName).toBe( "useSelectShippingQuoteMutation" ) @@ -1588,26 +1568,19 @@ describe.skip("Shipping", () => { }, }) await flushPromiseQueue() - expect(getAllPendingOperationNames(relayEnv)).toEqual([]) + expect(getAllPendingOperationNames(env)).toEqual([]) expect(mockPush).toHaveBeenCalledWith("/orders/2939023/payment") }) it("saves address upon selecting shipping quote if address is checked and it wasn't saved before", async () => { - const { mockResolveLastOperation } = renderWithRelay( - { - CommerceOrder: () => - UntouchedBuyOrderWithArtsyShippingDomesticFromUS, - Me: () => meWithoutAddress, - }, - undefined, - relayEnv - ) + const { mockResolveLastOperation, env } = renderWithRelay({ + CommerceOrder: () => UntouchedBuyOrderWithArtsyShippingDomesticFromUS, + Me: () => meWithoutAddress, + }) await fillAddressForm(validAddress) - const saveShippingAddressBox = screen.getByText( - /^Save shipping address/ - ) - await userEvent.click(saveShippingAddressBox) + await clickSaveAddress() + await saveAndContinue() const fulfillmentOperation = await resolveSaveFulfillmentDetails( @@ -1624,18 +1597,9 @@ describe.skip("Shipping", () => { name: /Premium/, }) await userEvent.click(premiumShipping) - await userEvent.click(saveShippingAddressBox) + await clickSaveAddress() await saveAndContinue() - await flushPromiseQueue() - - const selectShippingOptionOperation = await mockResolveLastOperation({ - CommerceSelectShippingOptionPayload: () => - selectShippingQuoteSuccess.commerceSelectShippingOption, - }) - expect(selectShippingOptionOperation.operationName).toBe( - "useSelectShippingQuoteMutation" - ) await flushPromiseQueue() const saveAddressOperation = await mockResolveLastOperation({ @@ -1659,26 +1623,42 @@ describe.skip("Shipping", () => { }, }, }) + + await flushPromiseQueue() + + const selectShippingOptionOperation = await mockResolveLastOperation({ + CommerceSelectShippingOptionPayload: () => + selectShippingQuoteSuccess.commerceSelectShippingOption, + }) + expect(selectShippingOptionOperation.operationName).toBe( + "useSelectShippingQuoteMutation" + ) + await flushPromiseQueue() - expect(getAllPendingOperationNames(relayEnv)).toEqual([]) + expect(getAllPendingOperationNames(env)).toEqual([]) expect(mockPush).toHaveBeenCalledWith("/orders/2939023/payment") }) it("removes saved address upon selecting shipping quote if save-address is unchecked after initially saving it", async () => { - const { mockResolveLastOperation } = renderWithRelay( - { - CommerceOrder: () => - UntouchedBuyOrderWithArtsyShippingDomesticFromUS, - Me: () => meWithoutAddress, - }, - undefined, - relayEnv - ) + const { mockResolveLastOperation } = renderWithRelay({ + CommerceOrder: () => UntouchedBuyOrderWithArtsyShippingDomesticFromUS, + Me: () => meWithoutAddress, + }) await fillAddressForm(validAddress) await saveAndContinue() + await flushPromiseQueue() + const saveAddressOperation = await mockResolveLastOperation({ + CreateUserAddressPayload: () => saveAddressSuccess.createUserAddress, + }) + + expect(saveAddressOperation.operationName).toBe( + "useCreateSavedAddressMutation" + ) + await flushPromiseQueue() + const fulfillmentOperation = await resolveSaveFulfillmentDetails( mockResolveLastOperation, settingOrderArtaShipmentSuccess.commerceSetShipping, @@ -1688,28 +1668,6 @@ describe.skip("Shipping", () => { "useSaveFulfillmentDetailsMutation" ) - await flushPromiseQueue() - const saveAddressOperation = await mockResolveLastOperation({ - CreateUserAddressPayload: () => saveAddressSuccess.createUserAddress, - }) - - expect(saveAddressOperation.operationName).toBe( - "useCreateSavedAddressMutation" - ) - expect(saveAddressOperation.operationVariables).toEqual({ - input: { - attributes: { - name: "Joelle Van Dyne", - phoneNumber: "120938120983", - addressLine1: "401 Broadway", - addressLine2: "Suite 25", - city: "New York", - region: "NY", - country: "US", - postalCode: "10013", - }, - }, - }) // FIXME: `getByRole` can be slow and cause test to time out. // https://github.com/testing-library/dom-testing-library/issues/552#issuecomment-625172052 const premiumShipping = await screen.findByRole("radio", { @@ -1717,15 +1675,21 @@ describe.skip("Shipping", () => { }) await userEvent.click(premiumShipping) - const saveShippingAddressBox = screen.getByText( - /^Save shipping address/ - ) - await userEvent.click(saveShippingAddressBox) + await clickSaveAddress() await flushPromiseQueue() await saveAndContinue() await flushPromiseQueue() + const deleteAddressOperation = await mockResolveLastOperation({ + // DeleteUserAddressPayload: () => saveAddressSuccess, + }) + expect(deleteAddressOperation.operationName).toBe( + "useDeleteSavedAddressMutation" + ) + + await flushPromiseQueue() + const selectShippingOptionOperation = await mockResolveLastOperation({ CommerceSelectShippingOptionPayload: () => selectShippingQuoteSuccess.commerceSelectShippingOption, @@ -1734,13 +1698,6 @@ describe.skip("Shipping", () => { "useSelectShippingQuoteMutation" ) - await flushPromiseQueue() - const deleteAddressOperation = await mockResolveLastOperation({ - // DeleteUserAddressPayload: () => saveAddressSuccess, - }) - expect(deleteAddressOperation.operationName).toBe( - "useDeleteSavedAddressMutation" - ) expect(deleteAddressOperation.operationVariables).toEqual({ input: { userAddressID: "address-id", @@ -1751,14 +1708,10 @@ describe.skip("Shipping", () => { describe("with saved addresses", () => { it("re-saves an already-saved shipping address on load to refresh shipping quotes without saving address", async () => { - const { mockResolveLastOperation } = renderWithRelay( - { - CommerceOrder: () => BuyOrderWithArtaShippingDetails, - Me: () => meWithoutAddress, - }, - undefined, - relayEnv - ) + const { mockResolveLastOperation, env } = renderWithRelay({ + CommerceOrder: () => BuyOrderWithArtaShippingDetails, + Me: () => meWithoutAddress, + }) await waitFor(() => { const shippingBox = screen.getByTestId("ShippingQuotes_collapse") @@ -1791,27 +1744,26 @@ describe.skip("Shipping", () => { }) await flushPromiseQueue() - expect(getAllPendingOperationNames(relayEnv)).toEqual([]) + expect(getAllPendingOperationNames(env)).toEqual([]) }) + // TODO: EMI-1526 https://artsyproduct.atlassian.net/browse/EMI-1526 - describe.skip("Artsy shipping international only", () => { + describe("Artsy shipping international only", () => { describe("with artwork located in the US", () => { - it.skip("sets shipping on order if the collector is in the EU", async () => { + it("sets shipping on order if the collector is in the EU", async () => { const meWithDefaultAddressInSpain = cloneDeep( meWithAddresses ) as any + meWithDefaultAddressInSpain.addressConnection.edges[0].node.isDefault = true // Spain meWithDefaultAddressInSpain.addressConnection.edges[1].node.isDefault = false // US - const { mockResolveLastOperation } = renderWithRelay( - { - CommerceOrder: () => - UntouchedBuyOrderWithArtsyShippingInternationalFromUS, - Me: () => meWithDefaultAddressInSpain, - }, - undefined, - relayEnv - ) + const { mockResolveLastOperation } = renderWithRelay({ + CommerceOrder: () => + UntouchedBuyOrderWithArtsyShippingInternationalFromUS, + Me: () => meWithDefaultAddressInSpain, + }) + const fulfillmentRequest = await resolveSaveFulfillmentDetails( mockResolveLastOperation, settingOrderShipmentSuccess.commerceSetShipping @@ -1831,55 +1783,37 @@ describe.skip("Shipping", () => { city: "Madrid", country: "ES", name: "Test Name", - phoneNumber: "555-555-5555", + phoneNumber: "", postalCode: "28001", region: "", }, }, }) }) - - it.skip("does not set shipping on order automatically if the collector is in the US", async () => { - const { env } = renderWithRelay( - { - CommerceOrder: () => - UntouchedBuyOrderWithArtsyShippingInternationalFromUS, - Me: () => meWithAddresses, - }, - undefined, - relayEnv - ) - await flushPromiseQueue() - - expect(() => env.mock.getMostRecentOperation()).toThrow() - expect( - screen.queryByRole("radio", { - name: /(^Standard|^Express|^White Glove|^Rush|^Premium)/, - }) - ).not.toBeInTheDocument() - }) }) describe("with artwork located in Germany", () => { - it.skip("does not set shipping on order automatically if the collector is in the EU", async () => { + it("does not set shipping on order automatically if the collector is in the EU", async () => { + // TODO: Why would we want this behavior? We can now set shipping on all valid saved addresses- + // no need to check whether it needs artsy shipping. const meWithDefaultAddressInSpain = cloneDeep( meWithAddresses ) as any meWithDefaultAddressInSpain.addressConnection.edges[0].node.isDefault = true // Spain meWithDefaultAddressInSpain.addressConnection.edges[1].node.isDefault = false // US - const { env } = renderWithRelay( - { - CommerceOrder: () => - UntouchedBuyOrderWithArtsyShippingInternationalFromGermany, - Me: () => meWithDefaultAddressInSpain, - }, - undefined, - relayEnv + const { env, mockResolveLastOperation } = renderWithRelay({ + CommerceOrder: () => + UntouchedBuyOrderWithArtsyShippingInternationalFromGermany, + Me: () => meWithDefaultAddressInSpain, + }) + + await resolveSaveFulfillmentDetails( + mockResolveLastOperation, + settingOrderShipmentSuccess.commerceSetShipping ) - await flushPromiseQueue() - expect(() => env.mock.getMostRecentOperation()).toThrow() + expect(getAllPendingOperationNames(env)).toEqual([]) expect( screen.queryByRole("radio", { name: /(^Standard|^Express|^White Glove|^Rush|^Premium)/, @@ -1887,16 +1821,13 @@ describe.skip("Shipping", () => { ).not.toBeInTheDocument() }) - it.skip("sets shipping on order automatically if the collector is in the US", async () => { - const { mockResolveLastOperation } = renderWithRelay( - { - CommerceOrder: () => - UntouchedBuyOrderWithArtsyShippingInternationalFromGermany, - Me: () => meWithAddresses, - }, - undefined, - relayEnv - ) + it("sets shipping on order automatically if the collector is in the US", async () => { + const { mockResolveLastOperation } = renderWithRelay({ + CommerceOrder: () => + UntouchedBuyOrderWithArtsyShippingInternationalFromGermany, + Me: () => meWithAddresses, + }) + const fulfillmentRequest = await resolveSaveFulfillmentDetails( mockResolveLastOperation, settingOrderShipmentSuccess.commerceSetShipping @@ -1916,7 +1847,7 @@ describe.skip("Shipping", () => { city: "New York", country: "US", name: "Test Name", - phoneNumber: "422-424-4242", + phoneNumber: "", postalCode: "10013", region: "NY", }, @@ -1925,25 +1856,25 @@ describe.skip("Shipping", () => { }) }) }) - // TODO: EMI-1526 https://artsyproduct.atlassian.net/browse/EMI-1526 - describe.skip("Artsy shipping domestic only", () => { + + describe("Artsy shipping domestic only", () => { describe("with artwork located in Germany", () => { - it.skip("sets shipping on order if the collector is in Germany", async () => { + // TODO: Like the test above, these tests assume we don't want to automatically set shipping + // unless the default address would require artsy shipping. That is no longer the case- + // We can safely set the shipping. See alsoped test above (~L1957) + it("sets shipping on order if the collector is in the EU", async () => { const meWithDefaultAddressInSpain = cloneDeep( meWithAddresses ) as any meWithDefaultAddressInSpain.addressConnection.edges[0].node.isDefault = true // Spain meWithDefaultAddressInSpain.addressConnection.edges[1].node.isDefault = false // US - const { mockResolveLastOperation } = renderWithRelay( - { - CommerceOrder: () => - UntouchedBuyOrderWithArtsyShippingDomesticFromGermany, - Me: () => meWithDefaultAddressInSpain, - }, - undefined, - relayEnv - ) + const { mockResolveLastOperation } = renderWithRelay({ + CommerceOrder: () => + UntouchedBuyOrderWithArtsyShippingDomesticFromGermany, + Me: () => meWithDefaultAddressInSpain, + }) + const fulfillmentRequest = await resolveSaveFulfillmentDetails( mockResolveLastOperation, settingOrderShipmentSuccess.commerceSetShipping @@ -1963,7 +1894,7 @@ describe.skip("Shipping", () => { city: "Madrid", country: "ES", name: "Test Name", - phoneNumber: "555-555-5555", + phoneNumber: "", postalCode: "28001", region: "", }, @@ -1971,19 +1902,19 @@ describe.skip("Shipping", () => { }) }) - it.skip("does not set shipping on order if the collector is in the US", async () => { - const { env } = renderWithRelay( - { - CommerceOrder: () => - UntouchedBuyOrderWithArtsyShippingDomesticFromGermany, - Me: () => meWithAddresses, - }, - undefined, - relayEnv + it("does not set shipping on order if the collector is in the US", async () => { + const { env, mockResolveLastOperation } = renderWithRelay({ + CommerceOrder: () => + UntouchedBuyOrderWithArtsyShippingDomesticFromGermany, + Me: () => meWithAddresses, + }) + + await resolveSaveFulfillmentDetails( + mockResolveLastOperation, + settingOrderShipmentSuccess.commerceSetShipping ) - await flushPromiseQueue() - expect(() => env.mock.getMostRecentOperation()).toThrow() + expect(getAllPendingOperationNames(env)).toEqual([]) expect( screen.queryByRole("radio", { name: /(^Standard|^Express|^White Glove|^Rush|^Premium)/, @@ -1993,25 +1924,25 @@ describe.skip("Shipping", () => { }) describe("with artwork located in the US", () => { - it.skip("does not fetch or show shipping quotes if the collector is in the EU", async () => { + it("does not fetch or show shipping quotes if the collector is in the EU", async () => { const meWithDefaultAddressInSpain = cloneDeep( meWithAddresses ) as any meWithDefaultAddressInSpain.addressConnection.edges[0].node.isDefault = true // Spain meWithDefaultAddressInSpain.addressConnection.edges[1].node.isDefault = false // US - const { env } = renderWithRelay( - { - CommerceOrder: () => - UntouchedBuyOrderWithArtsyShippingDomesticFromUS, - Me: () => meWithDefaultAddressInSpain, - }, - undefined, - relayEnv + const { env, mockResolveLastOperation } = renderWithRelay({ + CommerceOrder: () => + UntouchedBuyOrderWithArtsyShippingDomesticFromUS, + Me: () => meWithDefaultAddressInSpain, + }) + + await resolveSaveFulfillmentDetails( + mockResolveLastOperation, + settingOrderShipmentSuccess.commerceSetShipping ) - await flushPromiseQueue() - expect(() => env.mock.getMostRecentOperation()).toThrow() + expect(getAllPendingOperationNames(env)).toEqual([]) expect( screen.queryByRole("radio", { name: /(^Standard|^Express|^White Glove|^Rush|^Premium)/, @@ -2020,12 +1951,13 @@ describe.skip("Shipping", () => { }) describe("with the collector in the US", () => { - it.skip("sets shipping with the default address on load", async () => { + it("sets shipping with the default address on load", async () => { const { mockResolveLastOperation } = renderWithRelay({ CommerceOrder: () => UntouchedBuyOrderWithArtsyShippingDomesticFromUS, Me: () => meWithAddresses, }) + const fulfillmentRequest = await resolveSaveFulfillmentDetails( mockResolveLastOperation, settingOrderArtaShipmentSuccess.commerceSetShipping @@ -2034,7 +1966,7 @@ describe.skip("Shipping", () => { expect(fulfillmentRequest.operationName).toBe( "useSaveFulfillmentDetailsMutation" ) - expect(fulfillmentRequest.operationVariables).toEqual({ + expect(fulfillmentRequest.operationVariables.input).toEqual({ id: "2939023", fulfillmentType: "SHIP_ARTA", phoneNumber: "422-424-4242", @@ -2044,23 +1976,19 @@ describe.skip("Shipping", () => { city: "New York", country: "US", name: "Test Name", - phoneNumber: "422-424-4242", + phoneNumber: "", postalCode: "10013", region: "NY", }, }) }) - it.skip("shows shipping quotes for the default address on load", async () => { - const { mockResolveLastOperation } = renderWithRelay( - { - CommerceOrder: () => - UntouchedBuyOrderWithArtsyShippingDomesticFromUS, - Me: () => meWithAddresses, - }, - undefined, - relayEnv - ) + it("shows shipping quotes for the default address on load", async () => { + const { mockResolveLastOperation } = renderWithRelay({ + CommerceOrder: () => + UntouchedBuyOrderWithArtsyShippingDomesticFromUS, + Me: () => meWithAddresses, + }) const fulfillmentRequest = await resolveSaveFulfillmentDetails( mockResolveLastOperation, settingOrderArtaShipmentSuccess.commerceSetShipping @@ -2077,16 +2005,12 @@ describe.skip("Shipping", () => { ).toHaveLength(5) }) - it.skip("sets shipping on order, shows shipping quotes and saves the pre-selected quote", async () => { - const { mockResolveLastOperation } = renderWithRelay( - { - // Simulate the condition with an order with saved shipping quotes - CommerceOrder: () => UntouchedBuyOrderWithShippingQuotes, - Me: () => meWithAddresses, - }, - undefined, - relayEnv - ) + it("sets shipping on order, shows shipping quotes and saves the pre-selected quote", async () => { + const { mockResolveLastOperation } = renderWithRelay({ + // Simulate the condition with an order with saved shipping quotes + CommerceOrder: () => UntouchedBuyOrderWithShippingQuotes, + Me: () => meWithAddresses, + }) const fulfillmentRequest = await resolveSaveFulfillmentDetails( mockResolveLastOperation, @@ -2096,8 +2020,10 @@ describe.skip("Shipping", () => { expect(fulfillmentRequest.operationName).toBe( "useSaveFulfillmentDetailsMutation" ) + expect(screen.getByText("Save and Continue")).toBeEnabled() + await flushPromiseQueue() await saveAndContinue() await flushPromiseQueue() @@ -2117,16 +2043,12 @@ describe.skip("Shipping", () => { }) }) - it.skip("selects a different shipping quote and saves it", async () => { - const { mockResolveLastOperation } = renderWithRelay( - { - // Simulate the condition with an order with saved shipping quotes - CommerceOrder: () => UntouchedBuyOrderWithShippingQuotes, - Me: () => meWithAddresses, - }, - undefined, - relayEnv - ) + it("selects a different shipping quote and saves it", async () => { + const { mockResolveLastOperation } = renderWithRelay({ + // Simulate the condition with an order with saved shipping quotes + CommerceOrder: () => UntouchedBuyOrderWithShippingQuotes, + Me: () => meWithAddresses, + }) const fulfillmentRequest = await resolveSaveFulfillmentDetails( mockResolveLastOperation, settingOrderArtaShipmentSuccess.commerceSetShipping @@ -2146,7 +2068,7 @@ describe.skip("Shipping", () => { city: "New York", country: "US", name: "Test Name", - phoneNumber: "422-424-4242", + phoneNumber: "", postalCode: "10013", region: "NY", }, @@ -2173,16 +2095,12 @@ describe.skip("Shipping", () => { }) }) - it.skip("keeps the submit button enabled after selecting a shipping quote", async () => { - const { mockResolveLastOperation } = renderWithRelay( - { - // Simulate the condition with an order with saved shipping quotes - CommerceOrder: () => UntouchedBuyOrderWithShippingQuotes, - Me: () => meWithAddresses, - }, - undefined, - relayEnv - ) + it("keeps the submit button enabled after selecting a shipping quote", async () => { + const { mockResolveLastOperation } = renderWithRelay({ + // Simulate the condition with an order with saved shipping quotes + CommerceOrder: () => UntouchedBuyOrderWithShippingQuotes, + Me: () => meWithAddresses, + }) await resolveSaveFulfillmentDetails( mockResolveLastOperation, settingOrderArtaShipmentSuccess.commerceSetShipping @@ -2202,16 +2120,12 @@ describe.skip("Shipping", () => { ).toBeEnabled() }) - it.skip("routes to payment screen after saving shipping option", async () => { - const { mockResolveLastOperation } = renderWithRelay( - { - // Simulate the condition with an order with saved shipping quotes - CommerceOrder: () => UntouchedBuyOrderWithShippingQuotes, - Me: () => meWithAddresses, - }, - undefined, - relayEnv - ) + it("routes to payment screen after saving shipping option", async () => { + const { mockResolveLastOperation, env } = renderWithRelay({ + // Simulate the condition with an order with saved shipping quotes + CommerceOrder: () => UntouchedBuyOrderWithShippingQuotes, + Me: () => meWithAddresses, + }) const fulfillmentRequest = await resolveSaveFulfillmentDetails( mockResolveLastOperation, settingOrderArtaShipmentSuccess.commerceSetShipping @@ -2232,11 +2146,11 @@ describe.skip("Shipping", () => { "useSelectShippingQuoteMutation" ) - expect(getAllPendingOperationNames(relayEnv)).toEqual([]) + expect(getAllPendingOperationNames(env)).toEqual([]) expect(mockPush).toHaveBeenCalledWith("/orders/2939023/payment") }) - it.skip("reloads shipping quotes after editing the selected address", async () => { + it("reloads shipping quotes after editing the selected address", async () => { // const updateAddressResponse = cloneDeep( // updateAddressSuccess // ) as any @@ -2250,46 +2164,65 @@ describe.skip("Shipping", () => { // onSuccess(updateAddressResponse) // }) - const { mockResolveLastOperation } = renderWithRelay( - { - CommerceOrder: () => UntouchedBuyOrderWithShippingQuotes, - Me: () => meWithAddresses, - }, - undefined - ) + const { mockResolveLastOperation } = renderWithRelay({ + CommerceOrder: () => UntouchedBuyOrderWithShippingQuotes, + Me: () => meWithAddresses, + }) await resolveSaveFulfillmentDetails( mockResolveLastOperation, settingOrderArtaShipmentSuccess.commerceSetShipping ) - // Edit the selected address - const selectedAddress = screen.getByRole("radio", { - name: /401 Broadway/, - checked: true, - }) + const selectedAddress = screen.getAllByTestId("savedAddress")[1] + expect(selectedAddress).toHaveTextContent("401 Broadway") + await userEvent.click(within(selectedAddress).getByText("Edit")) - const modalTitle = screen.getByText("Edit address") + const modalTitle = await screen.findByText("Edit address") expect(modalTitle).toBeVisible() // TODO: need a better way to get a specific input field from multiple forms - const addressLine2 = screen.getAllByPlaceholderText( - /Apt, floor, suite/ - )[0] - userEvent.clear(addressLine2) - userEvent.paste(addressLine2, "25th fl.") - userEvent.click(screen.getByText("Save")) + await waitFor(async () => { + const addressModal = screen.getByTestId("AddressModal") + const addressLine2 = within(addressModal).getByPlaceholderText( + /Apt, floor, suite/ + ) + await userEvent.clear(addressLine2) + await userEvent.paste(addressLine2, "25th fl.") + }) + + userEvent.click(screen.getByRole("button", { name: "Save" })) await flushPromiseQueue() + const updateAddressOperation = await mockResolveLastOperation({ - UpdateUserAddressPayload: () => saveAddressSuccess, + UpdateUserAddressPayload: () => ({ + ...saveAddressSuccess.createUserAddress, + addressLine2: "25th fl.", + }), + Me: () => ({ + ...meWithAddresses, + addressConnection: { + totalCount: 2, + edges: [ + meWithAddresses.addressConnection!.edges![0], + + { + node: { + ...meWithAddresses.addressConnection!.edges![1], + addressLine2: "25th fl.", + }, + }, + ], + }, + }), }) - expect(updateAddressOperation.operationName).toBe( + expect(updateAddressOperation.operationName).toEqual( "useUpdateSavedAddressMutation" ) - expect(updateAddressOperation.operationVariables).toEqual({ + expect(updateAddressOperation.operationVariables).toMatchObject({ input: { attributes: { addressLine1: "401 Broadway", @@ -2305,9 +2238,15 @@ describe.skip("Shipping", () => { }, }) - await resolveSaveFulfillmentDetails( + await flushPromiseQueue() + + const saveFulfillmentOperation = await resolveSaveFulfillmentDetails( mockResolveLastOperation, - settingOrderArtaShipmentSuccess.commerceSetShipping + settingOrderShipmentSuccess.commerceSetShipping + ) + + expect(saveFulfillmentOperation.operationName).toEqual( + "useSaveFulfillmentDetailsMutation" ) await saveAndContinue() @@ -2331,122 +2270,6 @@ describe.skip("Shipping", () => { }, }) }) - - // TODO: Does this behavior matter? Test above shows migration from - // for a very similar usage of mockCommitMutation. stale code - // has been commented out - it.skip("does not reload shipping quotes after editing a non-selected address", async () => { - // mockCommitMutation - // .mockResolvedValueOnce(settingOrderArtaShipmentSuccess) - // .mockResolvedValueOnce(settingOrderArtaShipmentSuccess) - - // const updateAddressResponse = cloneDeep( - // updateAddressSuccess - // ) as any - // // Match the edited address with the selected address to trigger refetching quotes - // updateAddressResponse.updateUserAddress.userAddressOrErrors.internalID = - // "1" - // const updateAddressSpy = jest - // .spyOn(updateUserAddress, "updateUserAddress") - // // @ts-ignore - // .mockImplementationOnce((_, __, ___, ____, onSuccess) => { - // onSuccess(updateAddressResponse) - // }) - - const { env } = renderWithRelay( - { - CommerceOrder: () => UntouchedBuyOrderWithShippingQuotes, - Me: () => meWithAddresses, - }, - undefined, - relayEnv - ) - await flushPromiseQueue() - - // Set shipping/fetch quotes on load for the default address - // expect(mockCommitMutation).toHaveBeenCalledTimes(1) - // let mutationArg = mockCommitMutation.mock.calls[0][0] - // expect(mutationArg.mutation.default.operation.name).toEqual( - // "SetShippingMutation" - // ) - - // Edit the address that's not selected - const nonSelectedAddress = screen.getByRole("radio", { - name: /1 Main St/, - checked: false, - }) - await userEvent.click( - within(nonSelectedAddress).getByText("Edit") - ) - - const modalTitle = screen.getByText("Edit address") - expect(modalTitle).toBeVisible() - - // TODO: need a better way to get a specific input field from multiple forms - const addressLine2 = screen.getAllByPlaceholderText( - /Apt, floor, suite/ - )[0] - userEvent.clear(addressLine2) - userEvent.paste(addressLine2, "25th fl.") - userEvent.click(screen.getByText("Save")) - - await flushPromiseQueue() - - // expect(updateAddressSpy).toHaveBeenCalledTimes(1) - // expect(updateAddressSpy).toHaveBeenCalledWith( - // expect.anything(), - // "1", - // { - // addressLine1: "1 Main St", - // addressLine2: "25th fl.", - // addressLine3: "", - // city: "Madrid", - // country: "ES", - // name: "Test Name", - // phoneNumber: "555-555-5555", - // postalCode: "28001", - // region: "", - // }, - // expect.anything(), - // expect.anything(), - // expect.anything() - // ) - - // TODO: This uses relay mock environment to mock the refetch query. Is there a better way? - const mutation = env.mock.getMostRecentOperation() - expect(mutation.request.node.operation.name).toEqual( - "SavedAddressesRefetchQuery" - ) - expect(mutation.request.variables).toEqual({}) - - const updatedMe = cloneDeep(meWithAddresses) as any - updatedMe.addressConnection.edges[1].node.addressLine2 = - "25th fl." - env.mock.resolveMostRecentOperation(operation => { - return MockPayloadGenerator.generate(operation, { - CommerceOrder: () => UntouchedBuyOrderWithShippingQuotes, - Me: () => updatedMe, - }) - }) - - // Wait for the second setShipping mutation to complete before clicking save and continue - await flushPromiseQueue() - await saveAndContinue() - - // expect(mockCommitMutation).toHaveBeenCalledTimes(2) - - // mutationArg = mockCommitMutation.mock.calls[1][0] - // expect(mutationArg.mutation.default.operation.name).toEqual( - // "SelectShippingOptionMutation" - // ) - // expect(mutationArg.variables).toEqual({ - // input: { - // id: "2939023", - // selectedShippingQuoteId: - // "4a8f8080-23d3-4c0e-9811-7a41a9df6933", - // }, - // }) - }) }) }) }) @@ -2463,6 +2286,7 @@ describe.skip("Shipping", () => { await userEvent.click( screen.getByRole("radio", { name: /Arrange for pickup/ }) ) + const phoneNumber = screen.getByPlaceholderText( "Add phone number including country code" ) @@ -2472,18 +2296,16 @@ describe.skip("Shipping", () => { }) it("sets pickup on order and advances to payment", async () => { - const { mockResolveLastOperation } = renderWithRelay( - { - CommerceOrder: () => order, - Me: () => meWithoutAddress, - }, - undefined, - relayEnv - ) + const { mockResolveLastOperation, env } = renderWithRelay({ + CommerceOrder: () => order, + Me: () => meWithoutAddress, + }) await userEvent.click( screen.getByRole("radio", { name: /Arrange for pickup/ }) ) + + await flushPromiseQueue() await userEvent.paste( screen.getAllByPlaceholderText( "Add phone number including country code" @@ -2494,6 +2316,8 @@ describe.skip("Shipping", () => { screen.getByPlaceholderText("Full name"), "Joelle Van Dyne" ) + + await flushPromiseQueue() await saveAndContinue() const fulfillmentRequest = await resolveSaveFulfillmentDetails( @@ -2533,24 +2357,25 @@ describe.skip("Shipping", () => { }, }) await waitFor(() => { - expect(getAllPendingOperationNames(relayEnv)).toEqual([]) + expect(getAllPendingOperationNames(env)).toEqual([]) expect(mockPush).toHaveBeenCalledWith("/orders/2939023/payment") }) }) it("disables submission without a phone number", async () => { - renderWithRelay({ + const { env } = renderWithRelay({ CommerceOrder: () => order, Me: () => meWithoutAddress, }) userEvent.click(screen.getByRole("radio", { name: /Arrange for pickup/ })) + await flushPromiseQueue() - expect( - screen.getByRole("button", { - name: "Save and Continue", - }) as HTMLInputElement - ).toBeDisabled() + await saveAndContinue() + + expect(screen.getByText("Phone number is required")).toBeInTheDocument() + + expect(env.mock.getAllOperations()).toHaveLength(0) }) }) }) diff --git a/src/Apps/Order/Routes/Shipping2/index.tsx b/src/Apps/Order/Routes/Shipping2/index.tsx index 30031346e3e..9caef991de5 100644 --- a/src/Apps/Order/Routes/Shipping2/index.tsx +++ b/src/Apps/Order/Routes/Shipping2/index.tsx @@ -20,14 +20,17 @@ import { FulfillmentDetails } from "Apps/Order/Routes/Shipping2/Components/Fulfi import { ShippingContextProvider } from "Apps/Order/Routes/Shipping2/ShippingContext" import { useShippingContext } from "Apps/Order/Routes/Shipping2/Hooks/useShippingContext" import { SaveAndContinueButton } from "Apps/Order/Routes/Shipping2/Components/SaveAndContinueButton" -import { useBackToFullfillmentDetails } from "Apps/Order/Routes/Shipping2/Hooks/useBackToFulfillmentDetails" import { useSelectFirstShippingQuote } from "Apps/Order/Routes/Shipping2/Hooks/useSelectFirstShippingQuote" import { CollapseDetails } from "Apps/Order/Routes/Shipping2/Components/CollapseDetails" export type ShippingStage = + // User choosing fulfillment type | "fulfillment_details" + // Temporary stage after address has been automatically saved + // to wait for click + | "fulfillment_details_saved" + // User choosing shipping quote | "shipping_quotes" - | "refresh_shipping_quotes" export interface ShippingProps { order: Shipping2_order$data @@ -58,15 +61,11 @@ const ShippingRouteLayout: FC> = ({ const isOffer = order.mode === "OFFER" - // Go back to fulfillment details stage if the user edits the address or - // deletes a saved address. - useBackToFullfillmentDetails(me) - // Automatically selects first shipping quote when they change useSelectFirstShippingQuote() return ( - + { const testidSuffix = substrings.length > 2 ? `-${index}` : "" return ( - <> + {substring} {!isLastSubstring && ( { {SUPPORT_EMAIL} )} - + ) }) diff --git a/src/Apps/__tests__/Fixtures/Order.ts b/src/Apps/__tests__/Fixtures/Order.ts index 8a03bb54a0b..7c679731570 100644 --- a/src/Apps/__tests__/Fixtures/Order.ts +++ b/src/Apps/__tests__/Fixtures/Order.ts @@ -66,7 +66,7 @@ const artworkFromGermany = { const OrderArtworkVersionNode = { artworkVersion: { - id: "02393", + id: "av02393", artistNames: "Lisa Breslow", title: "Gramercy Park South", provenance: "", diff --git a/src/Components/__tests__/Utils/addressForm2.ts b/src/Components/__tests__/Utils/addressForm2.ts index 2af3e4a891a..3d327f2d8d9 100644 --- a/src/Components/__tests__/Utils/addressForm2.ts +++ b/src/Components/__tests__/Utils/addressForm2.ts @@ -47,37 +47,55 @@ export const fillCountrySelect = (component, value) => { input.props().onSelect(value) } -export const fillAddressForm = async (address: Address) => { +export const clickSaveAddress = async () => { + await userEvent.click( + screen.getByRole("checkbox", { name: /Save shipping address/ }) + ) +} + +export const fillAddressForm = async (address: Partial
) => { await waitFor(() => { const line1Input = screen.getByPlaceholderText("Street address") expect(line1Input).toBeEnabled() }) - const country = screen.getByTestId("AddressForm_country") - await userEvent.selectOptions(country, [address.country]) - const name = screen.getByPlaceholderText("Full name") - const addressLine1 = screen.getByPlaceholderText("Street address") - const addressLine2 = screen.getByPlaceholderText("Apt, floor, suite, etc.") - const city = screen.getByPlaceholderText("City") - const region = screen.getByPlaceholderText( - address.country === "US" ? "State" : "State, province, or region" - ) - const postalCode = screen.getByPlaceholderText( - address.country === "US" ? "ZIP code" : /ZIP\/postal code/, - { exact: false } - ) - const phoneNumber = screen.getAllByPlaceholderText( - "Add phone number including country code" - )[0] + if (address.country) { + const country = await screen.findByTestId(/Form_country/) + await userEvent.selectOptions(country, [address.country]) + } + + const [ + name, + addressLine1, + addressLine2, + city, + region, + postalCode, + phoneNumber, + ] = await Promise.all([ + screen.findByPlaceholderText("Full name"), + screen.findByPlaceholderText("Street address"), + screen.findByPlaceholderText("Apt, floor, suite, etc."), + screen.findByPlaceholderText("City"), + screen.findByPlaceholderText( + address.country === "US" ? "State" : "State, province, or region" + ), + screen.findByPlaceholderText( + address.country === "US" ? "ZIP code" : /ZIP\/Postal code/, + { exact: false } + ), + screen + .findAllByPlaceholderText("Add phone number including country code") + .then(inputs => inputs[0]), + ]) await Promise.all([ - userEvent.paste(name, address.name), - userEvent.paste(addressLine1, address.addressLine1), - userEvent.paste(addressLine2, address.addressLine2), - userEvent.paste(city, address.city), - userEvent.paste(region, address.region), - userEvent.paste(postalCode, address.postalCode), + address.name && userEvent.paste(name, address.name), + address.addressLine1 && userEvent.paste(addressLine1, address.addressLine1), + address.addressLine2 && userEvent.paste(addressLine2, address.addressLine2), + address.city && userEvent.paste(city, address.city), + address.region && userEvent.paste(region, address.region), + address.postalCode && userEvent.paste(postalCode, address.postalCode), + address.phoneNumber && userEvent.paste(phoneNumber, address.phoneNumber), ]) - address.phoneNumber && - (await userEvent.paste(phoneNumber, address.phoneNumber)) } diff --git a/src/System/useFeatureFlag.tsx b/src/System/useFeatureFlag.tsx index bf8b804b0b3..87b51094e4f 100644 --- a/src/System/useFeatureFlag.tsx +++ b/src/System/useFeatureFlag.tsx @@ -30,7 +30,7 @@ export function useFeatureFlag(featureName: string): boolean | null { if (flagEnabled === undefined) { warnInDevelopment( - "[Force] Warning: cannot find flagName in featureFlags: ", + `[Force] Warning: cannot find ${featureName} in featureFlags: `, featureFlags ) return null diff --git a/src/__generated__/SavedAddresses2Mutation_Test_Query.graphql.ts b/src/__generated__/SavedAddresses2Mutation_Test_Query.graphql.ts deleted file mode 100644 index a974fd25b46..00000000000 --- a/src/__generated__/SavedAddresses2Mutation_Test_Query.graphql.ts +++ /dev/null @@ -1,361 +0,0 @@ -/** - * @generated SignedSource<<6fcc7406799e51e5eaf5f0be29eb55ec>> - * @lightSyntaxTransform - * @nogrep - */ - -/* tslint:disable */ -/* eslint-disable */ -// @ts-nocheck - -import { ConcreteRequest, Query } from 'relay-runtime'; -import { FragmentRefs } from "relay-runtime"; -export type SavedAddresses2Mutation_Test_Query$variables = Record; -export type SavedAddresses2Mutation_Test_Query$data = { - readonly me: { - readonly " $fragmentSpreads": FragmentRefs<"SavedAddresses2_me">; - } | null | undefined; -}; -export type SavedAddresses2Mutation_Test_Query = { - response: SavedAddresses2Mutation_Test_Query$data; - variables: SavedAddresses2Mutation_Test_Query$variables; -}; - -const node: ConcreteRequest = (function(){ -var v0 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "id", - "storageKey": null -}, -v1 = [ - { - "kind": "Literal", - "name": "first", - "value": 30 - } -], -v2 = { - "enumValues": null, - "nullable": false, - "plural": false, - "type": "String" -}, -v3 = { - "enumValues": null, - "nullable": true, - "plural": false, - "type": "String" -}, -v4 = { - "enumValues": null, - "nullable": false, - "plural": false, - "type": "ID" -}, -v5 = { - "enumValues": null, - "nullable": false, - "plural": false, - "type": "Boolean" -}; -return { - "fragment": { - "argumentDefinitions": [], - "kind": "Fragment", - "metadata": null, - "name": "SavedAddresses2Mutation_Test_Query", - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "Me", - "kind": "LinkedField", - "name": "me", - "plural": false, - "selections": [ - { - "args": null, - "kind": "FragmentSpread", - "name": "SavedAddresses2_me" - } - ], - "storageKey": null - } - ], - "type": "Query", - "abstractKey": null - }, - "kind": "Request", - "operation": { - "argumentDefinitions": [], - "kind": "Operation", - "name": "SavedAddresses2Mutation_Test_Query", - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "Me", - "kind": "LinkedField", - "name": "me", - "plural": false, - "selections": [ - (v0/*: any*/), - { - "alias": null, - "args": (v1/*: any*/), - "concreteType": "UserAddressConnection", - "kind": "LinkedField", - "name": "addressConnection", - "plural": false, - "selections": [ - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "totalCount", - "storageKey": null - }, - { - "alias": null, - "args": null, - "concreteType": "UserAddressEdge", - "kind": "LinkedField", - "name": "edges", - "plural": true, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "UserAddress", - "kind": "LinkedField", - "name": "node", - "plural": false, - "selections": [ - (v0/*: any*/), - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "internalID", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "addressLine1", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "addressLine2", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "addressLine3", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "city", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "country", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "isDefault", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "name", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "phoneNumber", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "postalCode", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "region", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "__typename", - "storageKey": null - } - ], - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "cursor", - "storageKey": null - } - ], - "storageKey": null - }, - { - "alias": null, - "args": null, - "concreteType": "PageInfo", - "kind": "LinkedField", - "name": "pageInfo", - "plural": false, - "selections": [ - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "endCursor", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "hasNextPage", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "hasPreviousPage", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "startCursor", - "storageKey": null - } - ], - "storageKey": null - } - ], - "storageKey": "addressConnection(first:30)" - }, - { - "alias": null, - "args": (v1/*: any*/), - "filters": null, - "handle": "connection", - "key": "SavedAddresses_addressConnection", - "kind": "LinkedHandle", - "name": "addressConnection" - } - ], - "storageKey": null - } - ] - }, - "params": { - "cacheID": "470da9e59f3c08d04092160f164c773f", - "id": null, - "metadata": { - "relayTestingSelectionTypeInfo": { - "me": { - "enumValues": null, - "nullable": true, - "plural": false, - "type": "Me" - }, - "me.addressConnection": { - "enumValues": null, - "nullable": true, - "plural": false, - "type": "UserAddressConnection" - }, - "me.addressConnection.edges": { - "enumValues": null, - "nullable": true, - "plural": true, - "type": "UserAddressEdge" - }, - "me.addressConnection.edges.cursor": (v2/*: any*/), - "me.addressConnection.edges.node": { - "enumValues": null, - "nullable": true, - "plural": false, - "type": "UserAddress" - }, - "me.addressConnection.edges.node.__typename": (v2/*: any*/), - "me.addressConnection.edges.node.addressLine1": (v2/*: any*/), - "me.addressConnection.edges.node.addressLine2": (v3/*: any*/), - "me.addressConnection.edges.node.addressLine3": (v3/*: any*/), - "me.addressConnection.edges.node.city": (v2/*: any*/), - "me.addressConnection.edges.node.country": (v2/*: any*/), - "me.addressConnection.edges.node.id": (v4/*: any*/), - "me.addressConnection.edges.node.internalID": (v4/*: any*/), - "me.addressConnection.edges.node.isDefault": (v5/*: any*/), - "me.addressConnection.edges.node.name": (v3/*: any*/), - "me.addressConnection.edges.node.phoneNumber": (v3/*: any*/), - "me.addressConnection.edges.node.postalCode": (v3/*: any*/), - "me.addressConnection.edges.node.region": (v3/*: any*/), - "me.addressConnection.pageInfo": { - "enumValues": null, - "nullable": false, - "plural": false, - "type": "PageInfo" - }, - "me.addressConnection.pageInfo.endCursor": (v3/*: any*/), - "me.addressConnection.pageInfo.hasNextPage": (v5/*: any*/), - "me.addressConnection.pageInfo.hasPreviousPage": (v5/*: any*/), - "me.addressConnection.pageInfo.startCursor": (v3/*: any*/), - "me.addressConnection.totalCount": { - "enumValues": null, - "nullable": false, - "plural": false, - "type": "Int" - }, - "me.id": (v4/*: any*/) - } - }, - "name": "SavedAddresses2Mutation_Test_Query", - "operationKind": "query", - "text": "query SavedAddresses2Mutation_Test_Query {\n me {\n ...SavedAddresses2_me\n id\n }\n}\n\nfragment SavedAddresses2_me on Me {\n id\n addressConnection(first: 30) {\n totalCount\n edges {\n node {\n id\n internalID\n addressLine1\n addressLine2\n addressLine3\n city\n country\n isDefault\n name\n phoneNumber\n postalCode\n region\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n}\n" - } -}; -})(); - -(node as any).hash = "e9ad32612f2734769d1b219ed7961dc9"; - -export default node; diff --git a/src/__generated__/SavedAddresses2RefetchQuery.graphql.ts b/src/__generated__/SavedAddresses2TestQuery.graphql.ts similarity index 57% rename from src/__generated__/SavedAddresses2RefetchQuery.graphql.ts rename to src/__generated__/SavedAddresses2TestQuery.graphql.ts index 62d05e00e9b..db3d81f66d0 100644 --- a/src/__generated__/SavedAddresses2RefetchQuery.graphql.ts +++ b/src/__generated__/SavedAddresses2TestQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<609898a82f90e36e46b1fea2715f349e>> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -10,15 +10,15 @@ import { ConcreteRequest, Query } from 'relay-runtime'; import { FragmentRefs } from "relay-runtime"; -export type SavedAddresses2RefetchQuery$variables = Record; -export type SavedAddresses2RefetchQuery$data = { +export type SavedAddresses2TestQuery$variables = Record; +export type SavedAddresses2TestQuery$data = { readonly me: { readonly " $fragmentSpreads": FragmentRefs<"SavedAddresses2_me">; } | null | undefined; }; -export type SavedAddresses2RefetchQuery = { - response: SavedAddresses2RefetchQuery$data; - variables: SavedAddresses2RefetchQuery$variables; +export type SavedAddresses2TestQuery = { + response: SavedAddresses2TestQuery$data; + variables: SavedAddresses2TestQuery$variables; }; const node: ConcreteRequest = (function(){ @@ -28,20 +28,13 @@ var v0 = { "kind": "ScalarField", "name": "id", "storageKey": null -}, -v1 = [ - { - "kind": "Literal", - "name": "first", - "value": 30 - } -]; +}; return { "fragment": { "argumentDefinitions": [], "kind": "Fragment", "metadata": null, - "name": "SavedAddresses2RefetchQuery", + "name": "SavedAddresses2TestQuery", "selections": [ { "alias": null, @@ -67,7 +60,7 @@ return { "operation": { "argumentDefinitions": [], "kind": "Operation", - "name": "SavedAddresses2RefetchQuery", + "name": "SavedAddresses2TestQuery", "selections": [ { "alias": null, @@ -77,22 +70,20 @@ return { "name": "me", "plural": false, "selections": [ - (v0/*: any*/), { "alias": null, - "args": (v1/*: any*/), + "args": [ + { + "kind": "Literal", + "name": "first", + "value": 30 + } + ], "concreteType": "UserAddressConnection", "kind": "LinkedField", "name": "addressConnection", "plural": false, "selections": [ - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "totalCount", - "storageKey": null - }, { "alias": null, "args": null, @@ -109,7 +100,6 @@ return { "name": "node", "plural": false, "selections": [ - (v0/*: any*/), { "alias": null, "args": null, @@ -121,21 +111,21 @@ return { "alias": null, "args": null, "kind": "ScalarField", - "name": "addressLine1", + "name": "name", "storageKey": null }, { "alias": null, "args": null, "kind": "ScalarField", - "name": "addressLine2", + "name": "addressLine1", "storageKey": null }, { "alias": null, "args": null, "kind": "ScalarField", - "name": "addressLine3", + "name": "addressLine2", "storageKey": null }, { @@ -149,21 +139,21 @@ return { "alias": null, "args": null, "kind": "ScalarField", - "name": "country", + "name": "region", "storageKey": null }, { "alias": null, "args": null, "kind": "ScalarField", - "name": "isDefault", + "name": "postalCode", "storageKey": null }, { "alias": null, "args": null, "kind": "ScalarField", - "name": "name", + "name": "country", "storageKey": null }, { @@ -177,71 +167,12 @@ return { "alias": null, "args": null, "kind": "ScalarField", - "name": "postalCode", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "region", + "name": "isDefault", "storageKey": null }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "__typename", - "storageKey": null - } + (v0/*: any*/) ], "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "cursor", - "storageKey": null - } - ], - "storageKey": null - }, - { - "alias": null, - "args": null, - "concreteType": "PageInfo", - "kind": "LinkedField", - "name": "pageInfo", - "plural": false, - "selections": [ - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "endCursor", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "hasNextPage", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "hasPreviousPage", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "startCursor", - "storageKey": null } ], "storageKey": null @@ -249,31 +180,23 @@ return { ], "storageKey": "addressConnection(first:30)" }, - { - "alias": null, - "args": (v1/*: any*/), - "filters": null, - "handle": "connection", - "key": "SavedAddresses_addressConnection", - "kind": "LinkedHandle", - "name": "addressConnection" - } + (v0/*: any*/) ], "storageKey": null } ] }, "params": { - "cacheID": "a614695556421bb0853875a486fc9229", + "cacheID": "40438fb3c9643df7dc6ae439c3adffe8", "id": null, "metadata": {}, - "name": "SavedAddresses2RefetchQuery", + "name": "SavedAddresses2TestQuery", "operationKind": "query", - "text": "query SavedAddresses2RefetchQuery {\n me {\n ...SavedAddresses2_me\n id\n }\n}\n\nfragment SavedAddresses2_me on Me {\n id\n addressConnection(first: 30) {\n totalCount\n edges {\n node {\n id\n internalID\n addressLine1\n addressLine2\n addressLine3\n city\n country\n isDefault\n name\n phoneNumber\n postalCode\n region\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n}\n" + "text": "query SavedAddresses2TestQuery {\n me {\n ...SavedAddresses2_me\n id\n }\n}\n\nfragment SavedAddresses2_me on Me {\n addressConnection(first: 30) {\n edges {\n node {\n internalID\n name\n addressLine1\n addressLine2\n city\n region\n postalCode\n country\n phoneNumber\n isDefault\n id\n }\n }\n }\n}\n" } }; })(); -(node as any).hash = "2ca215e56a62f0d66eff2ce549cf63ec"; +(node as any).hash = "d79fce79cf787b03758c9871dec89b2b"; export default node; diff --git a/src/__generated__/SavedAddresses2_me.graphql.ts b/src/__generated__/SavedAddresses2_me.graphql.ts index eb7fdd8aab0..1cbe8f059f0 100644 --- a/src/__generated__/SavedAddresses2_me.graphql.ts +++ b/src/__generated__/SavedAddresses2_me.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<3e0e4fc3199c6e8190e9193a7212f14d>> + * @generated SignedSource<<8b4f3495917b5c9aedce4a77a73f39dd>> * @lightSyntaxTransform * @nogrep */ @@ -16,10 +16,8 @@ export type SavedAddresses2_me$data = { readonly node: { readonly addressLine1: string; readonly addressLine2: string | null | undefined; - readonly addressLine3: string | null | undefined; readonly city: string; readonly country: string; - readonly id: string; readonly internalID: string; readonly isDefault: boolean; readonly name: string | null | undefined; @@ -28,9 +26,7 @@ export type SavedAddresses2_me$data = { readonly region: string | null | undefined; } | null | undefined; } | null | undefined> | null | undefined; - readonly totalCount: number; } | null | undefined; - readonly id: string; readonly " $fragmentType": "SavedAddresses2_me"; }; export type SavedAddresses2_me$key = { @@ -38,15 +34,7 @@ export type SavedAddresses2_me$key = { readonly " $fragmentSpreads": FragmentRefs<"SavedAddresses2_me">; }; -const node: ReaderFragment = (function(){ -var v0 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "id", - "storageKey": null -}; -return { +const node: ReaderFragment = { "argumentDefinitions": [ { "defaultValue": null, @@ -70,36 +58,38 @@ return { } ], "kind": "Fragment", - "metadata": { - "connection": [ - { - "count": null, - "cursor": null, - "direction": "bidirectional", - "path": [ - "addressConnection" - ] - } - ] - }, + "metadata": null, "name": "SavedAddresses2_me", "selections": [ - (v0/*: any*/), { - "alias": "addressConnection", - "args": null, + "alias": null, + "args": [ + { + "kind": "Variable", + "name": "after", + "variableName": "after" + }, + { + "kind": "Variable", + "name": "before", + "variableName": "before" + }, + { + "kind": "Variable", + "name": "first", + "variableName": "first" + }, + { + "kind": "Variable", + "name": "last", + "variableName": "last" + } + ], "concreteType": "UserAddressConnection", "kind": "LinkedField", - "name": "__SavedAddresses_addressConnection_connection", + "name": "addressConnection", "plural": false, "selections": [ - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "totalCount", - "storageKey": null - }, { "alias": null, "args": null, @@ -116,7 +106,6 @@ return { "name": "node", "plural": false, "selections": [ - (v0/*: any*/), { "alias": null, "args": null, @@ -128,21 +117,21 @@ return { "alias": null, "args": null, "kind": "ScalarField", - "name": "addressLine1", + "name": "name", "storageKey": null }, { "alias": null, "args": null, "kind": "ScalarField", - "name": "addressLine2", + "name": "addressLine1", "storageKey": null }, { "alias": null, "args": null, "kind": "ScalarField", - "name": "addressLine3", + "name": "addressLine2", "storageKey": null }, { @@ -156,21 +145,21 @@ return { "alias": null, "args": null, "kind": "ScalarField", - "name": "country", + "name": "region", "storageKey": null }, { "alias": null, "args": null, "kind": "ScalarField", - "name": "isDefault", + "name": "postalCode", "storageKey": null }, { "alias": null, "args": null, "kind": "ScalarField", - "name": "name", + "name": "country", "storageKey": null }, { @@ -184,71 +173,11 @@ return { "alias": null, "args": null, "kind": "ScalarField", - "name": "postalCode", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "region", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "__typename", + "name": "isDefault", "storageKey": null } ], "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "cursor", - "storageKey": null - } - ], - "storageKey": null - }, - { - "alias": null, - "args": null, - "concreteType": "PageInfo", - "kind": "LinkedField", - "name": "pageInfo", - "plural": false, - "selections": [ - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "endCursor", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "hasNextPage", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "hasPreviousPage", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "startCursor", - "storageKey": null } ], "storageKey": null @@ -260,8 +189,7 @@ return { "type": "Me", "abstractKey": null }; -})(); -(node as any).hash = "0f7178f7033b8a9711893f215d6ae5e2"; +(node as any).hash = "867a8f08f34219c6ea9e11d3ef48d063"; export default node; diff --git a/src/__generated__/Shipping2TestQuery.graphql.ts b/src/__generated__/Shipping2TestQuery.graphql.ts index 0ce4531ad72..c0da864e9eb 100644 --- a/src/__generated__/Shipping2TestQuery.graphql.ts +++ b/src/__generated__/Shipping2TestQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<61ce496d445b98b9f133d8ab6eac62a8>> + * @generated SignedSource<<0f767a45a674117387d649a62744e1a6>> * @lightSyntaxTransform * @nogrep */ @@ -28,9 +28,7 @@ export type Shipping2TestQuery$rawResponse = { readonly me: { readonly addressConnection: { readonly edges: ReadonlyArray<{ - readonly cursor: string; readonly node: { - readonly __typename: "UserAddress"; readonly addressLine1: string; readonly addressLine2: string | null | undefined; readonly addressLine3: string | null | undefined; @@ -45,13 +43,6 @@ export type Shipping2TestQuery$rawResponse = { readonly region: string | null | undefined; } | null | undefined; } | null | undefined> | null | undefined; - readonly pageInfo: { - readonly endCursor: string | null | undefined; - readonly hasNextPage: boolean; - readonly hasPreviousPage: boolean; - readonly startCursor: string | null | undefined; - }; - readonly totalCount: number; } | null | undefined; readonly email: string | null | undefined; readonly id: string; @@ -551,37 +542,36 @@ v23 = [ }, (v12/*: any*/) ], -v24 = [ - { - "kind": "Literal", - "name": "first", - "value": 30 - } -], -v25 = { +v24 = { "enumValues": null, "nullable": false, "plural": false, "type": "String" }, -v26 = { +v25 = { "enumValues": null, "nullable": true, "plural": false, "type": "String" }, -v27 = { +v26 = { "enumValues": null, "nullable": false, "plural": false, "type": "ID" }, -v28 = { +v27 = { "enumValues": null, "nullable": false, "plural": false, "type": "Boolean" }, +v28 = { + "enumValues": null, + "nullable": true, + "plural": false, + "type": "CommerceOffer" +}, v29 = { "enumValues": null, "nullable": false, @@ -589,18 +579,12 @@ v29 = { "type": "Int" }, v30 = { - "enumValues": null, - "nullable": true, - "plural": false, - "type": "CommerceOffer" -}, -v31 = { "enumValues": null, "nullable": true, "plural": false, "type": "Int" }, -v32 = { +v31 = { "enumValues": [ "BUYER", "SELLER" @@ -609,13 +593,13 @@ v32 = { "plural": false, "type": "CommerceOrderParticipantEnum" }, -v33 = { +v32 = { "enumValues": null, "nullable": true, "plural": false, "type": "Boolean" }, -v34 = { +v33 = { "enumValues": null, "nullable": true, "plural": false, @@ -1168,19 +1152,18 @@ return { }, { "alias": null, - "args": (v24/*: any*/), + "args": [ + { + "kind": "Literal", + "name": "first", + "value": 30 + } + ], "concreteType": "UserAddressConnection", "kind": "LinkedField", "name": "addressConnection", "plural": false, "selections": [ - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "totalCount", - "storageKey": null - }, { "alias": null, "args": null, @@ -1197,94 +1180,38 @@ return { "name": "node", "plural": false, "selections": [ - (v12/*: any*/), (v2/*: any*/), + (v4/*: any*/), (v5/*: any*/), (v6/*: any*/), + (v7/*: any*/), + (v8/*: any*/), + (v10/*: any*/), + (v9/*: any*/), + (v3/*: any*/), { "alias": null, "args": null, "kind": "ScalarField", - "name": "addressLine3", + "name": "isDefault", "storageKey": null }, - (v7/*: any*/), - (v9/*: any*/), + (v12/*: any*/), { "alias": null, "args": null, "kind": "ScalarField", - "name": "isDefault", + "name": "addressLine3", "storageKey": null - }, - (v4/*: any*/), - (v3/*: any*/), - (v10/*: any*/), - (v8/*: any*/), - (v1/*: any*/) + } ], "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "cursor", - "storageKey": null - } - ], - "storageKey": null - }, - { - "alias": null, - "args": null, - "concreteType": "PageInfo", - "kind": "LinkedField", - "name": "pageInfo", - "plural": false, - "selections": [ - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "endCursor", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "hasNextPage", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "hasPreviousPage", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "startCursor", - "storageKey": null } ], "storageKey": null } ], "storageKey": "addressConnection(first:30)" - }, - { - "alias": null, - "args": (v24/*: any*/), - "filters": null, - "handle": "connection", - "key": "SavedAddresses_addressConnection", - "kind": "LinkedHandle", - "name": "addressConnection" } ], "storageKey": null @@ -1292,7 +1219,7 @@ return { ] }, "params": { - "cacheID": "6ee7501ded245d6d68047b8dec4b9588", + "cacheID": "9a388dc53efcfbf67a7b8a73bcea38ae", "id": null, "metadata": { "relayTestingSelectionTypeInfo": { @@ -1314,59 +1241,46 @@ return { "plural": true, "type": "UserAddressEdge" }, - "me.addressConnection.edges.cursor": (v25/*: any*/), "me.addressConnection.edges.node": { "enumValues": null, "nullable": true, "plural": false, "type": "UserAddress" }, - "me.addressConnection.edges.node.__typename": (v25/*: any*/), - "me.addressConnection.edges.node.addressLine1": (v25/*: any*/), - "me.addressConnection.edges.node.addressLine2": (v26/*: any*/), - "me.addressConnection.edges.node.addressLine3": (v26/*: any*/), - "me.addressConnection.edges.node.city": (v25/*: any*/), - "me.addressConnection.edges.node.country": (v25/*: any*/), - "me.addressConnection.edges.node.id": (v27/*: any*/), - "me.addressConnection.edges.node.internalID": (v27/*: any*/), - "me.addressConnection.edges.node.isDefault": (v28/*: any*/), - "me.addressConnection.edges.node.name": (v26/*: any*/), - "me.addressConnection.edges.node.phoneNumber": (v26/*: any*/), - "me.addressConnection.edges.node.postalCode": (v26/*: any*/), - "me.addressConnection.edges.node.region": (v26/*: any*/), - "me.addressConnection.pageInfo": { - "enumValues": null, - "nullable": false, - "plural": false, - "type": "PageInfo" - }, - "me.addressConnection.pageInfo.endCursor": (v26/*: any*/), - "me.addressConnection.pageInfo.hasNextPage": (v28/*: any*/), - "me.addressConnection.pageInfo.hasPreviousPage": (v28/*: any*/), - "me.addressConnection.pageInfo.startCursor": (v26/*: any*/), - "me.addressConnection.totalCount": (v29/*: any*/), - "me.email": (v26/*: any*/), - "me.id": (v27/*: any*/), + "me.addressConnection.edges.node.addressLine1": (v24/*: any*/), + "me.addressConnection.edges.node.addressLine2": (v25/*: any*/), + "me.addressConnection.edges.node.addressLine3": (v25/*: any*/), + "me.addressConnection.edges.node.city": (v24/*: any*/), + "me.addressConnection.edges.node.country": (v24/*: any*/), + "me.addressConnection.edges.node.id": (v26/*: any*/), + "me.addressConnection.edges.node.internalID": (v26/*: any*/), + "me.addressConnection.edges.node.isDefault": (v27/*: any*/), + "me.addressConnection.edges.node.name": (v25/*: any*/), + "me.addressConnection.edges.node.phoneNumber": (v25/*: any*/), + "me.addressConnection.edges.node.postalCode": (v25/*: any*/), + "me.addressConnection.edges.node.region": (v25/*: any*/), + "me.email": (v25/*: any*/), + "me.id": (v26/*: any*/), "me.location": { "enumValues": null, "nullable": true, "plural": false, "type": "MyLocation" }, - "me.location.country": (v26/*: any*/), - "me.location.id": (v27/*: any*/), - "me.name": (v26/*: any*/), + "me.location.country": (v25/*: any*/), + "me.location.id": (v26/*: any*/), + "me.name": (v25/*: any*/), "order": { "enumValues": null, "nullable": true, "plural": false, "type": "CommerceOrder" }, - "order.__isCommerceOrder": (v25/*: any*/), - "order.__typename": (v25/*: any*/), - "order.buyerTotal": (v26/*: any*/), - "order.code": (v25/*: any*/), - "order.currencyCode": (v25/*: any*/), + "order.__isCommerceOrder": (v24/*: any*/), + "order.__typename": (v24/*: any*/), + "order.buyerTotal": (v25/*: any*/), + "order.code": (v24/*: any*/), + "order.currencyCode": (v24/*: any*/), "order.displayState": { "enumValues": [ "ABANDONED", @@ -1384,22 +1298,22 @@ return { "plural": false, "type": "CommerceOrderDisplayStateEnum" }, - "order.id": (v27/*: any*/), - "order.internalID": (v27/*: any*/), - "order.itemsTotal": (v26/*: any*/), - "order.lastOffer": (v30/*: any*/), - "order.lastOffer.amount": (v26/*: any*/), + "order.id": (v26/*: any*/), + "order.internalID": (v26/*: any*/), + "order.itemsTotal": (v25/*: any*/), + "order.lastOffer": (v28/*: any*/), + "order.lastOffer.amount": (v25/*: any*/), "order.lastOffer.amountCents": (v29/*: any*/), - "order.lastOffer.buyerTotal": (v26/*: any*/), - "order.lastOffer.buyerTotalCents": (v31/*: any*/), - "order.lastOffer.fromParticipant": (v32/*: any*/), - "order.lastOffer.id": (v27/*: any*/), - "order.lastOffer.internalID": (v27/*: any*/), - "order.lastOffer.note": (v26/*: any*/), - "order.lastOffer.shippingTotal": (v26/*: any*/), - "order.lastOffer.shippingTotalCents": (v31/*: any*/), - "order.lastOffer.taxTotal": (v26/*: any*/), - "order.lastOffer.taxTotalCents": (v31/*: any*/), + "order.lastOffer.buyerTotal": (v25/*: any*/), + "order.lastOffer.buyerTotalCents": (v30/*: any*/), + "order.lastOffer.fromParticipant": (v31/*: any*/), + "order.lastOffer.id": (v26/*: any*/), + "order.lastOffer.internalID": (v26/*: any*/), + "order.lastOffer.note": (v25/*: any*/), + "order.lastOffer.shippingTotal": (v25/*: any*/), + "order.lastOffer.shippingTotalCents": (v30/*: any*/), + "order.lastOffer.taxTotal": (v25/*: any*/), + "order.lastOffer.taxTotalCents": (v30/*: any*/), "order.lineItems": { "enumValues": null, "nullable": true, @@ -1424,35 +1338,35 @@ return { "plural": false, "type": "Artwork" }, - "order.lineItems.edges.node.artwork.artsyShippingInternational": (v33/*: any*/), - "order.lineItems.edges.node.artwork.euShippingOrigin": (v33/*: any*/), - "order.lineItems.edges.node.artwork.id": (v27/*: any*/), - "order.lineItems.edges.node.artwork.onlyShipsDomestically": (v33/*: any*/), - "order.lineItems.edges.node.artwork.pickupAvailable": (v33/*: any*/), - "order.lineItems.edges.node.artwork.pickup_available": (v33/*: any*/), - "order.lineItems.edges.node.artwork.processWithArtsyShippingDomestic": (v33/*: any*/), - "order.lineItems.edges.node.artwork.shippingCountry": (v26/*: any*/), - "order.lineItems.edges.node.artwork.shippingOrigin": (v26/*: any*/), - "order.lineItems.edges.node.artwork.slug": (v27/*: any*/), + "order.lineItems.edges.node.artwork.artsyShippingInternational": (v32/*: any*/), + "order.lineItems.edges.node.artwork.euShippingOrigin": (v32/*: any*/), + "order.lineItems.edges.node.artwork.id": (v26/*: any*/), + "order.lineItems.edges.node.artwork.onlyShipsDomestically": (v32/*: any*/), + "order.lineItems.edges.node.artwork.pickupAvailable": (v32/*: any*/), + "order.lineItems.edges.node.artwork.pickup_available": (v32/*: any*/), + "order.lineItems.edges.node.artwork.processWithArtsyShippingDomestic": (v32/*: any*/), + "order.lineItems.edges.node.artwork.shippingCountry": (v25/*: any*/), + "order.lineItems.edges.node.artwork.shippingOrigin": (v25/*: any*/), + "order.lineItems.edges.node.artwork.slug": (v26/*: any*/), "order.lineItems.edges.node.artworkOrEditionSet": { "enumValues": null, "nullable": true, "plural": false, "type": "ArtworkOrEditionSetType" }, - "order.lineItems.edges.node.artworkOrEditionSet.__isNode": (v25/*: any*/), - "order.lineItems.edges.node.artworkOrEditionSet.__typename": (v25/*: any*/), - "order.lineItems.edges.node.artworkOrEditionSet.id": (v27/*: any*/), - "order.lineItems.edges.node.artworkOrEditionSet.price": (v26/*: any*/), + "order.lineItems.edges.node.artworkOrEditionSet.__isNode": (v24/*: any*/), + "order.lineItems.edges.node.artworkOrEditionSet.__typename": (v24/*: any*/), + "order.lineItems.edges.node.artworkOrEditionSet.id": (v26/*: any*/), + "order.lineItems.edges.node.artworkOrEditionSet.price": (v25/*: any*/), "order.lineItems.edges.node.artworkVersion": { "enumValues": null, "nullable": true, "plural": false, "type": "ArtworkVersion" }, - "order.lineItems.edges.node.artworkVersion.artistNames": (v26/*: any*/), - "order.lineItems.edges.node.artworkVersion.date": (v26/*: any*/), - "order.lineItems.edges.node.artworkVersion.id": (v27/*: any*/), + "order.lineItems.edges.node.artworkVersion.artistNames": (v25/*: any*/), + "order.lineItems.edges.node.artworkVersion.date": (v25/*: any*/), + "order.lineItems.edges.node.artworkVersion.id": (v26/*: any*/), "order.lineItems.edges.node.artworkVersion.image": { "enumValues": null, "nullable": true, @@ -1465,12 +1379,12 @@ return { "plural": false, "type": "ResizedImageUrl" }, - "order.lineItems.edges.node.artworkVersion.image.resized_ArtworkSummaryItem.url": (v25/*: any*/), - "order.lineItems.edges.node.artworkVersion.title": (v26/*: any*/), - "order.lineItems.edges.node.id": (v27/*: any*/), - "order.lineItems.edges.node.selectedShippingQuote": (v34/*: any*/), - "order.lineItems.edges.node.selectedShippingQuote.id": (v27/*: any*/), - "order.lineItems.edges.node.selectedShippingQuote.typeName": (v25/*: any*/), + "order.lineItems.edges.node.artworkVersion.image.resized_ArtworkSummaryItem.url": (v24/*: any*/), + "order.lineItems.edges.node.artworkVersion.title": (v25/*: any*/), + "order.lineItems.edges.node.id": (v26/*: any*/), + "order.lineItems.edges.node.selectedShippingQuote": (v33/*: any*/), + "order.lineItems.edges.node.selectedShippingQuote.id": (v26/*: any*/), + "order.lineItems.edges.node.selectedShippingQuote.typeName": (v24/*: any*/), "order.lineItems.edges.node.shippingQuoteOptions": { "enumValues": null, "nullable": true, @@ -1483,12 +1397,12 @@ return { "plural": true, "type": "CommerceShippingQuoteEdge" }, - "order.lineItems.edges.node.shippingQuoteOptions.edges.node": (v34/*: any*/), - "order.lineItems.edges.node.shippingQuoteOptions.edges.node.id": (v27/*: any*/), - "order.lineItems.edges.node.shippingQuoteOptions.edges.node.isSelected": (v28/*: any*/), - "order.lineItems.edges.node.shippingQuoteOptions.edges.node.price": (v26/*: any*/), + "order.lineItems.edges.node.shippingQuoteOptions.edges.node": (v33/*: any*/), + "order.lineItems.edges.node.shippingQuoteOptions.edges.node.id": (v26/*: any*/), + "order.lineItems.edges.node.shippingQuoteOptions.edges.node.isSelected": (v27/*: any*/), + "order.lineItems.edges.node.shippingQuoteOptions.edges.node.price": (v25/*: any*/), "order.lineItems.edges.node.shippingQuoteOptions.edges.node.priceCents": (v29/*: any*/), - "order.lineItems.edges.node.shippingQuoteOptions.edges.node.typeName": (v25/*: any*/), + "order.lineItems.edges.node.shippingQuoteOptions.edges.node.typeName": (v24/*: any*/), "order.mode": { "enumValues": [ "BUY", @@ -1498,55 +1412,55 @@ return { "plural": false, "type": "CommerceOrderModeEnum" }, - "order.myLastOffer": (v30/*: any*/), - "order.myLastOffer.amount": (v26/*: any*/), + "order.myLastOffer": (v28/*: any*/), + "order.myLastOffer.amount": (v25/*: any*/), "order.myLastOffer.amountCents": (v29/*: any*/), - "order.myLastOffer.buyerTotal": (v26/*: any*/), - "order.myLastOffer.buyerTotalCents": (v31/*: any*/), - "order.myLastOffer.fromParticipant": (v32/*: any*/), - "order.myLastOffer.id": (v27/*: any*/), - "order.myLastOffer.internalID": (v27/*: any*/), - "order.myLastOffer.note": (v26/*: any*/), - "order.myLastOffer.shippingTotal": (v26/*: any*/), - "order.myLastOffer.shippingTotalCents": (v31/*: any*/), - "order.myLastOffer.taxTotal": (v26/*: any*/), - "order.myLastOffer.taxTotalCents": (v31/*: any*/), + "order.myLastOffer.buyerTotal": (v25/*: any*/), + "order.myLastOffer.buyerTotalCents": (v30/*: any*/), + "order.myLastOffer.fromParticipant": (v31/*: any*/), + "order.myLastOffer.id": (v26/*: any*/), + "order.myLastOffer.internalID": (v26/*: any*/), + "order.myLastOffer.note": (v25/*: any*/), + "order.myLastOffer.shippingTotal": (v25/*: any*/), + "order.myLastOffer.shippingTotalCents": (v30/*: any*/), + "order.myLastOffer.taxTotal": (v25/*: any*/), + "order.myLastOffer.taxTotalCents": (v30/*: any*/), "order.paymentMethodDetails": { "enumValues": null, "nullable": true, "plural": false, "type": "PaymentMethodUnion" }, - "order.paymentMethodDetails.__typename": (v25/*: any*/), - "order.paymentMethodDetails.id": (v27/*: any*/), - "order.paymentMethodDetails.isManualPayment": (v28/*: any*/), + "order.paymentMethodDetails.__typename": (v24/*: any*/), + "order.paymentMethodDetails.id": (v26/*: any*/), + "order.paymentMethodDetails.isManualPayment": (v27/*: any*/), "order.requestedFulfillment": { "enumValues": null, "nullable": true, "plural": false, "type": "CommerceRequestedFulfillmentUnion" }, - "order.requestedFulfillment.__typename": (v25/*: any*/), - "order.requestedFulfillment.addressLine1": (v26/*: any*/), - "order.requestedFulfillment.addressLine2": (v26/*: any*/), - "order.requestedFulfillment.city": (v26/*: any*/), - "order.requestedFulfillment.country": (v26/*: any*/), - "order.requestedFulfillment.name": (v26/*: any*/), - "order.requestedFulfillment.phoneNumber": (v26/*: any*/), - "order.requestedFulfillment.postalCode": (v26/*: any*/), - "order.requestedFulfillment.region": (v26/*: any*/), + "order.requestedFulfillment.__typename": (v24/*: any*/), + "order.requestedFulfillment.addressLine1": (v25/*: any*/), + "order.requestedFulfillment.addressLine2": (v25/*: any*/), + "order.requestedFulfillment.city": (v25/*: any*/), + "order.requestedFulfillment.country": (v25/*: any*/), + "order.requestedFulfillment.name": (v25/*: any*/), + "order.requestedFulfillment.phoneNumber": (v25/*: any*/), + "order.requestedFulfillment.postalCode": (v25/*: any*/), + "order.requestedFulfillment.region": (v25/*: any*/), "order.sellerDetails": { "enumValues": null, "nullable": true, "plural": false, "type": "OrderParty" }, - "order.sellerDetails.__isNode": (v25/*: any*/), - "order.sellerDetails.__typename": (v25/*: any*/), - "order.sellerDetails.id": (v27/*: any*/), - "order.sellerDetails.name": (v26/*: any*/), - "order.shippingTotal": (v26/*: any*/), - "order.shippingTotalCents": (v31/*: any*/), + "order.sellerDetails.__isNode": (v24/*: any*/), + "order.sellerDetails.__typename": (v24/*: any*/), + "order.sellerDetails.id": (v26/*: any*/), + "order.sellerDetails.name": (v25/*: any*/), + "order.shippingTotal": (v25/*: any*/), + "order.shippingTotalCents": (v30/*: any*/), "order.source": { "enumValues": [ "artwork_page", @@ -1574,13 +1488,13 @@ return { "plural": false, "type": "CommerceOrderStateEnum" }, - "order.taxTotal": (v26/*: any*/), - "order.taxTotalCents": (v31/*: any*/) + "order.taxTotal": (v25/*: any*/), + "order.taxTotalCents": (v30/*: any*/) } }, "name": "Shipping2TestQuery", "operationKind": "query", - "text": "query Shipping2TestQuery {\n order: commerceOrder(id: \"unused\") {\n __typename\n ...Shipping2_order\n id\n }\n me {\n ...Shipping2_me\n id\n }\n}\n\nfragment ArtworkSummaryItem_order on CommerceOrder {\n __isCommerceOrder: __typename\n sellerDetails {\n __typename\n ... on Partner {\n name\n }\n ... on Node {\n __isNode: __typename\n id\n }\n }\n currencyCode\n mode\n source\n lineItems {\n edges {\n node {\n artworkOrEditionSet {\n __typename\n ... on Artwork {\n price\n }\n ... on EditionSet {\n price\n id\n }\n ... on Node {\n __isNode: __typename\n id\n }\n }\n artwork {\n slug\n shippingOrigin\n id\n }\n artworkVersion {\n date\n artistNames\n title\n image {\n resized_ArtworkSummaryItem: resized(width: 55) {\n url\n }\n }\n id\n }\n id\n }\n }\n }\n}\n\nfragment FulfillmentDetailsForm_me on Me {\n name\n email\n id\n location {\n country\n id\n }\n ...SavedAddresses2_me\n addressConnection(first: 30) {\n edges {\n node {\n id\n internalID\n addressLine1\n addressLine2\n addressLine3\n city\n country\n isDefault\n name\n phoneNumber\n postalCode\n region\n }\n }\n }\n}\n\nfragment FulfillmentDetailsForm_order on CommerceOrder {\n __isCommerceOrder: __typename\n internalID\n mode\n state\n requestedFulfillment {\n __typename\n ... on CommercePickup {\n phoneNumber\n }\n ... on CommerceShip {\n name\n addressLine1\n addressLine2\n city\n region\n country\n postalCode\n phoneNumber\n }\n ... on CommerceShipArta {\n name\n addressLine1\n addressLine2\n city\n region\n country\n postalCode\n phoneNumber\n }\n }\n lineItems {\n edges {\n node {\n artwork {\n slug\n processWithArtsyShippingDomestic\n artsyShippingInternational\n pickupAvailable\n onlyShipsDomestically\n euShippingOrigin\n shippingCountry\n id\n }\n shippingQuoteOptions {\n edges {\n ...ShippingQuotes_shippingQuotes\n node {\n id\n isSelected\n }\n }\n }\n id\n }\n }\n }\n ...ArtworkSummaryItem_order\n ...TransactionDetailsSummaryItem_order\n ...OrderStepper_order\n}\n\nfragment OrderStepper_order on CommerceOrder {\n __isCommerceOrder: __typename\n mode\n requestedFulfillment {\n __typename\n }\n paymentMethodDetails {\n __typename\n ... on CreditCard {\n id\n }\n ... on BankAccount {\n id\n }\n ... on WireTransfer {\n isManualPayment\n }\n }\n lineItems {\n edges {\n node {\n artwork {\n slug\n id\n }\n shippingQuoteOptions {\n edges {\n node {\n isSelected\n id\n }\n }\n }\n id\n }\n }\n }\n}\n\nfragment SaveAndContinueButton_order on CommerceOrder {\n __isCommerceOrder: __typename\n internalID\n}\n\nfragment SavedAddresses2_me on Me {\n id\n addressConnection(first: 30) {\n totalCount\n edges {\n node {\n id\n internalID\n addressLine1\n addressLine2\n addressLine3\n city\n country\n isDefault\n name\n phoneNumber\n postalCode\n region\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n}\n\nfragment Shipping2_me on Me {\n ...FulfillmentDetailsForm_me\n ...SavedAddresses2_me\n addressConnection(first: 30) {\n edges {\n node {\n id\n internalID\n addressLine1\n addressLine2\n addressLine3\n city\n country\n isDefault\n name\n phoneNumber\n postalCode\n region\n }\n }\n }\n}\n\nfragment Shipping2_order on CommerceOrder {\n __isCommerceOrder: __typename\n ...FulfillmentDetailsForm_order\n ...SaveAndContinueButton_order\n ...ArtworkSummaryItem_order\n ...TransactionDetailsSummaryItem_order\n ...OrderStepper_order\n __typename\n internalID\n mode\n requestedFulfillment {\n __typename\n ... on CommercePickup {\n phoneNumber\n }\n ... on CommerceShip {\n name\n addressLine1\n addressLine2\n city\n region\n country\n postalCode\n phoneNumber\n }\n ... on CommerceShipArta {\n name\n addressLine1\n addressLine2\n city\n region\n country\n postalCode\n phoneNumber\n }\n }\n lineItems {\n edges {\n node {\n ...ShippingQuotes2_commerceLineItem\n shippingQuoteOptions {\n edges {\n node {\n id\n isSelected\n price(precision: 2)\n priceCents\n typeName\n }\n }\n }\n artwork {\n slug\n processWithArtsyShippingDomestic\n artsyShippingInternational\n pickup_available: pickupAvailable\n onlyShipsDomestically\n euShippingOrigin\n shippingCountry\n id\n }\n id\n }\n }\n }\n}\n\nfragment ShippingQuotes2_commerceLineItem on CommerceLineItem {\n shippingQuoteOptions {\n edges {\n node {\n id\n isSelected\n price(precision: 2)\n priceCents\n typeName\n }\n }\n }\n}\n\nfragment ShippingQuotes_shippingQuotes on CommerceShippingQuoteEdge {\n node {\n id\n isSelected\n price(precision: 2)\n priceCents\n typeName\n }\n}\n\nfragment TransactionDetailsSummaryItem_order on CommerceOrder {\n __isCommerceOrder: __typename\n __typename\n requestedFulfillment {\n __typename\n }\n code\n lineItems {\n edges {\n node {\n artworkOrEditionSet {\n __typename\n ... on Artwork {\n price\n }\n ... on EditionSet {\n price\n id\n }\n ... on Node {\n __isNode: __typename\n id\n }\n }\n selectedShippingQuote {\n typeName\n id\n }\n id\n }\n }\n }\n mode\n source\n displayState\n shippingTotal(precision: 2)\n shippingTotalCents\n taxTotal(precision: 2)\n taxTotalCents\n itemsTotal(precision: 2)\n buyerTotal(precision: 2)\n currencyCode\n ... on CommerceOfferOrder {\n lastOffer {\n internalID\n amount(precision: 2)\n amountCents\n shippingTotal(precision: 2)\n shippingTotalCents\n taxTotal(precision: 2)\n taxTotalCents\n buyerTotal(precision: 2)\n buyerTotalCents\n fromParticipant\n note\n id\n }\n myLastOffer {\n internalID\n amount(precision: 2)\n amountCents\n shippingTotal(precision: 2)\n shippingTotalCents\n taxTotal(precision: 2)\n taxTotalCents\n buyerTotal(precision: 2)\n buyerTotalCents\n fromParticipant\n note\n id\n }\n }\n}\n" + "text": "query Shipping2TestQuery {\n order: commerceOrder(id: \"unused\") {\n __typename\n ...Shipping2_order\n id\n }\n me {\n ...Shipping2_me\n id\n }\n}\n\nfragment ArtworkSummaryItem_order on CommerceOrder {\n __isCommerceOrder: __typename\n sellerDetails {\n __typename\n ... on Partner {\n name\n }\n ... on Node {\n __isNode: __typename\n id\n }\n }\n currencyCode\n mode\n source\n lineItems {\n edges {\n node {\n artworkOrEditionSet {\n __typename\n ... on Artwork {\n price\n }\n ... on EditionSet {\n price\n id\n }\n ... on Node {\n __isNode: __typename\n id\n }\n }\n artwork {\n slug\n shippingOrigin\n id\n }\n artworkVersion {\n date\n artistNames\n title\n image {\n resized_ArtworkSummaryItem: resized(width: 55) {\n url\n }\n }\n id\n }\n id\n }\n }\n }\n}\n\nfragment FulfillmentDetailsForm_me on Me {\n name\n email\n id\n location {\n country\n id\n }\n ...SavedAddresses2_me\n addressConnection(first: 30) {\n edges {\n node {\n id\n internalID\n addressLine1\n addressLine2\n addressLine3\n city\n country\n isDefault\n name\n phoneNumber\n postalCode\n region\n }\n }\n }\n}\n\nfragment FulfillmentDetailsForm_order on CommerceOrder {\n __isCommerceOrder: __typename\n internalID\n mode\n state\n requestedFulfillment {\n __typename\n ... on CommercePickup {\n phoneNumber\n }\n ... on CommerceShip {\n name\n addressLine1\n addressLine2\n city\n region\n country\n postalCode\n phoneNumber\n }\n ... on CommerceShipArta {\n name\n addressLine1\n addressLine2\n city\n region\n country\n postalCode\n phoneNumber\n }\n }\n lineItems {\n edges {\n node {\n artwork {\n slug\n processWithArtsyShippingDomestic\n artsyShippingInternational\n pickupAvailable\n onlyShipsDomestically\n euShippingOrigin\n shippingCountry\n id\n }\n shippingQuoteOptions {\n edges {\n ...ShippingQuotes_shippingQuotes\n node {\n id\n isSelected\n }\n }\n }\n id\n }\n }\n }\n ...ArtworkSummaryItem_order\n ...TransactionDetailsSummaryItem_order\n ...OrderStepper_order\n}\n\nfragment OrderStepper_order on CommerceOrder {\n __isCommerceOrder: __typename\n mode\n requestedFulfillment {\n __typename\n }\n paymentMethodDetails {\n __typename\n ... on CreditCard {\n id\n }\n ... on BankAccount {\n id\n }\n ... on WireTransfer {\n isManualPayment\n }\n }\n lineItems {\n edges {\n node {\n artwork {\n slug\n id\n }\n shippingQuoteOptions {\n edges {\n node {\n isSelected\n id\n }\n }\n }\n id\n }\n }\n }\n}\n\nfragment SaveAndContinueButton_order on CommerceOrder {\n __isCommerceOrder: __typename\n internalID\n}\n\nfragment SavedAddresses2_me on Me {\n addressConnection(first: 30) {\n edges {\n node {\n internalID\n name\n addressLine1\n addressLine2\n city\n region\n postalCode\n country\n phoneNumber\n isDefault\n id\n }\n }\n }\n}\n\nfragment Shipping2_me on Me {\n ...FulfillmentDetailsForm_me\n addressConnection(first: 30) {\n edges {\n node {\n id\n internalID\n addressLine1\n addressLine2\n addressLine3\n city\n country\n isDefault\n name\n phoneNumber\n postalCode\n region\n }\n }\n }\n}\n\nfragment Shipping2_order on CommerceOrder {\n __isCommerceOrder: __typename\n ...FulfillmentDetailsForm_order\n ...SaveAndContinueButton_order\n ...ArtworkSummaryItem_order\n ...TransactionDetailsSummaryItem_order\n ...OrderStepper_order\n __typename\n internalID\n mode\n requestedFulfillment {\n __typename\n ... on CommercePickup {\n phoneNumber\n }\n ... on CommerceShip {\n name\n addressLine1\n addressLine2\n city\n region\n country\n postalCode\n phoneNumber\n }\n ... on CommerceShipArta {\n name\n addressLine1\n addressLine2\n city\n region\n country\n postalCode\n phoneNumber\n }\n }\n lineItems {\n edges {\n node {\n ...ShippingQuotes2_commerceLineItem\n shippingQuoteOptions {\n edges {\n node {\n id\n isSelected\n price(precision: 2)\n priceCents\n typeName\n }\n }\n }\n artwork {\n slug\n processWithArtsyShippingDomestic\n artsyShippingInternational\n pickup_available: pickupAvailable\n onlyShipsDomestically\n euShippingOrigin\n shippingCountry\n id\n }\n id\n }\n }\n }\n}\n\nfragment ShippingQuotes2_commerceLineItem on CommerceLineItem {\n shippingQuoteOptions {\n edges {\n node {\n id\n isSelected\n price(precision: 2)\n priceCents\n typeName\n }\n }\n }\n}\n\nfragment ShippingQuotes_shippingQuotes on CommerceShippingQuoteEdge {\n node {\n id\n isSelected\n price(precision: 2)\n priceCents\n typeName\n }\n}\n\nfragment TransactionDetailsSummaryItem_order on CommerceOrder {\n __isCommerceOrder: __typename\n __typename\n requestedFulfillment {\n __typename\n }\n code\n lineItems {\n edges {\n node {\n artworkOrEditionSet {\n __typename\n ... on Artwork {\n price\n }\n ... on EditionSet {\n price\n id\n }\n ... on Node {\n __isNode: __typename\n id\n }\n }\n selectedShippingQuote {\n typeName\n id\n }\n id\n }\n }\n }\n mode\n source\n displayState\n shippingTotal(precision: 2)\n shippingTotalCents\n taxTotal(precision: 2)\n taxTotalCents\n itemsTotal(precision: 2)\n buyerTotal(precision: 2)\n currencyCode\n ... on CommerceOfferOrder {\n lastOffer {\n internalID\n amount(precision: 2)\n amountCents\n shippingTotal(precision: 2)\n shippingTotalCents\n taxTotal(precision: 2)\n taxTotalCents\n buyerTotal(precision: 2)\n buyerTotalCents\n fromParticipant\n note\n id\n }\n myLastOffer {\n internalID\n amount(precision: 2)\n amountCents\n shippingTotal(precision: 2)\n shippingTotalCents\n taxTotal(precision: 2)\n taxTotalCents\n buyerTotal(precision: 2)\n buyerTotalCents\n fromParticipant\n note\n id\n }\n }\n}\n" } }; })(); diff --git a/src/__generated__/Shipping2_me.graphql.ts b/src/__generated__/Shipping2_me.graphql.ts index 27cdf47083f..691b0f82783 100644 --- a/src/__generated__/Shipping2_me.graphql.ts +++ b/src/__generated__/Shipping2_me.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<6fe4d569a270b763ef14bc4f7780cd39>> + * @generated SignedSource<<674a5a90c6f146126dd4d56803f8c68e>> * @lightSyntaxTransform * @nogrep */ @@ -29,7 +29,7 @@ export type Shipping2_me$data = { } | null | undefined; } | null | undefined> | null | undefined; } | null | undefined; - readonly " $fragmentSpreads": FragmentRefs<"FulfillmentDetailsForm_me" | "SavedAddresses2_me">; + readonly " $fragmentSpreads": FragmentRefs<"FulfillmentDetailsForm_me">; readonly " $fragmentType": "Shipping2_me"; }; export type Shipping2_me$key = { @@ -69,11 +69,6 @@ const node: ReaderFragment = { "kind": "FragmentSpread", "name": "FulfillmentDetailsForm_me" }, - { - "args": null, - "kind": "FragmentSpread", - "name": "SavedAddresses2_me" - }, { "alias": null, "args": [ @@ -217,6 +212,6 @@ const node: ReaderFragment = { "abstractKey": null }; -(node as any).hash = "3d53d3dbf9bf4b645d6902a001000ff0"; +(node as any).hash = "1f0a40ff07ee40074ab6f360a991a47c"; export default node; diff --git a/src/__generated__/orderRoutes_ShippingQuery.graphql.ts b/src/__generated__/orderRoutes_ShippingQuery.graphql.ts index 1f25a90088f..e4908c1f686 100644 --- a/src/__generated__/orderRoutes_ShippingQuery.graphql.ts +++ b/src/__generated__/orderRoutes_ShippingQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<879534cbe0662820523908656d1edcc0>> + * @generated SignedSource<<4c345b04b57c46ab7d1bc01769718566>> * @lightSyntaxTransform * @nogrep */ @@ -932,12 +932,12 @@ return { ] }, "params": { - "cacheID": "0e709da55c764fea609dfbf13dd49821", + "cacheID": "2ed228fd96b125568aa66237b83e0b92", "id": null, "metadata": {}, "name": "orderRoutes_ShippingQuery", "operationKind": "query", - "text": "query orderRoutes_ShippingQuery(\n $orderID: ID!\n) {\n order: commerceOrder(id: $orderID) {\n __typename\n ...Shipping_order\n ...Shipping2_order\n id\n }\n me {\n ...Shipping_me\n ...Shipping2_me\n id\n }\n}\n\nfragment ArtworkSummaryItem_order on CommerceOrder {\n __isCommerceOrder: __typename\n sellerDetails {\n __typename\n ... on Partner {\n name\n }\n ... on Node {\n __isNode: __typename\n id\n }\n }\n currencyCode\n mode\n source\n lineItems {\n edges {\n node {\n artworkOrEditionSet {\n __typename\n ... on Artwork {\n price\n }\n ... on EditionSet {\n price\n id\n }\n ... on Node {\n __isNode: __typename\n id\n }\n }\n artwork {\n slug\n shippingOrigin\n id\n }\n artworkVersion {\n date\n artistNames\n title\n image {\n resized_ArtworkSummaryItem: resized(width: 55) {\n url\n }\n }\n id\n }\n id\n }\n }\n }\n}\n\nfragment FulfillmentDetailsForm_me on Me {\n name\n email\n id\n location {\n country\n id\n }\n ...SavedAddresses2_me\n addressConnection(first: 30) {\n edges {\n node {\n id\n internalID\n addressLine1\n addressLine2\n addressLine3\n city\n country\n isDefault\n name\n phoneNumber\n postalCode\n region\n }\n }\n }\n}\n\nfragment FulfillmentDetailsForm_order on CommerceOrder {\n __isCommerceOrder: __typename\n internalID\n mode\n state\n requestedFulfillment {\n __typename\n ... on CommercePickup {\n phoneNumber\n }\n ... on CommerceShip {\n name\n addressLine1\n addressLine2\n city\n region\n country\n postalCode\n phoneNumber\n }\n ... on CommerceShipArta {\n name\n addressLine1\n addressLine2\n city\n region\n country\n postalCode\n phoneNumber\n }\n }\n lineItems {\n edges {\n node {\n artwork {\n slug\n processWithArtsyShippingDomestic\n artsyShippingInternational\n pickupAvailable\n onlyShipsDomestically\n euShippingOrigin\n shippingCountry\n id\n }\n shippingQuoteOptions {\n edges {\n ...ShippingQuotes_shippingQuotes\n node {\n id\n isSelected\n }\n }\n }\n id\n }\n }\n }\n ...ArtworkSummaryItem_order\n ...TransactionDetailsSummaryItem_order\n ...OrderStepper_order\n}\n\nfragment OrderStepper_order on CommerceOrder {\n __isCommerceOrder: __typename\n mode\n requestedFulfillment {\n __typename\n }\n paymentMethodDetails {\n __typename\n ... on CreditCard {\n id\n }\n ... on BankAccount {\n id\n }\n ... on WireTransfer {\n isManualPayment\n }\n }\n lineItems {\n edges {\n node {\n artwork {\n slug\n id\n }\n shippingQuoteOptions {\n edges {\n node {\n isSelected\n id\n }\n }\n }\n id\n }\n }\n }\n}\n\nfragment PartnerOfferTimerItem_order on CommerceOrder {\n __isCommerceOrder: __typename\n displayState\n stateExpiresAt\n}\n\nfragment SaveAndContinueButton_order on CommerceOrder {\n __isCommerceOrder: __typename\n internalID\n}\n\nfragment SavedAddresses2_me on Me {\n id\n addressConnection(first: 30) {\n totalCount\n edges {\n node {\n id\n internalID\n addressLine1\n addressLine2\n addressLine3\n city\n country\n isDefault\n name\n phoneNumber\n postalCode\n region\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n}\n\nfragment SavedAddresses_me on Me {\n id\n addressConnection(first: 30) {\n totalCount\n edges {\n node {\n id\n internalID\n addressLine1\n addressLine2\n addressLine3\n city\n country\n isDefault\n name\n phoneNumber\n postalCode\n region\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n}\n\nfragment Shipping2_me on Me {\n ...FulfillmentDetailsForm_me\n ...SavedAddresses2_me\n addressConnection(first: 30) {\n edges {\n node {\n id\n internalID\n addressLine1\n addressLine2\n addressLine3\n city\n country\n isDefault\n name\n phoneNumber\n postalCode\n region\n }\n }\n }\n}\n\nfragment Shipping2_order on CommerceOrder {\n __isCommerceOrder: __typename\n ...FulfillmentDetailsForm_order\n ...SaveAndContinueButton_order\n ...ArtworkSummaryItem_order\n ...TransactionDetailsSummaryItem_order\n ...OrderStepper_order\n __typename\n internalID\n mode\n requestedFulfillment {\n __typename\n ... on CommercePickup {\n phoneNumber\n }\n ... on CommerceShip {\n name\n addressLine1\n addressLine2\n city\n region\n country\n postalCode\n phoneNumber\n }\n ... on CommerceShipArta {\n name\n addressLine1\n addressLine2\n city\n region\n country\n postalCode\n phoneNumber\n }\n }\n lineItems {\n edges {\n node {\n ...ShippingQuotes2_commerceLineItem\n shippingQuoteOptions {\n edges {\n node {\n id\n isSelected\n price(precision: 2)\n priceCents\n typeName\n }\n }\n }\n artwork {\n slug\n processWithArtsyShippingDomestic\n artsyShippingInternational\n pickup_available: pickupAvailable\n onlyShipsDomestically\n euShippingOrigin\n shippingCountry\n id\n }\n id\n }\n }\n }\n}\n\nfragment ShippingQuotes2_commerceLineItem on CommerceLineItem {\n shippingQuoteOptions {\n edges {\n node {\n id\n isSelected\n price(precision: 2)\n priceCents\n typeName\n }\n }\n }\n}\n\nfragment ShippingQuotes_shippingQuotes on CommerceShippingQuoteEdge {\n node {\n id\n isSelected\n price(precision: 2)\n priceCents\n typeName\n }\n}\n\nfragment Shipping_me on Me {\n name\n email\n id\n location {\n country\n id\n }\n ...SavedAddresses_me\n addressConnection(first: 30) {\n edges {\n node {\n id\n internalID\n addressLine1\n addressLine2\n addressLine3\n city\n country\n isDefault\n name\n phoneNumber\n postalCode\n region\n }\n }\n }\n}\n\nfragment Shipping_order on CommerceOrder {\n __isCommerceOrder: __typename\n internalID\n mode\n state\n source\n requestedFulfillment {\n __typename\n ... on CommercePickup {\n phoneNumber\n }\n ... on CommerceShip {\n name\n addressLine1\n addressLine2\n city\n region\n country\n postalCode\n phoneNumber\n }\n ... on CommerceShipArta {\n name\n addressLine1\n addressLine2\n city\n region\n country\n postalCode\n phoneNumber\n }\n }\n lineItems {\n edges {\n node {\n artwork {\n slug\n processWithArtsyShippingDomestic\n artsyShippingInternational\n pickup_available: pickupAvailable\n onlyShipsDomestically\n euShippingOrigin\n shippingCountry\n id\n }\n shippingQuoteOptions {\n edges {\n ...ShippingQuotes_shippingQuotes\n node {\n id\n isSelected\n }\n }\n }\n id\n }\n }\n }\n ...PartnerOfferTimerItem_order\n ...ArtworkSummaryItem_order\n ...TransactionDetailsSummaryItem_order\n ...OrderStepper_order\n}\n\nfragment TransactionDetailsSummaryItem_order on CommerceOrder {\n __isCommerceOrder: __typename\n __typename\n requestedFulfillment {\n __typename\n }\n code\n lineItems {\n edges {\n node {\n artworkOrEditionSet {\n __typename\n ... on Artwork {\n price\n }\n ... on EditionSet {\n price\n id\n }\n ... on Node {\n __isNode: __typename\n id\n }\n }\n selectedShippingQuote {\n typeName\n id\n }\n id\n }\n }\n }\n mode\n source\n displayState\n shippingTotal(precision: 2)\n shippingTotalCents\n taxTotal(precision: 2)\n taxTotalCents\n itemsTotal(precision: 2)\n buyerTotal(precision: 2)\n currencyCode\n ... on CommerceOfferOrder {\n lastOffer {\n internalID\n amount(precision: 2)\n amountCents\n shippingTotal(precision: 2)\n shippingTotalCents\n taxTotal(precision: 2)\n taxTotalCents\n buyerTotal(precision: 2)\n buyerTotalCents\n fromParticipant\n note\n id\n }\n myLastOffer {\n internalID\n amount(precision: 2)\n amountCents\n shippingTotal(precision: 2)\n shippingTotalCents\n taxTotal(precision: 2)\n taxTotalCents\n buyerTotal(precision: 2)\n buyerTotalCents\n fromParticipant\n note\n id\n }\n }\n}\n" + "text": "query orderRoutes_ShippingQuery(\n $orderID: ID!\n) {\n order: commerceOrder(id: $orderID) {\n __typename\n ...Shipping_order\n ...Shipping2_order\n id\n }\n me {\n ...Shipping_me\n ...Shipping2_me\n id\n }\n}\n\nfragment ArtworkSummaryItem_order on CommerceOrder {\n __isCommerceOrder: __typename\n sellerDetails {\n __typename\n ... on Partner {\n name\n }\n ... on Node {\n __isNode: __typename\n id\n }\n }\n currencyCode\n mode\n source\n lineItems {\n edges {\n node {\n artworkOrEditionSet {\n __typename\n ... on Artwork {\n price\n }\n ... on EditionSet {\n price\n id\n }\n ... on Node {\n __isNode: __typename\n id\n }\n }\n artwork {\n slug\n shippingOrigin\n id\n }\n artworkVersion {\n date\n artistNames\n title\n image {\n resized_ArtworkSummaryItem: resized(width: 55) {\n url\n }\n }\n id\n }\n id\n }\n }\n }\n}\n\nfragment FulfillmentDetailsForm_me on Me {\n name\n email\n id\n location {\n country\n id\n }\n ...SavedAddresses2_me\n addressConnection(first: 30) {\n edges {\n node {\n id\n internalID\n addressLine1\n addressLine2\n addressLine3\n city\n country\n isDefault\n name\n phoneNumber\n postalCode\n region\n }\n }\n }\n}\n\nfragment FulfillmentDetailsForm_order on CommerceOrder {\n __isCommerceOrder: __typename\n internalID\n mode\n state\n requestedFulfillment {\n __typename\n ... on CommercePickup {\n phoneNumber\n }\n ... on CommerceShip {\n name\n addressLine1\n addressLine2\n city\n region\n country\n postalCode\n phoneNumber\n }\n ... on CommerceShipArta {\n name\n addressLine1\n addressLine2\n city\n region\n country\n postalCode\n phoneNumber\n }\n }\n lineItems {\n edges {\n node {\n artwork {\n slug\n processWithArtsyShippingDomestic\n artsyShippingInternational\n pickupAvailable\n onlyShipsDomestically\n euShippingOrigin\n shippingCountry\n id\n }\n shippingQuoteOptions {\n edges {\n ...ShippingQuotes_shippingQuotes\n node {\n id\n isSelected\n }\n }\n }\n id\n }\n }\n }\n ...ArtworkSummaryItem_order\n ...TransactionDetailsSummaryItem_order\n ...OrderStepper_order\n}\n\nfragment OrderStepper_order on CommerceOrder {\n __isCommerceOrder: __typename\n mode\n requestedFulfillment {\n __typename\n }\n paymentMethodDetails {\n __typename\n ... on CreditCard {\n id\n }\n ... on BankAccount {\n id\n }\n ... on WireTransfer {\n isManualPayment\n }\n }\n lineItems {\n edges {\n node {\n artwork {\n slug\n id\n }\n shippingQuoteOptions {\n edges {\n node {\n isSelected\n id\n }\n }\n }\n id\n }\n }\n }\n}\n\nfragment PartnerOfferTimerItem_order on CommerceOrder {\n __isCommerceOrder: __typename\n displayState\n stateExpiresAt\n}\n\nfragment SaveAndContinueButton_order on CommerceOrder {\n __isCommerceOrder: __typename\n internalID\n}\n\nfragment SavedAddresses2_me on Me {\n addressConnection(first: 30) {\n edges {\n node {\n internalID\n name\n addressLine1\n addressLine2\n city\n region\n postalCode\n country\n phoneNumber\n isDefault\n id\n }\n }\n }\n}\n\nfragment SavedAddresses_me on Me {\n id\n addressConnection(first: 30) {\n totalCount\n edges {\n node {\n id\n internalID\n addressLine1\n addressLine2\n addressLine3\n city\n country\n isDefault\n name\n phoneNumber\n postalCode\n region\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n}\n\nfragment Shipping2_me on Me {\n ...FulfillmentDetailsForm_me\n addressConnection(first: 30) {\n edges {\n node {\n id\n internalID\n addressLine1\n addressLine2\n addressLine3\n city\n country\n isDefault\n name\n phoneNumber\n postalCode\n region\n }\n }\n }\n}\n\nfragment Shipping2_order on CommerceOrder {\n __isCommerceOrder: __typename\n ...FulfillmentDetailsForm_order\n ...SaveAndContinueButton_order\n ...ArtworkSummaryItem_order\n ...TransactionDetailsSummaryItem_order\n ...OrderStepper_order\n __typename\n internalID\n mode\n requestedFulfillment {\n __typename\n ... on CommercePickup {\n phoneNumber\n }\n ... on CommerceShip {\n name\n addressLine1\n addressLine2\n city\n region\n country\n postalCode\n phoneNumber\n }\n ... on CommerceShipArta {\n name\n addressLine1\n addressLine2\n city\n region\n country\n postalCode\n phoneNumber\n }\n }\n lineItems {\n edges {\n node {\n ...ShippingQuotes2_commerceLineItem\n shippingQuoteOptions {\n edges {\n node {\n id\n isSelected\n price(precision: 2)\n priceCents\n typeName\n }\n }\n }\n artwork {\n slug\n processWithArtsyShippingDomestic\n artsyShippingInternational\n pickup_available: pickupAvailable\n onlyShipsDomestically\n euShippingOrigin\n shippingCountry\n id\n }\n id\n }\n }\n }\n}\n\nfragment ShippingQuotes2_commerceLineItem on CommerceLineItem {\n shippingQuoteOptions {\n edges {\n node {\n id\n isSelected\n price(precision: 2)\n priceCents\n typeName\n }\n }\n }\n}\n\nfragment ShippingQuotes_shippingQuotes on CommerceShippingQuoteEdge {\n node {\n id\n isSelected\n price(precision: 2)\n priceCents\n typeName\n }\n}\n\nfragment Shipping_me on Me {\n name\n email\n id\n location {\n country\n id\n }\n ...SavedAddresses_me\n addressConnection(first: 30) {\n edges {\n node {\n id\n internalID\n addressLine1\n addressLine2\n addressLine3\n city\n country\n isDefault\n name\n phoneNumber\n postalCode\n region\n }\n }\n }\n}\n\nfragment Shipping_order on CommerceOrder {\n __isCommerceOrder: __typename\n internalID\n mode\n state\n source\n requestedFulfillment {\n __typename\n ... on CommercePickup {\n phoneNumber\n }\n ... on CommerceShip {\n name\n addressLine1\n addressLine2\n city\n region\n country\n postalCode\n phoneNumber\n }\n ... on CommerceShipArta {\n name\n addressLine1\n addressLine2\n city\n region\n country\n postalCode\n phoneNumber\n }\n }\n lineItems {\n edges {\n node {\n artwork {\n slug\n processWithArtsyShippingDomestic\n artsyShippingInternational\n pickup_available: pickupAvailable\n onlyShipsDomestically\n euShippingOrigin\n shippingCountry\n id\n }\n shippingQuoteOptions {\n edges {\n ...ShippingQuotes_shippingQuotes\n node {\n id\n isSelected\n }\n }\n }\n id\n }\n }\n }\n ...PartnerOfferTimerItem_order\n ...ArtworkSummaryItem_order\n ...TransactionDetailsSummaryItem_order\n ...OrderStepper_order\n}\n\nfragment TransactionDetailsSummaryItem_order on CommerceOrder {\n __isCommerceOrder: __typename\n __typename\n requestedFulfillment {\n __typename\n }\n code\n lineItems {\n edges {\n node {\n artworkOrEditionSet {\n __typename\n ... on Artwork {\n price\n }\n ... on EditionSet {\n price\n id\n }\n ... on Node {\n __isNode: __typename\n id\n }\n }\n selectedShippingQuote {\n typeName\n id\n }\n id\n }\n }\n }\n mode\n source\n displayState\n shippingTotal(precision: 2)\n shippingTotalCents\n taxTotal(precision: 2)\n taxTotalCents\n itemsTotal(precision: 2)\n buyerTotal(precision: 2)\n currencyCode\n ... on CommerceOfferOrder {\n lastOffer {\n internalID\n amount(precision: 2)\n amountCents\n shippingTotal(precision: 2)\n shippingTotalCents\n taxTotal(precision: 2)\n taxTotalCents\n buyerTotal(precision: 2)\n buyerTotalCents\n fromParticipant\n note\n id\n }\n myLastOffer {\n internalID\n amount(precision: 2)\n amountCents\n shippingTotal(precision: 2)\n shippingTotalCents\n taxTotal(precision: 2)\n taxTotalCents\n buyerTotal(precision: 2)\n buyerTotalCents\n fromParticipant\n note\n id\n }\n }\n}\n" } }; })(); diff --git a/src/__generated__/useCreateSavedAddressMutation.graphql.ts b/src/__generated__/useCreateSavedAddressMutation.graphql.ts index 6708e559ad4..38adccdbe6e 100644 --- a/src/__generated__/useCreateSavedAddressMutation.graphql.ts +++ b/src/__generated__/useCreateSavedAddressMutation.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<02a98f3aa64fbe251f6ac3a53f655f5e>> + * @generated SignedSource<<4b56ebb76a4a12a74289da5354a09728>> * @lightSyntaxTransform * @nogrep */ @@ -81,97 +81,90 @@ v1 = [ } ], v2 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "__typename", - "storageKey": null -}, -v3 = { "alias": null, "args": null, "kind": "ScalarField", "name": "id", "storageKey": null }, -v4 = { +v3 = { "alias": null, "args": null, "kind": "ScalarField", "name": "internalID", "storageKey": null }, -v5 = { +v4 = { "alias": null, "args": null, "kind": "ScalarField", "name": "addressLine1", "storageKey": null }, -v6 = { +v5 = { "alias": null, "args": null, "kind": "ScalarField", "name": "addressLine2", "storageKey": null }, -v7 = { +v6 = { "alias": null, "args": null, "kind": "ScalarField", "name": "addressLine3", "storageKey": null }, -v8 = { +v7 = { "alias": null, "args": null, "kind": "ScalarField", "name": "city", "storageKey": null }, -v9 = { +v8 = { "alias": null, "args": null, "kind": "ScalarField", "name": "country", "storageKey": null }, -v10 = { +v9 = { "alias": null, "args": null, "kind": "ScalarField", "name": "isDefault", "storageKey": null }, -v11 = { +v10 = { "alias": null, "args": null, "kind": "ScalarField", "name": "name", "storageKey": null }, -v12 = { +v11 = { "alias": null, "args": null, "kind": "ScalarField", "name": "phoneNumber", "storageKey": null }, -v13 = { +v12 = { "alias": null, "args": null, "kind": "ScalarField", "name": "postalCode", "storageKey": null }, -v14 = { +v13 = { "alias": null, "args": null, "kind": "ScalarField", "name": "region", "storageKey": null }, -v15 = { +v14 = { "alias": null, "args": null, "concreteType": null, @@ -179,7 +172,13 @@ v15 = { "name": "userAddressOrErrors", "plural": false, "selections": [ - (v2/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + }, { "kind": "InlineFragment", "selections": [ @@ -208,6 +207,7 @@ v15 = { { "kind": "InlineFragment", "selections": [ + (v2/*: any*/), (v3/*: any*/), (v4/*: any*/), (v5/*: any*/), @@ -218,22 +218,14 @@ v15 = { (v10/*: any*/), (v11/*: any*/), (v12/*: any*/), - (v13/*: any*/), - (v14/*: any*/) + (v13/*: any*/) ], "type": "UserAddress", "abstractKey": null } ], "storageKey": null -}, -v16 = [ - { - "kind": "Literal", - "name": "first", - "value": 30 - } -]; +}; return { "fragment": { "argumentDefinitions": (v0/*: any*/), @@ -265,7 +257,7 @@ return { ], "storageKey": null }, - (v15/*: any*/) + (v14/*: any*/) ], "storageKey": null } @@ -295,7 +287,7 @@ return { "name": "me", "plural": false, "selections": [ - (v11/*: any*/), + (v10/*: any*/), { "alias": null, "args": null, @@ -303,7 +295,7 @@ return { "name": "email", "storageKey": null }, - (v3/*: any*/), + (v2/*: any*/), { "alias": null, "args": null, @@ -312,26 +304,25 @@ return { "name": "location", "plural": false, "selections": [ - (v9/*: any*/), - (v3/*: any*/) + (v8/*: any*/), + (v2/*: any*/) ], "storageKey": null }, { "alias": null, - "args": (v16/*: any*/), + "args": [ + { + "kind": "Literal", + "name": "first", + "value": 30 + } + ], "concreteType": "UserAddressConnection", "kind": "LinkedField", "name": "addressConnection", "plural": false, "selections": [ - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "totalCount", - "storageKey": null - }, { "alias": null, "args": null, @@ -349,98 +340,42 @@ return { "plural": false, "selections": [ (v3/*: any*/), + (v10/*: any*/), (v4/*: any*/), (v5/*: any*/), - (v6/*: any*/), (v7/*: any*/), + (v13/*: any*/), + (v12/*: any*/), (v8/*: any*/), - (v9/*: any*/), - (v10/*: any*/), (v11/*: any*/), - (v12/*: any*/), - (v13/*: any*/), - (v14/*: any*/), - (v2/*: any*/) + (v9/*: any*/), + (v2/*: any*/), + (v6/*: any*/) ], "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "cursor", - "storageKey": null - } - ], - "storageKey": null - }, - { - "alias": null, - "args": null, - "concreteType": "PageInfo", - "kind": "LinkedField", - "name": "pageInfo", - "plural": false, - "selections": [ - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "endCursor", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "hasNextPage", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "hasPreviousPage", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "startCursor", - "storageKey": null } ], "storageKey": null } ], "storageKey": "addressConnection(first:30)" - }, - { - "alias": null, - "args": (v16/*: any*/), - "filters": null, - "handle": "connection", - "key": "SavedAddresses_addressConnection", - "kind": "LinkedHandle", - "name": "addressConnection" } ], "storageKey": null }, - (v15/*: any*/) + (v14/*: any*/) ], "storageKey": null } ] }, "params": { - "cacheID": "b975ccee8e45a0e3c289f017feaeb77b", + "cacheID": "9ca255905a0bf311d35c8db4d14933ee", "id": null, "metadata": {}, "name": "useCreateSavedAddressMutation", "operationKind": "mutation", - "text": "mutation useCreateSavedAddressMutation(\n $input: CreateUserAddressInput!\n) {\n createUserAddress(input: $input) {\n me {\n ...Shipping2_me\n id\n }\n userAddressOrErrors {\n __typename\n ... on Errors {\n errors {\n message\n }\n }\n ... on UserAddress {\n id\n internalID\n addressLine1\n addressLine2\n addressLine3\n city\n country\n isDefault\n name\n phoneNumber\n postalCode\n region\n }\n }\n }\n}\n\nfragment FulfillmentDetailsForm_me on Me {\n name\n email\n id\n location {\n country\n id\n }\n ...SavedAddresses2_me\n addressConnection(first: 30) {\n edges {\n node {\n id\n internalID\n addressLine1\n addressLine2\n addressLine3\n city\n country\n isDefault\n name\n phoneNumber\n postalCode\n region\n }\n }\n }\n}\n\nfragment SavedAddresses2_me on Me {\n id\n addressConnection(first: 30) {\n totalCount\n edges {\n node {\n id\n internalID\n addressLine1\n addressLine2\n addressLine3\n city\n country\n isDefault\n name\n phoneNumber\n postalCode\n region\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n}\n\nfragment Shipping2_me on Me {\n ...FulfillmentDetailsForm_me\n ...SavedAddresses2_me\n addressConnection(first: 30) {\n edges {\n node {\n id\n internalID\n addressLine1\n addressLine2\n addressLine3\n city\n country\n isDefault\n name\n phoneNumber\n postalCode\n region\n }\n }\n }\n}\n" + "text": "mutation useCreateSavedAddressMutation(\n $input: CreateUserAddressInput!\n) {\n createUserAddress(input: $input) {\n me {\n ...Shipping2_me\n id\n }\n userAddressOrErrors {\n __typename\n ... on Errors {\n errors {\n message\n }\n }\n ... on UserAddress {\n id\n internalID\n addressLine1\n addressLine2\n addressLine3\n city\n country\n isDefault\n name\n phoneNumber\n postalCode\n region\n }\n }\n }\n}\n\nfragment FulfillmentDetailsForm_me on Me {\n name\n email\n id\n location {\n country\n id\n }\n ...SavedAddresses2_me\n addressConnection(first: 30) {\n edges {\n node {\n id\n internalID\n addressLine1\n addressLine2\n addressLine3\n city\n country\n isDefault\n name\n phoneNumber\n postalCode\n region\n }\n }\n }\n}\n\nfragment SavedAddresses2_me on Me {\n addressConnection(first: 30) {\n edges {\n node {\n internalID\n name\n addressLine1\n addressLine2\n city\n region\n postalCode\n country\n phoneNumber\n isDefault\n id\n }\n }\n }\n}\n\nfragment Shipping2_me on Me {\n ...FulfillmentDetailsForm_me\n addressConnection(first: 30) {\n edges {\n node {\n id\n internalID\n addressLine1\n addressLine2\n addressLine3\n city\n country\n isDefault\n name\n phoneNumber\n postalCode\n region\n }\n }\n }\n}\n" } }; })(); diff --git a/src/__generated__/useDeleteSavedAddressMutation.graphql.ts b/src/__generated__/useDeleteSavedAddressMutation.graphql.ts index c03108738a8..f0d835d9e6d 100644 --- a/src/__generated__/useDeleteSavedAddressMutation.graphql.ts +++ b/src/__generated__/useDeleteSavedAddressMutation.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<60c68b13f1d861dd52c9122cd1f4f55b>> + * @generated SignedSource<<792fff6fa9572bdadc7e6e8fc2bfc090>> * @lightSyntaxTransform * @nogrep */ @@ -106,14 +106,7 @@ v6 = { "kind": "ScalarField", "name": "country", "storageKey": null -}, -v7 = [ - { - "kind": "Literal", - "name": "first", - "value": 30 - } -]; +}; return { "fragment": { "argumentDefinitions": (v0/*: any*/), @@ -211,19 +204,18 @@ return { }, { "alias": null, - "args": (v7/*: any*/), + "args": [ + { + "kind": "Literal", + "name": "first", + "value": 30 + } + ], "concreteType": "UserAddressConnection", "kind": "LinkedField", "name": "addressConnection", "plural": false, "selections": [ - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "totalCount", - "storageKey": null - }, { "alias": null, "args": null, @@ -240,7 +232,6 @@ return { "name": "node", "plural": false, "selections": [ - (v5/*: any*/), { "alias": null, "args": null, @@ -248,6 +239,7 @@ return { "name": "internalID", "storageKey": null }, + (v4/*: any*/), { "alias": null, "args": null, @@ -266,25 +258,24 @@ return { "alias": null, "args": null, "kind": "ScalarField", - "name": "addressLine3", + "name": "city", "storageKey": null }, { "alias": null, "args": null, "kind": "ScalarField", - "name": "city", + "name": "region", "storageKey": null }, - (v6/*: any*/), { "alias": null, "args": null, "kind": "ScalarField", - "name": "isDefault", + "name": "postalCode", "storageKey": null }, - (v4/*: any*/), + (v6/*: any*/), { "alias": null, "args": null, @@ -296,80 +287,25 @@ return { "alias": null, "args": null, "kind": "ScalarField", - "name": "postalCode", + "name": "isDefault", "storageKey": null }, + (v5/*: any*/), { "alias": null, "args": null, "kind": "ScalarField", - "name": "region", + "name": "addressLine3", "storageKey": null - }, - (v2/*: any*/) + } ], "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "cursor", - "storageKey": null - } - ], - "storageKey": null - }, - { - "alias": null, - "args": null, - "concreteType": "PageInfo", - "kind": "LinkedField", - "name": "pageInfo", - "plural": false, - "selections": [ - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "endCursor", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "hasNextPage", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "hasPreviousPage", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "startCursor", - "storageKey": null } ], "storageKey": null } ], "storageKey": "addressConnection(first:30)" - }, - { - "alias": null, - "args": (v7/*: any*/), - "filters": null, - "handle": "connection", - "key": "SavedAddresses_addressConnection", - "kind": "LinkedHandle", - "name": "addressConnection" } ], "storageKey": null @@ -401,12 +337,12 @@ return { ] }, "params": { - "cacheID": "5d88746e4ad2ba47c45d543698b6575f", + "cacheID": "780819f97dc357fa6f40a863e6f40b55", "id": null, "metadata": {}, "name": "useDeleteSavedAddressMutation", "operationKind": "mutation", - "text": "mutation useDeleteSavedAddressMutation(\n $input: DeleteUserAddressInput!\n) {\n deleteUserAddress(input: $input) {\n me {\n ...Shipping2_me\n id\n }\n userAddressOrErrors {\n __typename\n ... on Errors {\n errors {\n message\n }\n }\n ... on UserAddress {\n id\n }\n }\n }\n}\n\nfragment FulfillmentDetailsForm_me on Me {\n name\n email\n id\n location {\n country\n id\n }\n ...SavedAddresses2_me\n addressConnection(first: 30) {\n edges {\n node {\n id\n internalID\n addressLine1\n addressLine2\n addressLine3\n city\n country\n isDefault\n name\n phoneNumber\n postalCode\n region\n }\n }\n }\n}\n\nfragment SavedAddresses2_me on Me {\n id\n addressConnection(first: 30) {\n totalCount\n edges {\n node {\n id\n internalID\n addressLine1\n addressLine2\n addressLine3\n city\n country\n isDefault\n name\n phoneNumber\n postalCode\n region\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n}\n\nfragment Shipping2_me on Me {\n ...FulfillmentDetailsForm_me\n ...SavedAddresses2_me\n addressConnection(first: 30) {\n edges {\n node {\n id\n internalID\n addressLine1\n addressLine2\n addressLine3\n city\n country\n isDefault\n name\n phoneNumber\n postalCode\n region\n }\n }\n }\n}\n" + "text": "mutation useDeleteSavedAddressMutation(\n $input: DeleteUserAddressInput!\n) {\n deleteUserAddress(input: $input) {\n me {\n ...Shipping2_me\n id\n }\n userAddressOrErrors {\n __typename\n ... on Errors {\n errors {\n message\n }\n }\n ... on UserAddress {\n id\n }\n }\n }\n}\n\nfragment FulfillmentDetailsForm_me on Me {\n name\n email\n id\n location {\n country\n id\n }\n ...SavedAddresses2_me\n addressConnection(first: 30) {\n edges {\n node {\n id\n internalID\n addressLine1\n addressLine2\n addressLine3\n city\n country\n isDefault\n name\n phoneNumber\n postalCode\n region\n }\n }\n }\n}\n\nfragment SavedAddresses2_me on Me {\n addressConnection(first: 30) {\n edges {\n node {\n internalID\n name\n addressLine1\n addressLine2\n city\n region\n postalCode\n country\n phoneNumber\n isDefault\n id\n }\n }\n }\n}\n\nfragment Shipping2_me on Me {\n ...FulfillmentDetailsForm_me\n addressConnection(first: 30) {\n edges {\n node {\n id\n internalID\n addressLine1\n addressLine2\n addressLine3\n city\n country\n isDefault\n name\n phoneNumber\n postalCode\n region\n }\n }\n }\n}\n" } }; })(); diff --git a/src/__generated__/useSaveFulfillmentDetailsMutation.graphql.ts b/src/__generated__/useSaveFulfillmentDetailsMutation.graphql.ts index 8d68e14a54b..04d08bad3e4 100644 --- a/src/__generated__/useSaveFulfillmentDetailsMutation.graphql.ts +++ b/src/__generated__/useSaveFulfillmentDetailsMutation.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<3ae9eddcaf8586d6ae6bdb3b22578c2c>> + * @generated SignedSource<<6986b811789e03b9fd519b33d0ae754b>> * @lightSyntaxTransform * @nogrep */ @@ -50,7 +50,7 @@ export type useSaveFulfillmentDetailsMutation$data = { readonly __typename: "CommerceOrderWithMutationSuccess"; readonly order: { readonly __typename: string; - readonly id: string; + readonly internalID: string; readonly " $fragmentSpreads": FragmentRefs<"Shipping2_order">; }; } | { @@ -91,7 +91,7 @@ v3 = { "alias": null, "args": null, "kind": "ScalarField", - "name": "id", + "name": "internalID", "storageKey": null }, v4 = { @@ -135,28 +135,21 @@ v5 = { "abstractKey": null }, v6 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "internalID", - "storageKey": null -}, -v7 = { "alias": null, "args": null, "kind": "ScalarField", "name": "phoneNumber", "storageKey": null }, -v8 = { +v7 = { "alias": null, "args": null, "kind": "ScalarField", "name": "name", "storageKey": null }, -v9 = [ - (v8/*: any*/), +v8 = [ + (v7/*: any*/), { "alias": null, "args": null, @@ -199,8 +192,15 @@ v9 = [ "name": "postalCode", "storageKey": null }, - (v7/*: any*/) + (v6/*: any*/) ], +v9 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null +}, v10 = [ { "kind": "Literal", @@ -223,7 +223,7 @@ v12 = { "storageKey": null }, v13 = [ - (v3/*: any*/) + (v9/*: any*/) ], v14 = { "kind": "InlineFragment", @@ -267,7 +267,7 @@ v19 = { "storageKey": "buyerTotal(precision:2)" }, v20 = [ - (v6/*: any*/), + (v3/*: any*/), { "alias": null, "args": (v10/*: any*/), @@ -308,7 +308,7 @@ v20 = [ "name": "note", "storageKey": null }, - (v3/*: any*/) + (v9/*: any*/) ]; return { "fragment": { @@ -410,7 +410,6 @@ return { "kind": "TypeDiscriminator", "abstractKey": "__isCommerceOrder" }, - (v6/*: any*/), { "alias": null, "args": null, @@ -437,20 +436,20 @@ return { { "kind": "InlineFragment", "selections": [ - (v7/*: any*/) + (v6/*: any*/) ], "type": "CommercePickup", "abstractKey": null }, { "kind": "InlineFragment", - "selections": (v9/*: any*/), + "selections": (v8/*: any*/), "type": "CommerceShip", "abstractKey": null }, { "kind": "InlineFragment", - "selections": (v9/*: any*/), + "selections": (v8/*: any*/), "type": "CommerceShipArta", "abstractKey": null } @@ -538,7 +537,7 @@ return { "name": "shippingCountry", "storageKey": null }, - (v3/*: any*/), + (v9/*: any*/), { "alias": null, "args": null, @@ -580,7 +579,7 @@ return { "name": "node", "plural": false, "selections": [ - (v3/*: any*/), + (v9/*: any*/), { "alias": null, "args": null, @@ -612,7 +611,7 @@ return { ], "storageKey": null }, - (v3/*: any*/), + (v9/*: any*/), { "alias": null, "args": null, @@ -634,7 +633,7 @@ return { "kind": "InlineFragment", "selections": [ (v12/*: any*/), - (v3/*: any*/) + (v9/*: any*/) ], "type": "EditionSet", "abstractKey": null @@ -707,7 +706,7 @@ return { ], "storageKey": null }, - (v3/*: any*/) + (v9/*: any*/) ], "storageKey": null }, @@ -720,7 +719,7 @@ return { "plural": false, "selections": [ (v11/*: any*/), - (v3/*: any*/) + (v9/*: any*/) ], "storageKey": null } @@ -745,7 +744,7 @@ return { { "kind": "InlineFragment", "selections": [ - (v8/*: any*/) + (v7/*: any*/) ], "type": "Partner", "abstractKey": null @@ -852,7 +851,8 @@ return { } ], "storageKey": null - } + }, + (v9/*: any*/) ], "storageKey": null } @@ -870,16 +870,16 @@ return { ] }, "params": { - "cacheID": "8af9980a03330016f56dd83b8de68fd4", + "cacheID": "74a79f900eac166efd14b1bf9d108eb4", "id": null, "metadata": {}, "name": "useSaveFulfillmentDetailsMutation", "operationKind": "mutation", - "text": "mutation useSaveFulfillmentDetailsMutation(\n $input: CommerceSetShippingInput!\n) {\n commerceSetShipping(input: $input) {\n orderOrError {\n __typename\n ... on CommerceOrderWithMutationSuccess {\n order {\n id\n __typename\n __isCommerceOrder: __typename\n ...Shipping2_order\n }\n }\n ... on CommerceOrderWithMutationFailure {\n error {\n type\n code\n data\n }\n }\n ... on CommerceOrderRequiresAction {\n __typename\n }\n }\n }\n}\n\nfragment ArtworkSummaryItem_order on CommerceOrder {\n __isCommerceOrder: __typename\n sellerDetails {\n __typename\n ... on Partner {\n name\n }\n ... on Node {\n __isNode: __typename\n id\n }\n }\n currencyCode\n mode\n source\n lineItems {\n edges {\n node {\n artworkOrEditionSet {\n __typename\n ... on Artwork {\n price\n }\n ... on EditionSet {\n price\n id\n }\n ... on Node {\n __isNode: __typename\n id\n }\n }\n artwork {\n slug\n shippingOrigin\n id\n }\n artworkVersion {\n date\n artistNames\n title\n image {\n resized_ArtworkSummaryItem: resized(width: 55) {\n url\n }\n }\n id\n }\n id\n }\n }\n }\n}\n\nfragment FulfillmentDetailsForm_order on CommerceOrder {\n __isCommerceOrder: __typename\n internalID\n mode\n state\n requestedFulfillment {\n __typename\n ... on CommercePickup {\n phoneNumber\n }\n ... on CommerceShip {\n name\n addressLine1\n addressLine2\n city\n region\n country\n postalCode\n phoneNumber\n }\n ... on CommerceShipArta {\n name\n addressLine1\n addressLine2\n city\n region\n country\n postalCode\n phoneNumber\n }\n }\n lineItems {\n edges {\n node {\n artwork {\n slug\n processWithArtsyShippingDomestic\n artsyShippingInternational\n pickupAvailable\n onlyShipsDomestically\n euShippingOrigin\n shippingCountry\n id\n }\n shippingQuoteOptions {\n edges {\n ...ShippingQuotes_shippingQuotes\n node {\n id\n isSelected\n }\n }\n }\n id\n }\n }\n }\n ...ArtworkSummaryItem_order\n ...TransactionDetailsSummaryItem_order\n ...OrderStepper_order\n}\n\nfragment OrderStepper_order on CommerceOrder {\n __isCommerceOrder: __typename\n mode\n requestedFulfillment {\n __typename\n }\n paymentMethodDetails {\n __typename\n ... on CreditCard {\n id\n }\n ... on BankAccount {\n id\n }\n ... on WireTransfer {\n isManualPayment\n }\n }\n lineItems {\n edges {\n node {\n artwork {\n slug\n id\n }\n shippingQuoteOptions {\n edges {\n node {\n isSelected\n id\n }\n }\n }\n id\n }\n }\n }\n}\n\nfragment SaveAndContinueButton_order on CommerceOrder {\n __isCommerceOrder: __typename\n internalID\n}\n\nfragment Shipping2_order on CommerceOrder {\n __isCommerceOrder: __typename\n ...FulfillmentDetailsForm_order\n ...SaveAndContinueButton_order\n ...ArtworkSummaryItem_order\n ...TransactionDetailsSummaryItem_order\n ...OrderStepper_order\n __typename\n internalID\n mode\n requestedFulfillment {\n __typename\n ... on CommercePickup {\n phoneNumber\n }\n ... on CommerceShip {\n name\n addressLine1\n addressLine2\n city\n region\n country\n postalCode\n phoneNumber\n }\n ... on CommerceShipArta {\n name\n addressLine1\n addressLine2\n city\n region\n country\n postalCode\n phoneNumber\n }\n }\n lineItems {\n edges {\n node {\n ...ShippingQuotes2_commerceLineItem\n shippingQuoteOptions {\n edges {\n node {\n id\n isSelected\n price(precision: 2)\n priceCents\n typeName\n }\n }\n }\n artwork {\n slug\n processWithArtsyShippingDomestic\n artsyShippingInternational\n pickup_available: pickupAvailable\n onlyShipsDomestically\n euShippingOrigin\n shippingCountry\n id\n }\n id\n }\n }\n }\n}\n\nfragment ShippingQuotes2_commerceLineItem on CommerceLineItem {\n shippingQuoteOptions {\n edges {\n node {\n id\n isSelected\n price(precision: 2)\n priceCents\n typeName\n }\n }\n }\n}\n\nfragment ShippingQuotes_shippingQuotes on CommerceShippingQuoteEdge {\n node {\n id\n isSelected\n price(precision: 2)\n priceCents\n typeName\n }\n}\n\nfragment TransactionDetailsSummaryItem_order on CommerceOrder {\n __isCommerceOrder: __typename\n __typename\n requestedFulfillment {\n __typename\n }\n code\n lineItems {\n edges {\n node {\n artworkOrEditionSet {\n __typename\n ... on Artwork {\n price\n }\n ... on EditionSet {\n price\n id\n }\n ... on Node {\n __isNode: __typename\n id\n }\n }\n selectedShippingQuote {\n typeName\n id\n }\n id\n }\n }\n }\n mode\n source\n displayState\n shippingTotal(precision: 2)\n shippingTotalCents\n taxTotal(precision: 2)\n taxTotalCents\n itemsTotal(precision: 2)\n buyerTotal(precision: 2)\n currencyCode\n ... on CommerceOfferOrder {\n lastOffer {\n internalID\n amount(precision: 2)\n amountCents\n shippingTotal(precision: 2)\n shippingTotalCents\n taxTotal(precision: 2)\n taxTotalCents\n buyerTotal(precision: 2)\n buyerTotalCents\n fromParticipant\n note\n id\n }\n myLastOffer {\n internalID\n amount(precision: 2)\n amountCents\n shippingTotal(precision: 2)\n shippingTotalCents\n taxTotal(precision: 2)\n taxTotalCents\n buyerTotal(precision: 2)\n buyerTotalCents\n fromParticipant\n note\n id\n }\n }\n}\n" + "text": "mutation useSaveFulfillmentDetailsMutation(\n $input: CommerceSetShippingInput!\n) {\n commerceSetShipping(input: $input) {\n orderOrError {\n __typename\n ... on CommerceOrderWithMutationSuccess {\n order {\n internalID\n __typename\n __isCommerceOrder: __typename\n ...Shipping2_order\n id\n }\n }\n ... on CommerceOrderWithMutationFailure {\n error {\n type\n code\n data\n }\n }\n ... on CommerceOrderRequiresAction {\n __typename\n }\n }\n }\n}\n\nfragment ArtworkSummaryItem_order on CommerceOrder {\n __isCommerceOrder: __typename\n sellerDetails {\n __typename\n ... on Partner {\n name\n }\n ... on Node {\n __isNode: __typename\n id\n }\n }\n currencyCode\n mode\n source\n lineItems {\n edges {\n node {\n artworkOrEditionSet {\n __typename\n ... on Artwork {\n price\n }\n ... on EditionSet {\n price\n id\n }\n ... on Node {\n __isNode: __typename\n id\n }\n }\n artwork {\n slug\n shippingOrigin\n id\n }\n artworkVersion {\n date\n artistNames\n title\n image {\n resized_ArtworkSummaryItem: resized(width: 55) {\n url\n }\n }\n id\n }\n id\n }\n }\n }\n}\n\nfragment FulfillmentDetailsForm_order on CommerceOrder {\n __isCommerceOrder: __typename\n internalID\n mode\n state\n requestedFulfillment {\n __typename\n ... on CommercePickup {\n phoneNumber\n }\n ... on CommerceShip {\n name\n addressLine1\n addressLine2\n city\n region\n country\n postalCode\n phoneNumber\n }\n ... on CommerceShipArta {\n name\n addressLine1\n addressLine2\n city\n region\n country\n postalCode\n phoneNumber\n }\n }\n lineItems {\n edges {\n node {\n artwork {\n slug\n processWithArtsyShippingDomestic\n artsyShippingInternational\n pickupAvailable\n onlyShipsDomestically\n euShippingOrigin\n shippingCountry\n id\n }\n shippingQuoteOptions {\n edges {\n ...ShippingQuotes_shippingQuotes\n node {\n id\n isSelected\n }\n }\n }\n id\n }\n }\n }\n ...ArtworkSummaryItem_order\n ...TransactionDetailsSummaryItem_order\n ...OrderStepper_order\n}\n\nfragment OrderStepper_order on CommerceOrder {\n __isCommerceOrder: __typename\n mode\n requestedFulfillment {\n __typename\n }\n paymentMethodDetails {\n __typename\n ... on CreditCard {\n id\n }\n ... on BankAccount {\n id\n }\n ... on WireTransfer {\n isManualPayment\n }\n }\n lineItems {\n edges {\n node {\n artwork {\n slug\n id\n }\n shippingQuoteOptions {\n edges {\n node {\n isSelected\n id\n }\n }\n }\n id\n }\n }\n }\n}\n\nfragment SaveAndContinueButton_order on CommerceOrder {\n __isCommerceOrder: __typename\n internalID\n}\n\nfragment Shipping2_order on CommerceOrder {\n __isCommerceOrder: __typename\n ...FulfillmentDetailsForm_order\n ...SaveAndContinueButton_order\n ...ArtworkSummaryItem_order\n ...TransactionDetailsSummaryItem_order\n ...OrderStepper_order\n __typename\n internalID\n mode\n requestedFulfillment {\n __typename\n ... on CommercePickup {\n phoneNumber\n }\n ... on CommerceShip {\n name\n addressLine1\n addressLine2\n city\n region\n country\n postalCode\n phoneNumber\n }\n ... on CommerceShipArta {\n name\n addressLine1\n addressLine2\n city\n region\n country\n postalCode\n phoneNumber\n }\n }\n lineItems {\n edges {\n node {\n ...ShippingQuotes2_commerceLineItem\n shippingQuoteOptions {\n edges {\n node {\n id\n isSelected\n price(precision: 2)\n priceCents\n typeName\n }\n }\n }\n artwork {\n slug\n processWithArtsyShippingDomestic\n artsyShippingInternational\n pickup_available: pickupAvailable\n onlyShipsDomestically\n euShippingOrigin\n shippingCountry\n id\n }\n id\n }\n }\n }\n}\n\nfragment ShippingQuotes2_commerceLineItem on CommerceLineItem {\n shippingQuoteOptions {\n edges {\n node {\n id\n isSelected\n price(precision: 2)\n priceCents\n typeName\n }\n }\n }\n}\n\nfragment ShippingQuotes_shippingQuotes on CommerceShippingQuoteEdge {\n node {\n id\n isSelected\n price(precision: 2)\n priceCents\n typeName\n }\n}\n\nfragment TransactionDetailsSummaryItem_order on CommerceOrder {\n __isCommerceOrder: __typename\n __typename\n requestedFulfillment {\n __typename\n }\n code\n lineItems {\n edges {\n node {\n artworkOrEditionSet {\n __typename\n ... on Artwork {\n price\n }\n ... on EditionSet {\n price\n id\n }\n ... on Node {\n __isNode: __typename\n id\n }\n }\n selectedShippingQuote {\n typeName\n id\n }\n id\n }\n }\n }\n mode\n source\n displayState\n shippingTotal(precision: 2)\n shippingTotalCents\n taxTotal(precision: 2)\n taxTotalCents\n itemsTotal(precision: 2)\n buyerTotal(precision: 2)\n currencyCode\n ... on CommerceOfferOrder {\n lastOffer {\n internalID\n amount(precision: 2)\n amountCents\n shippingTotal(precision: 2)\n shippingTotalCents\n taxTotal(precision: 2)\n taxTotalCents\n buyerTotal(precision: 2)\n buyerTotalCents\n fromParticipant\n note\n id\n }\n myLastOffer {\n internalID\n amount(precision: 2)\n amountCents\n shippingTotal(precision: 2)\n shippingTotalCents\n taxTotal(precision: 2)\n taxTotalCents\n buyerTotal(precision: 2)\n buyerTotalCents\n fromParticipant\n note\n id\n }\n }\n}\n" } }; })(); -(node as any).hash = "3b3deade273165acad6d2d44ef6dfb31"; +(node as any).hash = "8a9c7cc9adea112800df1b8170e814c5"; export default node; diff --git a/src/__generated__/useUpdateSavedAddressMutation.graphql.ts b/src/__generated__/useUpdateSavedAddressMutation.graphql.ts index 894b663f765..521fbc7efe7 100644 --- a/src/__generated__/useUpdateSavedAddressMutation.graphql.ts +++ b/src/__generated__/useUpdateSavedAddressMutation.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<0f2be4d99035784a2450ba5ee118e05d>> + * @generated SignedSource<<3a5187144e8a7c4c1f501978c6de44d9>> * @lightSyntaxTransform * @nogrep */ @@ -82,97 +82,90 @@ v1 = [ } ], v2 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "__typename", - "storageKey": null -}, -v3 = { "alias": null, "args": null, "kind": "ScalarField", "name": "id", "storageKey": null }, -v4 = { +v3 = { "alias": null, "args": null, "kind": "ScalarField", "name": "internalID", "storageKey": null }, -v5 = { +v4 = { "alias": null, "args": null, "kind": "ScalarField", "name": "addressLine1", "storageKey": null }, -v6 = { +v5 = { "alias": null, "args": null, "kind": "ScalarField", "name": "addressLine2", "storageKey": null }, -v7 = { +v6 = { "alias": null, "args": null, "kind": "ScalarField", "name": "addressLine3", "storageKey": null }, -v8 = { +v7 = { "alias": null, "args": null, "kind": "ScalarField", "name": "city", "storageKey": null }, -v9 = { +v8 = { "alias": null, "args": null, "kind": "ScalarField", "name": "country", "storageKey": null }, -v10 = { +v9 = { "alias": null, "args": null, "kind": "ScalarField", "name": "isDefault", "storageKey": null }, -v11 = { +v10 = { "alias": null, "args": null, "kind": "ScalarField", "name": "name", "storageKey": null }, -v12 = { +v11 = { "alias": null, "args": null, "kind": "ScalarField", "name": "phoneNumber", "storageKey": null }, -v13 = { +v12 = { "alias": null, "args": null, "kind": "ScalarField", "name": "postalCode", "storageKey": null }, -v14 = { +v13 = { "alias": null, "args": null, "kind": "ScalarField", "name": "region", "storageKey": null }, -v15 = { +v14 = { "alias": null, "args": null, "concreteType": null, @@ -180,7 +173,13 @@ v15 = { "name": "userAddressOrErrors", "plural": false, "selections": [ - (v2/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + }, { "kind": "InlineFragment", "selections": [ @@ -209,6 +208,7 @@ v15 = { { "kind": "InlineFragment", "selections": [ + (v2/*: any*/), (v3/*: any*/), (v4/*: any*/), (v5/*: any*/), @@ -219,22 +219,14 @@ v15 = { (v10/*: any*/), (v11/*: any*/), (v12/*: any*/), - (v13/*: any*/), - (v14/*: any*/) + (v13/*: any*/) ], "type": "UserAddress", "abstractKey": null } ], "storageKey": null -}, -v16 = [ - { - "kind": "Literal", - "name": "first", - "value": 30 - } -]; +}; return { "fragment": { "argumentDefinitions": (v0/*: any*/), @@ -266,7 +258,7 @@ return { ], "storageKey": null }, - (v15/*: any*/) + (v14/*: any*/) ], "storageKey": null } @@ -296,7 +288,7 @@ return { "name": "me", "plural": false, "selections": [ - (v11/*: any*/), + (v10/*: any*/), { "alias": null, "args": null, @@ -304,7 +296,7 @@ return { "name": "email", "storageKey": null }, - (v3/*: any*/), + (v2/*: any*/), { "alias": null, "args": null, @@ -313,26 +305,25 @@ return { "name": "location", "plural": false, "selections": [ - (v9/*: any*/), - (v3/*: any*/) + (v8/*: any*/), + (v2/*: any*/) ], "storageKey": null }, { "alias": null, - "args": (v16/*: any*/), + "args": [ + { + "kind": "Literal", + "name": "first", + "value": 30 + } + ], "concreteType": "UserAddressConnection", "kind": "LinkedField", "name": "addressConnection", "plural": false, "selections": [ - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "totalCount", - "storageKey": null - }, { "alias": null, "args": null, @@ -350,98 +341,42 @@ return { "plural": false, "selections": [ (v3/*: any*/), + (v10/*: any*/), (v4/*: any*/), (v5/*: any*/), - (v6/*: any*/), (v7/*: any*/), + (v13/*: any*/), + (v12/*: any*/), (v8/*: any*/), - (v9/*: any*/), - (v10/*: any*/), (v11/*: any*/), - (v12/*: any*/), - (v13/*: any*/), - (v14/*: any*/), - (v2/*: any*/) + (v9/*: any*/), + (v2/*: any*/), + (v6/*: any*/) ], "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "cursor", - "storageKey": null - } - ], - "storageKey": null - }, - { - "alias": null, - "args": null, - "concreteType": "PageInfo", - "kind": "LinkedField", - "name": "pageInfo", - "plural": false, - "selections": [ - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "endCursor", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "hasNextPage", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "hasPreviousPage", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "startCursor", - "storageKey": null } ], "storageKey": null } ], "storageKey": "addressConnection(first:30)" - }, - { - "alias": null, - "args": (v16/*: any*/), - "filters": null, - "handle": "connection", - "key": "SavedAddresses_addressConnection", - "kind": "LinkedHandle", - "name": "addressConnection" } ], "storageKey": null }, - (v15/*: any*/) + (v14/*: any*/) ], "storageKey": null } ] }, "params": { - "cacheID": "ccabe2ba76bc90883163daee946888ed", + "cacheID": "43656960056b6f3eafdb8889f15699f8", "id": null, "metadata": {}, "name": "useUpdateSavedAddressMutation", "operationKind": "mutation", - "text": "mutation useUpdateSavedAddressMutation(\n $input: UpdateUserAddressInput!\n) {\n updateUserAddress(input: $input) {\n me {\n ...Shipping2_me\n id\n }\n userAddressOrErrors {\n __typename\n ... on Errors {\n errors {\n message\n }\n }\n ... on UserAddress {\n id\n internalID\n addressLine1\n addressLine2\n addressLine3\n city\n country\n isDefault\n name\n phoneNumber\n postalCode\n region\n }\n }\n }\n}\n\nfragment FulfillmentDetailsForm_me on Me {\n name\n email\n id\n location {\n country\n id\n }\n ...SavedAddresses2_me\n addressConnection(first: 30) {\n edges {\n node {\n id\n internalID\n addressLine1\n addressLine2\n addressLine3\n city\n country\n isDefault\n name\n phoneNumber\n postalCode\n region\n }\n }\n }\n}\n\nfragment SavedAddresses2_me on Me {\n id\n addressConnection(first: 30) {\n totalCount\n edges {\n node {\n id\n internalID\n addressLine1\n addressLine2\n addressLine3\n city\n country\n isDefault\n name\n phoneNumber\n postalCode\n region\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n}\n\nfragment Shipping2_me on Me {\n ...FulfillmentDetailsForm_me\n ...SavedAddresses2_me\n addressConnection(first: 30) {\n edges {\n node {\n id\n internalID\n addressLine1\n addressLine2\n addressLine3\n city\n country\n isDefault\n name\n phoneNumber\n postalCode\n region\n }\n }\n }\n}\n" + "text": "mutation useUpdateSavedAddressMutation(\n $input: UpdateUserAddressInput!\n) {\n updateUserAddress(input: $input) {\n me {\n ...Shipping2_me\n id\n }\n userAddressOrErrors {\n __typename\n ... on Errors {\n errors {\n message\n }\n }\n ... on UserAddress {\n id\n internalID\n addressLine1\n addressLine2\n addressLine3\n city\n country\n isDefault\n name\n phoneNumber\n postalCode\n region\n }\n }\n }\n}\n\nfragment FulfillmentDetailsForm_me on Me {\n name\n email\n id\n location {\n country\n id\n }\n ...SavedAddresses2_me\n addressConnection(first: 30) {\n edges {\n node {\n id\n internalID\n addressLine1\n addressLine2\n addressLine3\n city\n country\n isDefault\n name\n phoneNumber\n postalCode\n region\n }\n }\n }\n}\n\nfragment SavedAddresses2_me on Me {\n addressConnection(first: 30) {\n edges {\n node {\n internalID\n name\n addressLine1\n addressLine2\n city\n region\n postalCode\n country\n phoneNumber\n isDefault\n id\n }\n }\n }\n}\n\nfragment Shipping2_me on Me {\n ...FulfillmentDetailsForm_me\n addressConnection(first: 30) {\n edges {\n node {\n id\n internalID\n addressLine1\n addressLine2\n addressLine3\n city\n country\n isDefault\n name\n phoneNumber\n postalCode\n region\n }\n }\n }\n}\n" } }; })(); diff --git a/src/__generated__/useUpdateUserDefaultAddressMutation.graphql.ts b/src/__generated__/useUpdateUserDefaultAddressMutation.graphql.ts index d3f3ab833a6..89cb3bd90f5 100644 --- a/src/__generated__/useUpdateUserDefaultAddressMutation.graphql.ts +++ b/src/__generated__/useUpdateUserDefaultAddressMutation.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<0cd1f057cd24d80abeda313ca68abb89>> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -106,14 +106,7 @@ v6 = { "kind": "ScalarField", "name": "country", "storageKey": null -}, -v7 = [ - { - "kind": "Literal", - "name": "first", - "value": 30 - } -]; +}; return { "fragment": { "argumentDefinitions": (v0/*: any*/), @@ -211,19 +204,18 @@ return { }, { "alias": null, - "args": (v7/*: any*/), + "args": [ + { + "kind": "Literal", + "name": "first", + "value": 30 + } + ], "concreteType": "UserAddressConnection", "kind": "LinkedField", "name": "addressConnection", "plural": false, "selections": [ - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "totalCount", - "storageKey": null - }, { "alias": null, "args": null, @@ -240,7 +232,6 @@ return { "name": "node", "plural": false, "selections": [ - (v5/*: any*/), { "alias": null, "args": null, @@ -248,6 +239,7 @@ return { "name": "internalID", "storageKey": null }, + (v4/*: any*/), { "alias": null, "args": null, @@ -266,25 +258,24 @@ return { "alias": null, "args": null, "kind": "ScalarField", - "name": "addressLine3", + "name": "city", "storageKey": null }, { "alias": null, "args": null, "kind": "ScalarField", - "name": "city", + "name": "region", "storageKey": null }, - (v6/*: any*/), { "alias": null, "args": null, "kind": "ScalarField", - "name": "isDefault", + "name": "postalCode", "storageKey": null }, - (v4/*: any*/), + (v6/*: any*/), { "alias": null, "args": null, @@ -296,80 +287,25 @@ return { "alias": null, "args": null, "kind": "ScalarField", - "name": "postalCode", + "name": "isDefault", "storageKey": null }, + (v5/*: any*/), { "alias": null, "args": null, "kind": "ScalarField", - "name": "region", + "name": "addressLine3", "storageKey": null - }, - (v2/*: any*/) + } ], "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "cursor", - "storageKey": null - } - ], - "storageKey": null - }, - { - "alias": null, - "args": null, - "concreteType": "PageInfo", - "kind": "LinkedField", - "name": "pageInfo", - "plural": false, - "selections": [ - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "endCursor", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "hasNextPage", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "hasPreviousPage", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "startCursor", - "storageKey": null } ], "storageKey": null } ], "storageKey": "addressConnection(first:30)" - }, - { - "alias": null, - "args": (v7/*: any*/), - "filters": null, - "handle": "connection", - "key": "SavedAddresses_addressConnection", - "kind": "LinkedHandle", - "name": "addressConnection" } ], "storageKey": null @@ -401,12 +337,12 @@ return { ] }, "params": { - "cacheID": "a4d1c81182eb4759612d12358bdf5fb2", + "cacheID": "246771be6b0549a8abeacc150a081b33", "id": null, "metadata": {}, "name": "useUpdateUserDefaultAddressMutation", "operationKind": "mutation", - "text": "mutation useUpdateUserDefaultAddressMutation(\n $input: UpdateUserDefaultAddressInput!\n) {\n updateUserDefaultAddress(input: $input) {\n me {\n ...Shipping2_me\n id\n }\n userAddressOrErrors {\n __typename\n ... on Errors {\n errors {\n message\n }\n }\n ... on UserAddress {\n id\n }\n }\n }\n}\n\nfragment FulfillmentDetailsForm_me on Me {\n name\n email\n id\n location {\n country\n id\n }\n ...SavedAddresses2_me\n addressConnection(first: 30) {\n edges {\n node {\n id\n internalID\n addressLine1\n addressLine2\n addressLine3\n city\n country\n isDefault\n name\n phoneNumber\n postalCode\n region\n }\n }\n }\n}\n\nfragment SavedAddresses2_me on Me {\n id\n addressConnection(first: 30) {\n totalCount\n edges {\n node {\n id\n internalID\n addressLine1\n addressLine2\n addressLine3\n city\n country\n isDefault\n name\n phoneNumber\n postalCode\n region\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n}\n\nfragment Shipping2_me on Me {\n ...FulfillmentDetailsForm_me\n ...SavedAddresses2_me\n addressConnection(first: 30) {\n edges {\n node {\n id\n internalID\n addressLine1\n addressLine2\n addressLine3\n city\n country\n isDefault\n name\n phoneNumber\n postalCode\n region\n }\n }\n }\n}\n" + "text": "mutation useUpdateUserDefaultAddressMutation(\n $input: UpdateUserDefaultAddressInput!\n) {\n updateUserDefaultAddress(input: $input) {\n me {\n ...Shipping2_me\n id\n }\n userAddressOrErrors {\n __typename\n ... on Errors {\n errors {\n message\n }\n }\n ... on UserAddress {\n id\n }\n }\n }\n}\n\nfragment FulfillmentDetailsForm_me on Me {\n name\n email\n id\n location {\n country\n id\n }\n ...SavedAddresses2_me\n addressConnection(first: 30) {\n edges {\n node {\n id\n internalID\n addressLine1\n addressLine2\n addressLine3\n city\n country\n isDefault\n name\n phoneNumber\n postalCode\n region\n }\n }\n }\n}\n\nfragment SavedAddresses2_me on Me {\n addressConnection(first: 30) {\n edges {\n node {\n internalID\n name\n addressLine1\n addressLine2\n city\n region\n postalCode\n country\n phoneNumber\n isDefault\n id\n }\n }\n }\n}\n\nfragment Shipping2_me on Me {\n ...FulfillmentDetailsForm_me\n addressConnection(first: 30) {\n edges {\n node {\n id\n internalID\n addressLine1\n addressLine2\n addressLine3\n city\n country\n isDefault\n name\n phoneNumber\n postalCode\n region\n }\n }\n }\n}\n" } }; })(); diff --git a/yarn.lock b/yarn.lock index ff0410ba59b..0347c18e9e6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4790,6 +4790,14 @@ dependencies: "@types/unist" "*" +"@types/hoist-non-react-statics@^3.3.1": + version "3.3.5" + resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz#dab7867ef789d87e2b4b0003c9d65c49cc44a494" + integrity sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg== + dependencies: + "@types/react" "*" + hoist-non-react-statics "^3.3.0" + "@types/html-minifier-terser@^5.0.0": version "5.1.1" resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz#3c9ee980f1a10d6021ae6632ca3e79ca2ec4fb50" @@ -11591,17 +11599,18 @@ formidable@^1.2.0: integrity sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg== formik@^2.2.9: - version "2.2.9" - resolved "https://registry.yarnpkg.com/formik/-/formik-2.2.9.tgz#8594ba9c5e2e5cf1f42c5704128e119fc46232d0" - integrity sha512-LQLcISMmf1r5at4/gyJigGn0gOwFbeEAlji+N9InZF6LIMXnFNkO42sCI8Jt84YZggpD4cPWObAZaxpEFtSzNA== + version "2.4.5" + resolved "https://registry.yarnpkg.com/formik/-/formik-2.4.5.tgz#f899b5b7a6f103a8fabb679823e8fafc7e0ee1b4" + integrity sha512-Gxlht0TD3vVdzMDHwkiNZqJ7Mvg77xQNfmBRrNtvzcHZs72TJppSTDKHpImCMJZwcWPBJ8jSQQ95GJzXFf1nAQ== dependencies: + "@types/hoist-non-react-statics" "^3.3.1" deepmerge "^2.1.1" hoist-non-react-statics "^3.3.0" lodash "^4.17.21" lodash-es "^4.17.21" react-fast-compare "^2.0.1" tiny-warning "^1.0.2" - tslib "^1.10.0" + tslib "^2.0.0" forwarded@0.2.0: version "0.2.0" @@ -21797,16 +21806,16 @@ tsconfig-paths@^3.9.0: minimist "^1.2.0" strip-bom "^3.0.0" -tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0: - version "1.13.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" - integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== - -tslib@^1.9.3: +tslib@^1.10.0, tslib@^1.9.3: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== +tslib@^1.8.1, tslib@^1.9.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" + integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== + tslib@^2, tslib@^2.1.0, tslib@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"