From 658ff5fb3b86babc8c894650043c818738e23116 Mon Sep 17 00:00:00 2001 From: Sahinur Date: Tue, 13 Oct 2026 11:34:13 +0600 Subject: [PATCH] fix(pie): keep label background fitted to wrapped text when lineHeight is set. close #21710 Signed-off-by: Sahinur --- src/chart/pie/labelLayout.ts | 26 ++++++++--- test/ut/spec/series/pie.test.ts | 82 +++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 7 deletions(-) create mode 100644 test/ut/spec/series/pie.test.ts diff --git a/src/chart/pie/labelLayout.ts b/src/chart/pie/labelLayout.ts index e617e61895..851c8eafac 100644 --- a/src/chart/pie/labelLayout.ts +++ b/src/chart/pie/labelLayout.ts @@ -289,17 +289,29 @@ function constrainTextWidth( if (availableWidth < oldOuterWidth || forceRecalculate) { if (overflow && overflow.match('break')) { - // Temporarily set background to be null to calculate - // the bounding box without background. - label.setStyle('backgroundColor', null); - // Set constraining width - label.setStyle('width', availableWidth - paddingH); + // Temporarily remove everything that makes zrender add a + // background rect (backgroundColor, lineHeight, border), because + // that rect takes the constraining width rather than the width of + // the wrapped text, and would be included in the bounding box. + const lineHeight = style.lineHeight; + const borderWidth = style.borderWidth; + label.setStyle({ + backgroundColor: null, + lineHeight: null, + borderWidth: null, + // Set constraining width + width: availableWidth - paddingH + }); // This is the real bounding box of the text without padding. const innerRect = label.getBoundingRect(); - label.setStyle('width', Math.ceil(innerRect.width)); - label.setStyle('backgroundColor', bgColor); + label.setStyle({ + width: Math.ceil(innerRect.width), + backgroundColor: bgColor, + lineHeight, + borderWidth + }); } else { const availableInnerWidth = availableWidth - paddingH; diff --git a/test/ut/spec/series/pie.test.ts b/test/ut/spec/series/pie.test.ts new file mode 100644 index 0000000000..1cd15d4ef9 --- /dev/null +++ b/test/ut/spec/series/pie.test.ts @@ -0,0 +1,82 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + + +import { createChart, removeChart } from '../../core/utHelper'; +import { EChartsType } from '@/src/echarts'; +import ZRText from 'zrender/src/graphic/Text'; +import Rect from 'zrender/src/graphic/shape/Rect'; + + +describe('pie', function () { + + let chart: EChartsType; + beforeEach(function () { + chart = createChart({width: 300, height: 200}); + }); + afterEach(function () { + removeChart(chart); + }); + + function getLabelBackgroundWidths(lineHeight?: number): number[] { + chart.setOption({ + animation: false, + series: [{ + type: 'pie', + radius: ['40%', '70%'], + center: ['50%', '70%'], + startAngle: 180, + endAngle: 360, + label: { + backgroundColor: '#ccc', + overflow: 'break', + lineHeight + }, + data: [ + {value: 1048, name: 'Search Engine '.repeat(20)}, + {value: 735, name: 'Direct '.repeat(3)}, + {value: 580, name: 'Email marketing campaign '.repeat(20)} + ] + }] + }, true); + const data = (chart as any).getModel().getSeriesByIndex(0).getData(); + const widths: number[] = []; + data.each(function (idx: number) { + const label = data.getItemGraphicEl(idx).getTextContent() as ZRText; + const bg = label.childrenRef().filter(el => el instanceof Rect)[0] as Rect; + let textWidth = 0; + label.childrenRef().forEach(el => { + if (!(el instanceof Rect)) { + textWidth = Math.max(textWidth, el.getBoundingRect().width); + } + }); + widths.push(bg.shape.width - Math.ceil(textWidth)); + }); + return widths; + } + + // https://github.com/apache/echarts/issues/21710 + it('label background with overflow: break should fit the wrapped text when lineHeight is set', function () { + // Width of the label background minus the width of the wrapped text. + expect(getLabelBackgroundWidths()).toEqual([0, 0, 0]); + expect(getLabelBackgroundWidths(14)).toEqual([0, 0, 0]); + expect(getLabelBackgroundWidths(80)).toEqual([0, 0, 0]); + }); + +});