From 97a08198e98e2a96b5da67d29032e2a03f025d67 Mon Sep 17 00:00:00 2001 From: Glen Kelley Date: Fri, 20 Jan 2023 10:05:51 +1100 Subject: [PATCH 1/5] Added tests for Accounting notation with unix suffixes. --- test/formatPrefix-test.js | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/test/formatPrefix-test.js b/test/formatPrefix-test.js index a237b4d..c6b0fe3 100644 --- a/test/formatPrefix-test.js +++ b/test/formatPrefix-test.js @@ -1,22 +1,40 @@ import assert from "assert"; -import {formatPrefix} from "../src/index.js"; +import {format, formatPrefix} from "../src/index.js"; -it("formatPrefix(\"s\", value)(number) formats with the SI prefix appropriate to the specified value", () => { +it("formatPrefix(\",.0s\", value)(number) formats with the SI prefix appropriate to the specified value", () => { assert.strictEqual(formatPrefix(",.0s", 1e-6)(.00042), "420µ"); assert.strictEqual(formatPrefix(",.0s", 1e-6)(.0042), "4,200µ"); +}); + +it("formatPrefix(\",.3s\", value)(number) formats with the SI prefix appropriate to the specified value", () => { assert.strictEqual(formatPrefix(",.3s", 1e-3)(.00042), "0.420m"); }); -it("formatPrefix(\"s\", value)(number) uses yocto for very small reference values", () => { +it("formatPrefix(\",.0s\", value)(number) uses yocto for very small reference values", () => { assert.strictEqual(formatPrefix(",.0s", 1e-27)(1e-24), "1y"); }); -it("formatPrefix(\"s\", value)(number) uses yotta for very small reference values", () => { +it("formatPrefix(\",.0s\", value)(number) uses yotta for very small reference values", () => { assert.strictEqual(formatPrefix(",.0s", 1e27)(1e24), "1Y"); }); -it("formatPrefix(\"$,s\", value)(number) formats with the specified SI prefix", () => { +it("formatPrefix(\" $12,.1s\", value)(number) formats with the specified SI prefix", () => { + // The fixed length of 12 is inclusive of the unit 'M' const f = formatPrefix(" $12,.1s", 1e6); - assert.strictEqual(f(-42e6), " −$42.0M"); - assert.strictEqual(f(+4.2e6), " $4.2M"); + assert.strictEqual(f(-42e6), " −$42.0M"); + assert.strictEqual(f(+4.2e6), " $4.2M"); +}) + +it("formatPrefix(\" $12,.1s\", value)(number) matches format(\" $12,.2s\")(number) when the units are the same", () => { + // The fixed length of 12 is inclusive of the unit 'M' + const fp = formatPrefix(" $12,.1s", 1e6); + const f = format(" $12,.2s"); + assert.strictEqual(fp(+4.2e6), " $4.2M"); + assert.strictEqual(f(+4.2e6), " $4.2M"); +}) + +it("formatPrefix(\"($~s\", value)(number) formats with the SI prefix inside parentheses", () => { + assert.strictEqual(formatPrefix("($~s", 1e3)(1e3), "$1k"); + assert.strictEqual(formatPrefix("($~s", 1e3)(-1e3), "($1k)"); }); + From 75dd171ca3558096035dfe831876821503b8c964 Mon Sep 17 00:00:00 2001 From: Glen Kelley Date: Fri, 20 Jan 2023 11:52:26 +1100 Subject: [PATCH 2/5] Fixed unit order to be inside parentheses. --- src/locale.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/locale.js b/src/locale.js index 404f941..5a029c6 100644 --- a/src/locale.js +++ b/src/locale.js @@ -20,7 +20,7 @@ export default function(locale) { minus = locale.minus === undefined ? "−" : locale.minus + "", nan = locale.nan === undefined ? "NaN" : locale.nan + ""; - function newFormat(specifier) { + function newFormat(specifier, fixedUnitParam="") { specifier = formatSpecifier(specifier); var fill = specifier.fill, @@ -32,7 +32,8 @@ export default function(locale) { comma = specifier.comma, precision = specifier.precision, trim = specifier.trim, - type = specifier.type; + type = specifier.type, + fixedUnit = fixedUnitParam; // The "n" type is an alias for ",g". if (type === "n") comma = true, type = "g"; @@ -87,7 +88,8 @@ export default function(locale) { // Compute the prefix and suffix. valuePrefix = (valueNegative ? (sign === "(" ? sign : minus) : sign === "-" || sign === "(" ? "" : sign) + valuePrefix; - valueSuffix = (type === "s" ? prefixes[8 + prefixExponent / 3] : "") + valueSuffix + (valueNegative && sign === "(" ? ")" : ""); + var units = fixedUnit === "" ? (type === "s" ? prefixes[8 + prefixExponent / 3] : "") : fixedUnit; + valueSuffix = units + valueSuffix + (valueNegative && sign === "(" ? ")" : ""); // Break the formatted value into the integer “value” part that can be // grouped, and fractional or exponential “suffix” part that is not. @@ -132,12 +134,12 @@ export default function(locale) { } function formatPrefix(specifier, value) { - var f = newFormat((specifier = formatSpecifier(specifier), specifier.type = "f", specifier)), - e = Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3, + var e = Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3, k = Math.pow(10, -e), - prefix = prefixes[8 + e / 3]; + prefix = prefixes[8 + e / 3], + f = newFormat((specifier = formatSpecifier(specifier), specifier.type = "f", specifier), prefix); return function(value) { - return f(k * value) + prefix; + return f(k * value); }; } From 63673a82ff691f8faac66e4ab495ab7244dea4d4 Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Tue, 13 Jan 2026 20:05:56 -0800 Subject: [PATCH 3/5] options --- src/locale.js | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/locale.js b/src/locale.js index 5a029c6..7dcf7d7 100644 --- a/src/locale.js +++ b/src/locale.js @@ -20,7 +20,7 @@ export default function(locale) { minus = locale.minus === undefined ? "−" : locale.minus + "", nan = locale.nan === undefined ? "NaN" : locale.nan + ""; - function newFormat(specifier, fixedUnitParam="") { + function newFormat(specifier, options) { specifier = formatSpecifier(specifier); var fill = specifier.fill, @@ -32,8 +32,7 @@ export default function(locale) { comma = specifier.comma, precision = specifier.precision, trim = specifier.trim, - type = specifier.type, - fixedUnit = fixedUnitParam; + type = specifier.type; // The "n" type is an alias for ",g". if (type === "n") comma = true, type = "g"; @@ -46,8 +45,8 @@ export default function(locale) { // Compute the prefix and suffix. // For SI-prefix, the suffix is lazily computed. - var prefix = symbol === "$" ? currencyPrefix : symbol === "#" && /[boxX]/.test(type) ? "0" + type.toLowerCase() : "", - suffix = symbol === "$" ? currencySuffix : /[%p]/.test(type) ? percent : ""; + var prefix = (options?.prefix !== undefined ? options.prefix : "") + (symbol === "$" ? currencyPrefix : symbol === "#" && /[boxX]/.test(type) ? "0" + type.toLowerCase() : ""), + suffix = (symbol === "$" ? currencySuffix : /[%p]/.test(type) ? percent : "") + (options?.suffix !== undefined ? options.suffix : ""); // What format function should we use? // Is this an integer type? @@ -88,8 +87,7 @@ export default function(locale) { // Compute the prefix and suffix. valuePrefix = (valueNegative ? (sign === "(" ? sign : minus) : sign === "-" || sign === "(" ? "" : sign) + valuePrefix; - var units = fixedUnit === "" ? (type === "s" ? prefixes[8 + prefixExponent / 3] : "") : fixedUnit; - valueSuffix = units + valueSuffix + (valueNegative && sign === "(" ? ")" : ""); + valueSuffix = (type === "s" ? prefixes[8 + prefixExponent / 3] : "") + valueSuffix + (valueNegative && sign === "(" ? ")" : ""); // Break the formatted value into the integer “value” part that can be // grouped, and fractional or exponential “suffix” part that is not. @@ -136,8 +134,7 @@ export default function(locale) { function formatPrefix(specifier, value) { var e = Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3, k = Math.pow(10, -e), - prefix = prefixes[8 + e / 3], - f = newFormat((specifier = formatSpecifier(specifier), specifier.type = "f", specifier), prefix); + f = newFormat((specifier = formatSpecifier(specifier), specifier.type = "f", specifier), {suffix: prefixes[8 + e / 3]}); return function(value) { return f(k * value); }; From 821649c47c4adfd4d91131e54bf34c3be8b06675 Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Tue, 13 Jan 2026 20:10:17 -0800 Subject: [PATCH 4/5] fix merge --- test/formatPrefix-test.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/test/formatPrefix-test.js b/test/formatPrefix-test.js index 70b32f5..fd7bb7a 100644 --- a/test/formatPrefix-test.js +++ b/test/formatPrefix-test.js @@ -6,7 +6,7 @@ test("formatPrefix(\",.0s\", value)(number) formats with the SI prefix appropria assert.strictEqual(formatPrefix(",.0s", 1e-6)(.0042), "4,200µ"); }); -it("formatPrefix(\",.3s\", value)(number) formats with the SI prefix appropriate to the specified value", () => { +test("formatPrefix(\",.3s\", value)(number) formats with the SI prefix appropriate to the specified value", () => { assert.strictEqual(formatPrefix(",.3s", 1e-3)(.00042), "0.420m"); }); @@ -23,18 +23,17 @@ test("formatPrefix(\" $12,.1s\", value)(number) formats with the specified SI pr const f = formatPrefix(" $12,.1s", 1e6); assert.strictEqual(f(-42e6), " −$42.0M"); assert.strictEqual(f(+4.2e6), " $4.2M"); -}) +}); -it("formatPrefix(\" $12,.1s\", value)(number) matches format(\" $12,.2s\")(number) when the units are the same", () => { +test("formatPrefix(\" $12,.1s\", value)(number) matches format(\" $12,.2s\")(number) when the units are the same", () => { // The fixed length of 12 is inclusive of the unit 'M' const fp = formatPrefix(" $12,.1s", 1e6); const f = format(" $12,.2s"); assert.strictEqual(fp(+4.2e6), " $4.2M"); assert.strictEqual(f(+4.2e6), " $4.2M"); -}) +}); -it("formatPrefix(\"($~s\", value)(number) formats with the SI prefix inside parentheses", () => { +test("formatPrefix(\"($~s\", value)(number) formats with the SI prefix inside parentheses", () => { assert.strictEqual(formatPrefix("($~s", 1e3)(1e3), "$1k"); assert.strictEqual(formatPrefix("($~s", 1e3)(-1e3), "($1k)"); }); - From 099536c533c8bc3d6905bce06c54869813d31794 Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Tue, 13 Jan 2026 20:11:15 -0800 Subject: [PATCH 5/5] fix merge, again --- test/formatPrefix-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/formatPrefix-test.js b/test/formatPrefix-test.js index fd7bb7a..69308ef 100644 --- a/test/formatPrefix-test.js +++ b/test/formatPrefix-test.js @@ -1,5 +1,5 @@ import {assert, test} from "vitest"; -import {formatPrefix} from "../src/index.js"; +import {format, formatPrefix} from "../src/index.js"; test("formatPrefix(\",.0s\", value)(number) formats with the SI prefix appropriate to the specified value", () => { assert.strictEqual(formatPrefix(",.0s", 1e-6)(.00042), "420µ");