mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	fix: impact metrics formatting (#10715)
This commit is contained in:
		
							parent
							
								
									b409cc8034
								
							
						
					
					
						commit
						921130a9c0
					
				| @ -1,8 +1,8 @@ | |||||||
| import type { FC } from 'react'; | import type { FC } from 'react'; | ||||||
| import millify from 'millify'; |  | ||||||
| import { Tooltip } from '@mui/material'; | import { Tooltip } from '@mui/material'; | ||||||
| import { LARGE_NUMBER_PRETTIFIED } from 'utils/testIds'; | import { LARGE_NUMBER_PRETTIFIED } from 'utils/testIds'; | ||||||
| import { ConditionallyRender } from '../ConditionallyRender/ConditionallyRender.tsx'; | import { ConditionallyRender } from '../ConditionallyRender/ConditionallyRender.tsx'; | ||||||
|  | import { prettifyLargeNumber } from './formatLargeNumber.js'; | ||||||
| 
 | 
 | ||||||
| interface IPrettifyLargeNumberProps { | interface IPrettifyLargeNumberProps { | ||||||
|     /** |     /** | ||||||
| @ -25,15 +25,6 @@ interface IPrettifyLargeNumberProps { | |||||||
|     'data-loading'?: string; |     'data-loading'?: string; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export const prettifyLargeNumber = |  | ||||||
|     (threshold: number = 1_000_000, precision: number = 2) => |  | ||||||
|     (value: number) => { |  | ||||||
|         if (value < threshold) { |  | ||||||
|             return value.toLocaleString(); |  | ||||||
|         } |  | ||||||
|         return millify(value, { precision }); |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
| export const PrettifyLargeNumber: FC<IPrettifyLargeNumberProps> = ({ | export const PrettifyLargeNumber: FC<IPrettifyLargeNumberProps> = ({ | ||||||
|     value, |     value, | ||||||
|     threshold = 1_000_000, |     threshold = 1_000_000, | ||||||
|  | |||||||
| @ -0,0 +1,10 @@ | |||||||
|  | import millify from 'millify'; | ||||||
|  | 
 | ||||||
|  | export const prettifyLargeNumber = | ||||||
|  |     (threshold: number = 1_000_000, precision: number = 2) => | ||||||
|  |     (value: number) => { | ||||||
|  |         if (value < threshold) { | ||||||
|  |             return value.toLocaleString(); | ||||||
|  |         } | ||||||
|  |         return millify(value, { precision }); | ||||||
|  |     }; | ||||||
| @ -5,7 +5,7 @@ import { SeriesSelector } from './SeriesSelector/SeriesSelector.tsx'; | |||||||
| import { RangeSelector } from './RangeSelector/RangeSelector.tsx'; | import { RangeSelector } from './RangeSelector/RangeSelector.tsx'; | ||||||
| import { ModeSelector } from './ModeSelector/ModeSelector.tsx'; | import { ModeSelector } from './ModeSelector/ModeSelector.tsx'; | ||||||
| import type { ChartFormState } from '../../hooks/useChartFormState.ts'; | import type { ChartFormState } from '../../hooks/useChartFormState.ts'; | ||||||
| import { getMetricType } from '../../utils.ts'; | import { getMetricType } from '../../metricsFormatters.ts'; | ||||||
| 
 | 
 | ||||||
| export type ImpactMetricsControlsProps = { | export type ImpactMetricsControlsProps = { | ||||||
|     formData: ChartFormState['formData']; |     formData: ChartFormState['formData']; | ||||||
|  | |||||||
| @ -7,7 +7,11 @@ import { | |||||||
| } from '../insights/components/LineChart/LineChart.tsx'; | } from '../insights/components/LineChart/LineChart.tsx'; | ||||||
| import { useImpactMetricsData } from 'hooks/api/getters/useImpactMetricsData/useImpactMetricsData'; | import { useImpactMetricsData } from 'hooks/api/getters/useImpactMetricsData/useImpactMetricsData'; | ||||||
| import { usePlaceholderData } from '../insights/hooks/usePlaceholderData.js'; | import { usePlaceholderData } from '../insights/hooks/usePlaceholderData.js'; | ||||||
| import { getDisplayFormat, getTimeUnit, formatLargeNumbers } from './utils.ts'; | import { | ||||||
|  |     getDisplayFormat, | ||||||
|  |     getTimeUnit, | ||||||
|  |     formatLargeNumbers, | ||||||
|  | } from './metricsFormatters.js'; | ||||||
| import { fromUnixTime } from 'date-fns'; | import { fromUnixTime } from 'date-fns'; | ||||||
| import { useChartData } from './hooks/useChartData.ts'; | import { useChartData } from './hooks/useChartData.ts'; | ||||||
| import type { AggregationMode } from './types.ts'; | import type { AggregationMode } from './types.ts'; | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| import { useMemo } from 'react'; | import { useMemo } from 'react'; | ||||||
| import { useTheme } from '@mui/material'; | import { useTheme } from '@mui/material'; | ||||||
| import type { ImpactMetricsSeries } from 'hooks/api/getters/useImpactMetricsData/useImpactMetricsData'; | import type { ImpactMetricsSeries } from 'hooks/api/getters/useImpactMetricsData/useImpactMetricsData'; | ||||||
| import { getSeriesLabel } from '../utils.ts'; | import { getSeriesLabel } from '../metricsFormatters.ts'; | ||||||
| 
 | 
 | ||||||
| const getColorStartingIndex = (modulo: number, series?: string): number => { | const getColorStartingIndex = (modulo: number, series?: string): number => { | ||||||
|     if (!series || series.length === 0 || modulo <= 0) { |     if (!series || series.length === 0 || modulo <= 0) { | ||||||
|  | |||||||
| @ -2,7 +2,7 @@ import { useState, useEffect } from 'react'; | |||||||
| import { useImpactMetricsData } from 'hooks/api/getters/useImpactMetricsData/useImpactMetricsData'; | import { useImpactMetricsData } from 'hooks/api/getters/useImpactMetricsData/useImpactMetricsData'; | ||||||
| import type { AggregationMode, ChartConfig } from '../types.ts'; | import type { AggregationMode, ChartConfig } from '../types.ts'; | ||||||
| import type { ImpactMetricsLabels } from 'hooks/api/getters/useImpactMetricsData/useImpactMetricsData'; | import type { ImpactMetricsLabels } from 'hooks/api/getters/useImpactMetricsData/useImpactMetricsData'; | ||||||
| import { getDefaultAggregation, getMetricType } from '../utils.ts'; | import { getDefaultAggregation, getMetricType } from '../metricsFormatters.ts'; | ||||||
| 
 | 
 | ||||||
| type UseChartConfigParams = { | type UseChartConfigParams = { | ||||||
|     open: boolean; |     open: boolean; | ||||||
|  | |||||||
| @ -0,0 +1,41 @@ | |||||||
|  | import { formatLargeNumbers } from './metricsFormatters.js'; | ||||||
|  | 
 | ||||||
|  | describe('formatLargeNumbers', () => { | ||||||
|  |     it('formats small numbers with locale formatting', () => { | ||||||
|  |         expect(formatLargeNumbers(0)).toBe('0'); | ||||||
|  |         expect(formatLargeNumbers(999)).toBe('999'); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     it('formats thousands correctly', () => { | ||||||
|  |         expect(formatLargeNumbers(1000)).toBe('1k'); | ||||||
|  |         expect(formatLargeNumbers(1200)).toBe('1.2k'); | ||||||
|  |         expect(formatLargeNumbers(1400)).toBe('1.4k'); | ||||||
|  |         expect(formatLargeNumbers(1600)).toBe('1.6k'); | ||||||
|  |         expect(formatLargeNumbers(5000)).toBe('5k'); | ||||||
|  |         expect(formatLargeNumbers(9500)).toBe('9.5k'); | ||||||
|  |         expect(formatLargeNumbers(10000)).toBe('10k'); | ||||||
|  |         expect(formatLargeNumbers(999000)).toBe('999k'); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     it('formats millions correctly', () => { | ||||||
|  |         expect(formatLargeNumbers(1000000)).toBe('1M'); | ||||||
|  |         expect(formatLargeNumbers(1500000)).toBe('1.5M'); | ||||||
|  |         expect(formatLargeNumbers(2700000)).toBe('2.7M'); | ||||||
|  |         expect(formatLargeNumbers(5000000)).toBe('5M'); | ||||||
|  |         expect(formatLargeNumbers(9900000)).toBe('9.9M'); | ||||||
|  |         expect(formatLargeNumbers(10000000)).toBe('10M'); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     it('formats billions correctly', () => { | ||||||
|  |         expect(formatLargeNumbers(1000000000)).toBe('1B'); | ||||||
|  |         expect(formatLargeNumbers(1500000000)).toBe('1.5B'); | ||||||
|  |         expect(formatLargeNumbers(10000000000)).toBe('10B'); | ||||||
|  |         expect(formatLargeNumbers(999000000000)).toBe('999B'); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     it('formats trillions correctly', () => { | ||||||
|  |         expect(formatLargeNumbers(1000000000000)).toBe('1T'); | ||||||
|  |         expect(formatLargeNumbers(2500000000000)).toBe('2.5T'); | ||||||
|  |         expect(formatLargeNumbers(10000000000000)).toBe('10T'); | ||||||
|  |     }); | ||||||
|  | }); | ||||||
| @ -1,3 +1,5 @@ | |||||||
|  | import { prettifyLargeNumber } from '../common/PrettifyLargeNumber/formatLargeNumber.js'; | ||||||
|  | 
 | ||||||
| export const getTimeUnit = (timeRange: string) => { | export const getTimeUnit = (timeRange: string) => { | ||||||
|     switch (timeRange) { |     switch (timeRange) { | ||||||
|         case 'hour': |         case 'hour': | ||||||
| @ -52,13 +54,9 @@ export const getSeriesLabel = (metric: Record<string, string>): string => { | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export const formatLargeNumbers = (value: number): string => { | export const formatLargeNumbers = (value: number): string => { | ||||||
|     if (value >= 1000000) { |     const formatter = prettifyLargeNumber(1000, 1); | ||||||
|         return `${(value / 1000000).toFixed(0)}M`; |     const result = formatter(value); | ||||||
|     } |     return result.replace(/K/g, 'k'); | ||||||
|     if (value >= 1000) { |  | ||||||
|         return `${(value / 1000).toFixed(0)}k`; |  | ||||||
|     } |  | ||||||
|     return value.toString(); |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export const getMetricType = (seriesName: string) => { | export const getMetricType = (seriesName: string) => { | ||||||
| @ -4,10 +4,8 @@ import { FilterItemParam } from 'utils/serializeQueryParams'; | |||||||
| import { InsightsSection } from 'component/insights/sections/InsightsSection'; | import { InsightsSection } from 'component/insights/sections/InsightsSection'; | ||||||
| import { LifecycleChart } from '../components/LifecycleChart/LifecycleChart.tsx'; | import { LifecycleChart } from '../components/LifecycleChart/LifecycleChart.tsx'; | ||||||
| import { styled, useTheme } from '@mui/material'; | import { styled, useTheme } from '@mui/material'; | ||||||
| import { | import { prettifyLargeNumber } from 'component/common/PrettifyLargeNumber/formatLargeNumber.js'; | ||||||
|     prettifyLargeNumber, | import { PrettifyLargeNumber } from 'component/common/PrettifyLargeNumber/PrettifyLargeNumber.tsx'; | ||||||
|     PrettifyLargeNumber, |  | ||||||
| } from 'component/common/PrettifyLargeNumber/PrettifyLargeNumber.tsx'; |  | ||||||
| import { FeatureLifecycleStageIcon } from 'component/common/FeatureLifecycle/FeatureLifecycleStageIcon.tsx'; | import { FeatureLifecycleStageIcon } from 'component/common/FeatureLifecycle/FeatureLifecycleStageIcon.tsx'; | ||||||
| import { normalizeDays } from './normalize-days.ts'; | import { normalizeDays } from './normalize-days.ts'; | ||||||
| import useLoading from 'hooks/useLoading.ts'; | import useLoading from 'hooks/useLoading.ts'; | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| import { prettifyLargeNumber } from 'component/common/PrettifyLargeNumber/PrettifyLargeNumber'; | import { prettifyLargeNumber } from 'component/common/PrettifyLargeNumber/formatLargeNumber.js'; | ||||||
| 
 | 
 | ||||||
| const prettifyNumber = prettifyLargeNumber(1000, 2); | const prettifyNumber = prettifyLargeNumber(1000, 2); | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user