mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-04 00:18:01 +01:00
feat: prettify large numbers in metrics (#2176)
* prettify large numbers * add tooltip for larger numbers * add test ids, add unit test * move dependency to devDependency * remove unused import * use conditional render component * use prettify large number component for feature overview metrics
This commit is contained in:
parent
e1b903a36c
commit
1a09d1778b
@ -69,6 +69,7 @@
|
||||
"immer": "9.0.15",
|
||||
"jsdom": "20.0.1",
|
||||
"lodash.clonedeep": "4.5.0",
|
||||
"millify": "^5.0.1",
|
||||
"msw": "0.47.4",
|
||||
"pkginfo": "0.4.1",
|
||||
"plausible-tracker": "0.3.8",
|
||||
|
@ -0,0 +1,62 @@
|
||||
import { render } from 'utils/testRenderer';
|
||||
import { screen } from '@testing-library/react';
|
||||
import { PrettifyLargeNumber } from './PrettifyLargeNumber';
|
||||
import { LARGE_NUMBER_PRETTIFIED } from 'utils/testIds';
|
||||
|
||||
describe('PrettifyLargeNumber', () => {
|
||||
it('should render number with separator for value less than threshold', async () => {
|
||||
render(<PrettifyLargeNumber value={999999} threshold={1000000} />);
|
||||
|
||||
const prettifiedText = await screen.getByTestId(
|
||||
LARGE_NUMBER_PRETTIFIED
|
||||
);
|
||||
|
||||
expect(prettifiedText.textContent).toBe('999,999');
|
||||
});
|
||||
|
||||
it('should render prettified number for value equal to the threshold', async () => {
|
||||
render(<PrettifyLargeNumber value={1000000} threshold={1000000} />);
|
||||
|
||||
const prettifiedText = await screen.getByTestId(
|
||||
LARGE_NUMBER_PRETTIFIED
|
||||
);
|
||||
|
||||
expect(prettifiedText.textContent).toBe('1M');
|
||||
});
|
||||
|
||||
it('should render prettified number for value greater than threshold', async () => {
|
||||
render(<PrettifyLargeNumber value={12345678} threshold={1000000} />);
|
||||
|
||||
const prettifiedText = await screen.getByTestId(
|
||||
LARGE_NUMBER_PRETTIFIED
|
||||
);
|
||||
|
||||
expect(prettifiedText.textContent).toBe('12.35M');
|
||||
});
|
||||
|
||||
it('should render prettified number with tooltip having raw value for value greater than threshold', async () => {
|
||||
render(<PrettifyLargeNumber value={12345678} threshold={1000000} />);
|
||||
|
||||
const prettifiedText = await screen.getByTestId(
|
||||
LARGE_NUMBER_PRETTIFIED
|
||||
);
|
||||
|
||||
expect(prettifiedText.getAttribute('aria-label')).toBe('12,345,678');
|
||||
});
|
||||
|
||||
it('should render prettified number with provided significant figures for value greater than threshold', async () => {
|
||||
render(
|
||||
<PrettifyLargeNumber
|
||||
value={12345678}
|
||||
threshold={1000000}
|
||||
precision={4}
|
||||
/>
|
||||
);
|
||||
|
||||
const prettifiedText = await screen.getByTestId(
|
||||
LARGE_NUMBER_PRETTIFIED
|
||||
);
|
||||
|
||||
expect(prettifiedText.textContent).toBe('12.3457M');
|
||||
});
|
||||
});
|
@ -0,0 +1,54 @@
|
||||
import { FC } from 'react';
|
||||
import millify from 'millify';
|
||||
import { Tooltip } from '@mui/material';
|
||||
import { LARGE_NUMBER_PRETTIFIED } from 'utils/testIds';
|
||||
import { ConditionallyRender } from '../ConditionallyRender/ConditionallyRender';
|
||||
|
||||
interface IPrettifyLargeNumberProps {
|
||||
/**
|
||||
* Value to prettify
|
||||
*/
|
||||
value: number;
|
||||
/**
|
||||
* Threshold above which the number will be prettified. Values lower than this will just have comma separators added
|
||||
* @default 1_000_000
|
||||
*/
|
||||
threshold?: number;
|
||||
/**
|
||||
* The number of significant figures
|
||||
* @default 2
|
||||
*/
|
||||
precision?: number;
|
||||
}
|
||||
|
||||
export const PrettifyLargeNumber: FC<IPrettifyLargeNumberProps> = ({
|
||||
value,
|
||||
threshold = 1_000_000,
|
||||
precision = 2,
|
||||
}) => {
|
||||
let prettyValue: string;
|
||||
let showTooltip = false;
|
||||
|
||||
if (value < threshold) {
|
||||
prettyValue = value.toLocaleString();
|
||||
} else {
|
||||
prettyValue = millify(value, { precision });
|
||||
showTooltip = true;
|
||||
}
|
||||
|
||||
const valueSpan = (
|
||||
<span data-testid={LARGE_NUMBER_PRETTIFIED}>{prettyValue}</span>
|
||||
);
|
||||
|
||||
return (
|
||||
<ConditionallyRender
|
||||
condition={showTooltip}
|
||||
show={
|
||||
<Tooltip title={value.toLocaleString()} arrow>
|
||||
{valueSpan}
|
||||
</Tooltip>
|
||||
}
|
||||
elseShow={valueSpan}
|
||||
/>
|
||||
);
|
||||
};
|
@ -1,6 +1,7 @@
|
||||
import { calculatePercentage } from 'utils/calculatePercentage';
|
||||
import { useStyles } from './FeatureMetricsStats.styles';
|
||||
import { Grid } from '@mui/material';
|
||||
import { PrettifyLargeNumber } from 'component/common/PrettifyLargeNumber/PrettifyLargeNumber';
|
||||
|
||||
export interface IFeatureMetricsStatsProps {
|
||||
totalYes: number;
|
||||
@ -34,7 +35,9 @@ export const FeatureMetricsStats = ({
|
||||
<Grid item xs={12} sm={4}>
|
||||
<article className={styles.item}>
|
||||
<h3 className={styles.title}>Exposure</h3>
|
||||
<p className={styles.value}>{totalYes}</p>
|
||||
<p className={styles.value}>
|
||||
<PrettifyLargeNumber value={totalYes} />
|
||||
</p>
|
||||
<p className={styles.text}>
|
||||
Total exposure of the feature in the environment{' '}
|
||||
{hoursSuffix}.
|
||||
@ -56,7 +59,9 @@ export const FeatureMetricsStats = ({
|
||||
<Grid item xs={12} sm={4}>
|
||||
<article className={styles.item}>
|
||||
<h3 className={styles.title}>Requests</h3>
|
||||
<p className={styles.value}>{totalYes + totalNo}</p>
|
||||
<p className={styles.value}>
|
||||
<PrettifyLargeNumber value={totalYes + totalNo} />
|
||||
</p>
|
||||
<p className={styles.text}>
|
||||
Total requests for the feature in the environment{' '}
|
||||
{hoursSuffix}.
|
||||
|
@ -4,6 +4,7 @@ import { IFeatureEnvironmentMetrics } from 'interfaces/featureToggle';
|
||||
import { calculatePercentage } from 'utils/calculatePercentage';
|
||||
import PercentageCircle from 'component/common/PercentageCircle/PercentageCircle';
|
||||
import { useStyles } from './FeatureOverviewEnvironmentMetrics.styles';
|
||||
import { PrettifyLargeNumber } from 'component/common/PrettifyLargeNumber/PrettifyLargeNumber';
|
||||
|
||||
interface IFeatureOverviewEnvironmentMetrics {
|
||||
environmentMetric?: IFeatureEnvironmentMetrics;
|
||||
@ -68,9 +69,15 @@ const FeatureOverviewEnvironmentMetrics = ({
|
||||
<p className={styles.percentage}>{percentage}%</p>
|
||||
<p className={styles.infoParagraph}>
|
||||
The feature has been requested{' '}
|
||||
<b>{environmentMetric.yes + environmentMetric.no} times</b>{' '}
|
||||
and exposed <b>{environmentMetric.yes} times</b> in the last
|
||||
hour
|
||||
<b>
|
||||
<PrettifyLargeNumber value={total} /> times
|
||||
</b>{' '}
|
||||
and exposed{' '}
|
||||
<b>
|
||||
<PrettifyLargeNumber value={environmentMetric.yes} />{' '}
|
||||
times
|
||||
</b>{' '}
|
||||
in the last hour
|
||||
</p>
|
||||
</div>
|
||||
<div className={styles.percentageCircle} data-loading>
|
||||
|
@ -73,3 +73,4 @@ export const AUTH_PAGE_ID = 'AUTH_PAGE_ID';
|
||||
export const ANNOUNCER_ELEMENT_TEST_ID = 'ANNOUNCER_ELEMENT_TEST_ID';
|
||||
export const INSTANCE_STATUS_BAR_ID = 'INSTANCE_STATUS_BAR_ID';
|
||||
export const TOAST_TEXT = 'TOAST_TEXT';
|
||||
export const LARGE_NUMBER_PRETTIFIED = 'LARGE_NUMBER_PRETTIFIED';
|
||||
|
@ -3319,6 +3319,15 @@ cliui@^7.0.2:
|
||||
strip-ansi "^6.0.0"
|
||||
wrap-ansi "^7.0.0"
|
||||
|
||||
cliui@^8.0.1:
|
||||
version "8.0.1"
|
||||
resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa"
|
||||
integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==
|
||||
dependencies:
|
||||
string-width "^4.2.0"
|
||||
strip-ansi "^6.0.1"
|
||||
wrap-ansi "^7.0.0"
|
||||
|
||||
clone@^1.0.2:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
|
||||
@ -5513,6 +5522,13 @@ micromatch@^4.0.2, micromatch@^4.0.4:
|
||||
braces "^3.0.2"
|
||||
picomatch "^2.3.1"
|
||||
|
||||
millify@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/millify/-/millify-5.0.1.tgz#f3f2cebf4d3071e127c05942f827c49754a03754"
|
||||
integrity sha512-1IacXjRDMbRevt2++mBnrI2iFxljWlQapMUT9Bs+uAF3o/TrYdE46Uf6CqlmoOWMX1JDAlMorXPv4/hM1eE/kw==
|
||||
dependencies:
|
||||
yargs "^17.0.1"
|
||||
|
||||
mime-db@1.52.0:
|
||||
version "1.52.0"
|
||||
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
|
||||
@ -7217,6 +7233,19 @@ yargs@^16.2.0:
|
||||
y18n "^5.0.5"
|
||||
yargs-parser "^20.2.2"
|
||||
|
||||
yargs@^17.0.1:
|
||||
version "17.6.0"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.6.0.tgz#e134900fc1f218bc230192bdec06a0a5f973e46c"
|
||||
integrity sha512-8H/wTDqlSwoSnScvV2N/JHfLWOKuh5MVla9hqLjK3nsfyy6Y4kDSYSvkU5YCUEPOSnRXfIyx3Sq+B/IWudTo4g==
|
||||
dependencies:
|
||||
cliui "^8.0.1"
|
||||
escalade "^3.1.1"
|
||||
get-caller-file "^2.0.5"
|
||||
require-directory "^2.1.1"
|
||||
string-width "^4.2.3"
|
||||
y18n "^5.0.5"
|
||||
yargs-parser "^21.0.0"
|
||||
|
||||
yargs@^17.3.1:
|
||||
version "17.4.1"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.4.1.tgz#ebe23284207bb75cee7c408c33e722bfb27b5284"
|
||||
|
Loading…
Reference in New Issue
Block a user