Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

formatNumber; filter log tickFormat function #2078

Merged
merged 4 commits into from
Jun 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions docs/features/formats.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@ import * as d3 from "d3";

These helper functions are provided for convenience as a **tickFormat** option for the [axis mark](../marks/axis.md), as the **text** option for a [text mark](../marks/text.md), or other use. See also [d3-format](https://d3js.org/d3-format), [d3-time-format](https://d3js.org/d3-time-format), and JavaScript’s built-in [date formatting](https://observablehq.com/@mbostock/date-formatting) and [number formatting](https://observablehq.com/@mbostock/number-formatting).

## formatNumber(*locale*) {#formatNumber}

```js
Plot.formatNumber("en-US")(Math.PI) // "3.142"
```

Returns a function that formats a given number according to the specified *locale*. The *locale* is a [BCP 47 language tag](https://tools.ietf.org/html/bcp47) and defaults to U.S. English.

## formatIsoDate(*date*) {#formatIsoDate}

```js
Expand Down
10 changes: 10 additions & 0 deletions src/format.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
/**
* Returns a function that formats a given number according to the specified
* *locale*.
*
* [1]: https://tools.ietf.org/html/bcp47
*
* @param locale - a [BCP 47 language tag][1]; defaults to U.S. English.
*/
export function formatNumber(locale?: string): (i: number) => string;

/**
* Returns a function that formats a given month number (from 0 = January to 11
* = December) according to the specified *locale* and *format*.
Expand Down
2 changes: 1 addition & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,6 @@ export {select, selectFirst, selectLast, selectMaxX, selectMaxY, selectMinX, sel
export {stackX, stackX1, stackX2, stackY, stackY1, stackY2} from "./transforms/stack.js";
export {treeNode, treeLink} from "./transforms/tree.js";
export {pointer, pointerX, pointerY} from "./interactions/pointer.js";
export {formatIsoDate, formatWeekday, formatMonth} from "./format.js";
export {formatIsoDate, formatNumber, formatWeekday, formatMonth} from "./format.js";
export {scale} from "./scales.js";
export {legend} from "./legends.js";
2 changes: 1 addition & 1 deletion src/marks/axis.js
Original file line number Diff line number Diff line change
Expand Up @@ -652,7 +652,7 @@ function inferTextChannel(scale, data, ticks, tickFormat, anchor) {
// possible, or the default ISO format (2014-01-26). TODO We need a better way
// to infer whether the ordinal scale is UTC or local time.
export function inferTickFormat(scale, data, ticks, tickFormat, anchor) {
return typeof tickFormat === "function"
return typeof tickFormat === "function" && !(scale.type === "log" && scale.tickFormat)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return typeof tickFormat === "function" && !(scale.type === "log" && scale.tickFormat)
return typeof tickFormat === "function" && scale.type !== "log"

Nit: I'm not sure about the case for the second part of the test. When scale.type is "log", the scale is an instance of d3.scaleLog() and it has a tickFormat method, which the user can't nullify.

Copy link
Member Author

@mbostock mbostock Jun 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn’t do this because I didn’t want to assume that log scales necessarily implement scale.tickFormat. That is currently true, but if this assumption doesn’t hold in the future, this would break.

? tickFormat
: tickFormat === undefined && data && isTemporal(data)
? inferTimeFormat(scale.type, data, anchor) ?? formatDefault
Expand Down
5 changes: 2 additions & 3 deletions test/marks/format-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ it("formatMonth(locale, format) does the right thing", () => {
assert.strictEqual(Plot.formatMonth("en", "narrow")(0), "J");
});

// GitHub Actions does not support locales.
it.skip("formatMonth('fr', format) does the right thing", () => {
it("formatMonth('fr', format) does the right thing", () => {
assert.strictEqual(Plot.formatMonth("fr", "long")(11), "décembre");
assert.strictEqual(Plot.formatMonth("fr", "short")(11), "déc.");
assert.strictEqual(Plot.formatMonth("fr", "narrow")(11), "D");
Expand All @@ -27,7 +26,7 @@ it("formatMonth(locale) has the expected default", () => {
assert.strictEqual(Plot.formatMonth("en", undefined)(0), "Jan");
});

it.skip("formatMonth('fr') has the expected default", () => {
it("formatMonth('fr') has the expected default", () => {
assert.strictEqual(Plot.formatMonth("fr")(11), "déc.");
assert.strictEqual(Plot.formatMonth("fr", undefined)(11), "déc.");
});
Expand Down
82 changes: 82 additions & 0 deletions test/output/logTickFormatFunction.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
82 changes: 82 additions & 0 deletions test/output/logTickFormatFunctionSv.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion test/plot.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ for (const [name, plot] of Object.entries(plots)) {
reindexMarker(root);
reindexClip(root);
let expected;
let actual = beautify.html(root.outerHTML, {
let actual = beautify.html(root.outerHTML.replaceAll(" ", "\xa0"), {
indent_size: 2,
inline: ["title", "tspan", "span", "svg", "a", "i"],
indent_inner_html: false
Expand Down
1 change: 1 addition & 0 deletions test/plots/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ export * from "./linear-regression-cars.js";
export * from "./linear-regression-mtcars.js";
export * from "./linear-regression-penguins.js";
export * from "./log-degenerate.js";
export * from "./log-tick-format.js";
export * from "./long-labels.js";
export * from "./markers.js";
export * from "./markov-chain.js";
Expand Down
9 changes: 9 additions & 0 deletions test/plots/log-tick-format.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import * as Plot from "@observablehq/plot";

export async function logTickFormatFunction() {
return Plot.plot({x: {type: "log", domain: [1, 4200], tickFormat: Plot.formatNumber()}});
}

export async function logTickFormatFunctionSv() {
return Plot.plot({x: {type: "log", domain: [1, 4200], tickFormat: Plot.formatNumber("sv-SE")}});
}