Merge branch 'round-follower-counts' into 'develop'
Improve formatting of short numbers See merge request soapbox-pub/soapbox-fe!1749
This commit is contained in:
commit
5ad4303c3e
3 changed files with 101 additions and 11 deletions
|
@ -17,10 +17,9 @@ import { initReport } from 'soapbox/actions/reports';
|
|||
import { setSearchAccount } from 'soapbox/actions/search';
|
||||
import { getSettings } from 'soapbox/actions/settings';
|
||||
import snackbar from 'soapbox/actions/snackbar';
|
||||
import Avatar from 'soapbox/components/avatar';
|
||||
import Badge from 'soapbox/components/badge';
|
||||
import StillImage from 'soapbox/components/still_image';
|
||||
import { HStack, IconButton, Menu, MenuButton, MenuItem, MenuList, MenuLink, MenuDivider } from 'soapbox/components/ui';
|
||||
import { HStack, IconButton, Menu, MenuButton, MenuItem, MenuList, MenuLink, MenuDivider, Avatar } from 'soapbox/components/ui';
|
||||
import SvgIcon from 'soapbox/components/ui/icon/svg-icon';
|
||||
import MovedNote from 'soapbox/features/account_timeline/components/moved_note';
|
||||
import ActionButton from 'soapbox/features/ui/components/action-button';
|
||||
|
@ -757,7 +756,8 @@ const Header: React.FC<IHeader> = ({ account }) => {
|
|||
<div className='flex'>
|
||||
<a href={account.avatar} onClick={handleAvatarClick} target='_blank'>
|
||||
<Avatar
|
||||
account={account}
|
||||
src={account.avatar}
|
||||
size={96}
|
||||
className='h-24 w-24 rounded-full ring-4 ring-white dark:ring-primary-900'
|
||||
/>
|
||||
</a>
|
||||
|
|
|
@ -27,11 +27,77 @@ describe('shortNumberFormat', () => {
|
|||
|
||||
test('formats numbers under 1,000,000', () => {
|
||||
render(<div data-testid='num'>{shortNumberFormat(5555)}</div>, undefined, null);
|
||||
expect(screen.getByTestId('num')).toHaveTextContent('5.6K');
|
||||
expect(screen.getByTestId('num')).toHaveTextContent('5.55k');
|
||||
});
|
||||
|
||||
test('formats numbers over 1,000,000', () => {
|
||||
render(<div data-testid='num'>{shortNumberFormat(5555555)}</div>, undefined, null);
|
||||
expect(screen.getByTestId('num')).toHaveTextContent('5.6M');
|
||||
expect(screen.getByTestId('num')).toHaveTextContent('5.55M');
|
||||
});
|
||||
|
||||
test('formats a multitude of numbers', () => {
|
||||
let result = render(<div data-testid='num'>{shortNumberFormat(0)}</div>, undefined, null);
|
||||
expect(screen.getByTestId('num')).toHaveTextContent('0');
|
||||
result.unmount();
|
||||
|
||||
result = render(<div data-testid='num'>{shortNumberFormat(1)}</div>);
|
||||
expect(screen.getByTestId('num')).toHaveTextContent('1');
|
||||
result.unmount();
|
||||
|
||||
result = render(<div data-testid='num'>{shortNumberFormat(999)}</div>, undefined, null);
|
||||
expect(screen.getByTestId('num')).toHaveTextContent('999');
|
||||
result.unmount();
|
||||
|
||||
result = render(<div data-testid='num'>{shortNumberFormat(1000)}</div>, undefined, null);
|
||||
expect(screen.getByTestId('num')).toHaveTextContent('1k');
|
||||
result.unmount();
|
||||
|
||||
result = render(<div data-testid='num'>{shortNumberFormat(1001)}</div>, undefined, null);
|
||||
expect(screen.getByTestId('num')).toHaveTextContent('1k');
|
||||
result.unmount();
|
||||
|
||||
result = render(<div data-testid='num'>{shortNumberFormat(1005)}</div>, undefined, null);
|
||||
expect(screen.getByTestId('num')).toHaveTextContent('1k');
|
||||
result.unmount();
|
||||
|
||||
result = render(<div data-testid='num'>{shortNumberFormat(1006)}</div>, undefined, null);
|
||||
expect(screen.getByTestId('num')).toHaveTextContent('1k');
|
||||
result.unmount();
|
||||
|
||||
result = render(<div data-testid='num'>{shortNumberFormat(1010)}</div>, undefined, null);
|
||||
expect(screen.getByTestId('num')).toHaveTextContent('1.01k');
|
||||
result.unmount();
|
||||
|
||||
result = render(<div data-testid='num'>{shortNumberFormat(1530)}</div>, undefined, null);
|
||||
expect(screen.getByTestId('num')).toHaveTextContent('1.53k');
|
||||
result.unmount();
|
||||
|
||||
result = render(<div data-testid='num'>{shortNumberFormat(10530)}</div>, undefined, null);
|
||||
expect(screen.getByTestId('num')).toHaveTextContent('10.5k');
|
||||
result.unmount();
|
||||
|
||||
result = render(<div data-testid='num'>{shortNumberFormat(999500)}</div>, undefined, null);
|
||||
expect(screen.getByTestId('num')).toHaveTextContent('999k');
|
||||
result.unmount();
|
||||
|
||||
result = render(<div data-testid='num'>{shortNumberFormat(999999)}</div>, undefined, null);
|
||||
expect(screen.getByTestId('num')).toHaveTextContent('999k');
|
||||
result.unmount();
|
||||
|
||||
result = render(<div data-testid='num'>{shortNumberFormat(999499)}</div>, undefined, null);
|
||||
expect(screen.getByTestId('num')).toHaveTextContent('999k');
|
||||
result.unmount();
|
||||
|
||||
result = render(<div data-testid='num'>{shortNumberFormat(1000000)}</div>, undefined, null);
|
||||
expect(screen.getByTestId('num')).toHaveTextContent('1M');
|
||||
result.unmount();
|
||||
|
||||
result = render(<div data-testid='num'>{shortNumberFormat(3905558)}</div>, undefined, null);
|
||||
expect(screen.getByTestId('num')).toHaveTextContent('3.9M');
|
||||
result.unmount();
|
||||
|
||||
result = render(<div data-testid='num'>{shortNumberFormat(1031511)}</div>, undefined, null);
|
||||
expect(screen.getByTestId('num')).toHaveTextContent('1.03M');
|
||||
result.unmount();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,17 +4,41 @@ import { FormattedNumber } from 'react-intl';
|
|||
/** Check if a value is REALLY a number. */
|
||||
export const isNumber = (value: unknown): value is number => typeof value === 'number' && !isNaN(value);
|
||||
|
||||
const roundDown = (num: number) => {
|
||||
if (num >= 100 && num < 1000) {
|
||||
num = Math.floor(num);
|
||||
}
|
||||
|
||||
const n = Number(num.toFixed(2));
|
||||
return (n > num) ? n - (1 / (Math.pow(10, 2))) : n;
|
||||
};
|
||||
|
||||
/** Display a number nicely for the UI, eg 1000 becomes 1K. */
|
||||
export const shortNumberFormat = (number: any): React.ReactNode => {
|
||||
if (!isNumber(number)) return '•';
|
||||
|
||||
if (number < 1000) {
|
||||
return <FormattedNumber value={number} />;
|
||||
} else if (number < 1000000) {
|
||||
return <span><FormattedNumber value={number / 1000} maximumFractionDigits={1} />K</span>;
|
||||
} else {
|
||||
return <span><FormattedNumber value={number / 1000000} maximumFractionDigits={1} />M</span>;
|
||||
let value = number;
|
||||
let factor: string = '';
|
||||
if (number >= 1000 && number < 1000000) {
|
||||
factor = 'k';
|
||||
value = roundDown(value / 1000);
|
||||
} else if (number >= 1000000) {
|
||||
factor = 'M';
|
||||
value = roundDown(value / 1000000);
|
||||
}
|
||||
|
||||
return (
|
||||
<span>
|
||||
<FormattedNumber
|
||||
value={value}
|
||||
maximumFractionDigits={0}
|
||||
minimumFractionDigits={0}
|
||||
maximumSignificantDigits={3}
|
||||
style='decimal'
|
||||
/>
|
||||
{factor}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
/** Check if an entity ID is an integer (eg not a FlakeId). */
|
||||
|
|
Loading…
Reference in a new issue