diff --git a/src/components/Tool/Appeal/AppealsContext/AppealsContext.tsx b/src/components/Tool/Appeal/AppealsContext/AppealsContext.tsx index 78e1c6e8e..b696ace3c 100644 --- a/src/components/Tool/Appeal/AppealsContext/AppealsContext.tsx +++ b/src/components/Tool/Appeal/AppealsContext/AppealsContext.tsx @@ -16,6 +16,7 @@ import { useAccountListId } from 'src/hooks/useAccountListId'; import { useMassSelection } from 'src/hooks/useMassSelection'; import { sanitizeFilters } from 'src/lib/sanitizeFilters'; import { useContactsQuery } from './contacts.generated'; +import { useContactsCountQuery } from './contactsCount.generated'; export enum AppealStatusEnum { Excluded = 'excluded', @@ -30,6 +31,19 @@ export enum TableViewModeEnum { Flows = 'flows', } +export const shouldSkipContactCount = ( + filterPanelOpen: boolean, + viewMode: TableViewModeEnum, +) => { + if (viewMode === TableViewModeEnum.Flows) { + return true; + } else if (viewMode === TableViewModeEnum.List && !filterPanelOpen) { + return false; + } else { + return false; + } +}; + export interface AppealsType extends Omit< ContactsType, @@ -47,6 +61,11 @@ export interface AppealsType contactsQueryResult: ReturnType; appealId: string | undefined; page: PageEnum | undefined; + askedCountQueryResult: ReturnType; + excludedCountQueryResult: ReturnType; + committedCountQueryResult: ReturnType; + givenCountQueryResult: ReturnType; + receivedCountQueryResult: ReturnType; } export const AppealsContext = React.createContext(null); @@ -205,6 +224,68 @@ export const AppealsProvider: React.FC = ({ }, }); + const nameSearch = searchTerm ? { wildcardSearch: searchTerm as string } : {}; + const defaultFilters = { + appeal: [appealId || ''], + ...nameSearch, + }; + const skip = shouldSkipContactCount(filterPanelOpen, viewMode); + + const askedCountQueryResult = useContactsCountQuery({ + variables: { + accountListId: accountListId || '', + contactsFilter: { + ...defaultFilters, + appealStatus: AppealStatusEnum.Asked, + }, + }, + skip, + }); + + const excludedCountQueryResult = useContactsCountQuery({ + variables: { + accountListId: accountListId || '', + contactsFilter: { + ...defaultFilters, + appealStatus: AppealStatusEnum.Excluded, + }, + }, + skip, + }); + + const committedCountQueryResult = useContactsCountQuery({ + variables: { + accountListId: accountListId || '', + contactsFilter: { + ...defaultFilters, + appealStatus: AppealStatusEnum.NotReceived, + }, + }, + skip, + }); + + const givenCountQueryResult = useContactsCountQuery({ + variables: { + accountListId: accountListId || '', + contactsFilter: { + ...defaultFilters, + appealStatus: AppealStatusEnum.Processed, + }, + }, + skip, + }); + + const receivedCountQueryResult = useContactsCountQuery({ + variables: { + accountListId: accountListId || '', + contactsFilter: { + ...defaultFilters, + appealStatus: AppealStatusEnum.ReceivedNotProcessed, + }, + }, + skip, + }); + const toggleFilterPanel = () => { setFilterPanelOpen(!filterPanelOpen); }; @@ -363,6 +444,11 @@ export const AppealsProvider: React.FC = ({ userOptionsLoading: userOptionsLoading, appealId, page, + askedCountQueryResult, + excludedCountQueryResult, + committedCountQueryResult, + givenCountQueryResult, + receivedCountQueryResult, }} > {children} diff --git a/src/components/Tool/Appeal/List/AppealsListFilterPanel/contactsCount.graphql b/src/components/Tool/Appeal/AppealsContext/contactsCount.graphql similarity index 100% rename from src/components/Tool/Appeal/List/AppealsListFilterPanel/contactsCount.graphql rename to src/components/Tool/Appeal/AppealsContext/contactsCount.graphql diff --git a/src/components/Tool/Appeal/List/AppealsListFilterPanel/AppealsListFilterPanel.test.tsx b/src/components/Tool/Appeal/List/AppealsListFilterPanel/AppealsListFilterPanel.test.tsx index 6da5ec6b3..c8c38a1c7 100644 --- a/src/components/Tool/Appeal/List/AppealsListFilterPanel/AppealsListFilterPanel.test.tsx +++ b/src/components/Tool/Appeal/List/AppealsListFilterPanel/AppealsListFilterPanel.test.tsx @@ -1,7 +1,8 @@ import React from 'react'; import { ThemeProvider } from '@mui/material/styles'; -import { render, waitFor } from '@testing-library/react'; +import { render } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; +import { SnackbarProvider } from 'notistack'; import TestRouter from '__tests__/util/TestRouter'; import { GqlMockedProvider } from '__tests__/util/graphqlMocking'; import { AppealsWrapper } from 'pages/accountLists/[accountListId]/tools/appeals/AppealsWrapper'; @@ -11,8 +12,8 @@ import { AppealsContext, AppealsType, } from '../../AppealsContext/AppealsContext'; +import { ContactsCountQuery } from '../../AppealsContext/contactsCount.generated'; import { AppealsListFilterPanel } from './AppealsListFilterPanel'; -import { ContactsCountQuery } from './contactsCount.generated'; const accountListId = 'accountListId'; const appealId = 'appealId'; @@ -26,35 +27,51 @@ const onClose = jest.fn(); const setActiveFilters = jest.fn(); const deselectAll = jest.fn(); +const defaultContactCountMock = { + data: { + contacts: { + totalCount: 5, + }, + }, + loading: false, +}; + const Components = ({ ids = selectedIds }) => ( - - mocks={{ - ContactsCount: { - contacts: { - totalCount: 5, + + + mocks={{ + ContactsCount: { + contacts: { + totalCount: 5, + }, }, - }, - }} - > - - - - - - + }} + > + + + + + + + ); @@ -76,17 +93,19 @@ describe('AppealsListFilterPanel', () => { }); it('default', async () => { - const { getByText, getByRole, getAllByRole } = render(); + const { getByText, getByRole, findByRole, getAllByRole } = render( + , + ); expect(getByText('Given')).toBeInTheDocument(); expect(getByText('Committed')).toBeInTheDocument(); expect(getByText('Excluded')).toBeInTheDocument(); - await waitFor(() => { - expect(getByRole('button', { name: /given 5/i })).toBeInTheDocument(); - expect(getByRole('button', { name: /received 5/i })).toBeInTheDocument(); - expect(getByRole('button', { name: /asked 5/i })).toBeInTheDocument(); - }); + expect( + await findByRole('button', { name: /given 5/i }), + ).toBeInTheDocument(); + expect(getByRole('button', { name: /received 5/i })).toBeInTheDocument(); + expect(getByRole('button', { name: /asked 5/i })).toBeInTheDocument(); expect(getByText('Add Contact to Appeal')).toBeInTheDocument(); expect(getByText('Delete Appeal')).toBeInTheDocument(); @@ -112,5 +131,81 @@ describe('AppealsListFilterPanel', () => { ...activeFilters, appealStatus: AppealStatusEnum.Processed, }); + + userEvent.click(getByText('Received')); + expect(deselectAll).toHaveBeenCalled(); + expect(setActiveFilters).toHaveBeenCalledWith({ + ...activeFilters, + appealStatus: AppealStatusEnum.ReceivedNotProcessed, + }); + + userEvent.click(getByText('Committed')); + expect(deselectAll).toHaveBeenCalled(); + expect(setActiveFilters).toHaveBeenCalledWith({ + ...activeFilters, + appealStatus: AppealStatusEnum.NotReceived, + }); + + userEvent.click(getByText('Asked')); + expect(deselectAll).toHaveBeenCalled(); + expect(setActiveFilters).toHaveBeenCalledWith({ + ...activeFilters, + appealStatus: AppealStatusEnum.Asked, + }); + + userEvent.click(getByText('Excluded')); + expect(deselectAll).toHaveBeenCalled(); + expect(setActiveFilters).toHaveBeenCalledWith({ + ...activeFilters, + appealStatus: AppealStatusEnum.Excluded, + }); + }); + + describe('Modals', () => { + it('should open export contacts modal', async () => { + const { findAllByRole, findByRole } = render(); + + const buttons = await findAllByRole('button', { + name: 'Export 2 Selected', + }); + userEvent.click(buttons[0]); + + expect( + await findByRole('heading', { name: 'Export Contacts' }), + ).toBeInTheDocument(); + }); + + it('should open export emails modal', async () => { + const { findAllByRole, findByTestId } = render(); + + const buttons = await findAllByRole('button', { + name: 'Export 2 Selected', + }); + userEvent.click(buttons[1]); + + expect(await findByTestId('ExportEmailsModal')).toBeInTheDocument(); + }); + + it('should open add contact to appeal modal', async () => { + const { findByRole, findByTestId } = render(); + + const button = await findByRole('button', { + name: 'Select Contact', + }); + userEvent.click(button); + + expect(await findByTestId('addContactToAppealModal')).toBeInTheDocument(); + }); + + it('should open delete appeal modal', async () => { + const { findByRole, findByTestId } = render(); + + const button = await findByRole('button', { + name: 'Permanently Delete Appeal', + }); + userEvent.click(button); + + expect(await findByTestId('deleteAppealModal')).toBeInTheDocument(); + }); }); }); diff --git a/src/components/Tool/Appeal/List/AppealsListFilterPanel/AppealsListFilterPanel.tsx b/src/components/Tool/Appeal/List/AppealsListFilterPanel/AppealsListFilterPanel.tsx index 1afb9bd50..180c8ffde 100644 --- a/src/components/Tool/Appeal/List/AppealsListFilterPanel/AppealsListFilterPanel.tsx +++ b/src/components/Tool/Appeal/List/AppealsListFilterPanel/AppealsListFilterPanel.tsx @@ -1,9 +1,8 @@ -import React, { useEffect, useState } from 'react'; +import React, { useState } from 'react'; import Close from '@mui/icons-material/Close'; import { Box, BoxProps, - Button, IconButton, List, Slide, @@ -20,7 +19,6 @@ import { preloadMassActionsExportEmailsModal, } from 'src/components/Contacts/MassActions/Exports/Emails/DynamicMassActionsExportEmailsModal'; import { DynamicMailMergedLabelModal } from 'src/components/Contacts/MassActions/Exports/MailMergedLabelModal/DynamicMailMergedLabelModal'; -import { sanitizeFilters } from 'src/lib/sanitizeFilters'; import { AppealStatusEnum, AppealsContext, @@ -36,7 +34,6 @@ import { } from '../../Modals/DeleteAppealModal/DynamicDeleteAppealModal'; import { AppealsListFilterPanelButton } from './AppealsListFilterPanelButton'; import { AppealsListFilterPanelItem } from './AppealsListFilterPanelItem'; -import { useContactsCountQuery } from './contactsCount.generated'; const FilterHeader = styled(Box)(({ theme }) => ({ padding: theme.spacing(2), @@ -61,15 +58,6 @@ export interface FilterPanelProps { onClose: () => void; } -const LinkButton = styled(Button)(({ theme }) => ({ - width: '100%', - textTransform: 'none', - fontSize: 16, - color: theme.palette.info.main, - fontWeight: 'bold', - marginTop: theme.spacing(1), -})); - export const AppealsListFilterPanel: React.FC = ({ onClose, }) => { @@ -80,6 +68,11 @@ export const AppealsListFilterPanel: React.FC = ({ setActiveFilters, selectedIds, deselectAll, + askedCountQueryResult, + excludedCountQueryResult, + committedCountQueryResult, + givenCountQueryResult, + receivedCountQueryResult, } = React.useContext(AppealsContext) as AppealsType; const [exportsModalOpen, setExportsModalOpen] = useState(false); const [labelModalOpen, setLabelModalOpen] = useState(false); @@ -87,37 +80,14 @@ export const AppealsListFilterPanel: React.FC = ({ const [addContactsModalOpen, setAddContactsModalOpen] = useState(false); const [deleteAppealModalOpen, setDeleteAppealModalOpen] = useState(false); + const { data: askedCount, loading: askedLoading } = askedCountQueryResult; + const { data: excludedCount, loading: excludedLoading } = + excludedCountQueryResult; const { data: committedCount, loading: committedLoading } = - useContactsCountQuery({ - variables: { - accountListId: accountListId || '', - contactsFilter: { - ...defaultFilters, - appealStatus: AppealStatusEnum.NotReceived, - }, - }, - }); - - const { data: givenCount, loading: givenLoading } = useContactsCountQuery({ - variables: { - accountListId: accountListId || '', - contactsFilter: { - ...defaultFilters, - appealStatus: AppealStatusEnum.Processed, - }, - }, - }); - + committedCountQueryResult; + const { data: givenCount, loading: givenLoading } = givenCountQueryResult; const { data: receivedCount, loading: receivedLoading } = - useContactsCountQuery({ - variables: { - accountListId: accountListId || '', - contactsFilter: { - ...defaultFilters, - appealStatus: AppealStatusEnum.ReceivedNotProcessed, - }, - }, - }); + receivedCountQueryResult; const handleFilterItemClick = (newAppealListView: AppealStatusEnum) => { deselectAll(); @@ -130,13 +100,10 @@ export const AppealsListFilterPanel: React.FC = ({ const appealListView = activeFilters.appealStatus; const noContactsSelected = !selectedIds.length; - const handleClearAllClick = () => { - setActiveFilters({}); + const handleExportModalClose = () => { + setExportsModalOpen(false); }; - const noActiveFilters = - Object.keys(sanitizeFilters(activeFilters)).length === 0; - return (
@@ -157,13 +124,6 @@ export const AppealsListFilterPanel: React.FC = ({ - - {t('Clear All')} - @@ -235,7 +195,6 @@ export const AppealsListFilterPanel: React.FC = ({ { setAddContactsModalOpen(true); }} @@ -244,7 +203,6 @@ export const AppealsListFilterPanel: React.FC = ({ { diff --git a/src/components/Tool/Appeal/List/AppealsListFilterPanel/AppealsListFilterPanelButton.tsx b/src/components/Tool/Appeal/List/AppealsListFilterPanel/AppealsListFilterPanelButton.tsx index 7e9396cdd..0cfa7d9d4 100644 --- a/src/components/Tool/Appeal/List/AppealsListFilterPanel/AppealsListFilterPanelButton.tsx +++ b/src/components/Tool/Appeal/List/AppealsListFilterPanel/AppealsListFilterPanelButton.tsx @@ -39,7 +39,7 @@ export const AppealsListFilterPanelButton = ({ buttonText, buttonError = 'primary', buttonVariant = 'contained', - disabled, + disabled = false, onMouseEnter, }: AppealsListFilterPanelButtonProps): ReactElement => { const { classes } = useStyles(); diff --git a/src/components/Tool/Appeal/Modals/DeleteAppealModal/DeleteAppealModal.tsx b/src/components/Tool/Appeal/Modals/DeleteAppealModal/DeleteAppealModal.tsx index 200f65b9d..6b52b627e 100644 --- a/src/components/Tool/Appeal/Modals/DeleteAppealModal/DeleteAppealModal.tsx +++ b/src/components/Tool/Appeal/Modals/DeleteAppealModal/DeleteAppealModal.tsx @@ -73,7 +73,7 @@ export const DeleteAppealModal: React.FC = ({ return ( - +