1
0
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:
Mateusz Kwasniewski 2025-10-01 16:33:58 +02:00 committed by GitHub
parent b409cc8034
commit 921130a9c0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 68 additions and 26 deletions

View File

@ -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,

View File

@ -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 });
};

View File

@ -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'];

View File

@ -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';

View File

@ -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) {

View File

@ -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;

View File

@ -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');
});
});

View File

@ -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) => {

View File

@ -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';

View File

@ -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);