Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MPDX-7946 use contact mutation when adding email #973

Merged
merged 5 commits into from
Aug 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 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 @@
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' });

Check warning on line 125 in src/components/Tool/FixEmailAddresses/EmailValidationForm.tsx

View check run for this annotation

Codecov / codecov/patch

src/components/Tool/FixEmailAddresses/EmailValidationForm.tsx#L124-L125

Added lines #L124 - L125 were not covered by tests
},
});
};

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],
dr-bizz marked this conversation as resolved.
Show resolved Hide resolved
);

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
Loading