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-8091 Preferences Setup steps UI #1002

Merged
merged 9 commits into from
Sep 4, 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import React from 'react';
import { ThemeProvider } from '@mui/material/styles';
import { render, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import TestRouter from '__tests__/util/TestRouter';
import { GqlMockedProvider } from '__tests__/util/graphqlMocking';
import { GetUserOptionsQuery } from 'src/components/Contacts/ContactFlow/GetUserOptions.generated';
import { MailchimpAccountQuery } from 'src/components/Settings/integrations/Mailchimp/MailchimpAccount.generated';
import { GetUsersOrganizationsAccountsQuery } from 'src/components/Settings/integrations/Organization/Organizations.generated';
import { PrayerlettersAccountQuery } from 'src/components/Settings/integrations/Prayerletters/PrayerlettersAccount.generated';
import useGetAppSettings from 'src/hooks/useGetAppSettings';
import theme from 'src/theme';
import Integrations from './index.page';

const accountListId = 'account-list-1';

const mockEnqueue = jest.fn();
const mutationSpy = jest.fn();
const push = jest.fn();

const router = {
query: { accountListId },
isReady: true,
push,
};

jest.mock('src/hooks/useGetAppSettings');
jest.mock('notistack', () => ({
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
...jest.requireActual('notistack'),
useSnackbar: () => {
return {
enqueueSnackbar: mockEnqueue,
};
},
}));
interface MocksProvidersProps {
children: JSX.Element;
setup?: string;
}

const MocksProviders: React.FC<MocksProvidersProps> = ({ children, setup }) => (
<ThemeProvider theme={theme}>
<TestRouter router={router}>
<GqlMockedProvider<{
GetUsersOrganizationsAccounts: GetUsersOrganizationsAccountsQuery;
MailchimpAccount: MailchimpAccountQuery;
PrayerlettersAccount: PrayerlettersAccountQuery;
GetUserOptions: GetUserOptionsQuery;
}>
mocks={{
caleballdrin marked this conversation as resolved.
Show resolved Hide resolved
GetUsersOrganizationsAccounts: {
userOrganizationAccounts: [
{
organization: {},
},
{
organization: {},
},
],
},
MailchimpAccount: { mailchimpAccount: [] },
PrayerlettersAccount: { prayerlettersAccount: [] },
GetUserOptions: {
userOptions: [
{
id: '1',
key: 'setup_position',
value: setup || 'finish',
},
],
},
}}
onCall={mutationSpy}
>
{children}
</GqlMockedProvider>
</TestRouter>
</ThemeProvider>
);

describe('Connect Services page', () => {
beforeEach(() => {
(useGetAppSettings as jest.Mock).mockReturnValue({
appName: 'MPDX',
});
});
it('should render', async () => {
const { findByText } = render(
<MocksProviders>
<Integrations />
</MocksProviders>,
);
expect(await findByText('Connect Services')).toBeInTheDocument();
expect(await findByText('Organization')).toBeInTheDocument();
});

describe('Setup Tour', () => {
it('should not show setup banner and accordions should not be disabled', async () => {
const { queryByText, queryByRole, findByText, getByText } = render(
<MocksProviders setup="start">
<Integrations />
</MocksProviders>,
);

await waitFor(() => {
expect(
queryByText('Make MPDX a part of your everyday life'),
).not.toBeInTheDocument();
expect(
queryByRole('button', { name: 'Next Step' }),
).not.toBeInTheDocument();
});

//Accordions should be clickable
userEvent.click(await findByText('Organization'));
await waitFor(() => {
expect(
getByText(
'Add or change the organizations that sync donation information with this MPDX account. Removing an organization will not remove past information, but will prevent future donations and contacts from syncing.',
),
).toBeVisible();
});
});

it('should show setup banner and open google', async () => {
const { findByText, getByRole, getByText } = render(
<MocksProviders setup="preferences.integrations">
<Integrations />
</MocksProviders>,
);
expect(
await findByText('Make MPDX a part of your everyday life'),
).toBeInTheDocument();

//Accordions should be disabled
await waitFor(() => {
const label = getByText('Organization');
expect(() => userEvent.click(label)).toThrow();
});

const nextButton = getByRole('button', { name: 'Next Step' });

// Start with Google
expect(await findByText(/Add Account/i)).toBeInTheDocument();

// Moves to MailChimp
userEvent.click(nextButton);
expect(await findByText(/Connect MailChimp/i)).toBeInTheDocument();
caleballdrin marked this conversation as resolved.
Show resolved Hide resolved

// PrayerLetters.com
await waitFor(() => userEvent.click(nextButton));
await waitFor(() =>
expect(
getByText(
'prayerletters.com is a significant way to save valuable ministry time while more effectively connecting with your partners. Keep your physical newsletter list up to date in MPDX and then sync it to your prayerletters.com account with this integration.',
),
).toBeInTheDocument(),
);

// Move to finish
userEvent.click(nextButton);
await waitFor(() => {
expect(mutationSpy).toHaveGraphqlOperation('UpdateUserOptions', {
key: 'setup_position',
value: 'finish',
});
expect(push).toHaveBeenCalledWith(
'/accountLists/account-list-1/setup/finish',
);
});
});
});
});
Original file line number Diff line number Diff line change
@@ -1,65 +1,138 @@
import { useRouter } from 'next/router';
import React, { useEffect, useState } from 'react';
import { Button } from '@mui/material';
import { useSnackbar } from 'notistack';
import { useTranslation } from 'react-i18next';
import { loadSession } from 'pages/api/utils/pagePropsHelpers';
import { useUpdateUserOptionsMutation } from 'src/components/Contacts/ContactFlow/ContactFlowSetup/UpdateUserOptions.generated';
import { useGetUserOptionsQuery } from 'src/components/Contacts/ContactFlow/GetUserOptions.generated';
import { ChalklineAccordion } from 'src/components/Settings/integrations/Chalkline/ChalklineAccordion';
import { GoogleAccordion } from 'src/components/Settings/integrations/Google/GoogleAccordion';
import { TheKeyAccordion } from 'src/components/Settings/integrations/Key/TheKeyAccordion';
import { MailchimpAccordion } from 'src/components/Settings/integrations/Mailchimp/MailchimpAccordion';
import { OrganizationAccordion } from 'src/components/Settings/integrations/Organization/OrganizationAccordion';
import { PrayerlettersAccordion } from 'src/components/Settings/integrations/Prayerletters/PrayerlettersAccordion';
import { SetupBanner } from 'src/components/Settings/preferences/SetupBanner';
import { AccordionGroup } from 'src/components/Shared/Forms/Accordions/AccordionGroup';
import { useAccountListId } from 'src/hooks/useAccountListId';
import useGetAppSettings from 'src/hooks/useGetAppSettings';
import { suggestArticles } from 'src/lib/helpScout';
import { SettingsWrapper } from '../Wrapper';
import { StickyBox } from '../preferences.page';

const Integrations: React.FC = () => {
const { t } = useTranslation();
const { query } = useRouter();
const { push, query } = useRouter();
const [expandedPanel, setExpandedPanel] = useState(
(query?.selectedTab as string | undefined) || '',
);
const accountListId = useAccountListId() || '';
const { appName } = useGetAppSettings();
const { enqueueSnackbar } = useSnackbar();
const [setup, setSetup] = useState(0);

useEffect(() => {
suggestArticles('HS_SETTINGS_SERVICES_SUGGESTIONS');
}, []);
const setupAccordions = ['google', 'mailchimp', 'prayerletters.com'];

const { data: userOptions } = useGetUserOptionsQuery();
const [updateUserOptions] = useUpdateUserOptionsMutation();

const isSettingUp = userOptions?.userOptions.some(
(option) =>
option.key === 'setup_position' &&
option.value === 'preferences.integrations',
);

const handleSetupChange = async () => {
if (!isSettingUp) {
return;

Check warning on line 47 in pages/accountLists/[accountListId]/settings/integrations/index.page.tsx

View check run for this annotation

Codecov / codecov/patch

pages/accountLists/[accountListId]/settings/integrations/index.page.tsx#L47

Added line #L47 was not covered by tests
}
const nextNav = setup + 1;

if (setupAccordions.length === nextNav) {
await updateUserOptions({
variables: {
key: 'setup_position',
value: 'finish',
},
onError: () => {
enqueueSnackbar(t('Saving setup phase failed.'), {

Check warning on line 58 in pages/accountLists/[accountListId]/settings/integrations/index.page.tsx

View check run for this annotation

Codecov / codecov/patch

pages/accountLists/[accountListId]/settings/integrations/index.page.tsx#L57-L58

Added lines #L57 - L58 were not covered by tests
variant: 'error',
});
},
});
push(`/accountLists/${accountListId}/setup/finish`);
} else {
setSetup(nextNav);
setExpandedPanel(setupAccordions[nextNav]);
}
};

const handleAccordionChange = (panel: string) => {
const panelLowercase = panel.toLowerCase();
setExpandedPanel(expandedPanel === panelLowercase ? '' : panelLowercase);
};
useEffect(() => {
suggestArticles('HS_SETTINGS_SERVICES_SUGGESTIONS');
}, []);

useEffect(() => {
if (isSettingUp) {
setExpandedPanel(setupAccordions[0]);
}
}, [isSettingUp]);

return (
<SettingsWrapper
pageTitle={t('Connect Services')}
pageHeading={t('Connect Services')}
selectedMenuId="integrations"
>
{isSettingUp && (
<StickyBox>
<SetupBanner
button={
<Button variant="contained" onClick={handleSetupChange}>
{t('Next Step')}
</Button>
}
title={t('Make {{appName}} a part of your everyday life', {
appName,
})}
/>
</StickyBox>
)}
<AccordionGroup title="">
<TheKeyAccordion
handleAccordionChange={handleAccordionChange}
expandedPanel={expandedPanel}
disabled={isSettingUp}
/>
<OrganizationAccordion
handleAccordionChange={handleAccordionChange}
expandedPanel={expandedPanel}
disabled={isSettingUp}
/>
</AccordionGroup>
<AccordionGroup title={t('External Services')}>
<GoogleAccordion
handleAccordionChange={handleAccordionChange}
expandedPanel={expandedPanel}
disabled={isSettingUp && setup !== 0}
/>
<MailchimpAccordion
handleAccordionChange={handleAccordionChange}
expandedPanel={expandedPanel}
disabled={isSettingUp && setup !== 1}
/>
<PrayerlettersAccordion
handleAccordionChange={handleAccordionChange}
expandedPanel={expandedPanel}
disabled={isSettingUp && setup !== 2}
/>
<ChalklineAccordion
handleAccordionChange={handleAccordionChange}
expandedPanel={expandedPanel}
disabled={isSettingUp}
/>
</AccordionGroup>
</SettingsWrapper>
Expand Down
Loading
Loading