From 716bac162d7c558d632521f65de5104ebcde4ac4 Mon Sep 17 00:00:00 2001 From: Emily KL <4672118+emilykl@users.noreply.github.com> Date: Sun, 9 Aug 2026 12:26:38 -0400 Subject: [PATCH 1/3] ensure that plot still renders in the case where an unsupported MathJax version is present --- src/lib/svg_text_utils.js | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/lib/svg_text_utils.js b/src/lib/svg_text_utils.js index 6e133611075..5b26613c14e 100644 --- a/src/lib/svg_text_utils.js +++ b/src/lib/svg_text_utils.js @@ -32,6 +32,7 @@ exports.convertToTspans = function(_context, gd, _callback) { var tex = (!_context.attr('data-notex')) && gd && gd._context.typesetMath && (typeof MathJax !== 'undefined') && + isMathJaxVersionSupported() && matchTex(str); var parent = d3.select(_context.node().parentNode); @@ -204,18 +205,25 @@ function cleanEscapesForTex(s) { // and reused for subsequent calls. var mathjaxSVGDocument = null; -function texToSVG(_texString, _config, _callback) { - const MathJaxVersion = parseInt( - (MathJax.version || '').split('.')[0] - ); +// plotly.js is only compatible with MathJax v3 and v4. +const mathJaxMajorVersion = () => parseInt((MathJax.version || '').split('.')[0]); - if( - MathJaxVersion !== 3 && - MathJaxVersion !== 4 - ) { +// Only warn once per page +var warnedUnsupportedMathJax = false; + +function isMathJaxVersionSupported() { + const version = mathJaxMajorVersion(); + if(version === 3 || version === 4) return true; + + if(!warnedUnsupportedMathJax) { + warnedUnsupportedMathJax = true; Lib.warn('Unsupported MathJax version:', MathJax.version); - return; } + return false; +} + +function texToSVG(_texString, _config, _callback) { + const MathJaxVersion = mathJaxMajorVersion(); var tmpDiv; From dae92a86bde2c817f80235e5ce060d4c0035ddc3 Mon Sep 17 00:00:00 2001 From: Emily KL <4672118+emilykl@users.noreply.github.com> Date: Sun, 9 Aug 2026 12:27:04 -0400 Subject: [PATCH 2/3] add regression tests --- test/jasmine/bundle_tests/mathjax_test.js | 44 +++++++++++++++++++++++ test/jasmine/tests/svg_text_utils_test.js | 41 +++++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/test/jasmine/bundle_tests/mathjax_test.js b/test/jasmine/bundle_tests/mathjax_test.js index 2602ff3818c..21704fa8130 100644 --- a/test/jasmine/bundle_tests/mathjax_test.js +++ b/test/jasmine/bundle_tests/mathjax_test.js @@ -159,4 +159,48 @@ describe('Test MathJax v' + mathjaxVersion + ':', function() { .then(done, done.fail); }); }); + + describe('Test tex rendering:', function() { + var gd; + + beforeEach(function() { + gd = createGraphDiv(); + }); + + afterEach(destroyGraphDiv); + + it('should hand tex titles and tick labels off to MathJax', function(done) { + Plotly.newPlot(gd, { + data: [{ + x: ['$\\phi$', '$\\nabla \\cdot \\vec{F}$'], + y: [1, 2] + }], + layout: { + title: { text: '$E = mc^2$' } + } + }) + .then(function() { + var gd3 = d3Select(gd); + + // '.gtitle-math-group' is only added once MathJax has typeset the + // string, so its presence is what tells us the tex was rendered + expect(gd3.selectAll('.gtitle-math-group').size()).toBe(1, 'title math group'); + + // tick label math groups carry the default 'text-math-group' class + expect(gd3.selectAll('.text-math-group').size()).toBe(2, 'tick label math groups'); + + var rendered = []; + gd3.selectAll('[class*=-math-group]').each(function() { + expect(this.getAttribute('data-math')).toBe('Y'); + rendered.push(this.getAttribute('data-unformatted')); + }); + expect(rendered.sort()).toEqual([ + '$E = mc^2$', + '$\\nabla \\cdot \\vec{F}$', + '$\\phi$' + ]); + }) + .then(done, done.fail); + }); + }); }); diff --git a/test/jasmine/tests/svg_text_utils_test.js b/test/jasmine/tests/svg_text_utils_test.js index 307f8d8c45c..5bcde86108e 100644 --- a/test/jasmine/tests/svg_text_utils_test.js +++ b/test/jasmine/tests/svg_text_utils_test.js @@ -1,8 +1,12 @@ var d3Select = require('../../strict-d3').select; var d3SelectAll = require('../../strict-d3').selectAll; +var Plotly = require('../../../lib/index'); var util = require('../../../src/lib/svg_text_utils'); +var createGraphDiv = require('../assets/create_graph_div'); +var destroyGraphDiv = require('../assets/destroy_graph_div'); + describe('svg+text utils', function() { 'use strict'; @@ -619,3 +623,40 @@ describe('sanitizeHTML', function() { expect(innerHTML).toEqual('click'); }); }); + +// regression test for https://github.com/plotly/plotly.js/issues/7926 +describe('convertToTspans with an unsupported MathJax version', function() { + 'use strict'; + + var gd; + var mathJaxBefore; + + beforeEach(function() { + gd = createGraphDiv(); + mathJaxBefore = window.MathJax; + // Fake the presence of MathJax v2 by clearing window.MathJax + // and setting window.MathJax.version to a v2.x version string + window.MathJax = {version: '2.7.9'}; + }); + + afterEach(function() { + if(mathJaxBefore === undefined) delete window.MathJax; + else window.MathJax = mathJaxBefore; + destroyGraphDiv(); + }); + + it('draws the plot with the tex left unevaluated', function(done) { + Plotly.newPlot(gd, [{y: [1, 2, 3]}], {title: {text: '$x^2$'}}) + .then(function() { + // whole plot should render except for tex string + expect(d3SelectAll('.scatterlayer .trace').size()).toBe(1, 'trace'); + expect(d3SelectAll('.xtick').size()).toBeGreaterThan(0, 'x ticks'); + + var title = d3Select('.gtitle'); + expect(title.size()).toBe(1, 'title'); + expect(title.text()).toBe('$x^2$', 'raw tex as title'); + expect(title.node().style.display).not.toBe('none', 'title is visible'); + }) + .then(done, done.fail); + }); +}); From 64eeb409cd7fd09eac53cedcc48e11f342fabd87 Mon Sep 17 00:00:00 2001 From: Emily KL <4672118+emilykl@users.noreply.github.com> Date: Sun, 9 Aug 2026 12:31:07 -0400 Subject: [PATCH 3/3] add draftlog --- draftlogs/7951_fix.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 draftlogs/7951_fix.md diff --git a/draftlogs/7951_fix.md b/draftlogs/7951_fix.md new file mode 100644 index 00000000000..bd25da8fb98 --- /dev/null +++ b/draftlogs/7951_fix.md @@ -0,0 +1 @@ +- Fix issue where plot failed to render if unsupported MathJax version was present on page [[#7951](https://github.com/plotly/plotly.js/pull/7951)]