Skip to content

Apply axis format= in the native exporters#287

Merged
Alek99 merged 1 commit into
alek/fix-legend-truncationfrom
alek/fix-static-axis-format
Jul 25, 2026
Merged

Apply axis format= in the native exporters#287
Alek99 merged 1 commit into
alek/fix-legend-truncationfrom
alek/fix-static-axis-format

Conversation

@Alek99

@Alek99 Alek99 commented Jul 25, 2026

Copy link
Copy Markdown
Member

Stacked on #286.

Problem

x_axis(format=) / y_axis(format=) reached the wire and the browser honored
it, but _fmt_axis dispatched on axis kind alone and never read
axis["format"]. Every native target — SVG, PNG, JPEG, WebP and PDF — drew
automatic labels.

So y_axis(format=".0%") read 30% in the browser and 0.30 in the
exported image, and engine=chromium disagreed with the default engine on the
same figure. Engine.auto resolves to native for every format, so this was the
default path.

It also dropped the time grammar, not just numeric: format="%Y/%m/%d"
rendered Jan 02.

spec/api/styling.md recorded it as a known gap. Closing it rather than
restating it, since the user-facing docs recommend format= with no caveat.

Before / after

y_axis(format=".0%"), native PNG, plus the browser for reference.

Before (native PNG) After (native PNG) Browser (unchanged)
before after browser

0.10–0.3010%–30%, matching the browser exactly.

Also verified end to end: $,.2f$1,000.00, ,.0fK1,000K,
%Y/%m/%d2024/01/04, %b %YJan 2024.

Fix

Port the client grammar exactly:

  • _fmt_number_spec mirrors fmtNumberSpec — prefix / group / digits / f /
    % / suffix, affixes only with an explicit f or %, fall back to the
    automatic formatter otherwise.
  • _fmt_time_spec mirrors fmtTimeSpec — the %Y %m %d %H %M %S %b %B subset,
    UTC, English months, unknown tokens copied verbatim, never falling back.
  • The log-axis guard that keeps a decade below 1 from collapsing to "0" comes
    across too.

One deliberate difference: group separators are the ,/. pair rather than
the viewer's locale. The browser reads toLocaleString(undefined, ...), but a
static export has no viewer and must be reproducible, so the same figure exports
byte-identical on every machine. Documented in the spec.

Testing

The two implementations are independent, so the test pins the grammar
itself
— both regexes must appear verbatim in js/src/30_ticks.ts — alongside
the worked examples from the spec and an end-to-end check that an unsupported
spec still degrades to automatic labels rather than raising.

One quirk asserted deliberately: "{:,.2f}" is not rejected, because {: and
} are legal affixes under the shared grammar. Both sides agree on it; the test
keeps them agreeing rather than only covering the happy path.

Found during a customizability audit.

`x_axis(format=)`/`y_axis(format=)` reached the wire and the browser honored it,
but `_fmt_axis` dispatched on axis kind alone and never read `axis["format"]`.
Every native target — SVG, PNG, JPEG, WebP and PDF — drew automatic labels, so
`y_axis(format=".0%")` read 30% in the browser and 0.30 in the exported image,
and `engine=chromium` disagreed with the default engine on the same figure.
spec/api/styling.md recorded this as a known gap; close it rather than restate
it, since the default export engine is native and the docs recommend `format=`
with no caveat.

Port the client grammar exactly: `_fmt_number_spec` mirrors `fmtNumberSpec`
(prefix/group/digits/f/% /suffix, affixes only with an explicit `f` or `%`,
fall back to the automatic formatter otherwise) and `_fmt_time_spec` mirrors
`fmtTimeSpec` (the `%Y %m %d %H %M %S %b %B` subset, UTC, English months,
unknown tokens copied verbatim, never falling back). The log-axis guard that
keeps a decade below 1 from collapsing to "0" comes across too.

One deliberate difference: group separators are the `,`/`.` pair rather than the
viewer's locale. The browser reads `toLocaleString(undefined, ...)`, but a
static export has no viewer and must be reproducible, so the same figure exports
byte-identical everywhere.

The two implementations are independent, so the test pins the grammar itself —
both regexes must appear verbatim in js/src/30_ticks.ts — alongside the worked
examples from the spec and an end-to-end check that an unsupported spec still
degrades to automatic labels.
@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7815bda4-0ded-4eaa-bc7a-0ec993474e66

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch alek/fix-static-axis-format

Comment @coderabbitai help to get the list of available commands.

@codspeed-hq

codspeed-hq Bot commented Jul 25, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 102 untouched benchmarks
⏩ 2 skipped benchmarks1


Comparing alek/fix-static-axis-format (24410a1) with main (b7d9694)2

Open in CodSpeed

Footnotes

  1. 2 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

  2. No successful run was found on alek/fix-legend-truncation (248e423) during the generation of this report, so main (b7d9694) was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@Alek99
Alek99 merged commit 341627b into alek/fix-legend-truncation Jul 25, 2026
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant