Skip to content

Commit e625e8e

Browse files
authored
Feature number-count animation for DORA-cards
* chore: downgrade axios version * feat: add count-up hook * feat: add the count-up animation in Dora Cards * refactor: update code as per review comment * fix: linting issue
1 parent 2b7ec16 commit e625e8e

File tree

5 files changed

+55
-4
lines changed

5 files changed

+55
-4
lines changed

web-server/src/content/DoraMetrics/DoraCards/ChangeFailureRateCard.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
NoDataImg
1414
} from '@/content/DoraMetrics/DoraCards/sharedComponents';
1515
import { useAuth } from '@/hooks/useAuth';
16+
import { useCountUp } from '@/hooks/useCountUp';
1617
import { useDoraMetricsGraph } from '@/hooks/useDoraMetricsGraph';
1718
import {
1819
useStateDateConfig,
@@ -80,6 +81,12 @@ export const ChangeFailureRateCard = () => {
8081
.incident_count
8182
);
8283

84+
const changeFailureRateCount = useCountUp(
85+
changeFailureRateProps.count || 0,
86+
1500, // animation duration
87+
2 // decimal place
88+
);
89+
8390
const series = useMemo(
8491
() => [
8592
{
@@ -195,7 +202,7 @@ export const ChangeFailureRateCard = () => {
195202
lineHeight={1}
196203
>
197204
{changeFailureRateProps.count ? (
198-
`${Number(changeFailureRateProps.count.toFixed(2))}%`
205+
`${Number(changeFailureRateCount.toFixed(2))}%`
199206
) : (
200207
<NoIncidentsLabel
201208
deploymentsCount={

web-server/src/content/DoraMetrics/DoraCards/ChangeTimeCard.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
} from '@/content/DoraMetrics/DoraCards/sharedComponents';
2020
import { usePropsForChangeTimeCard } from '@/content/DoraMetrics/DoraCards/sharedHooks';
2121
import { useAuth } from '@/hooks/useAuth';
22+
import { useCountUp } from '@/hooks/useCountUp';
2223
import { useSelector } from '@/store';
2324
import { ChangeTimeModes } from '@/types/resources';
2425
import { merge } from '@/utils/datatype';
@@ -100,6 +101,8 @@ export const ChangeTimeCard = () => {
100101
[activeModeProps.backgroundColor, mergedLeadTimeTrends]
101102
);
102103

104+
const leadTimeDuration = useCountUp(activeModeProps.count || 0);
105+
103106
return (
104107
<CardRoot
105108
onClick={() => {
@@ -293,7 +296,7 @@ export const ChangeTimeCard = () => {
293296
color={activeModeProps.color}
294297
sx={{ fontSize: '3em' }}
295298
>
296-
{getDurationString(activeModeProps.count) || 0}
299+
{getDurationString(leadTimeDuration) || 0}
297300
</Line>
298301
{Boolean(activeModeProps.count || prevChangeTime) && (
299302
<DoraMetricsComparisonPill

web-server/src/content/DoraMetrics/DoraCards/MeanTimeToRestoreCard.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
CardRoot,
1313
NoDataImg
1414
} from '@/content/DoraMetrics/DoraCards/sharedComponents';
15+
import { useCountUp } from '@/hooks/useCountUp';
1516
import { useDoraMetricsGraph } from '@/hooks/useDoraMetricsGraph';
1617
import { getDurationString } from '@/utils/date';
1718

@@ -76,6 +77,8 @@ export const MeanTimeToRestoreCard = () => {
7677
]
7778
);
7879

80+
const meanTimeToRestoreCount = useCountUp(meanTimeToRestoreProps.count || 0);
81+
7982
const { addPage } = useOverlayPage();
8083

8184
return (
@@ -168,7 +171,7 @@ export const MeanTimeToRestoreCard = () => {
168171
lineHeight={1}
169172
>
170173
{meanTimeToRestoreProps.count ? (
171-
getDurationString(meanTimeToRestoreProps.count)
174+
getDurationString(meanTimeToRestoreCount)
172175
) : (
173176
<NoIncidentsLabel />
174177
)}

web-server/src/content/DoraMetrics/DoraCards/WeeklyDeliveryVolumeCard.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
NoDataImg
1313
} from '@/content/DoraMetrics/DoraCards/sharedComponents';
1414
import { useAuth } from '@/hooks/useAuth';
15+
import { useCountUp } from '@/hooks/useCountUp';
1516
import {
1617
useCurrentDateRangeLabel,
1718
useStateDateConfig
@@ -55,6 +56,10 @@ export const WeeklyDeliveryVolumeCard = () => {
5556
const dateRangeLabel = useCurrentDateRangeLabel();
5657
const deploymentFrequencyProps = useAvgIntervalBasedDeploymentFrequency();
5758

59+
const deploymentFrequencyCount = useCountUp(
60+
deploymentFrequencyProps.count || 0
61+
);
62+
5863
const { addPage } = useOverlayPage();
5964
const deploymentsConfigured = true;
6065
const isCodeProviderIntegrationEnabled = integrationSet.has(
@@ -205,7 +210,7 @@ export const WeeklyDeliveryVolumeCard = () => {
205210
sx={{ fontSize: '3em' }}
206211
>
207212
{deploymentFrequencyProps.count ? (
208-
`${deploymentFrequencyProps.count}`
213+
`${deploymentFrequencyCount}`
209214
) : (
210215
<Line>No Deployments</Line>
211216
)}

web-server/src/hooks/useCountUp.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { useState, useEffect } from 'react';
2+
3+
const FRAME_DURATION_MS = 16; // Average frame duration for 60fps
4+
5+
export const useCountUp = (
6+
targetValue: number,
7+
duration: number = 1500,
8+
decimalPlaces: number = 0
9+
): number => {
10+
const [count, setCount] = useState<number>(0);
11+
12+
useEffect(() => {
13+
let start = 0;
14+
const increment = targetValue / (duration / FRAME_DURATION_MS);
15+
16+
const animateCount = () => {
17+
start += increment;
18+
19+
if (start >= targetValue) {
20+
setCount(parseFloat(targetValue.toFixed(decimalPlaces)));
21+
} else {
22+
setCount(parseFloat(start.toFixed(decimalPlaces)));
23+
requestAnimationFrame(animateCount);
24+
}
25+
};
26+
27+
animateCount();
28+
29+
return () => {};
30+
}, [targetValue, duration, decimalPlaces]);
31+
32+
return count;
33+
};

0 commit comments

Comments
 (0)