From e625e8e623403107e559e6b517d5fd5cbef87c23 Mon Sep 17 00:00:00 2001 From: Naveen yadav <52673659+xyfer17@users.noreply.github.com> Date: Thu, 31 Oct 2024 01:38:38 +0530 Subject: [PATCH] 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 --- .../DoraCards/ChangeFailureRateCard.tsx | 9 ++++- .../DoraMetrics/DoraCards/ChangeTimeCard.tsx | 5 ++- .../DoraCards/MeanTimeToRestoreCard.tsx | 5 ++- .../DoraCards/WeeklyDeliveryVolumeCard.tsx | 7 +++- web-server/src/hooks/useCountUp.ts | 33 +++++++++++++++++++ 5 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 web-server/src/hooks/useCountUp.ts diff --git a/web-server/src/content/DoraMetrics/DoraCards/ChangeFailureRateCard.tsx b/web-server/src/content/DoraMetrics/DoraCards/ChangeFailureRateCard.tsx index 928d7287..96a3c407 100644 --- a/web-server/src/content/DoraMetrics/DoraCards/ChangeFailureRateCard.tsx +++ b/web-server/src/content/DoraMetrics/DoraCards/ChangeFailureRateCard.tsx @@ -13,6 +13,7 @@ import { NoDataImg } from '@/content/DoraMetrics/DoraCards/sharedComponents'; import { useAuth } from '@/hooks/useAuth'; +import { useCountUp } from '@/hooks/useCountUp'; import { useDoraMetricsGraph } from '@/hooks/useDoraMetricsGraph'; import { useStateDateConfig, @@ -80,6 +81,12 @@ export const ChangeFailureRateCard = () => { .incident_count ); + const changeFailureRateCount = useCountUp( + changeFailureRateProps.count || 0, + 1500, // animation duration + 2 // decimal place + ); + const series = useMemo( () => [ { @@ -195,7 +202,7 @@ export const ChangeFailureRateCard = () => { lineHeight={1} > {changeFailureRateProps.count ? ( - `${Number(changeFailureRateProps.count.toFixed(2))}%` + `${Number(changeFailureRateCount.toFixed(2))}%` ) : ( { [activeModeProps.backgroundColor, mergedLeadTimeTrends] ); + const leadTimeDuration = useCountUp(activeModeProps.count || 0); + return ( { @@ -293,7 +296,7 @@ export const ChangeTimeCard = () => { color={activeModeProps.color} sx={{ fontSize: '3em' }} > - {getDurationString(activeModeProps.count) || 0} + {getDurationString(leadTimeDuration) || 0} {Boolean(activeModeProps.count || prevChangeTime) && ( { ] ); + const meanTimeToRestoreCount = useCountUp(meanTimeToRestoreProps.count || 0); + const { addPage } = useOverlayPage(); return ( @@ -168,7 +171,7 @@ export const MeanTimeToRestoreCard = () => { lineHeight={1} > {meanTimeToRestoreProps.count ? ( - getDurationString(meanTimeToRestoreProps.count) + getDurationString(meanTimeToRestoreCount) ) : ( )} diff --git a/web-server/src/content/DoraMetrics/DoraCards/WeeklyDeliveryVolumeCard.tsx b/web-server/src/content/DoraMetrics/DoraCards/WeeklyDeliveryVolumeCard.tsx index b5fb861d..791d490a 100644 --- a/web-server/src/content/DoraMetrics/DoraCards/WeeklyDeliveryVolumeCard.tsx +++ b/web-server/src/content/DoraMetrics/DoraCards/WeeklyDeliveryVolumeCard.tsx @@ -12,6 +12,7 @@ import { NoDataImg } from '@/content/DoraMetrics/DoraCards/sharedComponents'; import { useAuth } from '@/hooks/useAuth'; +import { useCountUp } from '@/hooks/useCountUp'; import { useCurrentDateRangeLabel, useStateDateConfig @@ -55,6 +56,10 @@ export const WeeklyDeliveryVolumeCard = () => { const dateRangeLabel = useCurrentDateRangeLabel(); const deploymentFrequencyProps = useAvgIntervalBasedDeploymentFrequency(); + const deploymentFrequencyCount = useCountUp( + deploymentFrequencyProps.count || 0 + ); + const { addPage } = useOverlayPage(); const deploymentsConfigured = true; const isCodeProviderIntegrationEnabled = integrationSet.has( @@ -205,7 +210,7 @@ export const WeeklyDeliveryVolumeCard = () => { sx={{ fontSize: '3em' }} > {deploymentFrequencyProps.count ? ( - `${deploymentFrequencyProps.count}` + `${deploymentFrequencyCount}` ) : ( No Deployments )} diff --git a/web-server/src/hooks/useCountUp.ts b/web-server/src/hooks/useCountUp.ts new file mode 100644 index 00000000..89893fc5 --- /dev/null +++ b/web-server/src/hooks/useCountUp.ts @@ -0,0 +1,33 @@ +import { useState, useEffect } from 'react'; + +const FRAME_DURATION_MS = 16; // Average frame duration for 60fps + +export const useCountUp = ( + targetValue: number, + duration: number = 1500, + decimalPlaces: number = 0 +): number => { + const [count, setCount] = useState(0); + + useEffect(() => { + let start = 0; + const increment = targetValue / (duration / FRAME_DURATION_MS); + + const animateCount = () => { + start += increment; + + if (start >= targetValue) { + setCount(parseFloat(targetValue.toFixed(decimalPlaces))); + } else { + setCount(parseFloat(start.toFixed(decimalPlaces))); + requestAnimationFrame(animateCount); + } + }; + + animateCount(); + + return () => {}; + }, [targetValue, duration, decimalPlaces]); + + return count; +};