From 91c4a2159c311dc4fdad01a19cc573a57d39be5a Mon Sep 17 00:00:00 2001 From: Yormee 103 Date: Fri, 26 Jun 2026 00:10:02 +0100 Subject: [PATCH] Add formatHolderCount helper for compact holder display. Closes #438 by formatting counts under 1K as plain strings and larger values with one-decimal K/M suffixes, with unit tests for each range and boundary values. Co-authored-by: Cursor --- .../__tests__/numberFormat.utils.test.ts | 29 +++++++++++++++++++ src/utils/numberFormat.utils.ts | 13 ++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/utils/__tests__/numberFormat.utils.test.ts b/src/utils/__tests__/numberFormat.utils.test.ts index b215130..b717784 100644 --- a/src/utils/__tests__/numberFormat.utils.test.ts +++ b/src/utils/__tests__/numberFormat.utils.test.ts @@ -3,6 +3,7 @@ import { formatNumber, formatCompactNumber, formatFollowerCount, + formatHolderCount, formatPercent, } from '../numberFormat.utils'; @@ -145,6 +146,34 @@ describe('formatFollowerCount: Legacy follower abbreviation', () => { }); }); +// --------------------------------------------------------------------------- +// Feature: Holder count formatting +// Validates: Issue #438 acceptance criteria +// --------------------------------------------------------------------------- +describe('formatHolderCount: Holder count abbreviation', () => { + it('returns values under 1000 as a plain string', () => { + expect(formatHolderCount(0)).toBe('0'); + expect(formatHolderCount(42)).toBe('42'); + expect(formatHolderCount(999)).toBe('999'); + }); + + it('formats values in the K range with one decimal place', () => { + expect(formatHolderCount(1200)).toBe('1.2K'); + expect(formatHolderCount(1500)).toBe('1.5K'); + expect(formatHolderCount(999_999)).toBe('1000K'); + }); + + it('formats values in the M range with one decimal place', () => { + expect(formatHolderCount(2_400_000)).toBe('2.4M'); + expect(formatHolderCount(1_250_000)).toBe('1.3M'); + }); + + it('handles boundary values at 1000 and 1000000', () => { + expect(formatHolderCount(1000)).toBe('1K'); + expect(formatHolderCount(1_000_000)).toBe('1M'); + }); +}); + // --------------------------------------------------------------------------- // Feature: Percentage formatting // Validates: Acceptance Criteria for badge display diff --git a/src/utils/numberFormat.utils.ts b/src/utils/numberFormat.utils.ts index ea6ecca..18b48bc 100644 --- a/src/utils/numberFormat.utils.ts +++ b/src/utils/numberFormat.utils.ts @@ -45,7 +45,14 @@ export function formatCompactNumber( return formatNumber(value, { ...options, style: 'compact' }); } -export function formatFollowerCount(count: number): string { +/** + * Formats holder counts for compact display across creator profile surfaces. + * + * - Below 1,000: plain string (e.g. `999`) + * - 1,000–999,999: one decimal K suffix (e.g. `1.2K`, `1K` at exactly 1,000) + * - 1,000,000+: one decimal M suffix (e.g. `2.4M`, `1M` at exactly 1,000,000) + */ +export function formatHolderCount(count: number): string { if (count >= 1_000_000) { return `${(count / 1_000_000).toFixed(1).replace(/\.0$/, '')}M`; } @@ -55,6 +62,10 @@ export function formatFollowerCount(count: number): string { return count.toString(); } +export function formatFollowerCount(count: number): string { + return formatHolderCount(count); +} + export interface FormatPercentOptions { /** Maximum fractional digits in the rendered value. Defaults to 2. */ maximumFractionDigits?: number;