chore: Update typography in sidebar, chatlist, conversation card
This commit is contained in:
@@ -361,4 +361,77 @@ describe('useNumberFormatter', () => {
|
||||
expect(result).toMatch(/1[.,\s]000[.,\s]000/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatCompactWithDecimal', () => {
|
||||
it('should return exact numbers for values under 1,000', () => {
|
||||
const { formatCompactWithDecimal } = useNumberFormatter();
|
||||
expect(formatCompactWithDecimal(0)).toBe('0');
|
||||
expect(formatCompactWithDecimal(1)).toBe('1');
|
||||
expect(formatCompactWithDecimal(42)).toBe('42');
|
||||
expect(formatCompactWithDecimal(999)).toBe('999');
|
||||
});
|
||||
|
||||
it('should return compact format with decimals for thousands', () => {
|
||||
const { formatCompactWithDecimal } = useNumberFormatter();
|
||||
expect(formatCompactWithDecimal(1000)).toBe('1K');
|
||||
expect(formatCompactWithDecimal(1500)).toBe('1.5K');
|
||||
expect(formatCompactWithDecimal(29400)).toBe('29.4K');
|
||||
expect(formatCompactWithDecimal(15000)).toBe('15K');
|
||||
expect(formatCompactWithDecimal(15500)).toBe('15.5K');
|
||||
});
|
||||
|
||||
it('should return compact format with decimals for millions', () => {
|
||||
const { formatCompactWithDecimal } = useNumberFormatter();
|
||||
expect(formatCompactWithDecimal(1000000)).toBe('1M');
|
||||
expect(formatCompactWithDecimal(1200000)).toBe('1.2M');
|
||||
expect(formatCompactWithDecimal(1234000)).toBe('1.2M');
|
||||
expect(formatCompactWithDecimal(2500000)).toBe('2.5M');
|
||||
expect(formatCompactWithDecimal(10000000)).toBe('10M');
|
||||
});
|
||||
|
||||
it('should handle edge cases gracefully', () => {
|
||||
const { formatCompactWithDecimal } = useNumberFormatter();
|
||||
expect(formatCompactWithDecimal(null)).toBe('0');
|
||||
expect(formatCompactWithDecimal(undefined)).toBe('0');
|
||||
expect(formatCompactWithDecimal(NaN)).toBe('0');
|
||||
expect(formatCompactWithDecimal('string')).toBe('0');
|
||||
});
|
||||
|
||||
it('should handle negative numbers', () => {
|
||||
const { formatCompactWithDecimal } = useNumberFormatter();
|
||||
expect(formatCompactWithDecimal(-500)).toBe('-500');
|
||||
expect(formatCompactWithDecimal(-1500)).toBe('-1.5K');
|
||||
expect(formatCompactWithDecimal(-29400)).toBe('-29.4K');
|
||||
expect(formatCompactWithDecimal(-1200000)).toBe('-1.2M');
|
||||
});
|
||||
|
||||
it('should respect custom decimal places', () => {
|
||||
const { formatCompactWithDecimal } = useNumberFormatter();
|
||||
expect(formatCompactWithDecimal(1234, 2)).toBe('1.23K');
|
||||
expect(formatCompactWithDecimal(1234567, 2)).toBe('1.23M');
|
||||
});
|
||||
|
||||
it('should format with de-DE locale', () => {
|
||||
const mockLocale = ref('de-DE');
|
||||
vi.mocked(useI18n).mockReturnValue({ locale: mockLocale });
|
||||
const { formatCompactWithDecimal } = useNumberFormatter();
|
||||
// German uses different compact notation
|
||||
const result = formatCompactWithDecimal(29400);
|
||||
expect(result).toMatch(/29[,.]?4/);
|
||||
});
|
||||
|
||||
it('should handle underscore-based locale tags', () => {
|
||||
const mockLocale = ref('en_US');
|
||||
vi.mocked(useI18n).mockReturnValue({ locale: mockLocale });
|
||||
const { formatCompactWithDecimal } = useNumberFormatter();
|
||||
expect(formatCompactWithDecimal(29400)).toBe('29.4K');
|
||||
});
|
||||
|
||||
it('should handle null/undefined locale gracefully', () => {
|
||||
const mockLocale = ref(null);
|
||||
vi.mocked(useI18n).mockReturnValue({ locale: mockLocale });
|
||||
const { formatCompactWithDecimal } = useNumberFormatter();
|
||||
expect(formatCompactWithDecimal(29400)).toBe('29.4K');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -60,8 +60,37 @@ export function useNumberFormatter() {
|
||||
return new Intl.NumberFormat(resolvedLocale.value).format(num);
|
||||
};
|
||||
|
||||
/**
|
||||
* Format a number with decimal precision using compact notation (standard Intl approach)
|
||||
* - Up to 1,000: show exact number (e.g., 999)
|
||||
* - 1,000+: show compact with decimals (e.g., 29400 → "29.4K", 1500 → "1.5K")
|
||||
* - 1,000,000+: show as "X.XM" (e.g., 1,234,000 → "1.2M")
|
||||
*
|
||||
* Uses browser-native Intl.NumberFormat for proper localization
|
||||
*
|
||||
* @param {number} num - The number to format
|
||||
* @param {number} [decimals=1] - Maximum decimal places to show
|
||||
* @returns {string} Formatted number string with decimal precision
|
||||
*/
|
||||
const formatCompactWithDecimal = (num, decimals = 1) => {
|
||||
if (typeof num !== 'number' || Number.isNaN(num)) {
|
||||
return '0';
|
||||
}
|
||||
|
||||
if (Math.abs(num) < 1000) {
|
||||
return new Intl.NumberFormat(resolvedLocale.value).format(num);
|
||||
}
|
||||
|
||||
return new Intl.NumberFormat(resolvedLocale.value, {
|
||||
notation: 'compact',
|
||||
compactDisplay: 'short',
|
||||
maximumFractionDigits: decimals,
|
||||
}).format(num);
|
||||
};
|
||||
|
||||
return {
|
||||
formatCompactNumber,
|
||||
formatFullNumber,
|
||||
formatCompactWithDecimal,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user