Skip to content

Commit 833c90f

Browse files
committed
Implement formatShortNum helper
1 parent ee790bf commit 833c90f

File tree

2 files changed

+89
-0
lines changed

2 files changed

+89
-0
lines changed

app/helpers/format-short-num.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import Helper from '@ember/component/helper';
2+
import { service } from '@ember/service';
3+
4+
/**
5+
* This matches the implementation in https://github.com/rust-lang/crates_io_og_image/blob/v0.2.1/src/formatting.rs
6+
* to ensure that we render roughly the same values in our user interface and the generated OpenGraph images.
7+
*/
8+
export default class FormatShortNumHelper extends Helper {
9+
@service intl;
10+
11+
compute([value]) {
12+
const THRESHOLD = 1500;
13+
const UNITS = ['', 'K', 'M'];
14+
15+
let numValue = Number(value);
16+
let unitIndex = 0;
17+
18+
// Keep dividing by 1000 until value is below threshold or we've reached the last unit
19+
while (numValue >= THRESHOLD && unitIndex < UNITS.length - 1) {
20+
numValue /= 1000;
21+
unitIndex += 1;
22+
}
23+
24+
let unit = UNITS[unitIndex];
25+
26+
// Special case for numbers without suffix - no decimal places
27+
if (unitIndex === 0) {
28+
return this.intl.formatNumber(value);
29+
}
30+
31+
// For K and M, format with appropriate decimal places
32+
// Determine number of decimal places to keep number under 4 chars
33+
let fractionDigits = numValue < 10 ? 1 : 0;
34+
let number = this.intl.formatNumber(numValue, {
35+
minimumFractionDigits: fractionDigits,
36+
maximumFractionDigits: fractionDigits,
37+
});
38+
39+
return number + unit;
40+
}
41+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { render } from '@ember/test-helpers';
2+
import { module, test } from 'qunit';
3+
4+
import formatShortNum from 'crates-io/helpers/format-short-num';
5+
import { setupRenderingTest } from 'crates-io/tests/helpers';
6+
7+
module('Unit | Helper | format-short-num', function (hooks) {
8+
setupRenderingTest(hooks);
9+
10+
async function check(assert, input, expected) {
11+
await render(<template>{{formatShortNum input}}</template>);
12+
assert.dom().hasText(expected);
13+
}
14+
15+
test('formats numbers without suffix (below 1500)', async function (assert) {
16+
this.owner.lookup('service:intl').locale = 'en';
17+
18+
await check(assert, 0, '0');
19+
await check(assert, 1, '1');
20+
await check(assert, 1000, '1,000');
21+
await check(assert, 1499, '1,499');
22+
});
23+
24+
test('formats numbers with K suffix (1500 to 1500000)', async function (assert) {
25+
this.owner.lookup('service:intl').locale = 'en';
26+
27+
await check(assert, 1500, '1.5K');
28+
await check(assert, 2000, '2.0K');
29+
await check(assert, 5000, '5.0K');
30+
await check(assert, 10_000, '10K');
31+
await check(assert, 50_000, '50K');
32+
await check(assert, 100_000, '100K');
33+
await check(assert, 500_000, '500K');
34+
await check(assert, 999_999, '1,000K');
35+
});
36+
37+
test('formats numbers with M suffix (above 1500000)', async function (assert) {
38+
this.owner.lookup('service:intl').locale = 'en';
39+
40+
await check(assert, 1_500_000, '1.5M');
41+
await check(assert, 2_000_000, '2.0M');
42+
await check(assert, 5_000_000, '5.0M');
43+
await check(assert, 10_000_000, '10M');
44+
await check(assert, 50_000_000, '50M');
45+
await check(assert, 100_000_000, '100M');
46+
await check(assert, 1_000_000_000, '1,000M');
47+
});
48+
});

0 commit comments

Comments
 (0)