From 9d8af3399258fd0799ebdd313c096ee9feb81449 Mon Sep 17 00:00:00 2001 From: Mandy Date: Thu, 30 May 2024 11:40:10 +0800 Subject: [PATCH] =?UTF-8?q?ADM-944:=20[frontend]=20feat:=20show=20thumbUp?= =?UTF-8?q?=20and=20thumDown=20Icon=20in=20tooltip=20a=E2=80=A6=20(#1473)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ADM-944: [frontend] feat: show thumbUp and thumDown Icon in tooltip and always show positive data * ADM-944: [frontend] feat: fix test * ADM-944: [frontend] feat: add TrendTypeIcon test * ADM-944: [frontend] feat: rename test] --- .../ReportStep/ChartAndTitleWrapper.test.tsx | 26 +++++++++---- .../containers/ReportStep/style.test.tsx | 14 +++++++ frontend/__tests__/utils/Util.test.tsx | 11 +++--- frontend/src/constants/resources.ts | 5 +++ .../ReportStep/BoardMetricsChart/index.tsx | 8 +++- .../ReportStep/ChartAndTitleWrapper/index.tsx | 38 ++++++++++++++----- .../ReportStep/ChartAndTitleWrapper/style.tsx | 14 ++++++- frontend/src/utils/util.ts | 13 ++++--- 8 files changed, 99 insertions(+), 30 deletions(-) diff --git a/frontend/__tests__/containers/ReportStep/ChartAndTitleWrapper.test.tsx b/frontend/__tests__/containers/ReportStep/ChartAndTitleWrapper.test.tsx index 1f890ec26..40d3c3741 100644 --- a/frontend/__tests__/containers/ReportStep/ChartAndTitleWrapper.test.tsx +++ b/frontend/__tests__/containers/ReportStep/ChartAndTitleWrapper.test.tsx @@ -1,13 +1,13 @@ import ChartAndTitleWrapper from '@src/containers/ReportStep/ChartAndTitleWrapper'; -import { CHART_TYPE, TREND_ICON } from '@src/constants/resources'; +import { CHART_TYPE, TREND_ICON, TREND_TYPE } from '@src/constants/resources'; import { render, screen } from '@testing-library/react'; describe('ChartAndTitleWrapper', () => { - it('should render green up icon when icon is set to up and green', () => { + it('should render green up icon given icon is set to up and better', () => { const testedTrendInfo = { - color: 'green', + trendType: TREND_TYPE.BETTER, icon: TREND_ICON.UP, - trendNumber: 83.72, + trendNumber: 0.83, type: CHART_TYPE.VELOCITY, }; render(); @@ -17,11 +17,11 @@ describe('ChartAndTitleWrapper', () => { expect(icon.parentElement?.parentElement).toHaveStyle({ color: 'green' }); }); - it('should render down icon when icon is set to down', () => { + it('should render down icon given icon is set to down and worse', () => { const testedTrendInfo = { - color: 'red', + trendType: TREND_TYPE.WORSE, icon: TREND_ICON.DOWN, - trendNumber: -83.72, + trendNumber: -0.83, type: CHART_TYPE.VELOCITY, }; render(); @@ -30,4 +30,16 @@ describe('ChartAndTitleWrapper', () => { expect(screen.getByTestId('TrendingDownSharpIcon')).toBeInTheDocument(); expect(icon.parentElement?.parentElement).toHaveStyle({ color: 'red' }); }); + + it('should show positive trend number even if the tend number is negative', () => { + const testedTrendInfo = { + trendType: TREND_TYPE.WORSE, + icon: TREND_ICON.DOWN, + trendNumber: -0.8372, + type: CHART_TYPE.VELOCITY, + }; + render(); + + expect(screen.getByLabelText('trend number')).toHaveTextContent('83.72%'); + }); }); diff --git a/frontend/__tests__/containers/ReportStep/style.test.tsx b/frontend/__tests__/containers/ReportStep/style.test.tsx index 27ef282be..508449156 100644 --- a/frontend/__tests__/containers/ReportStep/style.test.tsx +++ b/frontend/__tests__/containers/ReportStep/style.test.tsx @@ -1,5 +1,7 @@ +import { TrendTypeIcon } from '@src/containers/ReportStep/ChartAndTitleWrapper/style'; import { StyledCalendarWrapper } from '@src/containers/ReportStep/style'; import { render, screen } from '@testing-library/react'; +import ThumbUpIcon from '@mui/icons-material/ThumbUp'; describe('Report step styled components', () => { it('should render the bottom margin depend on whether StyledCalendarWrapper in summary page', () => { @@ -22,3 +24,15 @@ describe('Report step styled components', () => { expect(component2).toHaveStyle({ 'margin-bottom': '2rem' }); }); }); + +describe('ChartAndTitleWrapper styled component', () => { + it('should render the TrendTypeIcon with the given color', () => { + render( + + + , + ); + console.log(screen.getByLabelText('test component 1')); + expect(screen.getByLabelText('test component 1').children[0]).toHaveStyle({ color: 'red' }); + }); +}); diff --git a/frontend/__tests__/utils/Util.test.tsx b/frontend/__tests__/utils/Util.test.tsx index 208cf9e40..3e4eaf1dd 100644 --- a/frontend/__tests__/utils/Util.test.tsx +++ b/frontend/__tests__/utils/Util.test.tsx @@ -25,6 +25,7 @@ import { DOWN_TREND_IS_BETTER, METRICS_CONSTANTS, TREND_ICON, + TREND_TYPE, UP_TREND_IS_BETTER, } from '@src/constants/resources'; import { CleanedBuildKiteEmoji, OriginBuildKiteEmoji } from '@src/constants/emojis/emoji'; @@ -639,7 +640,7 @@ describe('calculateTrendInfo function', () => { expect(result.dateRangeList).toEqual(undefined); expect(result.trendNumber).toEqual(undefined); - expect(result.color).toEqual(undefined); + expect(result.trendType).toEqual(undefined); expect(result.icon).toEqual(undefined); expect(result.type).toEqual(CHART_TYPE.VELOCITY); }); @@ -652,7 +653,7 @@ describe('calculateTrendInfo function', () => { expect(result.dateRangeList).toEqual(['2024/01/22-2024/01/23', '2024/01/15-2024/01/19']); expect(result.trendNumber).toEqual(2); - expect(result.color).toEqual('green'); + expect(result.trendType).toEqual(TREND_TYPE.BETTER); expect(result.icon).toEqual(TREND_ICON.UP); expect(result.type).toEqual(type); }, @@ -667,7 +668,7 @@ describe('calculateTrendInfo function', () => { expect(result.dateRangeList).toEqual(['2024/01/22-2024/01/23', '2024/01/15-2024/01/19']); expect(Number(result.trendNumber?.toFixed(2))).toEqual(-0.67); - expect(result.color).toEqual('red'); + expect(result.trendType).toEqual(TREND_TYPE.WORSE); expect(result.icon).toEqual(TREND_ICON.DOWN); expect(result.type).toEqual(type); }, @@ -682,7 +683,7 @@ describe('calculateTrendInfo function', () => { expect(result.dateRangeList).toEqual(['2024/01/22-2024/01/23', '2024/01/15-2024/01/19']); expect(Number(result.trendNumber?.toFixed(2))).toEqual(-0.67); - expect(result.color).toEqual('green'); + expect(result.trendType).toEqual(TREND_TYPE.BETTER); expect(result.icon).toEqual(TREND_ICON.DOWN); expect(result.type).toEqual(type); }, @@ -697,7 +698,7 @@ describe('calculateTrendInfo function', () => { expect(result.dateRangeList).toEqual(['2024/01/22-2024/01/23', '2024/01/15-2024/01/19']); expect(result.trendNumber).toEqual(2); - expect(result.color).toEqual('red'); + expect(result.trendType).toEqual(TREND_TYPE.WORSE); expect(result.icon).toEqual(TREND_ICON.UP); expect(result.type).toEqual(type); }, diff --git a/frontend/src/constants/resources.ts b/frontend/src/constants/resources.ts index b39dc6f79..f0f0517df 100644 --- a/frontend/src/constants/resources.ts +++ b/frontend/src/constants/resources.ts @@ -101,6 +101,11 @@ export enum TREND_ICON { DOWN = 'DOWN', } +export enum TREND_TYPE { + BETTER = 'BETTER', + WORSE = 'WORSE', +} + export const CHART_TREND_TIP = { [CHART_TYPE.VELOCITY]: 'Velocity(Story point)', [CHART_TYPE.AVERAGE_CYCLE_TIME]: 'Days/Story point', diff --git a/frontend/src/containers/ReportStep/BoardMetricsChart/index.tsx b/frontend/src/containers/ReportStep/BoardMetricsChart/index.tsx index cfabbe3ab..54aa85fcb 100644 --- a/frontend/src/containers/ReportStep/BoardMetricsChart/index.tsx +++ b/frontend/src/containers/ReportStep/BoardMetricsChart/index.tsx @@ -2,8 +2,8 @@ import { stackedAreaOptionMapper, stackedBarOptionMapper, } from '@src/containers/ReportStep/BoardMetricsChart/ChartOption'; +import { CHART_TYPE, CYCLE_TIME_CHARTS_MAPPING, METRICS_CONSTANTS } from '@src/constants/resources'; import ChartAndTitleWrapper from '@src/containers/ReportStep/ChartAndTitleWrapper'; -import { CHART_TYPE, CYCLE_TIME_CHARTS_MAPPING } from '@src/constants/resources'; import { calculateTrendInfo, xAxisLabelDateFormatter } from '@src/utils/util'; import { ReportResponse, Swimlane } from '@src/clients/report/dto/response'; import { ChartContainer } from '@src/containers/MetricsStep/style'; @@ -159,7 +159,11 @@ function extractCycleTimeData(dateRanges: string[], mappedData?: ReportResponse[ for (const [name, data] of Object.entries(cycleTimeByStatus)) { indicators.push({ data, name: CYCLE_TIME_CHARTS_MAPPING[name], type: 'bar' }); } - const trendInfo = calculateTrendInfo(totalCycleTime, dateRanges, CHART_TYPE.CYCLE_TIME_ALLOCATION); + const developmentPercentageList = indicators.find( + ({ name }) => name === CYCLE_TIME_CHARTS_MAPPING[METRICS_CONSTANTS.inDevValue], + )?.data; + const trendInfo = calculateTrendInfo(developmentPercentageList, dateRanges, CHART_TYPE.CYCLE_TIME_ALLOCATION); + return { xAxis: dateRanges, yAxis: { diff --git a/frontend/src/containers/ReportStep/ChartAndTitleWrapper/index.tsx b/frontend/src/containers/ReportStep/ChartAndTitleWrapper/index.tsx index f9117b8ee..6381dfce7 100644 --- a/frontend/src/containers/ReportStep/ChartAndTitleWrapper/index.tsx +++ b/frontend/src/containers/ReportStep/ChartAndTitleWrapper/index.tsx @@ -1,23 +1,28 @@ import { ChartTitle, - StyledToolTipContent, + StyledTooltipContent, + StyledTooltipWrapper, TrendContainer, TrendIcon, + TrendTypeIcon, } from '@src/containers/ReportStep/ChartAndTitleWrapper/style'; -import { CHART_TREND_TIP, CHART_TYPE, TREND_ICON, UP_TREND_IS_BETTER } from '@src/constants/resources'; +import { CHART_TREND_TIP, CHART_TYPE, TREND_ICON, TREND_TYPE, UP_TREND_IS_BETTER } from '@src/constants/resources'; import TrendingDownSharpIcon from '@mui/icons-material/TrendingDownSharp'; import TrendingUpSharpIcon from '@mui/icons-material/TrendingUpSharp'; import { ChartWrapper } from '@src/containers/MetricsStep/style'; +import ThumbDownIcon from '@mui/icons-material/ThumbDown'; import { convertNumberToPercent } from '@src/utils/util'; import React, { ForwardedRef, forwardRef } from 'react'; +import ThumbUpIcon from '@mui/icons-material/ThumbUp'; import { Tooltip } from '@mui/material'; +import { theme } from '@src/theme'; export interface ITrendInfo { - color?: string; icon?: TREND_ICON; trendNumber?: number; dateRangeList?: string[]; type: CHART_TYPE; + trendType?: TREND_TYPE; } const TREND_ICON_MAPPING = { @@ -25,6 +30,16 @@ const TREND_ICON_MAPPING = { [TREND_ICON.DOWN]: , }; +const TREND_TYPE_ICON_MAPPING = { + [TREND_TYPE.BETTER]: , + [TREND_TYPE.WORSE]: , +}; + +const TREND_COLOR_MAP = { + [TREND_TYPE.BETTER]: theme.main.chartTrend.betterColor, + [TREND_TYPE.WORSE]: theme.main.chartTrend.worseColor, +}; + const DECREASE = 'decrease'; const INCREASE = 'increase'; @@ -50,10 +65,15 @@ const ChartAndTitleWrapper = forwardRef( } }; const tipContent = ( - -

{`The rate of ${trendDescribe()} for ${CHART_TREND_TIP[trendInfo.type]}: `}

- {trendInfo.dateRangeList?.map((dateRange) =>

{dateRange}

)} -
+ + + {TREND_TYPE_ICON_MAPPING[trendInfo.trendType!]} + + +

{`The rate of ${trendDescribe()} for ${CHART_TREND_TIP[trendInfo.type]}: `}

+ {trendInfo.dateRangeList?.map((dateRange) =>

{dateRange}

)} +
+
); return ( @@ -62,9 +82,9 @@ const ChartAndTitleWrapper = forwardRef( {trendInfo.type} {trendInfo.trendNumber !== undefined && ( - + {TREND_ICON_MAPPING[trendInfo.icon!]} - {convertNumberToPercent(trendInfo.trendNumber)} + {convertNumberToPercent(trendInfo.trendNumber)} )} diff --git a/frontend/src/containers/ReportStep/ChartAndTitleWrapper/style.tsx b/frontend/src/containers/ReportStep/ChartAndTitleWrapper/style.tsx index 386710fc9..faa0483aa 100644 --- a/frontend/src/containers/ReportStep/ChartAndTitleWrapper/style.tsx +++ b/frontend/src/containers/ReportStep/ChartAndTitleWrapper/style.tsx @@ -30,9 +30,21 @@ export const TrendContainer = styled('div')(({ color }: { color: string }) => ({ fontSize: '1.125rem', })); -export const StyledToolTipContent = styled('div')({ +export const StyledTooltipContent = styled('div')({ fontSize: '0.85rem', display: 'flex', flexDirection: 'column', gap: '0.5rem', }); + +export const StyledTooltipWrapper = styled('div')({ + display: 'flex', +}); + +export const TrendTypeIcon = styled('div')(({ color }: { color: string }) => ({ + '& svg': { + color: color, + fontSize: '1rem', + marginRight: '0.5rem', + }, +})); diff --git a/frontend/src/utils/util.ts b/frontend/src/utils/util.ts index 87c9e9e48..47a844400 100644 --- a/frontend/src/utils/util.ts +++ b/frontend/src/utils/util.ts @@ -5,6 +5,7 @@ import { DOWN_TREND_IS_BETTER, METRICS_CONSTANTS, TREND_ICON, + TREND_TYPE, UP_TREND_IS_BETTER, } from '@src/constants/resources'; import { CleanedBuildKiteEmoji, OriginBuildKiteEmoji } from '@src/constants/emojis/emoji'; @@ -17,7 +18,6 @@ import { DateRangeList } from '@src/context/config/configSlice'; import { BoardInfoResponse } from '@src/hooks/useGetBoardInfo'; import { DATE_FORMAT_TEMPLATE } from '@src/constants/template'; import duration from 'dayjs/plugin/duration'; -import { theme } from '@src/theme'; import dayjs from 'dayjs'; dayjs.extend(duration); @@ -225,19 +225,19 @@ export const getTrendInfo = (trendNumber: number, dateRangeList: string[], type: if (UP_TREND_IS_BETTER.includes(type)) { if (trendNumber >= 0) { - result.color = theme.main.chartTrend.betterColor; result.icon = TREND_ICON.UP; + result.trendType = TREND_TYPE.BETTER; } else { - result.color = theme.main.chartTrend.worseColor; result.icon = TREND_ICON.DOWN; + result.trendType = TREND_TYPE.WORSE; } } else if (DOWN_TREND_IS_BETTER.includes(type)) { if (trendNumber <= 0) { - result.color = theme.main.chartTrend.betterColor; result.icon = TREND_ICON.DOWN; + result.trendType = TREND_TYPE.BETTER; } else { - result.color = theme.main.chartTrend.worseColor; result.icon = TREND_ICON.UP; + result.trendType = TREND_TYPE.WORSE; } } return result; @@ -262,7 +262,8 @@ export const calculateTrendInfo = ( }; export const convertNumberToPercent = (num: number): string => { - return (num * 100).toFixed(2) + '%'; + const positiveNumber = num >= 0 ? num : -num; + return (positiveNumber * 100).toFixed(2) + '%'; }; export const percentageFormatter = (showPercentage = true) => {