diff --git a/draftlogs/7507_fix.md b/draftlogs/7507_fix.md new file mode 100644 index 00000000000..2da2ac29e8d --- /dev/null +++ b/draftlogs/7507_fix.md @@ -0,0 +1 @@ +Fix `histogram` autobin size for single-point traces in `overlay` mode on data updates via `Plotly.react` [[#7507](https://github.com/plotly/plotly.js/pull/7507)], with thanks to @Lexachoc for the contribution! diff --git a/src/traces/histogram/calc.js b/src/traces/histogram/calc.js index ec10098692d..8f57a3d5365 100644 --- a/src/traces/histogram/calc.js +++ b/src/traces/histogram/calc.js @@ -327,10 +327,10 @@ function calcAllAutoBins(gd, trace, pa, mainData, _overlayEdgeCase) { // Edge case: single-valued histogram overlaying others // Use them all together to calculate the bin size for the single-valued one - // Don't re-calculate bin width if user manually specified it (checing in bingroup=='' or xbins is defined) + // Don't re-calculate bin width if user manually specified it if(isOverlay && !Registry.traceIs(trace, '2dMap') && newBinSpec._dataSpan === 0 && pa.type !== 'category' && pa.type !== 'multicategory' && - trace.bingroup === '' && (typeof trace.xbins === 'undefined')) { + trace.bingroup === '' && !trace._input[binAttr]?.size) { // Several single-valued histograms! Stop infinite recursion, // just return an extra flag that tells handleSingleValueOverlays // to sort out this trace too diff --git a/test/jasmine/tests/histogram_test.js b/test/jasmine/tests/histogram_test.js index d6136772b5f..7f28f468b25 100644 --- a/test/jasmine/tests/histogram_test.js +++ b/test/jasmine/tests/histogram_test.js @@ -1335,6 +1335,75 @@ describe('Test histogram', function() { }) .then(done, done.fail); }); + + it('should correctly recalculate autobin size for single-value overlays on Plotly.react', function(done) { + var initialData = [ + {x: ['1457'], type: 'histogram'}, + {x: ['820'], type: 'histogram'}, + {x: ['720'], type: 'histogram'} + ]; + + var layout = { + barmode: 'overlay' + }; + + Plotly.newPlot(gd, initialData, layout) + .then(function() { + // minDiff = 820 - 720 = 100 + expect(gd._fullData[0].xbins.size).toBe(100); + expect(gd._fullData[1].xbins.size).toBe(100); + expect(gd._fullData[2].xbins.size).toBe(100); + + // Use a new trace data object + var newData = [ + {x: ['1400'], type: 'histogram'}, + {x: ['800'], type: 'histogram'}, + {x: ['600'], type: 'histogram'} + ]; + + return Plotly.react(gd, newData, layout); + }) + .then(function() { + // minDiff = 800 - 600 = 200. + expect(gd._fullData[0].xbins.size).toBe(200); + expect(gd._fullData[1].xbins.size).toBe(200); + expect(gd._fullData[2].xbins.size).toBe(200); + }) + .then(done, done.fail); + }); + + it('should preserve user-specified xbins.size for single-value overlays on Plotly.react', function(done) { + var initialData = [ + {x: ['1457'], type: 'histogram', xbins: {size: 50}}, + {x: ['820'], type: 'histogram', xbins: {size: 50}}, + {x: ['720'], type: 'histogram', xbins: {size: 50}} + ]; + + var layout = { + barmode: 'overlay' + }; + + Plotly.newPlot(gd, initialData, layout) + .then(function() { + expect(gd._fullData[0].xbins.size).toBe(50); + expect(gd._fullData[1].xbins.size).toBe(50); + expect(gd._fullData[2].xbins.size).toBe(50); + + var newData = [ + {x: ['1400'], type: 'histogram', xbins: {size: 50}}, + {x: ['800'], type: 'histogram', xbins: {size: 50}}, + {x: ['600'], type: 'histogram', xbins: {size: 50}} + ]; + + return Plotly.react(gd, newData, layout); + }) + .then(function() { + expect(gd._fullData[0].xbins.size).toBe(50); + expect(gd._fullData[1].xbins.size).toBe(50); + expect(gd._fullData[2].xbins.size).toBe(50); + }) + .then(done, done.fail); + }); }); });