From 753309c4cbcdefd84403bfc09e3950e6b961bab7 Mon Sep 17 00:00:00 2001 From: Lexachoc <20377719+Lexachoc@users.noreply.github.com> Date: Thu, 1 Oct 2026 18:26:29 +0200 Subject: [PATCH 1/3] fix rangeslider autorange --- src/plot_api/plot_api.js | 1 + test/jasmine/tests/range_slider_test.js | 31 +++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js index f48d808425b..66521a889d5 100644 --- a/src/plot_api/plot_api.js +++ b/src/plot_api/plot_api.js @@ -1847,6 +1847,7 @@ function addAxRangeSequence(seq, rangesAltered) { seq.push( clearOutline, subroutines.doAutoRangeAndConstraints, + Registry.getComponentMethod('rangeslider', 'calcAutorange'), drawAxes, subroutines.drawData, subroutines.finalDraw diff --git a/test/jasmine/tests/range_slider_test.js b/test/jasmine/tests/range_slider_test.js index fed44189d9a..4df89ed6c31 100644 --- a/test/jasmine/tests/range_slider_test.js +++ b/test/jasmine/tests/range_slider_test.js @@ -988,6 +988,37 @@ describe('rangesliders in general', function() { .then(done, done.fail); }); + it('should reset autoranged rangeslider range after axis autorange', function(done) { + var initialRange; + var precision = 1e-2; + + Plotly.newPlot(gd, [{ + y: [2, 1, 2] + }], { + xaxis: { rangeslider: {} } + }) + .then(function() { + initialRange = gd._fullLayout.xaxis.range.slice(); + + expect(gd._fullLayout.xaxis.rangeslider.range).toBeCloseToArray(initialRange, precision); + + // zoom out + return Plotly.relayout(gd, 'xaxis.range', [-1, 3]); + }) + .then(function() { + expect(gd._fullLayout.xaxis.range).toBeCloseToArray([-1, 3], precision); + expect(gd._fullLayout.xaxis.rangeslider.range).toBeCloseToArray([-1, 3], precision); + + // reset axis + return Plotly.relayout(gd, 'xaxis.autorange', true); + }) + .then(function() { + expect(gd._fullLayout.xaxis.range).toBeCloseToArray(initialRange, precision); + expect(gd._fullLayout.xaxis.rangeslider.range).toBeCloseToArray(initialRange, precision); + }) + .then(done, done.fail); + }); + it('should not expand its range when range slider range is set', function(done) { Plotly.newPlot(gd, [{ y: [2, 1, 2] From b56ad3bc5978043164b2bf62f21871aadfc532ca Mon Sep 17 00:00:00 2001 From: Lexachoc <20377719+Lexachoc@users.noreply.github.com> Date: Thu, 1 Oct 2026 18:57:50 +0200 Subject: [PATCH 2/3] add draftlog --- draftlogs/8098_fix.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 draftlogs/8098_fix.md diff --git a/draftlogs/8098_fix.md b/draftlogs/8098_fix.md new file mode 100644 index 00000000000..adf7b2fa021 --- /dev/null +++ b/draftlogs/8098_fix.md @@ -0,0 +1 @@ +- Fix `rangeslider` not resetting correctly after zooming out [[#8098](https://github.com/plotly/plotly.js/pull/8098)] From 6340c23ffecc87dab37760098188fd0c7aba779c Mon Sep 17 00:00:00 2001 From: Lexachoc <20377719+Lexachoc@users.noreply.github.com> Date: Thu, 1 Oct 2026 20:43:37 +0200 Subject: [PATCH 3/3] revert previous fix, fix root cause, update tests --- src/components/rangeslider/draw.js | 4 +- src/plot_api/plot_api.js | 1 - test/jasmine/tests/range_slider_test.js | 64 ++++++++++++++++++------- 3 files changed, 50 insertions(+), 19 deletions(-) diff --git a/src/components/rangeslider/draw.js b/src/components/rangeslider/draw.js index e86fc714c60..19fbfc09d20 100644 --- a/src/components/rangeslider/draw.js +++ b/src/components/rangeslider/draw.js @@ -76,7 +76,7 @@ module.exports = function(gd) { // update range // Expand slider range to the axis range if(opts.range) { - var rng = Lib.simpleMap(opts.range, axisOpts.r2l); + var rng = Lib.simpleMap(opts._input.range || opts.range, axisOpts.r2l); var axRng = Lib.simpleMap(axisOpts.range, axisOpts.r2l); var newRng; @@ -92,7 +92,7 @@ module.exports = function(gd) { ]; } - opts.range = opts._input.range = Lib.simpleMap(newRng, axisOpts.l2r); + opts.range = Lib.simpleMap(newRng, axisOpts.l2r); } axisOpts.cleanRange('rangeslider.range'); diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js index 66521a889d5..f48d808425b 100644 --- a/src/plot_api/plot_api.js +++ b/src/plot_api/plot_api.js @@ -1847,7 +1847,6 @@ function addAxRangeSequence(seq, rangesAltered) { seq.push( clearOutline, subroutines.doAutoRangeAndConstraints, - Registry.getComponentMethod('rangeslider', 'calcAutorange'), drawAxes, subroutines.drawData, subroutines.finalDraw diff --git a/test/jasmine/tests/range_slider_test.js b/test/jasmine/tests/range_slider_test.js index 4df89ed6c31..66dcdb83382 100644 --- a/test/jasmine/tests/range_slider_test.js +++ b/test/jasmine/tests/range_slider_test.js @@ -928,12 +928,15 @@ describe('rangesliders in general', function() { afterEach(destroyGraphDiv); - function assertRange(axRange, rsRange) { + function assertRange(axRange, rsRange, fullLayoutRsRange) { // lower toBeCloseToArray precision for FF38 on CI var precision = 1e-2; + if(!fullLayoutRsRange) fullLayoutRsRange = rsRange; + expect(gd.layout.xaxis.range).toBeCloseToArray(axRange, precision); expect(gd.layout.xaxis.rangeslider.range).toBeCloseToArray(rsRange, precision); + expect(gd._fullLayout.xaxis.rangeslider.range).toBeCloseToArray(fullLayoutRsRange, precision); } it('should plot when only x data is provided', function(done) { @@ -990,7 +993,6 @@ describe('rangesliders in general', function() { it('should reset autoranged rangeslider range after axis autorange', function(done) { var initialRange; - var precision = 1e-2; Plotly.newPlot(gd, [{ y: [2, 1, 2] @@ -1000,21 +1002,45 @@ describe('rangesliders in general', function() { .then(function() { initialRange = gd._fullLayout.xaxis.range.slice(); - expect(gd._fullLayout.xaxis.rangeslider.range).toBeCloseToArray(initialRange, precision); + assertRange(initialRange, initialRange); // zoom out return Plotly.relayout(gd, 'xaxis.range', [-1, 3]); }) .then(function() { - expect(gd._fullLayout.xaxis.range).toBeCloseToArray([-1, 3], precision); - expect(gd._fullLayout.xaxis.rangeslider.range).toBeCloseToArray([-1, 3], precision); + assertRange([-1, 3], initialRange, [-1, 3]); // reset axis return Plotly.relayout(gd, 'xaxis.autorange', true); }) .then(function() { - expect(gd._fullLayout.xaxis.range).toBeCloseToArray(initialRange, precision); - expect(gd._fullLayout.xaxis.rangeslider.range).toBeCloseToArray(initialRange, precision); + assertRange(initialRange, initialRange); + }) + .then(done, done.fail); + }); + + it('should restore fixed rangeslider range when axis range is restored', function(done) { + Plotly.newPlot(gd, [{ + x: [0, 50], + y: [1, 2] + }], { + xaxis: { + range: [10, 45], + rangeslider: {range: [20, 30]} + } + }) + .then(function() { + assertRange([10, 45], [20, 30], [10, 45]); + + return Plotly.relayout(gd, 'xaxis.range', [-10, 60]); + }) + .then(function() { + assertRange([-10, 60], [20, 30], [-10, 60]); + + return Plotly.relayout(gd, 'xaxis.range', [10, 45]); + }) + .then(function() { + assertRange([10, 45], [20, 30], [10, 45]); }) .then(done, done.fail); }); @@ -1057,11 +1083,11 @@ describe('rangesliders in general', function() { .then(function() { assertRange([-0.26, 4.26], [-0.26, 4.26]); - // smaller than xaxis.range - won't be accepted + // smaller than xaxis.range - displayed range expands to cover the axis range return Plotly.relayout(gd, {'xaxis.rangeslider.range': [0, 2]}); }) .then(function() { - assertRange([-0.26, 4.26], [-0.26, 4.26]); + assertRange([-0.26, 4.26], [0, 2], [-0.26, 4.26]); // will be accepted (and autorange is disabled by impliedEdits) return Plotly.relayout(gd, {'xaxis.rangeslider.range': [-2, 12]}); @@ -1193,7 +1219,7 @@ describe('rangesliders in general', function() { expect(xa.rangeslider.range) .toBeCloseToArray(exp.rangesliderRng, 1, 'rangeslider rng ' + msg); expect(xa.rangeslider._input.range) - .toBeCloseToArray(exp.rangesliderRng, 1, 'rangeslider input rng ' + msg); + .toBeCloseToArray(exp.rangesliderInputRng, 1, 'rangeslider input rng ' + msg); } Plotly.newPlot(gd, [{ @@ -1204,7 +1230,8 @@ describe('rangesliders in general', function() { .then(function() { _assert('base', { axRng: [0.935, 2.06], - rangesliderRng: [0.935, 2.06] + rangesliderRng: [0.935, 2.06], + rangesliderInputRng: [0.935, 2.06] }); return Plotly.relayout(gd, 'xaxis.autorange', 'reversed'); @@ -1212,7 +1239,8 @@ describe('rangesliders in general', function() { .then(function() { _assert('reversed!', { axRng: [2.06, 0.935], - rangesliderRng: [2.06, 0.935] + rangesliderRng: [2.06, 0.935], + rangesliderInputRng: [0.935, 2.06] }); return Plotly.relayout(gd, 'xaxis.range', [0, 3]); @@ -1220,7 +1248,8 @@ describe('rangesliders in general', function() { .then(function() { _assert('set increasing rng', { axRng: [0, 3], - rangesliderRng: [0, 3] + rangesliderRng: [0, 3], + rangesliderInputRng: [0.935, 2.06] }); return Plotly.relayout(gd, 'xaxis.range', [3, 0]); @@ -1228,7 +1257,8 @@ describe('rangesliders in general', function() { .then(function() { _assert('set reversed rng', { axRng: [3, 0], - rangesliderRng: [3, 0] + rangesliderRng: [3, 0], + rangesliderInputRng: [0.935, 2.06] }); return Plotly.relayout(gd, 'xaxis.rangeslider.range', [0, 3]); @@ -1236,7 +1266,8 @@ describe('rangesliders in general', function() { .then(function() { _assert('reversed ax rng / increasing rangeslider rng', { axRng: [3, 0], - rangesliderRng: [3, 0] + rangesliderRng: [3, 0], + rangesliderInputRng: [0, 3] }); return Plotly.relayout(gd, { @@ -1247,7 +1278,8 @@ describe('rangesliders in general', function() { .then(function() { _assert('increasing ax rng / reversed rangeslider rng', { axRng: [0, 3], - rangesliderRng: [0, 3] + rangesliderRng: [0, 3], + rangesliderInputRng: [3, 0] }); }) .then(done, done.fail);