From 3fa984da5fac7fc1fc7ba2b3c61c2ed80374c0b3 Mon Sep 17 00:00:00 2001
From: Erik <9088720+erikdstock@users.noreply.github.com>
Date: Thu, 21 Nov 2024 07:24:34 -0600
Subject: [PATCH 01/13] promote address form fields to generic component
---
.../Components/Form/Utils/initialValues.ts | 2 +-
.../Auction/Hooks/useCreateTokenAndSubmit.ts | 2 +-
src/Apps/Invoice/Components/AddressForm.tsx | 2 +-
.../Invoice/Components/InvoicePaymentForm.tsx | 2 +-
.../Invoice/Hooks/useCreateTokenAndSubmit.ts | 2 +-
.../Order/Components/CreditCardPicker.tsx | 4 +-
.../CreditCardPicker.jest.enzyme.enzyme.tsx | 3 +-
.../Utils/__tests__/formValidators.jest.tsx | 2 +-
src/Apps/Order/Utils/formValidators.tsx | 2 +-
.../Address/AddressAutocompleteInput.tsx | 2 +-
src/Components/Address/AddressForm.tsx | 35 +---
src/Components/Address/AddressFormFields.tsx | 178 ++++++++++++++++++
.../Address/useAddressAutocomplete.ts | 2 +-
src/Components/Address/utils.ts | 35 ++++
src/Components/__tests__/Utils/addressForm.ts | 2 +-
.../__tests__/Utils/addressForm2.ts | 2 +-
16 files changed, 229 insertions(+), 48 deletions(-)
create mode 100644 src/Components/Address/AddressFormFields.tsx
diff --git a/src/Apps/Auction/Components/Form/Utils/initialValues.ts b/src/Apps/Auction/Components/Form/Utils/initialValues.ts
index e895cb54a69..1d4a21f1138 100644
--- a/src/Apps/Auction/Components/Form/Utils/initialValues.ts
+++ b/src/Apps/Auction/Components/Form/Utils/initialValues.ts
@@ -1,5 +1,5 @@
import { FormikHelpers } from "formik"
-import { Address, emptyAddress } from "Components/Address/AddressForm"
+import { Address, emptyAddress } from "Components/Address/utils"
export interface AuctionFormValues {
address: Address
diff --git a/src/Apps/Auction/Hooks/useCreateTokenAndSubmit.ts b/src/Apps/Auction/Hooks/useCreateTokenAndSubmit.ts
index 7e93c286866..5ee1e1a4b9e 100644
--- a/src/Apps/Auction/Hooks/useCreateTokenAndSubmit.ts
+++ b/src/Apps/Auction/Hooks/useCreateTokenAndSubmit.ts
@@ -13,7 +13,7 @@ import { AuctionRegistrationRoute_me$data } from "__generated__/AuctionRegistrat
import { AuctionRegistrationRoute_sale$data } from "__generated__/AuctionRegistrationRoute_sale.graphql"
import { AuctionBidRoute_me$data } from "__generated__/AuctionBidRoute_me.graphql"
import { AuctionBidRoute_sale$data } from "__generated__/AuctionBidRoute_sale.graphql"
-import { toStripeAddress } from "Components/Address/AddressForm"
+import { toStripeAddress } from "Components/Address/utils"
import { useRefreshUserData } from "Apps/Auction/Queries/useRefreshUserData"
import {
errorMessageForCard,
diff --git a/src/Apps/Invoice/Components/AddressForm.tsx b/src/Apps/Invoice/Components/AddressForm.tsx
index e970eb76826..ec323e93017 100644
--- a/src/Apps/Invoice/Components/AddressForm.tsx
+++ b/src/Apps/Invoice/Components/AddressForm.tsx
@@ -1,6 +1,6 @@
import { Column, GridColumns, Input } from "@artsy/palette"
import { CountrySelect } from "Components/CountrySelect"
-import { Address } from "Components/Address/AddressForm"
+import { Address } from "Components/Address/utils"
import { useFormContext } from "Apps/Invoice/Hooks/useFormContext"
export interface AddressFormValues {
diff --git a/src/Apps/Invoice/Components/InvoicePaymentForm.tsx b/src/Apps/Invoice/Components/InvoicePaymentForm.tsx
index 231d2fdcdca..5d229d5a292 100644
--- a/src/Apps/Invoice/Components/InvoicePaymentForm.tsx
+++ b/src/Apps/Invoice/Components/InvoicePaymentForm.tsx
@@ -2,7 +2,7 @@ import { Button, Spacer } from "@artsy/palette"
import { AddressFormValues } from "Apps/Invoice/Components/AddressForm"
import { AddressFormWithCreditCard } from "Apps/Invoice/Components/AddressFormWithCreditCard"
import { useCreateTokenAndSubmit } from "Apps/Invoice/Hooks/useCreateTokenAndSubmit"
-import { emptyAddress } from "Components/Address/AddressForm"
+import { emptyAddress } from "Components/Address/utils"
import { Formik, Form } from "formik"
import { useRouter } from "System/Hooks/useRouter"
diff --git a/src/Apps/Invoice/Hooks/useCreateTokenAndSubmit.ts b/src/Apps/Invoice/Hooks/useCreateTokenAndSubmit.ts
index 87e892f50be..7444ac6afaa 100644
--- a/src/Apps/Invoice/Hooks/useCreateTokenAndSubmit.ts
+++ b/src/Apps/Invoice/Hooks/useCreateTokenAndSubmit.ts
@@ -6,7 +6,7 @@ import {
useStripe,
} from "@stripe/react-stripe-js"
import createLogger from "Utils/logger"
-import { toStripeAddress } from "Components/Address/AddressForm"
+import { toStripeAddress } from "Components/Address/utils"
import {
stripeCardElementNotFound,
stripeNotLoadedErrorMessage,
diff --git a/src/Apps/Order/Components/CreditCardPicker.tsx b/src/Apps/Order/Components/CreditCardPicker.tsx
index c29e0e31ba6..61d5ca744e9 100644
--- a/src/Apps/Order/Components/CreditCardPicker.tsx
+++ b/src/Apps/Order/Components/CreditCardPicker.tsx
@@ -2,14 +2,14 @@ import { CreditCardPicker_me$data } from "__generated__/CreditCardPicker_me.grap
import { CreditCardPicker_order$data } from "__generated__/CreditCardPicker_order.graphql"
import { CreditCardPickerCreateCreditCardMutation } from "__generated__/CreditCardPickerCreateCreditCardMutation.graphql"
import {
- Address,
AddressChangeHandler,
AddressErrors,
AddressForm,
AddressTouched,
- emptyAddress,
} from "Components/Address/AddressForm"
+import { Address, emptyAddress } from "Components/Address/utils"
+
import { CreditCardInput } from "Components/CreditCardInput"
import { validateAddress } from "Apps/Order/Utils/formValidators"
import * as DeprecatedSchema from "@artsy/cohesion/dist/DeprecatedSchema"
diff --git a/src/Apps/Order/Components/__tests__/CreditCardPicker.jest.enzyme.enzyme.tsx b/src/Apps/Order/Components/__tests__/CreditCardPicker.jest.enzyme.enzyme.tsx
index 4ac1f26084d..860bdb52742 100644
--- a/src/Apps/Order/Components/__tests__/CreditCardPicker.jest.enzyme.enzyme.tsx
+++ b/src/Apps/Order/Components/__tests__/CreditCardPicker.jest.enzyme.enzyme.tsx
@@ -13,7 +13,8 @@ import {
fillIn,
validAddress,
} from "Components/__tests__/Utils/addressForm"
-import { Address, AddressForm } from "Components/Address/AddressForm"
+import { AddressForm } from "Components/Address/AddressForm"
+import { Address } from "Components/Address/utils"
import { RootTestPage } from "DevTools/RootTestPage"
import { graphql } from "react-relay"
import {
diff --git a/src/Apps/Order/Utils/__tests__/formValidators.jest.tsx b/src/Apps/Order/Utils/__tests__/formValidators.jest.tsx
index e23acf824c8..632301b58f6 100644
--- a/src/Apps/Order/Utils/__tests__/formValidators.jest.tsx
+++ b/src/Apps/Order/Utils/__tests__/formValidators.jest.tsx
@@ -1,4 +1,4 @@
-import { Address } from "Components/Address/AddressForm"
+import { Address } from "Components/Address/utils"
import {
validateAddress,
validatePresence,
diff --git a/src/Apps/Order/Utils/formValidators.tsx b/src/Apps/Order/Utils/formValidators.tsx
index 018688f1fd6..595e8f2f973 100644
--- a/src/Apps/Order/Utils/formValidators.tsx
+++ b/src/Apps/Order/Utils/formValidators.tsx
@@ -1,4 +1,4 @@
-import { Address } from "Components/Address/AddressForm"
+import { Address } from "Components/Address/utils"
import { isEmpty } from "lodash"
export const validatePresence = (value: string): string | null => {
diff --git a/src/Components/Address/AddressAutocompleteInput.tsx b/src/Components/Address/AddressAutocompleteInput.tsx
index c0fb00f7ea2..bf45ba083bd 100644
--- a/src/Components/Address/AddressAutocompleteInput.tsx
+++ b/src/Components/Address/AddressAutocompleteInput.tsx
@@ -5,7 +5,7 @@ import {
Input,
usePrevious,
} from "@artsy/palette"
-import { Address } from "Components/Address/AddressForm"
+import { Address } from "Components/Address/utils"
import { useFeatureFlag } from "System/Hooks/useFeatureFlag"
import { getENV } from "Utils/getENV"
import { useCallback, useEffect, useMemo, useState } from "react"
diff --git a/src/Components/Address/AddressForm.tsx b/src/Components/Address/AddressForm.tsx
index f4be26c02d2..3d6e884d4d6 100644
--- a/src/Components/Address/AddressForm.tsx
+++ b/src/Components/Address/AddressForm.tsx
@@ -8,7 +8,6 @@ import {
} from "@artsy/palette"
import { CountrySelect } from "Components/CountrySelect"
import * as React from "react"
-import { CreateTokenCardData } from "@stripe/stripe-js"
import { isEqual } from "lodash"
import {
AddressAutocompleteSuggestion,
@@ -23,20 +22,10 @@ import {
SelectedItemFromAddressAutoCompletion,
} from "@artsy/cohesion"
import { useAnalyticsContext } from "System/Hooks/useAnalyticsContext"
+import { Address, emptyAddress } from "Components/Address/utils"
const ENABLE_SECONDARY_SUGGESTIONS = false
-export interface Address {
- name: string
- country: string
- postalCode: string
- addressLine1: string
- addressLine2: string
- city: string
- region: string
- phoneNumber?: string
-}
-
export type AddressErrors = Partial
export type AddressTouched = Partial<{ [T in keyof Address]: boolean }>
export type AddressChangeHandler = (
@@ -44,28 +33,6 @@ export type AddressChangeHandler = (
key: keyof Address
) => void
-export const emptyAddress: Address = {
- name: "",
- country: "",
- postalCode: "",
- addressLine1: "",
- addressLine2: "",
- city: "",
- region: "",
- phoneNumber: "",
-}
-
-export const toStripeAddress = (address: Address): CreateTokenCardData => {
- return {
- name: address.name,
- address_line1: address.addressLine1,
- address_line2: address.addressLine2,
- address_country: address.country,
- address_city: address.city,
- address_state: address.region,
- address_zip: address.postalCode,
- }
-}
export interface AddressFormProps {
onChange: AddressChangeHandler
value?: Partial
diff --git a/src/Components/Address/AddressFormFields.tsx b/src/Components/Address/AddressFormFields.tsx
new file mode 100644
index 00000000000..84af3e48a5d
--- /dev/null
+++ b/src/Components/Address/AddressFormFields.tsx
@@ -0,0 +1,178 @@
+import { ContextModule } from "@artsy/cohesion"
+import { Column, GridColumns, Input } from "@artsy/palette"
+import { AddressAutocompleteInput } from "Components/Address/AddressAutocompleteInput"
+import { Address } from "Components/Address/utils"
+import { CountrySelect } from "Components/CountrySelect"
+import { useFormikContext } from "formik"
+import { useAnalyticsContext } from "System/Hooks/useAnalyticsContext"
+
+interface FormikContextWithAddress {
+ address: Address
+ phoneNumber: string
+}
+
+const useFormContext = () => {
+ const context = useFormikContext()
+ return context
+}
+
+export const AddressForm = () => {
+ const {
+ handleChange,
+ handleBlur,
+ errors,
+ values,
+ touched,
+ setValues,
+ setFieldValue,
+ } = useFormContext()
+
+ const { contextPageOwnerId, contextPageOwnerType } = useAnalyticsContext()
+
+ const autocompleteTrackingValues = {
+ contextModule: ContextModule.auctionRegistration,
+ contextOwnerType: contextPageOwnerType,
+ contextPageOwnerId: contextPageOwnerId || "",
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ {
+ const selectedAddress = option.address
+ setValues({
+ ...values,
+ address: {
+ ...values.address,
+ addressLine1: selectedAddress.addressLine1,
+ addressLine2: selectedAddress.addressLine2,
+ city: selectedAddress.city,
+ region: selectedAddress.region,
+ postalCode: selectedAddress.postalCode,
+ country: selectedAddress.country,
+ },
+ })
+ }}
+ error={touched.address?.addressLine1 && errors.address?.addressLine1}
+ onClear={() => {
+ setFieldValue("address.addressLine1", "")
+ }}
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/src/Components/Address/useAddressAutocomplete.ts b/src/Components/Address/useAddressAutocomplete.ts
index e511955730f..1885f12d37d 100644
--- a/src/Components/Address/useAddressAutocomplete.ts
+++ b/src/Components/Address/useAddressAutocomplete.ts
@@ -1,5 +1,5 @@
import { AutocompleteInputOptionType } from "@artsy/palette"
-import { Address } from "Components/Address/AddressForm"
+import { Address } from "Components/Address/utils"
import { useFeatureFlag } from "System/Hooks/useFeatureFlag"
import { getENV } from "Utils/getENV"
import { useCallback, useEffect, useMemo, useState } from "react"
diff --git a/src/Components/Address/utils.ts b/src/Components/Address/utils.ts
index 1fe85e6bdde..e6af58ffdaa 100644
--- a/src/Components/Address/utils.ts
+++ b/src/Components/Address/utils.ts
@@ -1,6 +1,41 @@
import { validatePhoneNumber } from "Components/PhoneNumberInput"
+import { CreateTokenCardData } from "@stripe/stripe-js"
import * as Yup from "yup"
+export interface Address {
+ name: string
+ country: string
+ postalCode: string
+ addressLine1: string
+ addressLine2: string
+ city: string
+ region: string
+ phoneNumber?: string
+}
+
+export const emptyAddress: Address = {
+ name: "",
+ country: "",
+ postalCode: "",
+ addressLine1: "",
+ addressLine2: "",
+ city: "",
+ region: "",
+ phoneNumber: "",
+}
+
+export const toStripeAddress = (address: Address): CreateTokenCardData => {
+ return {
+ name: address.name,
+ address_line1: address.addressLine1,
+ address_line2: address.addressLine2,
+ address_country: address.country,
+ address_city: address.city,
+ address_state: address.region,
+ address_zip: address.postalCode,
+ }
+}
+
export const yupPhoneValidator = Yup.string()
.required("Phone Number is required")
.test({
diff --git a/src/Components/__tests__/Utils/addressForm.ts b/src/Components/__tests__/Utils/addressForm.ts
index 896677af73d..af82a1e1588 100644
--- a/src/Components/__tests__/Utils/addressForm.ts
+++ b/src/Components/__tests__/Utils/addressForm.ts
@@ -1,5 +1,5 @@
/* Address utilities for the old address form without improvements like state select */
-import { Address } from "Components/Address/AddressForm"
+import { Address } from "Components/Address/utils"
import { CountrySelect } from "Components/CountrySelect"
import { Input } from "@artsy/palette"
import { screen, waitFor } from "@testing-library/react"
diff --git a/src/Components/__tests__/Utils/addressForm2.ts b/src/Components/__tests__/Utils/addressForm2.ts
index 3d327f2d8d9..5c95340faa9 100644
--- a/src/Components/__tests__/Utils/addressForm2.ts
+++ b/src/Components/__tests__/Utils/addressForm2.ts
@@ -3,7 +3,7 @@
* with improvements like state select
*/
-import { Address } from "Components/Address/AddressForm"
+import { Address } from "Components/Address/utils"
import { CountrySelect } from "Components/CountrySelect"
import { Input } from "@artsy/palette"
import { screen, waitFor } from "@testing-library/react"
From fdf0fcae7eb26c44f9798ea2c8f5dc940e03047d Mon Sep 17 00:00:00 2001
From: Erik <9088720+erikdstock@users.noreply.github.com>
Date: Thu, 21 Nov 2024 07:25:15 -0600
Subject: [PATCH 02/13] fix non-null assertions
---
src/Apps/Auction/Hooks/useCreateTokenAndSubmit.ts | 15 ++++++++++-----
src/Apps/Order/Components/CreditCardPicker.tsx | 7 ++++---
src/Components/__tests__/Utils/addressForm.ts | 2 +-
3 files changed, 15 insertions(+), 9 deletions(-)
diff --git a/src/Apps/Auction/Hooks/useCreateTokenAndSubmit.ts b/src/Apps/Auction/Hooks/useCreateTokenAndSubmit.ts
index 5ee1e1a4b9e..6461e1c9b92 100644
--- a/src/Apps/Auction/Hooks/useCreateTokenAndSubmit.ts
+++ b/src/Apps/Auction/Hooks/useCreateTokenAndSubmit.ts
@@ -80,11 +80,16 @@ export const useCreateTokenAndSubmit = ({
helpers.setSubmitting(true)
+ if (!values.address) {
+ helpers.setStatus("SUBMISSION_FAILED")
+ return
+ }
+
try {
const { error, token } = await stripe.createToken(
cardNumberElement,
- toStripeAddress(values.address!)
- )!
+ toStripeAddress(values.address)
+ )
if (error) {
helpers.setFieldError("creditCard", error.message)
@@ -94,7 +99,7 @@ export const useCreateTokenAndSubmit = ({
await submitAddCreditCardAndUpdateProfileMutation({
variables: {
creditCardInput: {
- token: token?.id!,
+ token: token.id,
},
profileInput: {
phone: values.phoneNumber,
@@ -102,8 +107,8 @@ export const useCreateTokenAndSubmit = ({
},
rejectIf: res => {
if (res.createCreditCard?.creditCardOrError?.mutationError) {
- const mutationErrorDetail = res.createCreditCard?.creditCardOrError
- ?.mutationError?.detail!
+ const mutationErrorDetail =
+ res.createCreditCard.creditCardOrError.mutationError.detail
helpers.setFieldError(
"creditCard",
diff --git a/src/Apps/Order/Components/CreditCardPicker.tsx b/src/Apps/Order/Components/CreditCardPicker.tsx
index 61d5ca744e9..eda93dae0c3 100644
--- a/src/Apps/Order/Components/CreditCardPicker.tsx
+++ b/src/Apps/Order/Components/CreditCardPicker.tsx
@@ -91,7 +91,7 @@ export class CreditCardPicker extends React.Component<
return this.props.me.creditCards?.edges?.length
? {
type: "existing",
- id: this.props.me.creditCards.edges[0]?.node?.internalID!,
+ id: this.props.me.creditCards.edges[0]?.node?.internalID as string,
}
: { type: "new" }
}
@@ -121,6 +121,7 @@ export class CreditCardPicker extends React.Component<
try {
this.setState({ isCreatingStripeToken: true })
const stripeBillingAddress = this.getStripeBillingAddress()
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const cardNumberElement = this.props.elements.getElement(
CardNumberElement
)!
@@ -167,7 +168,7 @@ export class CreditCardPicker extends React.Component<
const creditCardOrError = (
await this.createCreditCard({
input: {
- token: stripeResult?.token?.id!,
+ token: stripeResult.token.id,
oneTimeUse: !saveNewCreditCard,
},
})
@@ -192,7 +193,7 @@ export class CreditCardPicker extends React.Component<
} else
return {
type: "success",
- creditCardId: creditCardOrError?.creditCard?.internalID!,
+ creditCardId: creditCardOrError?.creditCard?.internalID as string,
}
}
diff --git a/src/Components/__tests__/Utils/addressForm.ts b/src/Components/__tests__/Utils/addressForm.ts
index af82a1e1588..fb0319defbf 100644
--- a/src/Components/__tests__/Utils/addressForm.ts
+++ b/src/Components/__tests__/Utils/addressForm.ts
@@ -66,5 +66,5 @@ export const fillAddressForm = async (address: Address) => {
userEvent.paste(city, address.city)
userEvent.paste(region, address.region)
userEvent.paste(postalCode, address.postalCode)
- userEvent.paste(phoneNumber, address.phoneNumber!)
+ address.phoneNumber && userEvent.paste(phoneNumber, address.phoneNumber)
}
From 402b75a6339ace77ad65ea3758839ad170e87cb6 Mon Sep 17 00:00:00 2001
From: Erik <9088720+erikdstock@users.noreply.github.com>
Date: Thu, 21 Nov 2024 07:55:24 -0600
Subject: [PATCH 03/13] migrate auction registration form to use global address
component
---
.../Auction/Components/Form/AddressForm.tsx | 167 ------------------
.../Form/AddressFormWithCreditCard.tsx | 8 +-
.../Form/ConditionsOfSaleCheckbox.tsx | 5 +-
.../Auction/Components/Form/ErrorStatus.tsx | 5 +-
.../__tests__/AddressForm.jest.enzyme.tsx | 5 +-
.../AddressFormWithCreditCard.jest.enzyme.tsx | 8 +-
.../ConditionsOfSaleCheckbox.jest.enzyme.tsx | 11 +-
.../__tests__/ErrorStatus.jest.enzyme.tsx | 9 +-
...ormContext.ts => useAuctionFormContext.ts} | 2 +-
.../Bid/Components/PricingTransparency.tsx | 6 +-
src/Components/Address/AddressFormFields.tsx | 2 +-
11 files changed, 33 insertions(+), 195 deletions(-)
delete mode 100644 src/Apps/Auction/Components/Form/AddressForm.tsx
rename src/Apps/Auction/Hooks/{useFormContext.ts => useAuctionFormContext.ts} (81%)
diff --git a/src/Apps/Auction/Components/Form/AddressForm.tsx b/src/Apps/Auction/Components/Form/AddressForm.tsx
deleted file mode 100644
index a25cd18f1e2..00000000000
--- a/src/Apps/Auction/Components/Form/AddressForm.tsx
+++ /dev/null
@@ -1,167 +0,0 @@
-import { ContextModule } from "@artsy/cohesion"
-import { Column, GridColumns, Input } from "@artsy/palette"
-import { useFormContext } from "Apps/Auction/Hooks/useFormContext"
-import { AddressAutocompleteInput } from "Components/Address/AddressAutocompleteInput"
-import { CountrySelect } from "Components/CountrySelect"
-import { useAnalyticsContext } from "System/Hooks/useAnalyticsContext"
-
-export const AddressForm = () => {
- const {
- handleChange,
- handleBlur,
- errors,
- values,
- touched,
- setValues,
- setFieldValue,
- } = useFormContext()
-
- const { contextPageOwnerId, contextPageOwnerType } = useAnalyticsContext()
-
- const autocompleteTrackingValues = {
- contextModule: ContextModule.auctionRegistration,
- contextOwnerType: contextPageOwnerType,
- contextPageOwnerId: contextPageOwnerId || "",
- }
-
- return (
-
-
-
-
-
-
-
-
-
-
- {
- const selectedAddress = option.address
- setValues({
- ...values,
- address: {
- ...values.address,
- addressLine1: selectedAddress.addressLine1,
- addressLine2: selectedAddress.addressLine2,
- city: selectedAddress.city,
- region: selectedAddress.region,
- postalCode: selectedAddress.postalCode,
- country: selectedAddress.country,
- },
- })
- }}
- error={touched.address?.addressLine1 && errors.address?.addressLine1}
- onClear={() => {
- setFieldValue("address.addressLine1", "")
- }}
- />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- )
-}
diff --git a/src/Apps/Auction/Components/Form/AddressFormWithCreditCard.tsx b/src/Apps/Auction/Components/Form/AddressFormWithCreditCard.tsx
index ecd5de26e14..f576b7f2f55 100644
--- a/src/Apps/Auction/Components/Form/AddressFormWithCreditCard.tsx
+++ b/src/Apps/Auction/Components/Form/AddressFormWithCreditCard.tsx
@@ -1,7 +1,7 @@
import { Join, Spacer, Text } from "@artsy/palette"
import { CreditCardInput } from "Components/CreditCardInput"
-import { useFormContext } from "Apps/Auction/Hooks/useFormContext"
-import { AddressForm } from "./AddressForm"
+import { AddressFormFields } from "Components/Address/AddressFormFields"
+import { useAuctionFormContext } from "Apps/Auction/Hooks/useAuctionFormContext"
export const AddressFormWithCreditCard: React.FC> = () => {
const {
@@ -10,7 +10,7 @@ export const AddressFormWithCreditCard: React.FC}>
@@ -44,7 +44,7 @@ export const AddressFormWithCreditCard: React.FC
-
+
)
}
diff --git a/src/Apps/Auction/Components/Form/ConditionsOfSaleCheckbox.tsx b/src/Apps/Auction/Components/Form/ConditionsOfSaleCheckbox.tsx
index 767c50ca161..9914e9c3dcf 100644
--- a/src/Apps/Auction/Components/Form/ConditionsOfSaleCheckbox.tsx
+++ b/src/Apps/Auction/Components/Form/ConditionsOfSaleCheckbox.tsx
@@ -1,5 +1,6 @@
import { Checkbox, Spacer, Text } from "@artsy/palette"
-import { useFormContext } from "Apps/Auction/Hooks/useFormContext"
+import { useAuctionFormContext } from "Apps/Auction/Hooks/useAuctionFormContext"
+
import { RouterLink } from "System/Components/RouterLink"
export const ConditionsOfSaleCheckbox: React.FC> = () => {
@@ -9,7 +10,7 @@ export const ConditionsOfSaleCheckbox: React.FC
errors,
setFieldTouched,
setFieldValue,
- } = useFormContext()
+ } = useAuctionFormContext()
const showErrorMessage = !!(touched.agreeToTerms && errors.agreeToTerms)
diff --git a/src/Apps/Auction/Components/Form/ErrorStatus.tsx b/src/Apps/Auction/Components/Form/ErrorStatus.tsx
index 7b76160a57c..95798613a2a 100644
--- a/src/Apps/Auction/Components/Form/ErrorStatus.tsx
+++ b/src/Apps/Auction/Components/Form/ErrorStatus.tsx
@@ -1,10 +1,11 @@
import { Banner, BannerProps, Flex, Text } from "@artsy/palette"
import { errorMessageForBidding } from "Apps/Auction/Components/Form/Utils/errorMessages"
-import { useFormContext } from "Apps/Auction/Hooks/useFormContext"
+import { useAuctionFormContext } from "Apps/Auction/Hooks/useAuctionFormContext"
+
import { RouterLink } from "System/Components/RouterLink"
export const ErrorStatus = () => {
- const { status } = useFormContext()
+ const { status } = useAuctionFormContext()
if (!status) {
return null
diff --git a/src/Apps/Auction/Components/Form/__tests__/AddressForm.jest.enzyme.tsx b/src/Apps/Auction/Components/Form/__tests__/AddressForm.jest.enzyme.tsx
index ab2d0c73fda..aca7df7b439 100644
--- a/src/Apps/Auction/Components/Form/__tests__/AddressForm.jest.enzyme.tsx
+++ b/src/Apps/Auction/Components/Form/__tests__/AddressForm.jest.enzyme.tsx
@@ -1,8 +1,9 @@
import { mount } from "enzyme"
import { AddressForm } from "Apps/Auction/Components/Form/AddressForm"
-jest.mock("Apps/Auction/Hooks/useFormContext", () => ({
- useFormContext: () => {
+// TODO: Migrate these tests to new Components/Address/AddressFormFields.jest.tsx file
+jest.mock("Apps/Auction/Hooks/useAuctionFormContext", () => ({
+ useAuctionFormContext: () => {
return {
handleChange: jest.fn(),
handleBlur: jest.fn(),
diff --git a/src/Apps/Auction/Components/Form/__tests__/AddressFormWithCreditCard.jest.enzyme.tsx b/src/Apps/Auction/Components/Form/__tests__/AddressFormWithCreditCard.jest.enzyme.tsx
index 5f0ddb54203..c402353a460 100644
--- a/src/Apps/Auction/Components/Form/__tests__/AddressFormWithCreditCard.jest.enzyme.tsx
+++ b/src/Apps/Auction/Components/Form/__tests__/AddressFormWithCreditCard.jest.enzyme.tsx
@@ -1,8 +1,8 @@
import { mount } from "enzyme"
import { AddressFormWithCreditCard } from "Apps/Auction/Components/Form/AddressFormWithCreditCard"
-import { useFormContext } from "Apps/Auction/Hooks/useFormContext"
+import { useAuctionFormContext } from "Apps/Auction/Hooks/useAuctionFormContext"
-jest.mock("Apps/Auction/Hooks/useFormContext")
+jest.mock("Apps/Auction/Hooks/useAuctionFormContext")
jest.mock("Components/CreditCardInput", () => ({
CreditCardInput: () => null,
@@ -12,7 +12,7 @@ jest.mock("../AddressForm", () => ({
}))
describe("AddressFormWithCreditCard", () => {
- const mockUseFormContext = useFormContext as jest.Mock
+ const mockuseAuctionFormContext = useAuctionFormContext as jest.Mock
const setFieldTouched = jest.fn()
const setFieldValue = jest.fn()
const setFieldError = jest.fn()
@@ -22,7 +22,7 @@ describe("AddressFormWithCreditCard", () => {
}
beforeAll(() => {
- mockUseFormContext.mockImplementation(() => {
+ mockuseAuctionFormContext.mockImplementation(() => {
return {
setFieldTouched,
setFieldValue,
diff --git a/src/Apps/Auction/Components/Form/__tests__/ConditionsOfSaleCheckbox.jest.enzyme.tsx b/src/Apps/Auction/Components/Form/__tests__/ConditionsOfSaleCheckbox.jest.enzyme.tsx
index 5416efa74bd..a08b64b9678 100644
--- a/src/Apps/Auction/Components/Form/__tests__/ConditionsOfSaleCheckbox.jest.enzyme.tsx
+++ b/src/Apps/Auction/Components/Form/__tests__/ConditionsOfSaleCheckbox.jest.enzyme.tsx
@@ -1,12 +1,13 @@
import { mount } from "enzyme"
-import { useFormContext } from "Apps/Auction/Hooks/useFormContext"
+import { useAuctionFormContext } from "Apps/Auction/Hooks/useAuctionFormContext"
+
import { ConditionsOfSaleCheckbox } from "Apps/Auction/Components/Form/ConditionsOfSaleCheckbox"
-jest.mock("Apps/Auction/Hooks/useFormContext")
+jest.mock("Apps/Auction/Hooks/useAuctionFormContext")
jest.mock("System/Hooks/useFeatureFlag")
describe("ConditionsOfSaleCheckbox", () => {
- const mockUseFormContext = useFormContext as jest.Mock
+ const mockuseAuctionFormContext = useAuctionFormContext as jest.Mock
const setFieldTouched = jest.fn()
const setFieldValue = jest.fn()
const formProps = {
@@ -22,13 +23,13 @@ describe("ConditionsOfSaleCheckbox", () => {
}
beforeAll(() => {
- mockUseFormContext.mockImplementation(() => {
+ mockuseAuctionFormContext.mockImplementation(() => {
return formProps
})
})
it("shows error message if error", () => {
- mockUseFormContext.mockImplementation(() => {
+ mockuseAuctionFormContext.mockImplementation(() => {
return {
...formProps,
touched: {
diff --git a/src/Apps/Auction/Components/Form/__tests__/ErrorStatus.jest.enzyme.tsx b/src/Apps/Auction/Components/Form/__tests__/ErrorStatus.jest.enzyme.tsx
index b5d8e00662b..0bd3507f1fc 100644
--- a/src/Apps/Auction/Components/Form/__tests__/ErrorStatus.jest.enzyme.tsx
+++ b/src/Apps/Auction/Components/Form/__tests__/ErrorStatus.jest.enzyme.tsx
@@ -1,15 +1,16 @@
import { mount } from "enzyme"
-import { useFormContext } from "Apps/Auction/Hooks/useFormContext"
+import { useAuctionFormContext } from "Apps/Auction/Hooks/useAuctionFormContext"
+
import { ErrorStatus } from "Apps/Auction/Components/Form/ErrorStatus"
-jest.mock("Apps/Auction/Hooks/useFormContext")
+jest.mock("Apps/Auction/Hooks/useAuctionFormContext")
describe("ErrorStatus", () => {
- const mockUseFormContext = useFormContext as jest.Mock
+ const mockuseAuctionFormContext = useAuctionFormContext as jest.Mock
let status
const getWrapper = () => {
- mockUseFormContext.mockImplementation(() => {
+ mockuseAuctionFormContext.mockImplementation(() => {
return {
status,
}
diff --git a/src/Apps/Auction/Hooks/useFormContext.ts b/src/Apps/Auction/Hooks/useAuctionFormContext.ts
similarity index 81%
rename from src/Apps/Auction/Hooks/useFormContext.ts
rename to src/Apps/Auction/Hooks/useAuctionFormContext.ts
index e31e1c3801e..bb82b6fb5c2 100644
--- a/src/Apps/Auction/Hooks/useFormContext.ts
+++ b/src/Apps/Auction/Hooks/useAuctionFormContext.ts
@@ -1,7 +1,7 @@
import { AuctionFormValues } from "Apps/Auction/Components/Form/Utils/initialValues"
import { useFormikContext } from "formik"
-export const useFormContext = () => {
+export const useAuctionFormContext = () => {
const context = useFormikContext()
return context
}
diff --git a/src/Apps/Auction/Routes/Bid/Components/PricingTransparency.tsx b/src/Apps/Auction/Routes/Bid/Components/PricingTransparency.tsx
index 344c2e8da81..34ea9f4d394 100644
--- a/src/Apps/Auction/Routes/Bid/Components/PricingTransparency.tsx
+++ b/src/Apps/Auction/Routes/Bid/Components/PricingTransparency.tsx
@@ -7,7 +7,7 @@ import {
} from "__generated__/PricingTransparencyQuery.graphql"
import { useSystemContext } from "System/Hooks/useSystemContext"
import { SystemQueryRenderer } from "System/Relay/SystemQueryRenderer"
-import { useFormContext } from "Apps/Auction/Hooks/useFormContext"
+import { useAuctionFormContext } from "Apps/Auction/Hooks/useAuctionFormContext"
import {
Text,
@@ -86,8 +86,8 @@ export const PricingTransparencyQueryRenderer = ({
artworkId,
}: Omit) => {
const { relayEnvironment } = useSystemContext()
- const { values } = useFormContext()
- const bidAmountMinor = parseInt(values.selectedBid!)
+ const { values } = useAuctionFormContext()
+ const bidAmountMinor = parseInt(values.selectedBid || "0")
// Hack to prevent invalid refetch / preloader state during route transition
// when the url changes after user places a successful bid and we redirect
diff --git a/src/Components/Address/AddressFormFields.tsx b/src/Components/Address/AddressFormFields.tsx
index 84af3e48a5d..42d11095957 100644
--- a/src/Components/Address/AddressFormFields.tsx
+++ b/src/Components/Address/AddressFormFields.tsx
@@ -16,7 +16,7 @@ const useFormContext = () => {
return context
}
-export const AddressForm = () => {
+export const AddressFormFields = () => {
const {
handleChange,
handleBlur,
From 307fabe00b21355d79587f4f8edf585d1af458e1 Mon Sep 17 00:00:00 2001
From: Erik <9088720+erikdstock@users.noreply.github.com>
Date: Thu, 21 Nov 2024 17:47:57 -0600
Subject: [PATCH 04/13] types cleanup to guarantee form used in correct ctx
- maybe move phone number into address object on address type since it
is already redundantly in there?
---
.../Form/AddressFormWithCreditCard.tsx | 3 +-
.../Components/Form/Utils/initialValues.ts | 2 +-
.../Routes/Shipping/Utils/computeOrderData.ts | 4 +--
.../Routes/Shipping/Utils/shippingUtils.ts | 32 +++++------------
src/Components/Address/AddressFormFields.tsx | 36 +++++++++++--------
5 files changed, 34 insertions(+), 43 deletions(-)
diff --git a/src/Apps/Auction/Components/Form/AddressFormWithCreditCard.tsx b/src/Apps/Auction/Components/Form/AddressFormWithCreditCard.tsx
index f576b7f2f55..b398cbfc978 100644
--- a/src/Apps/Auction/Components/Form/AddressFormWithCreditCard.tsx
+++ b/src/Apps/Auction/Components/Form/AddressFormWithCreditCard.tsx
@@ -2,6 +2,7 @@ import { Join, Spacer, Text } from "@artsy/palette"
import { CreditCardInput } from "Components/CreditCardInput"
import { AddressFormFields } from "Components/Address/AddressFormFields"
import { useAuctionFormContext } from "Apps/Auction/Hooks/useAuctionFormContext"
+import { AuctionFormValues } from "Apps/Auction/Components/Form/Utils/initialValues"
export const AddressFormWithCreditCard: React.FC> = () => {
const {
@@ -44,7 +45,7 @@ export const AddressFormWithCreditCard: React.FC
-
+ />
)
}
diff --git a/src/Apps/Auction/Components/Form/Utils/initialValues.ts b/src/Apps/Auction/Components/Form/Utils/initialValues.ts
index 1d4a21f1138..16275e8ecb0 100644
--- a/src/Apps/Auction/Components/Form/Utils/initialValues.ts
+++ b/src/Apps/Auction/Components/Form/Utils/initialValues.ts
@@ -5,7 +5,7 @@ export interface AuctionFormValues {
address: Address
agreeToTerms: boolean
creditCard?: boolean
- phoneNumber?: string
+ phoneNumber: string
selectedBid?: string
}
diff --git a/src/Apps/Order/Routes/Shipping/Utils/computeOrderData.ts b/src/Apps/Order/Routes/Shipping/Utils/computeOrderData.ts
index 64b9650e19d..08d2aea97fb 100644
--- a/src/Apps/Order/Routes/Shipping/Utils/computeOrderData.ts
+++ b/src/Apps/Order/Routes/Shipping/Utils/computeOrderData.ts
@@ -2,7 +2,7 @@ import { ShippingContextProps } from "Apps/Order/Routes/Shipping/ShippingContext
import {
FulfillmentType,
PickupValues,
- ShippingAddressFormValues,
+ Address,
addressWithFallbackValues,
matchAddressFields,
} from "Apps/Order/Routes/Shipping/Utils/shippingUtils"
@@ -33,7 +33,7 @@ type SavedFulfillmentData =
| {
fulfillmentType: FulfillmentType.SHIP
isArtsyShipping: boolean
- attributes: ShippingAddressFormValues
+ attributes: Address
selectedSavedAddressID: string | null
}
| null
diff --git a/src/Apps/Order/Routes/Shipping/Utils/shippingUtils.ts b/src/Apps/Order/Routes/Shipping/Utils/shippingUtils.ts
index 4b72e31a6bd..b12b683269e 100644
--- a/src/Apps/Order/Routes/Shipping/Utils/shippingUtils.ts
+++ b/src/Apps/Order/Routes/Shipping/Utils/shippingUtils.ts
@@ -2,7 +2,7 @@ import * as Yup from "yup"
import { AddressVerifiedBy } from "Apps/Order/Components/AddressVerificationFlow"
import { ShippingContext_me$data } from "__generated__/ShippingContext_me.graphql"
import { pick, omitBy, isNil, isEqual } from "lodash"
-import { postalCodeValidator } from "Components/Address/utils"
+import { Address, postalCodeValidator } from "Components/Address/utils"
export enum FulfillmentType {
SHIP = "SHIP",
@@ -29,7 +29,7 @@ export interface PickupValues {
export interface ShipValues {
fulfillmentType: FulfillmentType.SHIP
- attributes: ShippingAddressFormValues
+ attributes: Address
meta: FormMetaValues
}
@@ -46,17 +46,6 @@ interface FormMetaValues {
export type FulfillmentValues = ShipValues | PickupValues
-export interface ShippingAddressFormValues {
- name: string
- phoneNumber: string
- addressLine1: string
- addressLine2: string
- city: string
- region: string
- country: string
- postalCode: string
-}
-
// TODO: Replace with what we use in SettingsShippingAddressForm when we have
// a rich phone input
export const BASIC_PHONE_VALIDATION_SHAPE = {
@@ -80,7 +69,7 @@ export const ADDRESS_VALIDATION_SHAPE = {
country: Yup.string().required("Country is required"),
}
-const ORDER_EMPTY_ADDRESS: ShippingAddressFormValues = {
+const ORDER_EMPTY_ADDRESS: Address = {
name: "",
phoneNumber: "",
addressLine1: "",
@@ -92,22 +81,17 @@ const ORDER_EMPTY_ADDRESS: ShippingAddressFormValues = {
}
export const onlyAddressValues = (values: any) => {
- return pick(
- values,
- Object.keys(ORDER_EMPTY_ADDRESS)
- )
+ return pick(values, Object.keys(ORDER_EMPTY_ADDRESS))
}
/**
* 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.
+ * a SavedAddress from relay to a Address object.
*/
-export const addressWithFallbackValues = (
- address: any
-): ShippingAddressFormValues => ({
+export const addressWithFallbackValues = (address: any): Address => ({
...ORDER_EMPTY_ADDRESS,
- ...omitBy(onlyAddressValues(address), isNil),
+ ...omitBy(onlyAddressValues(address), isNil),
})
export type SavedAddressType = NonNullable<
@@ -189,7 +173,7 @@ export const getInitialShippingValues = (
export const matchAddressFields = (...addressPair: [object, object]) => {
const [a1, a2] = addressPair.map(a => addressWithFallbackValues(a))
- const fields: Array = [
+ const fields: Array = [
"addressLine1",
"addressLine2",
"city",
diff --git a/src/Components/Address/AddressFormFields.tsx b/src/Components/Address/AddressFormFields.tsx
index 42d11095957..06f31876fb3 100644
--- a/src/Components/Address/AddressFormFields.tsx
+++ b/src/Components/Address/AddressFormFields.tsx
@@ -11,12 +11,7 @@ interface FormikContextWithAddress {
phoneNumber: string
}
-const useFormContext = () => {
- const context = useFormikContext()
- return context
-}
-
-export const AddressFormFields = () => {
+export const AddressFormFields = () => {
const {
handleChange,
handleBlur,
@@ -25,7 +20,16 @@ export const AddressFormFields = () => {
touched,
setValues,
setFieldValue,
- } = useFormContext()
+ } = useFormikContext()
+
+ // Formik types don't understand our specific nested structure
+ // so we need to cast these to what we know to be the correct types
+ const touchedAddress = touched.address as
+ | Partial>
+ | undefined
+ const errorsAddress = errors.address as
+ | Partial>
+ | undefined
const { contextPageOwnerId, contextPageOwnerType } = useAnalyticsContext()
@@ -47,7 +51,7 @@ export const AddressFormFields = () => {
value={values.address?.name}
onChange={handleChange}
onBlur={handleBlur}
- error={touched.address?.name && errors.address?.name}
+ error={touchedAddress?.name && errorsAddress?.name}
required
/>
@@ -61,7 +65,7 @@ export const AddressFormFields = () => {
value={values.address.country}
onChange={handleChange}
onBlur={handleBlur}
- error={touched.address?.country && errors.address?.country}
+ error={touchedAddress?.country && errorsAddress?.country}
required
/>
@@ -96,7 +100,7 @@ export const AddressFormFields = () => {
},
})
}}
- error={touched.address?.addressLine1 && errors.address?.addressLine1}
+ error={touchedAddress?.addressLine1 && errorsAddress?.addressLine1}
onClear={() => {
setFieldValue("address.addressLine1", "")
}}
@@ -112,7 +116,7 @@ export const AddressFormFields = () => {
value={values.address?.addressLine2}
onChange={handleChange}
onBlur={handleBlur}
- error={touched.address?.addressLine2 && errors.address?.addressLine2}
+ error={touchedAddress?.addressLine2 && errorsAddress?.addressLine2}
/>
@@ -125,7 +129,7 @@ export const AddressFormFields = () => {
value={values.address?.city}
onChange={handleChange}
onBlur={handleBlur}
- error={touched.address?.city && errors.address?.city}
+ error={touchedAddress?.city && errorsAddress?.city}
required
/>
@@ -139,7 +143,7 @@ export const AddressFormFields = () => {
value={values.address?.region}
onChange={handleChange}
onBlur={handleBlur}
- error={touched.address?.region && errors.address?.region}
+ error={touchedAddress?.region && errorsAddress?.region}
required
/>
@@ -153,7 +157,7 @@ export const AddressFormFields = () => {
value={values.address?.postalCode}
onChange={handleChange}
onBlur={handleBlur}
- error={touched.address?.postalCode && errors.address?.postalCode}
+ error={touchedAddress?.postalCode && errorsAddress?.postalCode}
required
/>
@@ -169,7 +173,9 @@ export const AddressFormFields = () => {
value={values.phoneNumber}
onChange={handleChange}
onBlur={handleBlur}
- error={touched.phoneNumber && errors.phoneNumber}
+ error={
+ touched.phoneNumber && (errors.phoneNumber as string | undefined)
+ }
required
/>
From 4f3c9ccb4627e6cef9392d66e3fed38cd5136490 Mon Sep 17 00:00:00 2001
From: Erik <9088720+erikdstock@users.noreply.github.com>
Date: Fri, 22 Nov 2024 12:46:47 -0600
Subject: [PATCH 05/13] port old enzyme test to new form
---
.../__tests__/AddressForm.jest.enzyme.tsx | 43 -------------
.../Components/AddressFormWithCreditCard.tsx | 5 +-
src/Components/Address/AddressFormFields.tsx | 12 +++-
.../__tests__/AddressFormFields.jest.tsx | 62 +++++++++++++++++++
4 files changed, 75 insertions(+), 47 deletions(-)
delete mode 100644 src/Apps/Auction/Components/Form/__tests__/AddressForm.jest.enzyme.tsx
create mode 100644 src/Components/Address/__tests__/AddressFormFields.jest.tsx
diff --git a/src/Apps/Auction/Components/Form/__tests__/AddressForm.jest.enzyme.tsx b/src/Apps/Auction/Components/Form/__tests__/AddressForm.jest.enzyme.tsx
deleted file mode 100644
index aca7df7b439..00000000000
--- a/src/Apps/Auction/Components/Form/__tests__/AddressForm.jest.enzyme.tsx
+++ /dev/null
@@ -1,43 +0,0 @@
-import { mount } from "enzyme"
-import { AddressForm } from "Apps/Auction/Components/Form/AddressForm"
-
-// TODO: Migrate these tests to new Components/Address/AddressFormFields.jest.tsx file
-jest.mock("Apps/Auction/Hooks/useAuctionFormContext", () => ({
- useAuctionFormContext: () => {
- return {
- handleChange: jest.fn(),
- handleBlur: jest.fn(),
- errors: {},
- values: {
- address: {},
- },
- touched: {},
- }
- },
-}))
-
-describe("AddressForm", () => {
- const getWrapper = () => {
- return mount()
- }
-
- it("renders the correct components", () => {
- const wrapper = getWrapper()
- expect(wrapper.find("CountrySelect")).toHaveLength(1)
-
- const text = wrapper.text()
-
- ;[
- "Full Name",
- "Country",
- "ZIP/Postal code",
- "Street address",
- "Apt, floor, suite, etc. (optional)",
- "City",
- "State, region or province",
- "Phone number",
- ].forEach(label => {
- expect(text).toContain(label)
- })
- })
-})
diff --git a/src/Apps/Invoice/Components/AddressFormWithCreditCard.tsx b/src/Apps/Invoice/Components/AddressFormWithCreditCard.tsx
index f836349b9e1..f5098644cd4 100644
--- a/src/Apps/Invoice/Components/AddressFormWithCreditCard.tsx
+++ b/src/Apps/Invoice/Components/AddressFormWithCreditCard.tsx
@@ -1,7 +1,8 @@
import { Join, Spacer } from "@artsy/palette"
import { CreditCardInput } from "Components/CreditCardInput"
-import { AddressForm } from "./AddressForm"
+import { AddressForm, AddressFormValues } from "./AddressForm"
import { useFormContext } from "Apps/Invoice/Hooks/useFormContext"
+import { AddressFormFields } from "Components/Address/AddressFormFields"
export const AddressFormWithCreditCard: React.FC> = () => {
const {
@@ -36,7 +37,7 @@ export const AddressFormWithCreditCard: React.FC
-
+ />
)
}
diff --git a/src/Components/Address/AddressFormFields.tsx b/src/Components/Address/AddressFormFields.tsx
index 06f31876fb3..e002fbe6927 100644
--- a/src/Components/Address/AddressFormFields.tsx
+++ b/src/Components/Address/AddressFormFields.tsx
@@ -44,7 +44,8 @@ export const AddressFormFields = () => {
() => {
() => {
() => {
flip={false}
required
disableAutocomplete={values.address.region === "AK"}
- name="address.addressLine1"
placeholder="Add street address"
title="Street address"
value={values.address.addressLine1}
@@ -110,6 +113,7 @@ export const AddressFormFields = () => {
() => {
() => {
() => {
() => {
{
+ return {
+ useFormikContext: jest.fn(() => ({
+ handleChange: jest.fn(),
+ handleBlur: jest.fn(),
+ errors: {},
+ values: {
+ address: {},
+ },
+ touched: {},
+ setValues: jest.fn(),
+ setFieldValue: jest.fn(),
+ })),
+ }
+})
+
+const INPUT_EXPECTATIONS = [
+ { label: "Full name", placeholder: "Add full name" },
+ {
+ label: "Country",
+ placeholder: null,
+ },
+ {
+ label: "ZIP/Postal code",
+ placeholder: "Add ZIP/Postal code",
+ },
+ {
+ label: "Street address",
+ placeholder: "Add street address",
+ },
+ {
+ label: "Apt, floor, suite, etc. (optional)",
+ placeholder: "Add apartment, floor, suite, etc.",
+ },
+ { label: "City", placeholder: "Add city" },
+ {
+ label: "State, region or province",
+ placeholder: "Add state, region or province",
+ },
+ { label: "Phone number", placeholder: "Add phone number" },
+]
+
+describe("AddressForm", () => {
+ it("renders the correct components with label & placeholder copy", () => {
+ render()
+
+ INPUT_EXPECTATIONS.forEach(({ label, placeholder }) => {
+ const input = screen.getByLabelText(label)
+ expect(input).toBeInTheDocument()
+ if (placeholder) {
+ // eslint-disable-next-line jest/no-conditional-expect
+ expect(input).toHaveAttribute("placeholder", placeholder)
+ } else {
+ // eslint-disable-next-line jest/no-conditional-expect
+ expect(input).not.toHaveAttribute("placeholder")
+ }
+ })
+ })
+})
From 74c79a5ec8246fa534eee2eadb052cd313542de4 Mon Sep 17 00:00:00 2001
From: Erik <9088720+erikdstock@users.noreply.github.com>
Date: Fri, 22 Nov 2024 21:54:46 -0600
Subject: [PATCH 06/13] add more tests
---
.../Form/AddressFormWithCreditCard.tsx | 6 +-
.../Components/AddressFormWithCreditCard.tsx | 6 +-
src/Components/Address/AddressFormFields.tsx | 61 ++--
.../__tests__/AddressFormFields.jest.tsx | 262 ++++++++++++++----
src/Components/Address/__tests__/utils.ts | 61 ++++
src/Components/Address/utils.ts | 18 ++
6 files changed, 333 insertions(+), 81 deletions(-)
create mode 100644 src/Components/Address/__tests__/utils.ts
diff --git a/src/Apps/Auction/Components/Form/AddressFormWithCreditCard.tsx b/src/Apps/Auction/Components/Form/AddressFormWithCreditCard.tsx
index b398cbfc978..f4425419d63 100644
--- a/src/Apps/Auction/Components/Form/AddressFormWithCreditCard.tsx
+++ b/src/Apps/Auction/Components/Form/AddressFormWithCreditCard.tsx
@@ -4,7 +4,9 @@ import { AddressFormFields } from "Components/Address/AddressFormFields"
import { useAuctionFormContext } from "Apps/Auction/Hooks/useAuctionFormContext"
import { AuctionFormValues } from "Apps/Auction/Components/Form/Utils/initialValues"
-export const AddressFormWithCreditCard: React.FC> = () => {
+export const AddressFormWithCreditCard: React.FC> = () => {
const {
setFieldValue,
setFieldTouched,
@@ -45,7 +47,7 @@ export const AddressFormWithCreditCard: React.FC
- />
+ withPhoneNumber />
)
}
diff --git a/src/Apps/Invoice/Components/AddressFormWithCreditCard.tsx b/src/Apps/Invoice/Components/AddressFormWithCreditCard.tsx
index f5098644cd4..fdb856b9d9d 100644
--- a/src/Apps/Invoice/Components/AddressFormWithCreditCard.tsx
+++ b/src/Apps/Invoice/Components/AddressFormWithCreditCard.tsx
@@ -1,10 +1,12 @@
import { Join, Spacer } from "@artsy/palette"
import { CreditCardInput } from "Components/CreditCardInput"
-import { AddressForm, AddressFormValues } from "./AddressForm"
+import { AddressFormValues } from "./AddressForm"
import { useFormContext } from "Apps/Invoice/Hooks/useFormContext"
import { AddressFormFields } from "Components/Address/AddressFormFields"
-export const AddressFormWithCreditCard: React.FC> = () => {
+export const AddressFormWithCreditCard: React.FC> = () => {
const {
setFieldValue,
setFieldTouched,
diff --git a/src/Components/Address/AddressFormFields.tsx b/src/Components/Address/AddressFormFields.tsx
index e002fbe6927..12d928766f3 100644
--- a/src/Components/Address/AddressFormFields.tsx
+++ b/src/Components/Address/AddressFormFields.tsx
@@ -1,17 +1,34 @@
import { ContextModule } from "@artsy/cohesion"
import { Column, GridColumns, Input } from "@artsy/palette"
import { AddressAutocompleteInput } from "Components/Address/AddressAutocompleteInput"
-import { Address } from "Components/Address/utils"
+import {
+ Address,
+ basicPhoneValidator,
+ yupAddressValidator,
+} from "Components/Address/utils"
import { CountrySelect } from "Components/CountrySelect"
import { useFormikContext } from "formik"
import { useAnalyticsContext } from "System/Hooks/useAnalyticsContext"
interface FormikContextWithAddress {
address: Address
- phoneNumber: string
+ phoneNumber?: string
}
-export const AddressFormFields = () => {
+interface Props {
+ withPhoneNumber?: boolean
+}
+
+export const addressFormFieldsValidator = (
+ args: { withPhoneNumber?: boolean } = {}
+) => ({
+ address: yupAddressValidator,
+ ...(args.withPhoneNumber && { phoneNumber: basicPhoneValidator }),
+})
+
+export const AddressFormFields = (
+ props: Props
+) => {
const {
handleChange,
handleBlur,
@@ -169,24 +186,26 @@ export const AddressFormFields = () => {
/>
-
-
-
+ {props.withPhoneNumber && (
+
+
+
+ )}
)
}
diff --git a/src/Components/Address/__tests__/AddressFormFields.jest.tsx b/src/Components/Address/__tests__/AddressFormFields.jest.tsx
index 1ef8881e774..1a42ff64873 100644
--- a/src/Components/Address/__tests__/AddressFormFields.jest.tsx
+++ b/src/Components/Address/__tests__/AddressFormFields.jest.tsx
@@ -1,62 +1,212 @@
-import { act, render, screen } from "@testing-library/react"
-import { AddressFormFields } from "Components/Address/AddressFormFields"
-
-jest.mock("formik", () => {
- return {
- useFormikContext: jest.fn(() => ({
- handleChange: jest.fn(),
- handleBlur: jest.fn(),
- errors: {},
- values: {
- address: {},
- },
- touched: {},
- setValues: jest.fn(),
- setFieldValue: jest.fn(),
- })),
- }
-})
-
-const INPUT_EXPECTATIONS = [
- { label: "Full name", placeholder: "Add full name" },
- {
- label: "Country",
- placeholder: null,
- },
- {
- label: "ZIP/Postal code",
- placeholder: "Add ZIP/Postal code",
- },
- {
- label: "Street address",
- placeholder: "Add street address",
- },
- {
- label: "Apt, floor, suite, etc. (optional)",
- placeholder: "Add apartment, floor, suite, etc.",
- },
- { label: "City", placeholder: "Add city" },
- {
- label: "State, region or province",
- placeholder: "Add state, region or province",
- },
- { label: "Phone number", placeholder: "Add phone number" },
-]
+import { Button } from "@artsy/palette"
+import { render, screen } from "@testing-library/react"
+import userEvent from "@testing-library/user-event"
+import {
+ ADDRESS_FORM_INPUTS,
+ fillAddressFormFields,
+} from "Components/Address/__tests__/utils"
+import {
+ AddressFormFields,
+ addressFormFieldsValidator,
+} from "Components/Address/AddressFormFields"
+import { Address, emptyAddress } from "Components/Address/utils"
+import { flushPromiseQueue } from "DevTools/flushPromiseQueue"
+import { Formik } from "formik"
+import * as Yup from "yup"
describe("AddressForm", () => {
- it("renders the correct components with label & placeholder copy", () => {
- render()
-
- INPUT_EXPECTATIONS.forEach(({ label, placeholder }) => {
- const input = screen.getByLabelText(label)
- expect(input).toBeInTheDocument()
- if (placeholder) {
- // eslint-disable-next-line jest/no-conditional-expect
- expect(input).toHaveAttribute("placeholder", placeholder)
- } else {
- // eslint-disable-next-line jest/no-conditional-expect
- expect(input).not.toHaveAttribute("placeholder")
+ const mockOnSubmit = jest.fn()
+ beforeEach(() => {
+ mockOnSubmit.mockClear()
+ })
+
+ describe("without phone number input", () => {
+ beforeEach(() => {
+ render(
+
+ {formikBag => (
+ <>
+
+
+ >
+ )}
+
+ )
+ })
+
+ it("renders the correct components with label & placeholder copy", () => {
+ const { phoneNumber, ...remainingInputs } = ADDRESS_FORM_INPUTS
+
+ Object.values(remainingInputs).forEach(({ label, placeholder }) => {
+ const input = screen.getByLabelText(label)
+ expect(input).toBeInTheDocument()
+ if (placeholder) {
+ // eslint-disable-next-line jest/no-conditional-expect
+ expect(input).toHaveAttribute("placeholder", placeholder)
+ } else {
+ // eslint-disable-next-line jest/no-conditional-expect
+ expect(input).not.toHaveAttribute("placeholder")
+ }
+ })
+ })
+
+ it("can be filled out with valid values", async () => {
+ const address: Address = {
+ name: "Mr Art Collector",
+ country: "US",
+ postalCode: "10013",
+ addressLine1: "123 Main St",
+ addressLine2: "Apt 23",
+ city: "New York",
+ region: "NY",
+ }
+
+ await fillAddressFormFields(address)
+
+ await userEvent.click(screen.getByText("Submit"))
+
+ await flushPromiseQueue()
+ expect(mockOnSubmit).toHaveBeenCalledWith(
+ {
+ address: {
+ addressLine1: "123 Main St",
+ addressLine2: "Apt 23",
+ city: "New York",
+ country: "US",
+ name: "Mr Art Collector",
+ phoneNumber: "",
+ postalCode: "10013",
+ region: "NY",
+ },
+ },
+ expect.anything()
+ )
+ })
+
+ it("validates required fields", async () => {
+ await userEvent.click(screen.getByText("Submit"))
+
+ await flushPromiseQueue()
+ expect(mockOnSubmit).not.toHaveBeenCalled()
+
+ screen.getByText("Full name is required")
+ screen.getByText("Country is required")
+ screen.getByText("Street address is required")
+ screen.getByText("City is required")
+ expect(
+ screen.queryByText("Phone number is required")
+ ).not.toBeInTheDocument()
+ expect(screen.queryByText("State is required")).not.toBeInTheDocument()
+ })
+ })
+
+ describe("with phone number input", () => {
+ beforeEach(() => {
+ render(
+
+ {formikBag => (
+ <>
+
+
+ >
+ )}
+
+ )
+ })
+
+ it("renders the correct components with label & placeholder copy", () => {
+ Object.values(ADDRESS_FORM_INPUTS).forEach(({ label, placeholder }) => {
+ const input = screen.getByLabelText(label)
+ expect(input).toBeInTheDocument()
+ if (placeholder) {
+ // eslint-disable-next-line jest/no-conditional-expect
+ expect(input).toHaveAttribute("placeholder", placeholder)
+ } else {
+ // eslint-disable-next-line jest/no-conditional-expect
+ expect(input).not.toHaveAttribute("placeholder")
+ }
+ })
+ })
+
+ it("can be filled out with valid values", async () => {
+ const address: Address = {
+ name: "Mr Art Collector",
+ country: "US",
+ postalCode: "10013",
+ addressLine1: "123 Main St",
+ addressLine2: "Apt 23",
+ city: "New York",
+ region: "NY",
+ phoneNumber: "5555937743",
}
+
+ await fillAddressFormFields(address)
+
+ await userEvent.click(screen.getByText("Submit"))
+
+ await flushPromiseQueue()
+ expect(mockOnSubmit).toHaveBeenCalledWith(
+ {
+ address: {
+ addressLine1: "123 Main St",
+ addressLine2: "Apt 23",
+ city: "New York",
+ country: "US",
+ name: "Mr Art Collector",
+ phoneNumber: "",
+ postalCode: "10013",
+ region: "NY",
+ },
+ phoneNumber: "5555937743",
+ },
+ expect.anything()
+ )
+ })
+
+ it("validates required fields", async () => {
+ await userEvent.click(screen.getByText("Submit"))
+
+ await flushPromiseQueue()
+ expect(mockOnSubmit).not.toHaveBeenCalled()
+
+ screen.getByText("Full name is required")
+ screen.getByText("Country is required")
+ screen.getByText("Street address is required")
+ screen.getByText("City is required")
+ screen.getByText("Phone number is required")
+ expect(screen.queryByText("State is required")).not.toBeInTheDocument()
+ expect(screen.queryByText("ZIP code is required")).not.toBeInTheDocument()
+
+ await fillAddressFormFields({ country: "US" })
+ await userEvent.click(screen.getByText("Submit"))
+
+ await flushPromiseQueue()
+ expect(mockOnSubmit).not.toHaveBeenCalled()
+
+ expect(screen.queryByText("Country is required")).not.toBeInTheDocument()
+ screen.getByText("State is required")
+ screen.getByText("ZIP code is required")
})
})
})
diff --git a/src/Components/Address/__tests__/utils.ts b/src/Components/Address/__tests__/utils.ts
new file mode 100644
index 00000000000..1057eb58bac
--- /dev/null
+++ b/src/Components/Address/__tests__/utils.ts
@@ -0,0 +1,61 @@
+import { screen } from "@testing-library/react"
+import userEvent from "@testing-library/user-event"
+import { Address } from "Components/Address/utils"
+import { flushPromiseQueue } from "DevTools/flushPromiseQueue"
+
+export const ADDRESS_FORM_INPUTS: Record<
+ keyof Address,
+ { label: string; placeholder: string | null }
+> = {
+ name: { label: "Full name", placeholder: "Add full name" },
+ country: {
+ label: "Country",
+ placeholder: null,
+ },
+ postalCode: {
+ label: "ZIP/Postal code",
+ placeholder: "Add ZIP/Postal code",
+ },
+ addressLine1: {
+ label: "Street address",
+ placeholder: "Add street address",
+ },
+ addressLine2: {
+ label: "Apt, floor, suite, etc. (optional)",
+ placeholder: "Add apartment, floor, suite, etc.",
+ },
+ city: { label: "City", placeholder: "Add city" },
+ region: {
+ label: "State, region or province",
+ placeholder: "Add state, region or province",
+ },
+ phoneNumber: { label: "Phone number", placeholder: "Add phone number" },
+}
+
+/**
+ * Fill the `` component's inputs with the provided address
+ */
+export const fillAddressFormFields = async (address: Partial) => {
+ const { country, phoneNumber, ...defaultTextInputs } = address
+
+ if (country) {
+ const countrySelect = await screen.findByLabelText(
+ ADDRESS_FORM_INPUTS.country.label
+ )
+ await userEvent.selectOptions(countrySelect, [country])
+ await flushPromiseQueue()
+ }
+
+ if (phoneNumber) {
+ const phoneNumberInput = await screen.findByLabelText(
+ ADDRESS_FORM_INPUTS.phoneNumber.label
+ )
+ await userEvent.paste(phoneNumberInput, phoneNumber)
+ }
+ await Promise.all(
+ Object.entries(defaultTextInputs).map(async ([key, value]) => {
+ const input = await screen.findByLabelText(ADDRESS_FORM_INPUTS[key].label)
+ await userEvent.paste(input, value)
+ })
+ )
+}
diff --git a/src/Components/Address/utils.ts b/src/Components/Address/utils.ts
index e6af58ffdaa..0bb7dc83f2d 100644
--- a/src/Components/Address/utils.ts
+++ b/src/Components/Address/utils.ts
@@ -36,6 +36,10 @@ export const toStripeAddress = (address: Address): CreateTokenCardData => {
}
}
+export const basicPhoneValidator = Yup.string()
+ .required("Phone number is required")
+ .matches(/^[+\-\(\)\d\s]+$/, "Please enter a valid phone number")
+
export const yupPhoneValidator = Yup.string()
.required("Phone Number is required")
.test({
@@ -65,3 +69,17 @@ export const postalCodeValidator = Yup.string().when("country", {
otherwise: Yup.string(),
}),
})
+
+export const yupAddressValidator = Yup.object().shape({
+ name: Yup.string().required("Full name is required"),
+ 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"),
+})
From d9ee50c1fb371508e1b3d23e7e13bb6a26f6cf2f Mon Sep 17 00:00:00 2001
From: Erik <9088720+erikdstock@users.noreply.github.com>
Date: Fri, 22 Nov 2024 22:02:16 -0600
Subject: [PATCH 07/13] implement no-phone invoice address form
---
src/Apps/Invoice/Components/InvoicePaymentForm.tsx | 8 +++++++-
src/Components/Address/AddressFormFields.tsx | 6 +++---
2 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/src/Apps/Invoice/Components/InvoicePaymentForm.tsx b/src/Apps/Invoice/Components/InvoicePaymentForm.tsx
index 5d229d5a292..e8161eefab6 100644
--- a/src/Apps/Invoice/Components/InvoicePaymentForm.tsx
+++ b/src/Apps/Invoice/Components/InvoicePaymentForm.tsx
@@ -2,6 +2,7 @@ import { Button, Spacer } from "@artsy/palette"
import { AddressFormValues } from "Apps/Invoice/Components/AddressForm"
import { AddressFormWithCreditCard } from "Apps/Invoice/Components/AddressFormWithCreditCard"
import { useCreateTokenAndSubmit } from "Apps/Invoice/Hooks/useCreateTokenAndSubmit"
+import { addressFormFieldsValidator } from "Components/Address/AddressFormFields"
import { emptyAddress } from "Components/Address/utils"
import { Formik, Form } from "formik"
import { useRouter } from "System/Hooks/useRouter"
@@ -12,7 +13,9 @@ export interface InvoicePaymentFormProps {
amountMinor: number
}
-export const InvoicePaymentForm: React.FC> = props => {
+export const InvoicePaymentForm: React.FC> = props => {
const { match, router } = useRouter()
const token = match.params.token
const invoiceRoute = `/invoice/${token}`
@@ -28,6 +31,9 @@ export const InvoicePaymentForm: React.FC
onSubmit={handleSubmit}
initialValues={{ address: emptyAddress, creditCard: false }}
+ validationSchema={{
+ ...addressFormFieldsValidator({ withPhoneNumber: false }),
+ }}
>
{({ isSubmitting, isValid }) => {
return (
diff --git a/src/Components/Address/AddressFormFields.tsx b/src/Components/Address/AddressFormFields.tsx
index 12d928766f3..1f032ded1df 100644
--- a/src/Components/Address/AddressFormFields.tsx
+++ b/src/Components/Address/AddressFormFields.tsx
@@ -19,9 +19,9 @@ interface Props {
withPhoneNumber?: boolean
}
-export const addressFormFieldsValidator = (
- args: { withPhoneNumber?: boolean } = {}
-) => ({
+export const addressFormFieldsValidator = (args: {
+ withPhoneNumber: boolean
+}) => ({
address: yupAddressValidator,
...(args.withPhoneNumber && { phoneNumber: basicPhoneValidator }),
})
From b4a2c84619d3db9ac518e976372fe983d380e5b8 Mon Sep 17 00:00:00 2001
From: Erik <9088720+erikdstock@users.noreply.github.com>
Date: Fri, 22 Nov 2024 22:14:23 -0600
Subject: [PATCH 08/13] revert changes to shipping route types for now
---
.../Routes/Shipping/Utils/computeOrderData.ts | 4 +--
.../Routes/Shipping/Utils/shippingUtils.ts | 32 ++++++++++++++-----
2 files changed, 26 insertions(+), 10 deletions(-)
diff --git a/src/Apps/Order/Routes/Shipping/Utils/computeOrderData.ts b/src/Apps/Order/Routes/Shipping/Utils/computeOrderData.ts
index 08d2aea97fb..64b9650e19d 100644
--- a/src/Apps/Order/Routes/Shipping/Utils/computeOrderData.ts
+++ b/src/Apps/Order/Routes/Shipping/Utils/computeOrderData.ts
@@ -2,7 +2,7 @@ import { ShippingContextProps } from "Apps/Order/Routes/Shipping/ShippingContext
import {
FulfillmentType,
PickupValues,
- Address,
+ ShippingAddressFormValues,
addressWithFallbackValues,
matchAddressFields,
} from "Apps/Order/Routes/Shipping/Utils/shippingUtils"
@@ -33,7 +33,7 @@ type SavedFulfillmentData =
| {
fulfillmentType: FulfillmentType.SHIP
isArtsyShipping: boolean
- attributes: Address
+ attributes: ShippingAddressFormValues
selectedSavedAddressID: string | null
}
| null
diff --git a/src/Apps/Order/Routes/Shipping/Utils/shippingUtils.ts b/src/Apps/Order/Routes/Shipping/Utils/shippingUtils.ts
index b12b683269e..4b72e31a6bd 100644
--- a/src/Apps/Order/Routes/Shipping/Utils/shippingUtils.ts
+++ b/src/Apps/Order/Routes/Shipping/Utils/shippingUtils.ts
@@ -2,7 +2,7 @@ import * as Yup from "yup"
import { AddressVerifiedBy } from "Apps/Order/Components/AddressVerificationFlow"
import { ShippingContext_me$data } from "__generated__/ShippingContext_me.graphql"
import { pick, omitBy, isNil, isEqual } from "lodash"
-import { Address, postalCodeValidator } from "Components/Address/utils"
+import { postalCodeValidator } from "Components/Address/utils"
export enum FulfillmentType {
SHIP = "SHIP",
@@ -29,7 +29,7 @@ export interface PickupValues {
export interface ShipValues {
fulfillmentType: FulfillmentType.SHIP
- attributes: Address
+ attributes: ShippingAddressFormValues
meta: FormMetaValues
}
@@ -46,6 +46,17 @@ interface FormMetaValues {
export type FulfillmentValues = ShipValues | PickupValues
+export interface ShippingAddressFormValues {
+ name: string
+ phoneNumber: string
+ addressLine1: string
+ addressLine2: string
+ city: string
+ region: string
+ country: string
+ postalCode: string
+}
+
// TODO: Replace with what we use in SettingsShippingAddressForm when we have
// a rich phone input
export const BASIC_PHONE_VALIDATION_SHAPE = {
@@ -69,7 +80,7 @@ export const ADDRESS_VALIDATION_SHAPE = {
country: Yup.string().required("Country is required"),
}
-const ORDER_EMPTY_ADDRESS: Address = {
+const ORDER_EMPTY_ADDRESS: ShippingAddressFormValues = {
name: "",
phoneNumber: "",
addressLine1: "",
@@ -81,17 +92,22 @@ const ORDER_EMPTY_ADDRESS: Address = {
}
export const onlyAddressValues = (values: any) => {
- return pick(values, Object.keys(ORDER_EMPTY_ADDRESS))
+ return pick(
+ values,
+ Object.keys(ORDER_EMPTY_ADDRESS)
+ )
}
/**
* 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 Address object.
+ * a SavedAddress from relay to a ShippingAddressFormValues object.
*/
-export const addressWithFallbackValues = (address: any): Address => ({
+export const addressWithFallbackValues = (
+ address: any
+): ShippingAddressFormValues => ({
...ORDER_EMPTY_ADDRESS,
- ...omitBy(onlyAddressValues(address), isNil),
+ ...omitBy(onlyAddressValues(address), isNil),
})
export type SavedAddressType = NonNullable<
@@ -173,7 +189,7 @@ export const getInitialShippingValues = (
export const matchAddressFields = (...addressPair: [object, object]) => {
const [a1, a2] = addressPair.map(a => addressWithFallbackValues(a))
- const fields: Array = [
+ const fields: Array = [
"addressLine1",
"addressLine2",
"city",
From 5e26bfb30056d1636147b51be95544c4bd4e73df Mon Sep 17 00:00:00 2001
From: Erik <9088720+erikdstock@users.noreply.github.com>
Date: Fri, 22 Nov 2024 22:26:56 -0600
Subject: [PATCH 09/13] cleanup
---
src/Apps/Invoice/Components/InvoicePaymentForm.tsx | 7 ++++---
src/Components/Address/AddressAutocompleteInput.tsx | 1 +
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/src/Apps/Invoice/Components/InvoicePaymentForm.tsx b/src/Apps/Invoice/Components/InvoicePaymentForm.tsx
index e8161eefab6..4347d0872aa 100644
--- a/src/Apps/Invoice/Components/InvoicePaymentForm.tsx
+++ b/src/Apps/Invoice/Components/InvoicePaymentForm.tsx
@@ -1,10 +1,11 @@
+import { Formik, Form } from "formik"
+import * as Yup from "yup"
import { Button, Spacer } from "@artsy/palette"
import { AddressFormValues } from "Apps/Invoice/Components/AddressForm"
import { AddressFormWithCreditCard } from "Apps/Invoice/Components/AddressFormWithCreditCard"
import { useCreateTokenAndSubmit } from "Apps/Invoice/Hooks/useCreateTokenAndSubmit"
import { addressFormFieldsValidator } from "Components/Address/AddressFormFields"
import { emptyAddress } from "Components/Address/utils"
-import { Formik, Form } from "formik"
import { useRouter } from "System/Hooks/useRouter"
export interface InvoicePaymentFormProps {
@@ -31,9 +32,9 @@ export const InvoicePaymentForm: React.FC
onSubmit={handleSubmit}
initialValues={{ address: emptyAddress, creditCard: false }}
- validationSchema={{
+ validationSchema={Yup.object().shape({
...addressFormFieldsValidator({ withPhoneNumber: false }),
- }}
+ })}
>
{({ isSubmitting, isValid }) => {
return (
diff --git a/src/Components/Address/AddressAutocompleteInput.tsx b/src/Components/Address/AddressAutocompleteInput.tsx
index bf45ba083bd..4c3e6ebcf22 100644
--- a/src/Components/Address/AddressAutocompleteInput.tsx
+++ b/src/Components/Address/AddressAutocompleteInput.tsx
@@ -312,6 +312,7 @@ export const AddressAutocompleteInput = ({
onChange={onChange}
error={error}
data-testid={dataTestId}
+ required={!!autocompleteProps.required}
/>
)
}
From defdffee340aea81ec017c2b4c0875a72bcdaf35 Mon Sep 17 00:00:00 2001
From: Erik <9088720+erikdstock@users.noreply.github.com>
Date: Fri, 22 Nov 2024 22:55:43 -0600
Subject: [PATCH 10/13] fix tests
---
.../__tests__/AddressFormWithCreditCard.jest.enzyme.tsx | 6 +++---
.../Hooks/__tests__/useCreateTokenAndSubmit.jest.tsx | 9 ++++++---
.../Hooks/__tests__/useCreateTokenAndSubmit.jest.ts | 7 ++++---
3 files changed, 13 insertions(+), 9 deletions(-)
diff --git a/src/Apps/Auction/Components/Form/__tests__/AddressFormWithCreditCard.jest.enzyme.tsx b/src/Apps/Auction/Components/Form/__tests__/AddressFormWithCreditCard.jest.enzyme.tsx
index c402353a460..13a00652304 100644
--- a/src/Apps/Auction/Components/Form/__tests__/AddressFormWithCreditCard.jest.enzyme.tsx
+++ b/src/Apps/Auction/Components/Form/__tests__/AddressFormWithCreditCard.jest.enzyme.tsx
@@ -7,8 +7,8 @@ jest.mock("Apps/Auction/Hooks/useAuctionFormContext")
jest.mock("Components/CreditCardInput", () => ({
CreditCardInput: () => null,
}))
-jest.mock("../AddressForm", () => ({
- AddressForm: () => null,
+jest.mock("Components/Address/AddressFormFields", () => ({
+ AddressFormFields: () => null,
}))
describe("AddressFormWithCreditCard", () => {
@@ -35,7 +35,7 @@ describe("AddressFormWithCreditCard", () => {
it("renders correct components", () => {
const wrapper = getWrapper()
expect(wrapper.find("CreditCardInput")).toHaveLength(1)
- expect(wrapper.find("AddressForm")).toHaveLength(1)
+ expect(wrapper.find("AddressFormFields")).toHaveLength(1)
})
describe("credit card error handling", () => {
diff --git a/src/Apps/Auction/Hooks/__tests__/useCreateTokenAndSubmit.jest.tsx b/src/Apps/Auction/Hooks/__tests__/useCreateTokenAndSubmit.jest.tsx
index 4e3da4634e5..c8949ad25d8 100644
--- a/src/Apps/Auction/Hooks/__tests__/useCreateTokenAndSubmit.jest.tsx
+++ b/src/Apps/Auction/Hooks/__tests__/useCreateTokenAndSubmit.jest.tsx
@@ -11,9 +11,10 @@ import { flushPromiseQueue } from "DevTools/flushPromiseQueue"
import { useAuctionTracking } from "Apps/Auction/Hooks/useAuctionTracking"
import { useRefreshUserData } from "Apps/Auction/Queries/useRefreshUserData"
-jest.mock("Components/Address/AddressForm", () => ({
- toStripeAddress: jest.fn(),
-}))
+jest.mock("Components/Address/utils", () => {
+ const actual = jest.requireActual("Components/Address/utils")
+ return { ...actual, toStripeAddress: jest.fn() }
+})
jest.mock("Apps/Auction/Queries/useRefreshUserData")
jest.mock("Apps/Auction/Hooks/useAuctionTracking")
@@ -39,6 +40,7 @@ describe("useCreateTokenAndSubmit", () => {
const values = {
phoneNumber: "+1 (123) 456-7890",
+ address: {},
}
const helpers = {
@@ -252,6 +254,7 @@ describe("useCreateTokenAndSubmit", () => {
it("sets submitting to false at the very end", async () => {
await setupHook()
await flushPromiseQueue()
+
expect(helpers.setSubmitting).toHaveBeenCalledWith(false)
})
})
diff --git a/src/Apps/Invoice/Hooks/__tests__/useCreateTokenAndSubmit.jest.ts b/src/Apps/Invoice/Hooks/__tests__/useCreateTokenAndSubmit.jest.ts
index 40d6550d36d..b742ef9f5ab 100644
--- a/src/Apps/Invoice/Hooks/__tests__/useCreateTokenAndSubmit.jest.ts
+++ b/src/Apps/Invoice/Hooks/__tests__/useCreateTokenAndSubmit.jest.ts
@@ -8,9 +8,10 @@ import {
UseCreateTokenAndSubmitProps,
} from "Apps/Invoice/Hooks/useCreateTokenAndSubmit"
-jest.mock("Components/Address/AddressForm", () => ({
- toStripeAddress: jest.fn(),
-}))
+jest.mock("Components/Address/utils", () => {
+ const actual = jest.requireActual("Components/Address/utils")
+ return { ...actual, toStripeAddress: jest.fn() }
+})
jest.mock("Apps/Invoice/Hooks/useMakeInvoicePayment")
From 13be370981698e1e24e9e92181215709fdba191c Mon Sep 17 00:00:00 2001
From: Erik <9088720+erikdstock@users.noreply.github.com>
Date: Sat, 23 Nov 2024 08:47:17 -0600
Subject: [PATCH 11/13] delete unused AddressForm
---
src/Apps/Invoice/Components/AddressForm.tsx | 113 ------------------
.../Components/AddressFormWithCreditCard.tsx | 6 +-
.../Invoice/Components/InvoicePaymentForm.tsx | 2 +-
.../Invoice/Hooks/useCreateTokenAndSubmit.ts | 2 +-
src/Apps/Invoice/Hooks/useFormContext.ts | 2 +-
5 files changed, 8 insertions(+), 117 deletions(-)
delete mode 100644 src/Apps/Invoice/Components/AddressForm.tsx
diff --git a/src/Apps/Invoice/Components/AddressForm.tsx b/src/Apps/Invoice/Components/AddressForm.tsx
deleted file mode 100644
index ec323e93017..00000000000
--- a/src/Apps/Invoice/Components/AddressForm.tsx
+++ /dev/null
@@ -1,113 +0,0 @@
-import { Column, GridColumns, Input } from "@artsy/palette"
-import { CountrySelect } from "Components/CountrySelect"
-import { Address } from "Components/Address/utils"
-import { useFormContext } from "Apps/Invoice/Hooks/useFormContext"
-
-export interface AddressFormValues {
- address: Address
- creditCard?: boolean
-}
-
-export const AddressForm = () => {
- const { handleChange, handleBlur, errors, values, touched } = useFormContext()
-
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- )
-}
diff --git a/src/Apps/Invoice/Components/AddressFormWithCreditCard.tsx b/src/Apps/Invoice/Components/AddressFormWithCreditCard.tsx
index fdb856b9d9d..5ae4aaea9ac 100644
--- a/src/Apps/Invoice/Components/AddressFormWithCreditCard.tsx
+++ b/src/Apps/Invoice/Components/AddressFormWithCreditCard.tsx
@@ -1,9 +1,13 @@
import { Join, Spacer } from "@artsy/palette"
import { CreditCardInput } from "Components/CreditCardInput"
-import { AddressFormValues } from "./AddressForm"
import { useFormContext } from "Apps/Invoice/Hooks/useFormContext"
import { AddressFormFields } from "Components/Address/AddressFormFields"
+import { Address } from "Components/Address/utils"
+export interface AddressFormValues {
+ address: Address
+ creditCard?: boolean
+}
export const AddressFormWithCreditCard: React.FC> = () => {
diff --git a/src/Apps/Invoice/Components/InvoicePaymentForm.tsx b/src/Apps/Invoice/Components/InvoicePaymentForm.tsx
index 4347d0872aa..04dcba2b851 100644
--- a/src/Apps/Invoice/Components/InvoicePaymentForm.tsx
+++ b/src/Apps/Invoice/Components/InvoicePaymentForm.tsx
@@ -1,7 +1,7 @@
import { Formik, Form } from "formik"
import * as Yup from "yup"
import { Button, Spacer } from "@artsy/palette"
-import { AddressFormValues } from "Apps/Invoice/Components/AddressForm"
+import { AddressFormValues } from "Apps/Invoice/Components/AddressFormWithCreditCard"
import { AddressFormWithCreditCard } from "Apps/Invoice/Components/AddressFormWithCreditCard"
import { useCreateTokenAndSubmit } from "Apps/Invoice/Hooks/useCreateTokenAndSubmit"
import { addressFormFieldsValidator } from "Components/Address/AddressFormFields"
diff --git a/src/Apps/Invoice/Hooks/useCreateTokenAndSubmit.ts b/src/Apps/Invoice/Hooks/useCreateTokenAndSubmit.ts
index 7444ac6afaa..f9d1a9635bd 100644
--- a/src/Apps/Invoice/Hooks/useCreateTokenAndSubmit.ts
+++ b/src/Apps/Invoice/Hooks/useCreateTokenAndSubmit.ts
@@ -12,7 +12,7 @@ import {
stripeNotLoadedErrorMessage,
} from "Apps/Auction/Components/Form/Utils/errorMessages"
import { FormikHelpers } from "formik"
-import { AddressFormValues } from "Apps/Invoice/Components/AddressForm"
+import { AddressFormValues } from "Apps/Invoice/Components/AddressFormWithCreditCard"
import { useMakeInvoicePayment } from "Apps/Invoice/Hooks/useMakeInvoicePayment"
import { InvoicePaymentFormProps } from "Apps/Invoice/Components/InvoicePaymentForm"
import { useToasts } from "@artsy/palette"
diff --git a/src/Apps/Invoice/Hooks/useFormContext.ts b/src/Apps/Invoice/Hooks/useFormContext.ts
index a38a23d8422..bbb4f8fe135 100644
--- a/src/Apps/Invoice/Hooks/useFormContext.ts
+++ b/src/Apps/Invoice/Hooks/useFormContext.ts
@@ -1,4 +1,4 @@
-import { AddressFormValues } from "Apps/Invoice/Components/AddressForm"
+import { AddressFormValues } from "Apps/Invoice/Components/AddressFormWithCreditCard"
import { useFormikContext } from "formik"
export const useFormContext = () => {
From 8a9f7f91453d088b4119ee2c4d3abc9f93939ebd Mon Sep 17 00:00:00 2001
From: Erik <9088720+erikdstock@users.noreply.github.com>
Date: Mon, 25 Nov 2024 10:02:09 -0600
Subject: [PATCH 12/13] cleanup
---
src/Components/Address/__tests__/AddressFormFields.jest.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/Components/Address/__tests__/AddressFormFields.jest.tsx b/src/Components/Address/__tests__/AddressFormFields.jest.tsx
index 1a42ff64873..aacac55892f 100644
--- a/src/Components/Address/__tests__/AddressFormFields.jest.tsx
+++ b/src/Components/Address/__tests__/AddressFormFields.jest.tsx
@@ -14,7 +14,7 @@ import { flushPromiseQueue } from "DevTools/flushPromiseQueue"
import { Formik } from "formik"
import * as Yup from "yup"
-describe("AddressForm", () => {
+describe("AddressFormFields", () => {
const mockOnSubmit = jest.fn()
beforeEach(() => {
mockOnSubmit.mockClear()
From f1202358d880f0c9473940fa856499f2c6fc0d5d Mon Sep 17 00:00:00 2001
From: Erik <9088720+erikdstock@users.noreply.github.com>
Date: Mon, 25 Nov 2024 17:24:35 -0600
Subject: [PATCH 13/13] cleanup + docs
---
src/Components/Address/AddressFormFields.tsx | 43 ++++++++++++++++++--
1 file changed, 40 insertions(+), 3 deletions(-)
diff --git a/src/Components/Address/AddressFormFields.tsx b/src/Components/Address/AddressFormFields.tsx
index 1f032ded1df..b5b3639f817 100644
--- a/src/Components/Address/AddressFormFields.tsx
+++ b/src/Components/Address/AddressFormFields.tsx
@@ -19,13 +19,50 @@ interface Props {
withPhoneNumber?: boolean
}
-export const addressFormFieldsValidator = (args: {
- withPhoneNumber: boolean
-}) => ({
+/**
+ * Validation schema for address form fields. Arguments match the
+ * component - e.g. to include phone number validation
+ * @example
+ * ```tsx
+ * const validationSchema = yup.object().shape({
+ * ...addressFormFieldsValidator({ withPhoneNumber: true }),
+ * saveAddress: boolean
+ * })
+ * // later...
+ *
+ *
+ * withPhoneNumber />
+ * ```
+ */
+export const addressFormFieldsValidator = (args: Props = {}) => ({
address: yupAddressValidator,
...(args.withPhoneNumber && { phoneNumber: basicPhoneValidator }),
})
+/**
+ * Form fields for collecting address information. This component is intended
+ * to be used within a Formik form, and the `Values` interface of that form
+ * should fulfill a `FormikContextWithAddress` interface:
+ * - the relevant nested `address` object
+ * - plus a `phoneNumber` if the `withPhoneNumber` prop is passed
+ * For a composable validation schema, see `addressFormFieldsValidator()`.
+ *
+ * @example
+ * ```tsx
+ * interface MyFormValues {
+ * address: Address
+ * phoneNumber: string
+ * saveAddress: boolean
+ * }
+ *
+ * {...otherFormikProps}>
+ * withPhoneNumber />
+ *
+ *
+ */
export const AddressFormFields = (
props: Props
) => {