Repository navigation
[BUG]: hovertemplate/texttemplate/tickformat silently ignore a valid d3-format spec that begins with a sign flag (e.g. "+.2f") #7897
Description
Description
A d3-format specifier that starts with a sign flag — +, -, (, or a space — is silently dropped when used in hovertemplate, texttemplate, tickformat, or hoverformat, and the raw (unformatted) number is shown instead.
For example, %{y:+.2f} should render +12.35, but it renders the full-precision value 12.3456789. The sibling spec %{y:.2f} (no sign flag) works correctly and renders 12.35.
+.2f is a valid d3-format specifier (grammar: [[fill]align][sign][symbol][0][width][,][.precision][~][type]), so it is expected to work.
Root cause is in Lib.adjustFormat (src/lib/index.js). Its "add a leading tilde to trim trailing zeros" heuristic does not account for a leading sign flag:
// src/lib/index.js
lib.adjustFormat = function adjustFormat(formatStr) {
if (!formatStr || /^\d[.]\df/.test(formatStr) || /[.]\d%/.test(formatStr)) return formatStr;
if (formatStr === '0.f') return '~f';
if (/^\d%/.test(formatStr)) return '~%';
if (/^\ds/.test(formatStr)) return '~s';
// try adding tilde to the start of format in order to trim
if (!/^[~,.0$]/.test(formatStr) && /[&fps]/.test(formatStr)) return '~' + formatStr;
return formatStr;
};For formatStr = "+.2f":
/^[~,.0$]/.test("+.2f")isfalse(it starts with+, which is not in the set), so!(...)istrue;/[&fps]/.test("+.2f")istrue(containsf);- therefore
adjustFormatreturns"~+.2f".
"~+.2f" is an invalid d3-format spec (the ~ trim flag must appear immediately before the type, e.g. +.2~f; a leading ~ followed by a sign does not parse). d3Format('~+.2f') throws, and Lib.numberFormat catches it and falls back to Lib.noFormat, which returns String(value) — the unformatted number:
lib.numberFormat = function (formatStr) {
var fn;
try {
fn = d3Format(lib.adjustFormat(formatStr));
} catch (e) {
lib.warnBadFormat(formatStr); // logs: encountered bad format: "+.2f"
return lib.noFormat; // -> String(value), unformatted
}
return fn;
};By contrast, ".2f" starts with ., which is in [~,.0$], so adjustFormat leaves it untouched and it formats correctly.
This affects every code path that goes through Lib.numberFormat: hovertemplate, texttemplate, tickformat, and hoverformat.
Screenshots/Video
Hovering the bar in the repro below: field A (:.2f) shows 12.35; field B (:+.2f) shows 12.3456789 instead of +12.35. The browser console also logs encountered bad format: "+.2f".
Steps to reproduce
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script src="https://cdn.plot.ly/plotly-3.7.0.js"></script>
</head>
<body>
<div id="chart" style="width:700px;height:260px;"></div>
<script>
var delta = 12.3456789;
Plotly.newPlot('chart', [{
type: 'bar', orientation: 'h',
y: ['example'], x: [delta],
customdata: [[delta, delta]],
hovertemplate:
'A (:.2f) = %{customdata[0]:.2f}<br>' +
'B (:+.2f) = %{customdata[1]:+.2f}<extra></extra>'
}]);
// Expected hover: A = 12.35 B = +12.35
// Actual hover: A = 12.35 B = 12.3456789
</script>
</body>
</html>- Open the page in a browser (JSFiddle).
- Hover over the bar.
- Note that field B (
%{...:+.2f}) shows12.3456789instead of+12.35, while field A (%{...:.2f}) correctly shows12.35. - Open the developer console and note the warning:
encountered bad format: "+.2f".
The same failure occurs with any spec beginning with a sign flag and containing one of f/p/s/&, e.g. -.2f, ( .2f, +,.0f, +.1%, +.3s. It also reproduces via tickformat: '+.2f' and hoverformat: '+.2f'.
Notes
- Confirmed present on
master(and releasedv3.7.0) —Lib.adjustFormatis unchanged as of commit227bba91and still contains the tilde-prepend heuristic above. - Suggested fix: in the tilde-prepend branch, do not prepend
~when the spec begins with a sign flag. For example, exclude a leading[+\-( ](and any leading fill/align) from that branch, or only insert~immediately before the type character rather than at the start of the spec. A regression test covering+.2f,-.2f,( .2f,+,.0f,+.3s,+.1%would guard this. - Workaround for users: pre-format the value in the data (pass a pre-rendered string via
customdata/text) or omit the leading+sign flag.
Activity
camdecoster commentedon Jul 22, 2026
Thanks for the report! Someone already created a PR to fix this, so I'll review that.
Fix adjustFormat dropping d3-format specs that start with a sign flag