diff --git a/__tests__/Unit/Components/Tasks/ProgressUpdateCard/ProgressUpdateCard.test.tsx b/__tests__/Unit/Components/Tasks/ProgressUpdateCard/ProgressUpdateCard.test.tsx index d006fc9ea..f91e1f86b 100644 --- a/__tests__/Unit/Components/Tasks/ProgressUpdateCard/ProgressUpdateCard.test.tsx +++ b/__tests__/Unit/Components/Tasks/ProgressUpdateCard/ProgressUpdateCard.test.tsx @@ -14,6 +14,21 @@ beforeEach(() => { }); describe('ProgressUpdateCard Component', () => { + it('should check if the onCardClick function is called when user click on card', () => { + renderWithRouter( + + + + ); + const progressUpdateCardContainer = screen.getByTestId( + 'progress-update-card-container' + ); + fireEvent.click(progressUpdateCardContainer); + expect(progressUpdateCardContainer).toHaveClass( + 'progress-update-card__container expand' + ); + }); + it('should render completed section string as title in card', () => { renderWithRouter( diff --git a/__tests__/Unit/Components/Tasks/ProgressUpdateCard/ProgressUpdateCardPresentation.test.tsx b/__tests__/Unit/Components/Tasks/ProgressUpdateCard/ProgressUpdateCardPresentation.test.tsx index 3660fc1ae..260d5bffd 100644 --- a/__tests__/Unit/Components/Tasks/ProgressUpdateCard/ProgressUpdateCardPresentation.test.tsx +++ b/__tests__/Unit/Components/Tasks/ProgressUpdateCard/ProgressUpdateCardPresentation.test.tsx @@ -2,11 +2,12 @@ import moment from 'moment'; import { MouseEvent } from 'react'; import { Provider } from 'react-redux'; import { fireEvent, screen } from '@testing-library/react'; - +import { useRouter } from 'next/router'; import { mockGetTaskProgress } from '../../../../../__mocks__/db/progresses'; import { renderWithRouter } from '@/test_utils/createMockRouter'; import { readMoreFormatter } from '@/utils/common'; import { store } from '@/app/store'; +import { DEFAULT_AVATAR, USER_MANAGEMENT_URL } from '@/constants/url'; import { ProgressUpdateCardPresentationProps, @@ -17,6 +18,8 @@ import ProgressUpdateCardPresentation from '@/components/taskDetails/ProgressUpd let initialProps: ProgressUpdateCardPresentationProps; const titleToShow = mockGetTaskProgress.data[1].completed; +const username = 'mock-user-name'; +const userProfileImageUrl = 'random-profile-pic-url'; const momentDate = moment(mockGetTaskProgress.data[2].createdAt); const fullDate = momentDate.format('DD-MM-YY'); const time = momentDate.format('hh:mmA'); @@ -29,6 +32,9 @@ let mockedOnMoreOrLessButtonClick: jest.Mock< [React.MouseEvent] >; let mockedOnCardClick: jest.Mock]>; +jest.mock('next/router', () => ({ + useRouter: jest.fn(), +})); const charactersToShow = 70; const dataToShowState = [ { @@ -114,6 +120,8 @@ describe('ProgressUpdateCardPresentation Component', () => { jest.fn]>(); mockedOnCardClick = jest.fn]>(); initialProps = { + username: username, + userProfileImageUrl: userProfileImageUrl, titleToShow: titleToShow, isExpanded: false, dateInAgoFormat: dateInAgoFormat, @@ -123,7 +131,76 @@ describe('ProgressUpdateCardPresentation Component', () => { onCardClick: mockedOnCardClick, }; }); + + it('should rotate the angle icon when expanded', () => { + (useRouter as jest.Mock).mockReturnValue({ + query: { dev: 'false' }, + }); + const props = { ...initialProps, isExpanded: true }; + renderWithRouter( + + + + ); + const angleIcon = screen.getByTestId('progress-update-card-angle-icon'); + expect(angleIcon).toHaveStyle('transform: rotate(90deg)'); + }); + it('should have respective classes on date container and date text', () => { + (useRouter as jest.Mock).mockReturnValue({ + query: { dev: 'false' }, + }); + renderWithRouter( + + + + ); + const dateContainer = screen.getByTestId('progress-update-card-date'); + const dateText = screen.getByText(dateInAgoFormat); + + expect(dateContainer).toHaveClass( + 'progress-update-card__date-container' + ); + expect(dateText).toHaveClass('progress-update-card__date-text'); + }); + + it('should not rotate the angle icon when not expanded', () => { + (useRouter as jest.Mock).mockReturnValue({ + query: { dev: 'false' }, + }); + const props = { ...initialProps, isExpanded: false }; + renderWithRouter( + + + + ); + const angleIcon = screen.getByTestId('progress-update-card-angle-icon'); + expect(angleIcon).toHaveStyle('transform: none'); + }); + + it('should prevent event propagation when clicking on the date container', () => { + (useRouter as jest.Mock).mockReturnValue({ + query: { dev: 'false' }, + }); + renderWithRouter( + + + + ); + + const dateContainer = screen.getByTestId('progress-update-card-date'); + const stopPropagationMock = jest.fn(); + dateContainer.addEventListener( + 'click', + (event) => (event.stopPropagation = stopPropagationMock) + ); + fireEvent.click(dateContainer); + expect(stopPropagationMock).toHaveBeenCalled(); + }); + it('should render completed section string as title in card', () => { + (useRouter as jest.Mock).mockReturnValue({ + query: { dev: 'false' }, + }); renderWithRouter( @@ -135,6 +212,9 @@ describe('ProgressUpdateCardPresentation Component', () => { ); }); it('should render date with ago format', () => { + (useRouter as jest.Mock).mockReturnValue({ + query: { dev: 'false' }, + }); renderWithRouter( @@ -144,6 +224,106 @@ describe('ProgressUpdateCardPresentation Component', () => { expect(date).toHaveTextContent(dateInAgoFormat); }); + it('should render the name and profile picture of the updater', () => { + (useRouter as jest.Mock).mockReturnValue({ + query: { dev: 'true' }, + }); + const props: ProgressUpdateCardPresentationProps = { + ...initialProps, + }; + renderWithRouter( + + + + ); + const usernameElement = screen.getByTestId( + 'progress-update-card-user-info-container' + ); + expect(usernameElement).toBeInTheDocument(); + expect(usernameElement.textContent).toContain('mock-user-name'); + const userProfileImageUrlElement = screen.getByTestId( + 'progress-update-card-profile-picture' + ); + + expect(userProfileImageUrlElement).toBeInTheDocument(); + expect(userProfileImageUrlElement).toHaveAttribute( + 'src', + userProfileImageUrl + ); + expect(userProfileImageUrlElement).toHaveAttribute('alt', 'Avatar'); + }); + it('should display the default avatar if profile url is empty', () => { + (useRouter as jest.Mock).mockReturnValue({ + query: { dev: 'true' }, + }); + const props: ProgressUpdateCardPresentationProps = { + ...initialProps, + userProfileImageUrl: '', + }; + renderWithRouter( + + + + ); + const userProfileImageUrlElement = screen.getByTestId( + 'progress-update-card-profile-picture' + ); + + expect(userProfileImageUrlElement).toBeInTheDocument(); + expect(userProfileImageUrlElement).toHaveAttribute( + 'src', + DEFAULT_AVATAR + ); + }); + it('should render the updater name as a link with the correct href', () => { + (useRouter as jest.Mock).mockReturnValue({ + query: { dev: 'true' }, + }); + const props: ProgressUpdateCardPresentationProps = { + ...initialProps, + }; + renderWithRouter( + + + + ); + + const linkElement = screen + .getByTestId('progress-update-card-user-info-container') + .querySelector('a'); + expect(linkElement).toBeInTheDocument(); + expect(linkElement).toHaveAttribute( + 'href', + `${USER_MANAGEMENT_URL}?username=${username}` + ); + expect(linkElement?.textContent).toContain(username); + }); + it('should prevent event propagation when clicking on the user info container', () => { + (useRouter as jest.Mock).mockReturnValue({ + query: { dev: 'true' }, + }); + + renderWithRouter( + + + + ); + + const userInfoContainer = screen.getByTestId( + 'progress-update-card-user-info-container' + ); + + const stopPropagationMock = jest.fn(); + + userInfoContainer.addEventListener( + 'click', + (event) => (event.stopPropagation = stopPropagationMock) + ); + + fireEvent.click(userInfoContainer); + expect(stopPropagationMock).toHaveBeenCalled(); + }); + it('should not render the tooltip when isToolisTooltipVisible is false', () => { const props: ProgressUpdateCardPresentationProps = { ...initialProps, diff --git a/src/app/services/usersApi.ts b/src/app/services/usersApi.ts index 94984f134..9d528482a 100644 --- a/src/app/services/usersApi.ts +++ b/src/app/services/usersApi.ts @@ -15,6 +15,10 @@ type UsernameQueryArgs = { searchString?: string; size?: number; }; +type UserResponse = { + message: string; + user: userDataType; +}; export const usersApi = api.injectEndpoints({ endpoints: (build) => ({ @@ -39,6 +43,10 @@ export const usersApi = api.injectEndpoints({ query: ({ searchString }) => `/users?search=${searchString}`, providesTags: ['Users'], }), + getUserDetailsById: build.query({ + query: ({ searchString }) => `/users?id=${searchString}`, + providesTags: ['Users'], + }), }), }); @@ -48,4 +56,5 @@ export const { useGetUsersQuery, useGetAllUsersByUsernameQuery, useGetAllUsersQuery, + useGetUserDetailsByIdQuery, } = usersApi; diff --git a/src/components/common/Tooltip/tooltip.module.scss b/src/components/common/Tooltip/tooltip.module.scss index ec48ad4ef..21906382c 100644 --- a/src/components/common/Tooltip/tooltip.module.scss +++ b/src/components/common/Tooltip/tooltip.module.scss @@ -2,7 +2,7 @@ .tooltip { position: absolute; - width: 17rem; + width: 18rem; // position - top, bottom, right, left will come from props for reusability background-color: $theme-primary; border: 1px solid $theme-primary; diff --git a/src/components/taskDetails/ProgressUpdateCard/LatestProgressUpdateCard.tsx b/src/components/taskDetails/ProgressUpdateCard/LatestProgressUpdateCard.tsx index 564b5bd61..8ae6ba79a 100644 --- a/src/components/taskDetails/ProgressUpdateCard/LatestProgressUpdateCard.tsx +++ b/src/components/taskDetails/ProgressUpdateCard/LatestProgressUpdateCard.tsx @@ -3,6 +3,7 @@ import React, { MouseEvent, useState } from 'react'; import { readMoreFormatter } from '@/utils/common'; import { ProgressDetailsData } from '@/types/standup.type'; import LatestProgressUpdateCardPresentation from './LatestProgressUpdateCardPresentation'; +import { useGetUserDetailsByIdQuery } from '@/app/services/usersApi'; type LatestProgressUpdateCardProps = { data: ProgressDetailsData; @@ -21,11 +22,17 @@ export default function LatestProgressUpdateCard({ data, }: LatestProgressUpdateCardProps) { const momentDate = moment(data?.createdAt); + const userId = data?.userId; + const { data: userData } = useGetUserDetailsByIdQuery({ + searchString: userId, + }); + const username = userData?.user?.username ?? ''; + const userProfileImageUrl = userData?.user?.picture?.url ?? ''; + const dateInAgoFormat = momentDate.fromNow(); const fullDate = momentDate.format('dddd, MMMM DD, YYYY, hh:mm A [GMT] Z'); const tooltipText = `Updated at ${fullDate}`; const charactersToShow = 70; - const dataToShow = [ { id: `completed-${data.id}`, @@ -55,7 +62,6 @@ export default function LatestProgressUpdateCard({ const [dataToShowState, setDataToShowState] = useState(dataToShow); - function onMoreOrLessButtonClick( e: MouseEvent, clickedOnData: ProgressUpdateDataToShow @@ -74,6 +80,8 @@ export default function LatestProgressUpdateCard({ return ( (
@@ -79,6 +89,41 @@ export default function LatestProgressUpdateCardPresentation({ + {isDevMode && ( + + by + + {'Avatar'} + {username} + + + )}
diff --git a/src/components/taskDetails/ProgressUpdateCard/ProgressUpdateCard.tsx b/src/components/taskDetails/ProgressUpdateCard/ProgressUpdateCard.tsx index 707938bb7..00982c104 100644 --- a/src/components/taskDetails/ProgressUpdateCard/ProgressUpdateCard.tsx +++ b/src/components/taskDetails/ProgressUpdateCard/ProgressUpdateCard.tsx @@ -6,12 +6,20 @@ import { ProgressUpdateCardProps, ProgressUpdateDataToShow, } from './progressUpdateCard.types'; +import { useGetUserDetailsByIdQuery } from '@/app/services/usersApi'; export default memo(function ProgressUpdateCard({ data, }: ProgressUpdateCardProps) { const momentDate = moment(data?.createdAt); const dateInAgoFormat = momentDate.fromNow(); + const userId = data?.userId; + const { data: userData } = useGetUserDetailsByIdQuery({ + searchString: userId, + }); + const username = userData?.user?.username ?? ''; + const userProfileImageUrl = userData?.user?.picture?.url ?? ''; + const charactersToShow = 70; const readMoreTitle = readMoreFormatter(data?.completed, charactersToShow); const titleToShow = readMoreTitle; @@ -72,6 +80,8 @@ export default memo(function ProgressUpdateCard({ } return ( (
onMoreOrLessButtonClick(e, datum)} + data-testid="progress-update-more-less-button" className={ styles['progress-update-card__more-less-button'] } @@ -59,34 +67,90 @@ export default function ProgressUpdateCardPresentation({

{titleToShow}

- - event.stopPropagation()} - data-testid="progress-update-card-date" + content={tooltipString} > - event.stopPropagation()} + data-testid="progress-update-card-date" > - {dateInAgoFormat} + + + {dateInAgoFormat} + - - - + + + {isDevMode && ( + event.stopPropagation()} + > + by + + {'Avatar'}{' '} + {username} + + + )} + +
+
) => void; export type ProgressUpdateCardPresentationProps = { + username: string; + userProfileImageUrl: string; titleToShow: string; dateInAgoFormat: string; tooltipString: string; @@ -21,6 +23,8 @@ export type ProgressUpdateCardPresentationProps = { }; export type LatestProgressUpdateCardPresentationProps = { + username: string; + userProfileImageUrl: string; dataToShowState: ProgressUpdateDataToShow[]; tooltipText: string; onMoreOrLessButtonClick: (