From f85f2778d0799b349ab51606d5d5b231a7eaa288 Mon Sep 17 00:00:00 2001 From: Julien Dupuy Date: Thu, 4 Jun 2026 12:03:58 +0200 Subject: [PATCH] Fix stale scattergl error bars on legend toggle --- draftlogs/7773_fix.md | 1 + src/traces/scattergl/plot.js | 5 +- test/jasmine/tests/scattergl_test.js | 71 ++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 draftlogs/7773_fix.md diff --git a/draftlogs/7773_fix.md b/draftlogs/7773_fix.md new file mode 100644 index 00000000000..9436109a7d3 --- /dev/null +++ b/draftlogs/7773_fix.md @@ -0,0 +1 @@ + - Fix stale `scattergl` error bars after toggling traces with mixed error bar visibility [[#7773](https://github.com/plotly/plotly.js/issues/7773)] diff --git a/src/traces/scattergl/plot.js b/src/traces/scattergl/plot.js index bcc0af6d149..9fd954fe5d4 100644 --- a/src/traces/scattergl/plot.js +++ b/src/traces/scattergl/plot.js @@ -128,7 +128,10 @@ var exports = module.exports = function plot(gd, subplot, cdata) { scene.line2d.update(scene.lineOptions); } if(scene.error2d) { - var errorBatch = (scene.errorXOptions || []).concat(scene.errorYOptions || []); + var errorBatch = (scene.errorXOptions || []).concat(scene.errorYOptions || []) + .map(function(errorOptions) { + return errorOptions || {positions: [], errors: []}; + }); scene.error2d.update(errorBatch); } if(scene.scatter2d) { diff --git a/test/jasmine/tests/scattergl_test.js b/test/jasmine/tests/scattergl_test.js index 5a430582f77..8e3595ffd15 100644 --- a/test/jasmine/tests/scattergl_test.js +++ b/test/jasmine/tests/scattergl_test.js @@ -216,6 +216,77 @@ describe('end-to-end scattergl tests', function() { .then(done, done.fail); }); + it('@gl should clear error bars when toggling traces with mixed error bars', function(done) { + function assertNoStaleErrorGroups(scene, msg) { + scene.error2d.groups.forEach(function(group, i) { + var hasSceneOptions = i < scene.count ? + scene.errorXOptions[i] : + scene.errorYOptions[i - scene.count]; + + if(!hasSceneOptions && group) { + expect(group.count).toBe(0, msg + ': group ' + i); + } + }); + } + + function assertVisibleErrorBars(msg, exp) { + var scene = gd._fullLayout._plots.xy._scene; + var countSceneErrorYOptions = scene.errorYOptions.filter(function(opts) { return opts; }).length; + var countDrawableErrorGroups = scene.error2d.groups.filter(function(group) { return group && group.count; }).length; + + expect(countSceneErrorYOptions).toBe(exp, msg + ' scene options'); + expect(countDrawableErrorGroups).toBe(exp, msg + ' renderer groups'); + assertNoStaleErrorGroups(scene, msg); + } + + Plotly.newPlot(gd, [{ + type: 'scattergl', + mode: 'lines', + name: 'no error 1', + x: [0, 1, 2], + y: [0, 1, 0] + }, { + type: 'scattergl', + mode: 'lines', + name: 'my', + x: [0, 1, 2], + y: [1, 2, 1], + error_y: {value: 0.2} + }, { + type: 'scattergl', + mode: 'lines', + name: 'no error 2', + x: [0, 1, 2], + y: [2, 3, 2] + }, { + type: 'scattergl', + mode: 'lines', + name: 'mz', + x: [0, 1, 2], + y: [3, 4, 3], + error_y: {value: 0.2} + }]) + .then(function() { + assertVisibleErrorBars('base', 2); + + return Plotly.restyle(gd, 'visible', 'legendonly', [3]); + }) + .then(function() { + assertVisibleErrorBars('after hiding last error trace', 1); + + return Plotly.restyle(gd, 'visible', true, [3]); + }) + .then(function() { + assertVisibleErrorBars('after showing last error trace', 2); + + return Plotly.restyle(gd, 'visible', 'legendonly', [1]); + }) + .then(function() { + assertVisibleErrorBars('after hiding first error trace', 1); + }) + .then(done, done.fail); + }); + it('@gl should change plot type with incomplete data', function(done) { Plotly.newPlot(gd, [{}]) .then(function() {