Skip to content

Commit

Permalink
Merge pull request #973 from CruGlobal/MPDX-7946-Use-Contact-Mutation…
Browse files Browse the repository at this point in the history
…-When-Adding-Email

MPDX-7946 use contact mutation when adding email
  • Loading branch information
dr-bizz authored Aug 2, 2024
2 parents b77c3de + 5d87678 commit 63b1170
Show file tree
Hide file tree
Showing 8 changed files with 536 additions and 150 deletions.
11 changes: 11 additions & 0 deletions src/components/Tool/FixEmailAddresses/AddEmailAddress.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
mutation EmailAddresses($input: PersonUpdateMutationInput!) {
updatePerson(input: $input) {
person {
emailAddresses {
nodes {
email
}
}
}
}
}
104 changes: 88 additions & 16 deletions src/components/Tool/FixEmailAddresses/EmailValidationForm.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
import { Grid, IconButton, TextField } from '@mui/material';
import { styled } from '@mui/material/styles';
import { Form, Formik } from 'formik';
import { useSnackbar } from 'notistack';
import { useTranslation } from 'react-i18next';
import * as Yup from 'yup';
import * as yup from 'yup';
import { AddIcon } from 'src/components/Contacts/ContactDetails/ContactDetailsTab/StyledComponents';
import { useAccountListId } from 'src/hooks/useAccountListId';
import i18n from 'src/lib/i18n';
import { useEmailAddressesMutation } from './AddEmailAddress.generated';
import { RowWrapper } from './FixEmailAddressPerson';
import {
GetInvalidEmailAddressesDocument,
GetInvalidEmailAddressesQuery,
} from './FixEmailAddresses.generated';

const ContactInputField = styled(TextField, {
shouldForwardProp: (prop) => prop !== 'destroyed',
Expand All @@ -24,16 +32,25 @@ interface EmailValidationFormEmail {
interface EmailValidationFormProps {
index: number;
personId: string;
handleAdd?: (personId: string, email: string) => void;
}

//TODO: Implement during MPDX-7946
const onSubmit = (values, actions) => {
actions.resetForm();
};
const validationSchema = yup.object({
email: yup
.string()
.email(i18n.t('Invalid Email Address Format'))
.required(i18n.t('Please enter a valid email address')),
isPrimary: yup.bool().default(false),
updatedAt: yup.string(),
source: yup.string(),
personId: yup.string(),
isValid: yup.bool().default(false),
});

const EmailValidationForm = ({ personId }: EmailValidationFormProps) => {
const { t } = useTranslation();
const accountListId = useAccountListId();
const [emailAddressesMutation] = useEmailAddressesMutation();
const { enqueueSnackbar } = useSnackbar();

const initialEmail = {
email: '',
Expand All @@ -44,16 +61,71 @@ const EmailValidationForm = ({ personId }: EmailValidationFormProps) => {
isValid: false,
} as EmailValidationFormEmail;

const validationSchema = Yup.object({
email: Yup.string()
.email(t('Invalid Email Address Format'))
.required('Please enter a valid email address'),
isPrimary: Yup.bool().default(false),
updatedAt: Yup.string(),
source: Yup.string(),
personId: Yup.string(),
isValid: Yup.bool().default(false),
});
const onSubmit = (values, actions) => {
emailAddressesMutation({
variables: {
input: {
accountListId: accountListId || '',
attributes: {
id: personId,
emailAddresses: [
{
email: values.email,
},
],
},
},
},
update: (cache, { data: addEmailAddressData }) => {
actions.resetForm();
const query = {
query: GetInvalidEmailAddressesDocument,
variables: {
accountListId: accountListId,
},
};
const dataFromCache =
cache.readQuery<GetInvalidEmailAddressesQuery>(query);
if (dataFromCache) {
const peopleWithNewEmail = dataFromCache.people.nodes.map(
(person) => {
if (
person.id === personId &&
addEmailAddressData?.updatePerson?.person.emailAddresses.nodes
) {
return {
...person,
emailAddresses: {
nodes:
addEmailAddressData?.updatePerson?.person.emailAddresses
.nodes,
},
};
} else {
return person;
}
},
);

cache.writeQuery({
...query,
data: {
people: {
...dataFromCache.people,
nodes: peopleWithNewEmail,
},
},
});
}
},
onCompleted: () => {
enqueueSnackbar(t('Added email address'), { variant: 'success' });
},
onError: () => {
enqueueSnackbar(t('Failed to add email address'), { variant: 'error' });
},
});
};

return (
<Formik
Expand Down
106 changes: 85 additions & 21 deletions src/components/Tool/FixEmailAddresses/FixEmailAddressPerson.test.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import React from 'react';
import { ThemeProvider } from '@mui/material/styles';
import userEvent from '@testing-library/user-event';
import { ApolloErgonoMockMap } from 'graphql-ergonomock';
import { DateTime } from 'luxon';
import TestWrapper from '__tests__/util/TestWrapper';
import { GqlMockedProvider } from '__tests__/util/graphqlMocking';
import { render, waitFor } from '__tests__/util/testingLibraryReactMock';
import { PersonEmailAddressInput } from 'src/graphql/types.generated';
import theme from '../../../theme';
import { EmailAddressesMutation } from './AddEmailAddress.generated';
import {
FixEmailAddressPerson,
FixEmailAddressPersonProps,
} from './FixEmailAddressPerson';
import { EmailAddressData } from './FixEmailAddresses';
import { EmailAddressData, PersonEmailAddresses } from './FixEmailAddresses';
import { GetInvalidEmailAddressesQuery } from './FixEmailAddresses.generated';
import { mockInvalidEmailAddressesResponse } from './FixEmailAddressesMocks';

const testData = {
name: 'Test Contact',
Expand Down Expand Up @@ -38,26 +44,40 @@ const testData = {
const setContactFocus = jest.fn();
const handleChangeMock = jest.fn();
const handleDeleteModalOpenMock = jest.fn();
const handleAddMock = jest.fn();
const handleChangePrimaryMock = jest.fn();

const TestComponent: React.FC = () => {
const TestComponent = ({ mocks }: { mocks: ApolloErgonoMockMap }) => {
const toDelete = [] as PersonEmailAddressInput[];
const dataState = {
id: {
emailAddresses: testData.emailAddresses as EmailAddressData[],
toDelete,
},
} as { [key: string]: PersonEmailAddresses };

return (
<ThemeProvider theme={theme}>
<TestWrapper>
<FixEmailAddressPerson
toDelete={[]}
name={testData.name}
key={testData.name}
personId={testData.personId}
contactId={testData.contactId}
emailAddresses={testData.emailAddresses}
handleChange={handleChangeMock}
handleDelete={handleDeleteModalOpenMock}
handleAdd={handleAddMock}
handleChangePrimary={handleChangePrimaryMock}
setContactFocus={setContactFocus}
/>
<GqlMockedProvider<{
GetInvalidEmailAddresses: GetInvalidEmailAddressesQuery;
EmailAddresses: EmailAddressesMutation;
}>
mocks={mocks}
>
<FixEmailAddressPerson
toDelete={toDelete}
name={testData.name}
key={testData.name}
personId={testData.personId}
dataState={dataState}
contactId={testData.contactId}
emailAddresses={testData.emailAddresses}
handleChange={handleChangeMock}
handleDelete={handleDeleteModalOpenMock}
handleChangePrimary={handleChangePrimaryMock}
setContactFocus={setContactFocus}
/>
</GqlMockedProvider>
</TestWrapper>
</ThemeProvider>
);
Expand All @@ -66,7 +86,15 @@ const TestComponent: React.FC = () => {
describe('FixEmailAddressPerson', () => {
it('default', () => {
const { getByText, getByTestId, getByDisplayValue } = render(
<TestComponent />,
<TestComponent
mocks={{
GetInvalidEmailAddresses: {
people: {
nodes: mockInvalidEmailAddressesResponse,
},
},
}}
/>,
);

expect(getByText(testData.name)).toBeInTheDocument();
Expand All @@ -80,7 +108,17 @@ describe('FixEmailAddressPerson', () => {
});

it('input reset after adding an email address', async () => {
const { getByTestId, getByLabelText } = render(<TestComponent />);
const { getByTestId, getByLabelText } = render(
<TestComponent
mocks={{
GetInvalidEmailAddresses: {
people: {
nodes: mockInvalidEmailAddressesResponse,
},
},
}}
/>,
);

const addInput = getByLabelText('New Email Address');
const addButton = getByTestId('addButton-testid');
Expand All @@ -98,7 +136,15 @@ describe('FixEmailAddressPerson', () => {
describe('validation', () => {
it('should show an error message if there is no email', async () => {
const { getByLabelText, getByTestId, getByText } = render(
<TestComponent />,
<TestComponent
mocks={{
GetInvalidEmailAddresses: {
people: {
nodes: mockInvalidEmailAddressesResponse,
},
},
}}
/>,
);

const addInput = getByLabelText('New Email Address');
Expand All @@ -114,7 +160,15 @@ describe('FixEmailAddressPerson', () => {

it('should show an error message if there is an invalid email', async () => {
const { getByLabelText, getByTestId, getByText } = render(
<TestComponent />,
<TestComponent
mocks={{
GetInvalidEmailAddresses: {
people: {
nodes: mockInvalidEmailAddressesResponse,
},
},
}}
/>,
);

const addInput = getByLabelText('New Email Address');
Expand All @@ -129,7 +183,17 @@ describe('FixEmailAddressPerson', () => {
});

it('should not disable the add button', async () => {
const { getByLabelText, getByTestId } = render(<TestComponent />);
const { getByLabelText, getByTestId } = render(
<TestComponent
mocks={{
GetInvalidEmailAddresses: {
people: {
nodes: mockInvalidEmailAddressesResponse,
},
},
}}
/>,
);

const addInput = getByLabelText('New Email Address');
userEvent.type(addInput, '[email protected]');
Expand Down
14 changes: 5 additions & 9 deletions src/components/Tool/FixEmailAddresses/FixEmailAddressPerson.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { dateFormatShort } from 'src/lib/intlFormat';
import theme from '../../../theme';
import { ConfirmButtonIcon } from '../ConfirmButtonIcon';
import EmailValidationForm from './EmailValidationForm';
import { EmailAddressData } from './FixEmailAddresses';
import { EmailAddressData, PersonEmailAddresses } from './FixEmailAddresses';

const PersonCard = styled(Box)(({ theme }) => ({
[theme.breakpoints.up('md')]: {
Expand Down Expand Up @@ -96,6 +96,7 @@ export interface FixEmailAddressPersonProps {
name: string;
emailAddresses?: EmailAddressData[];
personId: string;
dataState: { [key: string]: PersonEmailAddresses };
toDelete: PersonEmailAddressInput[];
contactId: string;
handleChange: (
Expand All @@ -104,7 +105,6 @@ export interface FixEmailAddressPersonProps {
event: React.ChangeEvent<HTMLInputElement>,
) => void;
handleDelete: (personId: string, emailAddress: number) => void;
handleAdd: (personId: string, email: string) => void;
handleChangePrimary: (personId: string, emailIndex: number) => void;
setContactFocus: SetContactFocus;
}
Expand All @@ -113,12 +113,12 @@ export const FixEmailAddressPerson: React.FC<FixEmailAddressPersonProps> = ({
name,
emailAddresses,
personId,
dataState,
contactId,
handleChange,
handleDelete,
handleChangePrimary,
setContactFocus,
handleAdd,
}) => {
const { t } = useTranslation();
const locale = useLocale();
Expand All @@ -132,7 +132,7 @@ export const FixEmailAddressPerson: React.FC<FixEmailAddressPersonProps> = ({
personId: personId,
isPrimary: email.primary,
})) || [],
[emailAddresses],
[emailAddresses, dataState],
);

const handleContactNameClick = () => {
Expand Down Expand Up @@ -277,11 +277,7 @@ export const FixEmailAddressPerson: React.FC<FixEmailAddressPersonProps> = ({
{
//TODO: index will need to be mapped to the correct personId
}
<EmailValidationForm
handleAdd={handleAdd}
index={0}
personId={personId}
/>
<EmailValidationForm index={0} personId={personId} />
</BoxWithResponsiveBorder>
</RowWrapper>
</Grid>
Expand Down
Loading

0 comments on commit 63b1170

Please sign in to comment.