From a6b2c2a4af659415dece74a2a3d49385a9ac88b1 Mon Sep 17 00:00:00 2001 From: Tem Revil Date: Fri, 10 Jul 2026 16:04:43 +0300 Subject: [PATCH 1/5] Fix adjustFormat dropping d3-format specs that start with a sign flag A d3-format specifier beginning with a sign flag (+, -, (, space) was silently ignored: adjustFormat prepended a trim tilde to the whole string, producing an invalid spec like ~+.2f. d3.format then threw, numberFormat fell back to noFormat, and the raw unformatted number was shown (e.g. %{y:+.2f} rendered 12.3456789 instead of +12.35). Skip past an optional leading sign flag and symbol ($, #) before the trim heuristic and reattach it, so the tilde is only added in a valid position. Formats without such a prefix are unaffected. Fixes #7897 --- src/lib/index.js | 11 +++++++++-- test/jasmine/tests/lib_number_format_test.js | 6 ++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/lib/index.js b/src/lib/index.js index d898a526140..9259ffbb83b 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -19,8 +19,15 @@ lib.adjustFormat = function adjustFormat(formatStr) { 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; + // A d3-format spec may begin with a sign flag (+, -, (, space) and/or a + // symbol ($, #). Look past that prefix before deciding whether to trim, and + // reattach it: prepending the tilde to the whole string (e.g. "~+.2f") is an + // invalid spec that d3Format rejects, so "+.2f" used to be silently dropped. + var prefix = (formatStr.match(/^[+\-( ]?[$#]?/) || [''])[0]; + var rest = formatStr.slice(prefix.length); + + // try adding tilde to trim trailing zeros + if (!/^[~,.0$]/.test(rest) && /[&fps]/.test(rest)) return prefix + '~' + rest; return formatStr; }; diff --git a/test/jasmine/tests/lib_number_format_test.js b/test/jasmine/tests/lib_number_format_test.js index 21ddad84809..9c898beea65 100644 --- a/test/jasmine/tests/lib_number_format_test.js +++ b/test/jasmine/tests/lib_number_format_test.js @@ -53,6 +53,12 @@ describe('number format', function() { { format: '0f', number: float, exp: '12345.678901'}, { format: '1f', number: float, exp: '12345.678901'}, + // sign flag with an explicit precision must not be dropped (was silently + // ignored because adjustFormat produced the invalid spec "~+.2f") + { format: '+.2f', number: float, exp: '+12345.68'}, + { format: '+.0f', number: float, exp: '+12346'}, + { format: '-.4f', number: float, exp: '12345.6789'}, + // space-filled and default sign { format: '-13', number: float, exp: '-12345.678901'}, { format: '-14', number: float, exp: ' -12345.678901'}, From 053662fdfa9ed4567faab51570c525648f26caa3 Mon Sep 17 00:00:00 2001 From: Tem Revil Date: Fri, 10 Jul 2026 16:05:36 +0300 Subject: [PATCH 2/5] Add draftlog for # --- draftlogs/_fix.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 draftlogs/_fix.md diff --git a/draftlogs/_fix.md b/draftlogs/_fix.md new file mode 100644 index 00000000000..569d1a7d57f --- /dev/null +++ b/draftlogs/_fix.md @@ -0,0 +1 @@ +- Fix `hovertemplate`/`texttemplate`/`tickformat`/`hoverformat` silently ignoring d3-format specs that start with a sign flag such as `+.2f` [[#](https://github.com/plotly/plotly.js/pull/)] From 6ae433c967cbaca1e7fe61ac8a43793f9737fcae Mon Sep 17 00:00:00 2001 From: Tem Revil Date: Fri, 10 Jul 2026 16:06:12 +0300 Subject: [PATCH 3/5] Remove malformed draftlog --- draftlogs/_fix.md | 1 - 1 file changed, 1 deletion(-) delete mode 100644 draftlogs/_fix.md diff --git a/draftlogs/_fix.md b/draftlogs/_fix.md deleted file mode 100644 index 569d1a7d57f..00000000000 --- a/draftlogs/_fix.md +++ /dev/null @@ -1 +0,0 @@ -- Fix `hovertemplate`/`texttemplate`/`tickformat`/`hoverformat` silently ignoring d3-format specs that start with a sign flag such as `+.2f` [[#](https://github.com/plotly/plotly.js/pull/)] From 95dc3adb6afafe8d45918ef14097ce032e5a01a9 Mon Sep 17 00:00:00 2001 From: Tem Revil Date: Fri, 10 Jul 2026 16:06:30 +0300 Subject: [PATCH 4/5] Add draftlog for #7900 --- draftlogs/7900_fix.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 draftlogs/7900_fix.md diff --git a/draftlogs/7900_fix.md b/draftlogs/7900_fix.md new file mode 100644 index 00000000000..3fd68e814d0 --- /dev/null +++ b/draftlogs/7900_fix.md @@ -0,0 +1 @@ +- Fix `hovertemplate`/`texttemplate`/`tickformat`/`hoverformat` silently ignoring d3-format specs that start with a sign flag such as `+.2f` [[#7900](https://github.com/plotly/plotly.js/pull/7900)] From a6c98162022da78fb2ce7a334b009d9ab863d843 Mon Sep 17 00:00:00 2001 From: Tem Revil Date: Tue, 28 Jul 2026 16:13:23 +0300 Subject: [PATCH 5/5] Keep symbol-led specs out of the trimmed-prefix path camdecoster pointed out that folding $/# into the prefix group broke symbol-led specs: "$f" became "$~f", an invalid spec, and stripping the symbol before the trim check meant "$1.500000" would come out as "$1.5" once the tilde got added. Only sign flags (+, -, (, space) belong in the stripped prefix; symbol-led specs now fall straight through to the untrimmed return, matching the existing hex/octal/binary "#" cases. Added test coverage for $f/#f/$s, reworded the draftlog line to "improperly handling" since it's not strictly a silent drop anymore. --- draftlogs/7900_fix.md | 2 +- src/lib/index.js | 15 ++++++++------- test/jasmine/tests/lib_number_format_test.js | 5 +++++ 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/draftlogs/7900_fix.md b/draftlogs/7900_fix.md index 3fd68e814d0..bee5d62ee9e 100644 --- a/draftlogs/7900_fix.md +++ b/draftlogs/7900_fix.md @@ -1 +1 @@ -- Fix `hovertemplate`/`texttemplate`/`tickformat`/`hoverformat` silently ignoring d3-format specs that start with a sign flag such as `+.2f` [[#7900](https://github.com/plotly/plotly.js/pull/7900)] +- Fix `hovertemplate`/`texttemplate`/`tickformat`/`hoverformat` improperly handling d3-format specs that start with a sign flag such as `+.2f` [[#7900](https://github.com/plotly/plotly.js/pull/7900)] diff --git a/src/lib/index.js b/src/lib/index.js index 9259ffbb83b..ec1cf71b51e 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -19,15 +19,16 @@ lib.adjustFormat = function adjustFormat(formatStr) { if (/^\d%/.test(formatStr)) return '~%'; if (/^\ds/.test(formatStr)) return '~s'; - // A d3-format spec may begin with a sign flag (+, -, (, space) and/or a - // symbol ($, #). Look past that prefix before deciding whether to trim, and - // reattach it: prepending the tilde to the whole string (e.g. "~+.2f") is an - // invalid spec that d3Format rejects, so "+.2f" used to be silently dropped. - var prefix = (formatStr.match(/^[+\-( ]?[$#]?/) || [''])[0]; + // A d3-format spec may begin with a sign flag (+, -, (, space). Look past + // that prefix before deciding whether to trim, and reattach it: prepending + // the tilde to the whole string (e.g. "~+.2f") is an invalid spec that + // d3Format rejects, so "+.2f" used to be silently dropped. + var prefix = (formatStr.match(/^[+\-( ]/) || [''])[0]; var rest = formatStr.slice(prefix.length); - // try adding tilde to trim trailing zeros - if (!/^[~,.0$]/.test(rest) && /[&fps]/.test(rest)) return prefix + '~' + rest; + // try adding tilde to trim trailing zeros; leave symbol-led specs ($, #) + // untrimmed, since the symbol isn't part of the prefix we stripped above + if (!/^[~,.0$#]/.test(rest) && /[&fps]/.test(rest)) return prefix + '~' + rest; return formatStr; }; diff --git a/test/jasmine/tests/lib_number_format_test.js b/test/jasmine/tests/lib_number_format_test.js index 9c898beea65..e0c40479f2b 100644 --- a/test/jasmine/tests/lib_number_format_test.js +++ b/test/jasmine/tests/lib_number_format_test.js @@ -59,6 +59,11 @@ describe('number format', function() { { format: '+.0f', number: float, exp: '+12346'}, { format: '-.4f', number: float, exp: '12345.6789'}, + // symbol-led specs ($, #) are left untrimmed + { format: '$f', number: 1.5, exp: '$1.500000'}, + { format: '#f', number: 1.5, exp: '1.500000'}, + { format: '$s', number: 1500, exp: '$1.50000k'}, + // space-filled and default sign { format: '-13', number: float, exp: '-12345.678901'}, { format: '-14', number: float, exp: ' -12345.678901'},