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-8373 Task Row #1139

Merged
merged 12 commits into from
Oct 24, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -335,8 +335,8 @@ const TasksPage: React.FC = () => {
isChecked={isRowChecked(task.id)}
useTopMargin={index === 0}
getContactHrefObject={getContactHrefObject}
contactDetailsOpen={contactDetailsOpen}
removeSelectedIds={deselectMultipleIds}
filterPanelOpen={filterPanelOpen}
canac marked this conversation as resolved.
Show resolved Hide resolved
/>
</Box>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,9 @@ describe('ContactTaskRow', () => {
expect(await findByText(task.subject)).toBeVisible();

expect(
await findByText(`${task.user?.firstName} ${task.user?.lastName}`),
await findByText(
`${task.user?.firstName?.[0]}${task.user?.lastName?.[0]}`,
),
).toBeVisible();

expect(queryByTestId('loadingRow')).toBeNull();
Expand All @@ -101,8 +103,34 @@ describe('ContactTaskRow', () => {
mocks: {
startAt,
result: ResultEnum.None,
user: {
firstName: 'John',
lastName: 'Wayne',
},
},
});
const taskWithTags = gqlMock<TaskRowFragment>(TaskRowFragmentDoc, {
mocks: {
id: '123',
startAt,
result: ResultEnum.None,
tagList: ['testTag'],
user: null,
},
});

it('renders the assignee avatar', async () => {
const { findByText } = render(<Components task={task} />);

expect(await findByText('JW')).toBeVisible();
});

it('renders the tag icon', async () => {
const { findByTestId } = render(<Components task={taskWithTags} />);

expect(await findByTestId('tagIcon-123')).toBeVisible();
});

it('handles complete button click', async () => {
const { findByText, getByRole } = render(<Components task={task} />);

Expand Down Expand Up @@ -166,7 +194,7 @@ describe('ContactTaskRow', () => {

const { getByText } = render(<Components task={task} />);

expect(getByText(activity?.value)).toBeVisible();
expect(getByText(activity?.value.split(' - ')[1].trim())).toBeVisible();
},
);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,28 @@
import React, { useState } from 'react';
import { Box, Checkbox, Skeleton, Typography } from '@mui/material';
import LocalOffer from '@mui/icons-material/LocalOffer';
import {
Avatar,
Box,
Checkbox,
Skeleton,
Tooltip,
Typography,
} from '@mui/material';
import { styled } from '@mui/material/styles';
import { DateTime } from 'luxon';
import { useTranslation } from 'react-i18next';
import { StyledCheckbox } from 'src/components/Contacts/ContactRow/ContactRow';
import { TaskModalEnum } from 'src/components/Task/Modal/TaskModal';
import {
CommentTooltipText,
TooltipTypography,
} from 'src/components/Task/TaskRow/CommentTooltipText';
import { TaskActionPhase } from 'src/components/Task/TaskRow/TaskActionPhase';
import { TaskRowFragment } from 'src/components/Task/TaskRow/TaskRow.generated';
import { StarredItemIcon } from 'src/components/common/StarredItemIcon/StarredItemIcon';
import { usePhaseData } from 'src/hooks/usePhaseData';
import useTaskModal from 'src/hooks/useTaskModal';
import theme from 'src/theme';
import { getLocalizedTaskType } from 'src/utils/functions/getLocalizedTaskType';
import { DeleteTaskIconButton } from '../DeleteTaskIconButton/DeleteTaskIconButton';
import { StarTaskIconButton } from '../StarTaskIconButton/StarTaskIconButton';
import { TaskCommentsButton } from './TaskCommentsButton/TaskCommentsButton';
Expand All @@ -36,12 +49,6 @@
height: '100%',
}));

const TaskType = styled(Typography)(({ theme }) => ({
fontSize: 14,
fontWeight: 700,
color: theme.palette.text.primary,
}));

const TaskDescription = styled(Typography)(({ theme }) => ({
fontSize: 14,
color: theme.palette.text.primary,
Expand All @@ -66,14 +73,6 @@
},
}));

const AssigneeName = styled(Typography)(({ theme }) => ({
fontSize: 14,
color: theme.palette.text.primary,
margin: theme.spacing(1),
overflow: 'hidden',
textOverflow: 'ellipsis',
}));

const StarIconWrap = styled(Box)(({ theme }) => ({
margin: theme.spacing(1),
}));
Expand Down Expand Up @@ -156,12 +155,13 @@
const dateToShow = completedAt ?? startAt;
const taskDate = (dateToShow && DateTime.fromISO(dateToShow)) || null;
const assigneeName = user ? `${user.firstName} ${user.lastName}` : '';
const tagList = !!task.tagList.length ? task.tagList.join(', ') : '';
const activityData = activityType ? activityTypes.get(activityType) : null;

return !hasBeenDeleted ? (
<TaskRowWrap isChecked={isChecked}>
<TaskItemWrap width={theme.spacing(20)} justifyContent="space-between">
<Checkbox
<StyledCheckbox
checked={isChecked}
color="secondary"
onChange={() => onTaskCheckToggle(task.id)}
Expand All @@ -177,30 +177,82 @@
onClick={handleSubjectPressed}
onMouseEnter={() => preloadTaskModal(TaskModalEnum.Edit)}
>
<TaskType>
{activityData && `${activityData.phase} - `}
{getLocalizedTaskType(t, activityType)}
</TaskType>
<TaskDescription>{subject}</TaskDescription>
<TaskActionPhase
activityData={activityData}
activityType={activityType}
/>

<Tooltip title={subject} placement="top-start" arrow>
<TaskDescription>{subject}</TaskDescription>
</Tooltip>
</SubjectWrap>

<TaskItemWrap justifyContent="end" maxWidth={theme.spacing(45)}>
<AssigneeName noWrap>{assigneeName}</AssigneeName>
<Box width={theme.spacing(16)}>
<TaskDate isComplete={isComplete} taskDate={taskDate} />
{(assigneeName || tagList) && (
<Tooltip
title={
<>
{assigneeName && (
<TooltipTypography>
{t('Assignee: ') + assigneeName}
</TooltipTypography>
)}
{tagList && (
<TooltipTypography>{t('Tags: ') + tagList}</TooltipTypography>
)}
</>
}
placement="top"
arrow
enterTouchDelay={0}
>
{assigneeName ? (
<Avatar
data-testid={`assigneeAvatar-${task.id}`}
sx={{
width: 30,
height: 30,
}}
>
{(task?.user?.firstName?.[0] || '') +
task?.user?.lastName?.[0] || ''}

Check warning on line 218 in src/components/Contacts/ContactDetails/ContactTasksTab/ContactTaskRow/ContactTaskRow.tsx

View check run for this annotation

Codecov / codecov/patch

src/components/Contacts/ContactDetails/ContactTasksTab/ContactTaskRow/ContactTaskRow.tsx#L218

Added line #L218 was not covered by tests
</Avatar>
) : (
<LocalOffer
data-testid={`tagIcon-${task.id}`}
sx={{ color: theme.palette.secondary.dark }}
/>
)}
</Tooltip>
)}
<Box>
<TaskDate isComplete={isComplete} taskDate={taskDate} small />
<Tooltip
title={
comments?.totalCount ? (
<CommentTooltipText comments={comments.nodes} />
) : null

Check warning on line 234 in src/components/Contacts/ContactDetails/ContactTasksTab/ContactTaskRow/ContactTaskRow.tsx

View check run for this annotation

Codecov / codecov/patch

src/components/Contacts/ContactDetails/ContactTasksTab/ContactTaskRow/ContactTaskRow.tsx#L234

Added line #L234 was not covered by tests
}
placement="top"
arrow
>
<Box>
<TaskCommentsButton
isComplete={isComplete}
numberOfComments={comments?.totalCount}
onClick={handleCommentButtonPressed}
onMouseEnter={() => preloadTaskModal(TaskModalEnum.Comments)}
small
/>
</Box>
</Tooltip>
</Box>
<TaskCommentsButton
isComplete={isComplete}
numberOfComments={comments?.totalCount}
onClick={handleCommentButtonPressed}
onMouseEnter={() => preloadTaskModal(TaskModalEnum.Comments)}
detailsPage
/>

<DeleteTaskIconButton
accountListId={accountListId}
taskId={task.id}
onDeleteConfirm={handleDeleteConfirm}
small
/>
<StarTaskIconButton
accountListId={accountListId}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const TaskRowWrap = styled(Box)(({ theme }) => ({
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
margin: theme.spacing(1),
margin: theme.spacing(1, 0.5),
}));

const TaskCommentIcon = styled(CalendarToday, {
Expand All @@ -32,7 +32,7 @@ const DateText = styled(Typography, {
: isComplete
? theme.palette.text.secondary
: theme.palette.text.primary,
marginLeft: theme.spacing(1),
marginLeft: small ? theme.spacing(0.5) : theme.spacing(1),
}),
);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useEffect, useMemo, useRef, useState } from 'react';
import Add from '@mui/icons-material/Add';
import CheckCircleOutline from '@mui/icons-material/CheckCircleOutline';
import { Box, Button, Checkbox, Divider, Typography } from '@mui/material';
import { Box, Button, Divider, Hidden, Typography } from '@mui/material';
import { styled } from '@mui/material/styles';
import { DateTime } from 'luxon';
import { useTranslation } from 'react-i18next';
Expand All @@ -18,6 +18,7 @@ import { TaskFilterSetInput } from 'src/graphql/types.generated';
import { useGetTaskIdsForMassSelectionQuery } from 'src/hooks/GetIdsForMassSelection.generated';
import { useMassSelection } from 'src/hooks/useMassSelection';
import useTaskModal from 'src/hooks/useTaskModal';
import { StyledCheckbox } from '../../ContactRow/ContactRow';
import { ContactTaskRow } from './ContactTaskRow/ContactTaskRow';
import {
useContactPhaseQuery,
Expand Down Expand Up @@ -220,7 +221,7 @@ export const ContactTasksTab: React.FC<ContactTasksTabProps> = ({
</HeaderRow>
<HeaderRow mb={1}>
<HeaderItemsWrap>
<Checkbox
<StyledCheckbox
checked={selectionType === ListHeaderCheckBoxState.Checked}
color="secondary"
indeterminate={selectionType === ListHeaderCheckBoxState.Partial}
Expand All @@ -232,6 +233,13 @@ export const ContactTasksTab: React.FC<ContactTasksTabProps> = ({
placeholder={t('Search Tasks')}
page={PageEnum.Task}
/>
{!!ids.length && (
<Hidden smDown>
<Typography ml={2}>
{t('{{count}} Selected', { count: ids.length })}
</Typography>
</Hidden>
)}
</HeaderItemsWrap>
<HeaderItemsWrap>
<PlaceholderActionBar />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,26 @@ import { useTranslation } from 'react-i18next';
import { DeletedItemIcon } from '../../../../common/DeleteItemIcon/DeleteItemIcon';
import { DeleteConfirmation } from '../../../../common/Modal/DeleteConfirmation/DeleteConfirmation';

const DeleteButton = styled(IconButton)(({ theme }) => ({
margin: theme.spacing(1),
const DeleteButton = styled(IconButton, {
shouldForwardProp: (prop) => prop !== 'small',
})<{ small?: boolean }>(({ small, theme }) => ({
margin: small ? theme.spacing(0.5) : theme.spacing(1),
}));

interface DeleteTaskIconButtonProps {
accountListId: string;
taskId: string;
onDeleteConfirm?: () => void;
removeSelectedIds?: (id: string[]) => void;
small?: boolean;
}

export const DeleteTaskIconButton: React.FC<DeleteTaskIconButtonProps> = ({
accountListId,
taskId,
onDeleteConfirm,
removeSelectedIds,
small = false,
}) => {
const { t } = useTranslation();

Expand All @@ -31,6 +35,7 @@ export const DeleteTaskIconButton: React.FC<DeleteTaskIconButtonProps> = ({
<DeleteButton
onClick={() => setRemoveDialogOpen(true)}
data-testid={`DeleteIconButton-${taskId}`}
small={small}
>
<DeletedItemIcon />
</DeleteButton>
Expand Down
2 changes: 1 addition & 1 deletion src/components/Task/Modal/Form/TaskModalForm.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ describe('TaskModalForm', () => {
<TaskModalForm
accountListId={accountListId}
onClose={onClose}
task={mockTask}
task={null}
/>
</GqlMockedProvider>
</SnackbarProvider>
Expand Down
2 changes: 1 addition & 1 deletion src/components/Task/Modal/Form/TaskModalForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ const TaskModalForm = ({
nextAction: task.nextAction ?? null,
tagList: additionalTags ?? [],
contactIds: task.contacts.nodes.map(({ id }) => id),
userId: task.user?.id ?? session.data?.user.userID ?? null,
userId: task.user?.id ?? null,
notificationTimeBefore: task.notificationTimeBefore,
notificationType: task.notificationType,
notificationTimeUnit: task.notificationTimeUnit,
Expand Down
46 changes: 46 additions & 0 deletions src/components/Task/TaskRow/CommentTooltipText.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { render } from '@testing-library/react';
import { DateTime } from 'luxon';
import TestRouter from '__tests__/util/TestRouter';
import { GqlMockedProvider, gqlMock } from '__tests__/util/graphqlMocking';
import {
TaskModalCommentFragment,
TaskModalCommentFragmentDoc,
} from '../Modal/Comments/TaskListComments.generated';
import { CommentTooltipText } from './CommentTooltipText';

const accountListId = 'account-list-1';
const commentId = 'comment-1';
const router = {
query: { accountListId },
isReady: true,
};

const TestComponent: React.FC = () => {
const comment = gqlMock<TaskModalCommentFragment>(
TaskModalCommentFragmentDoc,
{
mocks: {
id: commentId,
body: 'Comment',
updatedAt: DateTime.local(2020, 1, 2).toISODate() ?? '',
},
},
);

return (
<TestRouter router={router}>
<GqlMockedProvider>
<CommentTooltipText comments={[comment]} />
</GqlMockedProvider>
</TestRouter>
);
};

describe('CommentTooltipText', () => {
it('should render', async () => {
const { findByText, getByText } = render(<TestComponent />);

expect(await findByText('Comment')).toBeInTheDocument();
expect(getByText('Jan 2, 2020')).toBeInTheDocument();
});
});
Loading
Loading