From 704cc0487e11340ab89ded053b9154b810f5c2c3 Mon Sep 17 00:00:00 2001 From: Caleb Cox Date: Tue, 5 Nov 2024 13:10:06 -0600 Subject: [PATCH 1/2] Make address street optional --- .../AddAddressModal/AddAddressModal.tsx | 22 ++++++------ .../AddAddressModal/createAddressSchema.ts | 19 ----------- .../EditContactAddressModal.tsx | 18 +++++----- .../updateAddressSchema.ts | 20 ----------- .../Mailing/addressSchema.ts | 34 +++++++++++++++++++ 5 files changed, 54 insertions(+), 59 deletions(-) delete mode 100644 src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/createAddressSchema.ts delete mode 100644 src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/updateAddressSchema.ts create mode 100644 src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/addressSchema.ts diff --git a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/AddAddressModal.tsx b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/AddAddressModal.tsx index b7a7a930f..21fd9cf64 100644 --- a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/AddAddressModal.tsx +++ b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/AddAddressModal.tsx @@ -22,7 +22,6 @@ import { CancelButton, SubmitButton, } from 'src/components/common/Modal/ActionButtons/ActionButtons'; -import { AddressCreateInput } from 'src/graphql/types.generated'; import { useUpdateCache } from 'src/hooks/useUpdateCache'; import Modal from '../../../../../common/Modal/Modal'; import { @@ -35,8 +34,8 @@ import { } from '../AddressLocation'; import { useSetContactPrimaryAddressMutation } from '../SetPrimaryAddress.generated'; import { StreetAutocomplete } from '../StreetAutocomplete/StreetAutocomplete'; +import { AddressSchema, addressSchema } from '../addressSchema'; import { useCreateContactAddressMutation } from './CreateContactAddress.generated'; -import { createAddressSchema } from './createAddressSchema'; const ContactEditContainer = styled(Box)(({ theme }) => ({ display: 'flex', @@ -78,21 +77,24 @@ export const AddAddressModal: React.FC = ({ const onSubmit = async ({ primaryMailingAddress, + street, ...attributes - }: Omit & { - primaryMailingAddress: boolean; - }) => { + }: AddressSchema) => { const response = await createContactAddress({ variables: { accountListId, - attributes, + attributes: { + contactId, + street: street ?? '', + ...attributes, + }, }, update: (cache, { data: createdAddressData }) => { if (handleUpdateCache) { handleUpdateCache(cache, { createAddress: { address: createdAddressData?.createAddress?.address, - contactId: attributes.contactId, + contactId, }, }); } else { @@ -137,14 +139,13 @@ export const AddAddressModal: React.FC = ({ enqueueSnackbar(t('Address added successfully'), { variant: 'success', }); - await handleClose(); + handleClose(); }; return ( = ({ street: '', primaryMailingAddress: true, }} - validationSchema={createAddressSchema} + validationSchema={addressSchema} validateOnMount onSubmit={onSubmit} > @@ -201,7 +202,6 @@ export const AddAddressModal: React.FC = ({ TextFieldProps={{ name: 'street', label: t('Street'), - required: true, fullWidth: true, }} /> diff --git a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/createAddressSchema.ts b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/createAddressSchema.ts deleted file mode 100644 index 0cb663491..000000000 --- a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/createAddressSchema.ts +++ /dev/null @@ -1,19 +0,0 @@ -import * as yup from 'yup'; -import { AddressCreateInput } from 'src/graphql/types.generated'; - -export const createAddressSchema: yup.SchemaOf> = - yup.object({ - contactId: yup.string().required(), - city: yup.string().nullable(), - country: yup.string().nullable(), - historic: yup.boolean().nullable(), - location: yup.string().nullable(), - metroArea: yup.string().nullable(), - postalCode: yup.string().nullable(), - region: yup.string().nullable(), - state: yup.string().nullable(), - street: yup.string().required(), - primaryMailingAddress: yup.boolean().nullable(false), - }); - -export type CreateAddressSchema = yup.InferType; diff --git a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.tsx b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.tsx index 5aa8c634b..cd518145b 100644 --- a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.tsx +++ b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.tsx @@ -26,7 +26,6 @@ import { DeleteButton, SubmitButton, } from 'src/components/common/Modal/ActionButtons/ActionButtons'; -import { AddressUpdateInput } from 'src/graphql/types.generated'; import { useUpdateCache } from 'src/hooks/useUpdateCache'; import { isEditableSource } from 'src/utils/sourceHelper'; import Modal from '../../../../../common/Modal/Modal'; @@ -41,13 +40,13 @@ import { import { ContactMailingFragment } from '../ContactMailing.generated'; import { useSetContactPrimaryAddressMutation } from '../SetPrimaryAddress.generated'; import { StreetAutocomplete } from '../StreetAutocomplete/StreetAutocomplete'; +import { AddressSchema, addressSchema } from '../addressSchema'; import { useDeleteContactAddressMutation, useDonationServicesEmailQuery, useUpdateContactAddressMutation, } from './EditContactAddress.generated'; import { generateEmailBody } from './helpers'; -import { updateAddressSchema } from './updateAddressSchema'; const ContactEditContainer = styled(Box)(({ theme }) => ({ display: 'flex', @@ -95,14 +94,17 @@ export const EditContactAddressModal: React.FC< const onSubmit = async ({ primaryMailingAddress, + street, ...attributes - }: Omit & { - primaryMailingAddress: boolean; - }) => { + }: AddressSchema) => { await updateContactAddress({ variables: { accountListId, - attributes, + attributes: { + id: address.id, + street: street ?? '', + ...attributes, + }, }, }); // updateContactAddress doesn't set support setting the primaryMailingAddress field, so if @@ -183,7 +185,6 @@ export const EditContactAddressModal: React.FC< {({ @@ -280,7 +281,6 @@ export const EditContactAddressModal: React.FC< TextFieldProps={{ name: 'street', label: t('Street'), - required: true, fullWidth: true, }} disabled={editingDisabled} diff --git a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/updateAddressSchema.ts b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/updateAddressSchema.ts deleted file mode 100644 index 567a9896a..000000000 --- a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/updateAddressSchema.ts +++ /dev/null @@ -1,20 +0,0 @@ -import * as yup from 'yup'; -import { AddressUpdateInput } from 'src/graphql/types.generated'; - -export const updateAddressSchema: yup.SchemaOf< - Omit -> = yup.object({ - city: yup.string().nullable(), - country: yup.string().nullable(), - historic: yup.boolean().nullable(), - id: yup.string().required(), - location: yup.string().nullable(), - metroArea: yup.string().nullable(), - postalCode: yup.string().nullable(), - region: yup.string().nullable(), - state: yup.string().nullable(), - street: yup.string().nullable(), - primaryMailingAddress: yup.boolean().nullable(false), -}); - -export type UpdateAddressSchema = yup.InferType; diff --git a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/addressSchema.ts b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/addressSchema.ts new file mode 100644 index 000000000..ca192881c --- /dev/null +++ b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/addressSchema.ts @@ -0,0 +1,34 @@ +import * as yup from 'yup'; + +export const addressSchema = yup.object({ + city: yup.string().nullable(), + country: yup.string().nullable(), + historic: yup.boolean().nullable(), + location: yup.string().nullable(), + metroArea: yup.string().nullable(), + postalCode: yup.string().nullable(), + region: yup.string().nullable(), + state: yup.string().nullable(), + // Formik ignores test functions defined at the object-level, so this needs to be defined on a + // specific field. It doesn't matter which field. + street: yup + .string() + .nullable() + .test( + 'one-field', + () => 'At least one address field must be filled out', + (value, { parent: address }) => + Boolean( + address.city || + address.country || + address.metroArea || + address.postalCode || + address.region || + address.state || + address.street, + ), + ), + primaryMailingAddress: yup.boolean().nullable(false), +}); + +export type AddressSchema = yup.InferType; From 56e645f4ba4f0ad83858687548577f859878aa5a Mon Sep 17 00:00:00 2001 From: Caleb Cox Date: Tue, 5 Nov 2024 13:28:50 -0600 Subject: [PATCH 2/2] Add tests --- .../AddAddressModal/AddAddressModal.test.tsx | 22 ++++++++++ .../EditContactAddressModal.test.tsx | 41 +++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/AddAddressModal.test.tsx b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/AddAddressModal.test.tsx index f5ecb6267..71f7ee82d 100644 --- a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/AddAddressModal.test.tsx +++ b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/AddAddressModal.test.tsx @@ -90,6 +90,28 @@ describe('AddAddressModal', () => { expect(handleClose).toHaveBeenCalled(); }); + it('requires at least one field to be filled', async () => { + const { getByRole } = render( + + + + + + + , + ); + + const saveButton = getByRole('button', { name: 'Save' }); + await waitFor(() => expect(saveButton).toBeDisabled()); + + userEvent.type(getByRole('textbox', { name: 'City' }), 'City'); + await waitFor(() => expect(saveButton).not.toBeDisabled()); + }); + it('should create contact address', async () => { const mutationSpy = jest.fn(); const newStreet = '4321 Neat Street'; diff --git a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.test.tsx b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.test.tsx index 0bcaf3c43..bad110dae 100644 --- a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.test.tsx +++ b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.test.tsx @@ -115,6 +115,47 @@ describe('EditContactAddressModal', () => { expect(handleClose).toHaveBeenCalled(); }); + it('requires at least one field to be filled', async () => { + const { getByRole } = render( + + + + + + + , + ); + + const saveButton = getByRole('button', { name: 'Save' }); + await waitFor(() => expect(saveButton).not.toBeDisabled()); + + userEvent.clear(getByRole('combobox', { name: 'Street' })); + await waitFor(() => expect(saveButton).toBeDisabled()); + + userEvent.type(getByRole('textbox', { name: 'City' }), 'City'); + await waitFor(() => expect(saveButton).not.toBeDisabled()); + }); + it('should edit contact address', async () => { const mutationSpy = jest.fn(); const newStreet = '4321 Neat Street';