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-8405] Fix filtering the map by selected contacts #1154

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
279 changes: 111 additions & 168 deletions src/components/Contacts/ContactsContext/ContactsContext.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,19 @@ import TestRouter from '__tests__/util/TestRouter';
import { GqlMockedProvider } from '__tests__/util/graphqlMocking';
import { ContactFiltersQuery } from 'pages/accountLists/[accountListId]/contacts/Contacts.generated';
import { ContactsWrapper } from 'pages/accountLists/[accountListId]/contacts/ContactsWrapper';
import { GetUserOptionsQuery } from 'src/components/Contacts/ContactFlow/GetUserOptions.generated';
import { GetIdsForMassSelectionQuery } from 'src/hooks/GetIdsForMassSelection.generated';
import { UserOptionQuery } from 'src/hooks/UserPreference.generated';
import { useMassSelection } from '../../../hooks/useMassSelection';
import theme from '../../../theme';
import {
ListHeaderCheckBoxState,
TableViewModeEnum,
} from '../../Shared/Header/ListHeader';
import { TableViewModeEnum } from '../../Shared/Header/ListHeader';
import {
ContactsContext,
ContactsContextSavedFilters,
ContactsType,
} from './ContactsContext';

const accountListId = 'account-list-1';
const isReady = true;
const replace = jest.fn();

jest.mock('src/hooks/useMassSelection');

(useMassSelection as jest.Mock).mockReturnValue({
selectionType: ListHeaderCheckBoxState.Unchecked,
isRowChecked: jest.fn(),
toggleSelectAll: jest.fn(),
toggleSelectionById: jest.fn(),
});

const mockEnqueue = jest.fn();

jest.mock('notistack', () => ({
Expand All @@ -47,12 +33,74 @@ jest.mock('notistack', () => ({
},
}));

const TestRender: React.FC = () => {
const { viewMode, handleViewModeChange, userOptionsLoading } = useContext(
ContactsContext,
) as ContactsType;
interface TestWrapper {
contactId?: string[];
contactView?: string;
savedFilter?: string;
children: JSX.Element;
}

const TestWrapper: React.FC<TestWrapper> = ({
contactId,
contactView = 'list',
savedFilter,
children,
}) => {
return (
<Box>
<ThemeProvider theme={theme}>
<TestRouter
router={{
query: { accountListId, contactId },
pathname: '/accountLists/[accountListId]/contacts/[[...contactId]]',
isReady: true,
replace,
}}
>
<GqlMockedProvider<{
GetIdsForMassSelection: GetIdsForMassSelectionQuery;
UserOption: UserOptionQuery;
ContactFilters: ContactFiltersQuery;
}>
mocks={{
GetIdsForMassSelection: {
contacts: {
nodes: [{ id: 'contact-1' }],
},
},
UserOption: {
userOption: {
key: 'contacts_view',
value: contactView,
},
},
ContactFilters: {
userOptions: [
{
key: 'saved_contacts_filter_My_Cool_Filter',
value: savedFilter ?? null,
},
],
},
}}
>
<ContactsWrapper addViewMode>{children}</ContactsWrapper>
</GqlMockedProvider>
</TestRouter>
</ThemeProvider>
);
};

const InnerComponent: React.FC = () => {
const {
viewMode,
handleViewModeChange,
userOptionsLoading,
toggleSelectionById,
activeFilters,
} = useContext(ContactsContext) as ContactsType;

return (
<div>
{!userOptionsLoading ? (
<>
<Typography>{viewMode}</Typography>
Expand Down Expand Up @@ -81,7 +129,11 @@ const TestRender: React.FC = () => {
) : (
<>Loading</>
)}
</Box>
<button onClick={() => toggleSelectionById('contact-1')}>
Select contact
</button>
<div data-testid="active-filters">{JSON.stringify(activeFilters)}</div>
</div>
);
};

Expand All @@ -100,33 +152,11 @@ const TestRenderContactsFilters: React.FC = () => {
describe('ContactsPageContext', () => {
it('has a contact id and switches from list to flows', async () => {
const { getByText, findByText } = render(
<ThemeProvider theme={theme}>
<TestRouter
router={{
query: { accountListId, contactId: ['list', 'abc'] },
pathname: '/accountLists/[accountListId]/contacts/[[...contactId]]',
isReady,
replace,
}}
>
<GqlMockedProvider<{ UserOption: UserOptionQuery }>
mocks={{
UserOption: {
userOption: {
id: 'test-id',
key: 'contacts_view',
value: 'flows',
},
},
}}
>
<ContactsWrapper addViewMode>
<TestRender />
</ContactsWrapper>
</GqlMockedProvider>
</TestRouter>
</ThemeProvider>,
<TestWrapper contactId={['list', 'abc']}>
<InnerComponent />
</TestWrapper>,
);

expect(getByText('Loading')).toBeInTheDocument();
userEvent.click(await findByText('Flows Button'));
expect(await findByText('flows')).toBeInTheDocument();
Expand All @@ -143,33 +173,11 @@ describe('ContactsPageContext', () => {

it('has a contact id and switches twice', async () => {
const { getByText, findByText } = render(
<ThemeProvider theme={theme}>
<TestRouter
router={{
query: { accountListId, contactId: ['list', 'abc'] },
pathname: '/accountLists/[accountListId]/contacts/[[...contactId]]',
isReady,
replace,
}}
>
<GqlMockedProvider<{ UserOption: UserOptionQuery }>
mocks={{
UserOption: {
userOption: {
id: 'test-id',
key: 'contacts_view',
value: 'flows',
},
},
}}
>
<ContactsWrapper addViewMode>
<TestRender />
</ContactsWrapper>
</GqlMockedProvider>
</TestRouter>
</ThemeProvider>,
<TestWrapper contactId={['list', 'abc']} contactView="flows">
<InnerComponent />
</TestWrapper>,
);

expect(getByText('Loading')).toBeInTheDocument();
userEvent.click(await findByText('Map Button'));
expect(await findByText('map')).toBeInTheDocument();
Expand Down Expand Up @@ -198,33 +206,11 @@ describe('ContactsPageContext', () => {

it('does not have a contact id and changes to map', async () => {
const { getByText, findByText } = render(
<ThemeProvider theme={theme}>
<TestRouter
router={{
query: { accountListId },
pathname: '/accountLists/[accountListId]/contacts/[[...contactId]]',
isReady,
replace,
}}
>
<GqlMockedProvider<{ UserOption: UserOptionQuery }>
mocks={{
UserOption: {
userOption: {
id: 'test-id',
key: 'contacts_view',
value: 'list',
},
},
}}
>
<ContactsWrapper addViewMode>
<TestRender />
</ContactsWrapper>
</GqlMockedProvider>
</TestRouter>
</ThemeProvider>,
<TestWrapper contactView="list">
<InnerComponent />
</TestWrapper>,
);

expect(getByText('Loading')).toBeInTheDocument();
userEvent.click(await findByText('Map Button'));
expect(await findByText('map')).toBeInTheDocument();
Expand All @@ -239,84 +225,41 @@ describe('ContactsPageContext', () => {
);
});

it('shows the selected contacts on the map', async () => {
const { findByRole, getByRole, getByTestId } = render(
<TestWrapper>
<InnerComponent />
</TestWrapper>,
);

userEvent.click(getByRole('button', { name: 'Select contact' }));
userEvent.click(await findByRole('button', { name: 'Map Button' }));
expect(getByTestId('active-filters')).toHaveTextContent(
'{"ids":["contact-1"]}',
);

userEvent.click(getByRole('button', { name: 'List Button' }));
expect(getByTestId('active-filters')).toHaveTextContent('{}');
});

it('Saved Filters with correct JSON', async () => {
const userOptions = [
{
id: '123',
key: 'saved_contacts_filter_My_Cool_Filter',
value: `{"any_tags":false,"account_list_id":"${accountListId}","params":{"status": "true"},"tags":null,"exclude_tags":null,"wildcard_search":""}`,
},
];
const savedFilter = `{"any_tags":false,"account_list_id":"${accountListId}","params":{"status": "true"},"tags":null,"exclude_tags":null,"wildcard_search":""}`;
const { findByTestId } = render(
<ThemeProvider theme={theme}>
<TestRouter
router={{
query: { accountListId },
pathname: '/accountLists/[accountListId]/contacts',
isReady,
}}
>
<GqlMockedProvider<{
GetUserOptions: GetUserOptionsQuery;
ContactFilters: ContactFiltersQuery;
}>
mocks={{
GetUserOptions: {
userOptions,
},
ContactFilters: {
userOptions,
},
}}
>
<ContactsWrapper>
<TestRenderContactsFilters />
</ContactsWrapper>
</GqlMockedProvider>
</TestRouter>
</ThemeProvider>,
<TestWrapper savedFilter={savedFilter}>
<TestRenderContactsFilters />
</TestWrapper>,
);

expect(await findByTestId('savedfilters-testid')).toBeInTheDocument();
});

it('Saved Filters with incorrect JSON', async () => {
const userOptions = [
{
id: '123',
key: 'saved_contacts_filter_My_Cool_Filter',
value: `{"any_tags":false,"account_list_id":"${accountListId}","params":{"status" error },"tags":null,"exclude_tags":null,"wildcard_search":""}`,
},
];
const savedFilter = `{"any_tags":false,"account_list_id":"${accountListId}","params":{"status" error },"tags":null,"exclude_tags":null,"wildcard_search":""}`;

const { queryByTestId } = render(
<ThemeProvider theme={theme}>
<TestRouter
router={{
query: { accountListId },
pathname: '/accountLists/[accountListId]/contacts',
isReady,
}}
>
<GqlMockedProvider<{
GetUserOptions: GetUserOptionsQuery;
ContactFilters: ContactFiltersQuery;
}>
mocks={{
GetUserOptions: {
userOptions,
},
ContactFilters: {
userOptions,
},
}}
>
<ContactsWrapper>
<TestRenderContactsFilters />
</ContactsWrapper>
</GqlMockedProvider>
</TestRouter>
</ThemeProvider>,
<TestWrapper savedFilter={savedFilter}>
<TestRenderContactsFilters />
</TestWrapper>,
);

await waitFor(() =>
Expand Down
17 changes: 12 additions & 5 deletions src/components/Contacts/ContactsContext/ContactsContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -150,16 +150,16 @@ export const ContactsProvider: React.FC<ContactsContextProps> = ({
[activeFilters],
);

const [contactsView, updateOptions, { loading: userOptionsLoading }] =
const [contactsView, saveContactsView, { loading: userOptionsLoading }] =
useUserPreference({
key: 'contacts_view',
defaultValue: TableViewModeEnum.List,
});
useEffect(() => {
if (contactsView && !userOptionsLoading) {
if (contactsView) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dr-bizz When I asked you to add this additional check, I was mistaken. I thought this effect saved the user preference, but it is just updating the view mode in the contacts wrapper. It was right how you had it first, so I'm switching it back.

setViewMode(contactsView);
}
}, [contactsView, userOptionsLoading]);
}, [contactsView]);

const contactsFilters = useMemo(() => {
// Remove filters in the map view
Expand Down Expand Up @@ -257,8 +257,15 @@ export const ContactsProvider: React.FC<ContactsContextProps> = ({
_: React.MouseEvent<HTMLElement>,
view: string,
) => {
setViewMode(view as TableViewModeEnum);
updateOptions(view as TableViewModeEnum);
const newViewMode = view as TableViewModeEnum;
saveContactsView(newViewMode);
if (newViewMode === TableViewModeEnum.Map) {
// When switching to the map, make the filter only show the selected contacts, if any
setActiveFilters({ ids });
} else if (viewMode === TableViewModeEnum.Map) {
// When switching away from the map, reset the filter to show all contacts
setActiveFilters({});
canac marked this conversation as resolved.
Show resolved Hide resolved
}
};
//#endregion

Expand Down
Loading