From 52ca054b7d25ad047102ea0ebb9b15a847262a6a Mon Sep 17 00:00:00 2001 From: b84330808 Date: Mon, 26 Oct 2026 12:44:32 +0900 Subject: [PATCH 1/2] fix(pie): clear stale leader lines when outside labels move back inside A redraw without a recalc (for example a resize through relayout or react) reuses the calcdata points. The plot step set labelExtraX, labelExtraY and yLabelMid only for labels that were outside on the current draw. A label that moved back inside kept the values of the previous draw. scootLabels then treated the label as an outside label, and plotTextLines drew its old leader line and shifted the other labels. Reset this state for every slice at the start of each draw. Fixes #4979 and #5616. Co-Authored-By: Claude Opus 5.5 --- draftlogs/0000_fix.md | 1 + src/traces/pie/plot.js | 10 +++++++-- test/jasmine/tests/pie_test.js | 37 ++++++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 draftlogs/0000_fix.md diff --git a/draftlogs/0000_fix.md b/draftlogs/0000_fix.md new file mode 100644 index 00000000000..2eaf8d3089b --- /dev/null +++ b/draftlogs/0000_fix.md @@ -0,0 +1 @@ + - Remove stale leader lines and label offsets of pie slices when the labels move back inside after a resize [[#0000](https://github.com/plotly/plotly.js/pull/0000)] diff --git a/src/traces/pie/plot.js b/src/traces/pie/plot.js index d31ad0a7c32..b5f79673bb5 100644 --- a/src/traces/pie/plot.js +++ b/src/traces/pie/plot.js @@ -54,6 +54,14 @@ function plot(gd, cdModule) { var hasOutsideText = false; slices.each(function (pt, i) { + // Clear the outside-label state of the previous draw. + // Otherwise a label that moved back inside keeps its old leader line and offset. + pt.yLabelMin = undefined; + pt.yLabelMid = undefined; + pt.yLabelMax = undefined; + pt.labelExtraX = 0; + pt.labelExtraY = 0; + if (pt.hidden) { d3.select(this).selectAll('path,g').remove(); return; @@ -219,8 +227,6 @@ function plot(gd, cdModule) { pt.yLabelMin = targetY - textBB.height / 2; pt.yLabelMid = targetY; pt.yLabelMax = targetY + textBB.height / 2; - pt.labelExtraX = 0; - pt.labelExtraY = 0; hasOutsideText = true; } diff --git a/test/jasmine/tests/pie_test.js b/test/jasmine/tests/pie_test.js index c2176782a47..af78cac5196 100644 --- a/test/jasmine/tests/pie_test.js +++ b/test/jasmine/tests/pie_test.js @@ -1671,6 +1671,43 @@ describe('pie relayout', function() { }) .then(done, done.fail); }); + + it('removes leader lines and label offsets that a larger size no longer needs', (done) => { + const labels = []; + const values = []; + for (let i = 0; i < 8; i++) { + labels.push(`Label ${i}`); + values.push(i < 4 ? 20 : 3); + } + const data = [{ type: 'pie', labels, values, textposition: 'auto', textinfo: 'label' }]; + const largeLayout = { width: 1000, height: 1000, showlegend: false }; + const countTextLines = () => d3SelectAll('.pielayer path.textline').size(); + const getTextTransforms = () => { + const transforms = []; + d3SelectAll(SLICES_TEXT_SELECTOR).each(function () { + transforms.push(this.getAttribute('transform')); + }); + return transforms; + }; + let textLinesAfterRelayout; + let transformsAfterRelayout; + + Plotly.newPlot(gd, data, { width: 300, height: 300, showlegend: false }) + .then(() => { + expect(countTextLines()).toBeGreaterThan(0, 'leader lines at the small size'); + return Plotly.relayout(gd, { width: largeLayout.width, height: largeLayout.height }); + }) + .then(() => { + textLinesAfterRelayout = countTextLines(); + transformsAfterRelayout = getTextTransforms(); + return Plotly.newPlot(gd, data, largeLayout); + }) + .then(() => { + expect(textLinesAfterRelayout).toBe(countTextLines(), 'leader lines after relayout'); + expect(transformsAfterRelayout).toEqual(getTextTransforms()); + }) + .then(done, done.fail); + }); }); describe('Test pie interactions edge cases:', function() { From ab2ac813b7c0287f2568db68fe94092962638b4e Mon Sep 17 00:00:00 2001 From: b84330808 Date: Mon, 26 Oct 2026 22:39:49 +0900 Subject: [PATCH 2/2] Rename the draftlog to the pull request number Co-Authored-By: Claude Opus 5.5 --- draftlogs/0000_fix.md | 1 - draftlogs/8146_fix.md | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 draftlogs/0000_fix.md create mode 100644 draftlogs/8146_fix.md diff --git a/draftlogs/0000_fix.md b/draftlogs/0000_fix.md deleted file mode 100644 index 2eaf8d3089b..00000000000 --- a/draftlogs/0000_fix.md +++ /dev/null @@ -1 +0,0 @@ - - Remove stale leader lines and label offsets of pie slices when the labels move back inside after a resize [[#0000](https://github.com/plotly/plotly.js/pull/0000)] diff --git a/draftlogs/8146_fix.md b/draftlogs/8146_fix.md new file mode 100644 index 00000000000..8baba5cf175 --- /dev/null +++ b/draftlogs/8146_fix.md @@ -0,0 +1 @@ + - Remove stale leader lines and label offsets of pie slices when the labels move back inside after a resize [[#8146](https://github.com/plotly/plotly.js/pull/8146)]