From 0384d7a3c3da9a50222ba652c726a41606f35990 Mon Sep 17 00:00:00 2001 From: vaibhavmashal Date: Wed, 2 Sep 2026 21:39:07 +0530 Subject: [PATCH] fix(legend): apply dataset borderDash when pointStyle is line (#12291) --- src/helpers/helpers.canvas.ts | 6 +++ src/plugins/plugin.legend.js | 28 +++++++++--- test/specs/helpers.canvas.tests.js | 35 +++++++++++++++ test/specs/plugin.legend.tests.js | 72 ++++++++++++++++++++++++++++++ 4 files changed, 135 insertions(+), 6 deletions(-) diff --git a/src/helpers/helpers.canvas.ts b/src/helpers/helpers.canvas.ts index f37504c0097..d032822a28e 100644 --- a/src/helpers/helpers.canvas.ts +++ b/src/helpers/helpers.canvas.ts @@ -151,6 +151,8 @@ export interface DrawPointOptions { rotation?: number; radius: number; borderWidth: number; + lineDash?: number[]; + lineDashOffset?: number; } export function drawPoint( @@ -287,6 +289,10 @@ export function drawPointLegend( ctx.lineTo(x - yOffsetW, y + xOffset); break; case 'line': + if (options.lineDash) { + ctx.setLineDash(options.lineDash); + ctx.lineDashOffset = options.lineDashOffset || 0; + } xOffset = w ? w / 2 : Math.cos(rad) * radius; yOffset = Math.sin(rad) * radius; ctx.moveTo(x - xOffset, y - yOffset); diff --git a/src/plugins/plugin.legend.js b/src/plugins/plugin.legend.js index 6ed99413536..189b38173d0 100644 --- a/src/plugins/plugin.legend.js +++ b/src/plugins/plugin.legend.js @@ -327,7 +327,9 @@ export class Legend extends Element { radius: boxHeight * Math.SQRT2 / 2, pointStyle: legendItem.pointStyle, rotation: legendItem.rotation, - borderWidth: lineWidth + borderWidth: lineWidth, + lineDash: legendItem.lineDash, + lineDashOffset: legendItem.lineDashOffset }; const centerX = rtlHelper.xPlus(x, boxWidth / 2); const centerY = y + halfFontSize; @@ -589,6 +591,19 @@ function isListened(type, opts) { return false; } +function getLegendItemStyle(meta, usePointStyle, pointStyle) { + const style = meta.controller.getStyle(usePointStyle ? 0 : undefined); + const resolvedPointStyle = pointStyle || style.pointStyle; + const datasetStyle = usePointStyle && resolvedPointStyle === 'line' ? meta.controller.getStyle() : {}; + return { + pointStyle: resolvedPointStyle, + lineCap: valueOrDefault(style.borderCapStyle, datasetStyle.borderCapStyle), + lineDash: valueOrDefault(style.borderDash, datasetStyle.borderDash), + lineDashOffset: valueOrDefault(style.borderDashOffset, datasetStyle.borderDashOffset), + lineJoin: valueOrDefault(style.borderJoinStyle, datasetStyle.borderJoinStyle), + }; +} + export default { id: 'legend', @@ -679,19 +694,20 @@ export default { return chart._getSortedDatasetMetas().map((meta) => { const style = meta.controller.getStyle(usePointStyle ? 0 : undefined); const borderWidth = toPadding(style.borderWidth); + const lineStyle = getLegendItemStyle(meta, usePointStyle, pointStyle); return { text: datasets[meta.index].label, fillStyle: style.backgroundColor, fontColor: color, hidden: !meta.visible, - lineCap: style.borderCapStyle, - lineDash: style.borderDash, - lineDashOffset: style.borderDashOffset, - lineJoin: style.borderJoinStyle, + lineCap: lineStyle.lineCap, + lineDash: lineStyle.lineDash, + lineDashOffset: lineStyle.lineDashOffset, + lineJoin: lineStyle.lineJoin, lineWidth: (borderWidth.width + borderWidth.height) / 4, strokeStyle: style.borderColor, - pointStyle: pointStyle || style.pointStyle, + pointStyle: lineStyle.pointStyle, rotation: style.rotation, textAlign: textAlign || style.textAlign, borderRadius: useBorderRadius && (borderRadius || style.borderRadius), diff --git a/test/specs/helpers.canvas.tests.js b/test/specs/helpers.canvas.tests.js index ba28e3f78d9..f2af33735b2 100644 --- a/test/specs/helpers.canvas.tests.js +++ b/test/specs/helpers.canvas.tests.js @@ -352,4 +352,39 @@ describe('Chart.helpers.canvas', function() { }]); }); }); + + describe('drawPointLegend', function() { + it('should apply lineDash and lineDashOffset for pointStyle line', function() { + var context = window.createMockContext(); + helpers.drawPointLegend(context, { + pointStyle: 'line', + radius: 10, + borderWidth: 2, + lineDash: [6, 3], + lineDashOffset: 2 + }, 50, 50, 40); + + expect(context.getCalls()).toEqual([{ + name: 'beginPath', + args: [] + }, { + name: 'setLineDash', + args: [[6, 3]] + }, { + name: 'moveTo', + args: [30, 50] + }, { + name: 'lineTo', + args: [70, 50] + }, { + name: 'fill', + args: [] + }, { + name: 'stroke', + args: [] + }]); + expect(context.lineDashOffset).toBe(2); + }); + }); }); + diff --git a/test/specs/plugin.legend.tests.js b/test/specs/plugin.legend.tests.js index e0bed42c263..36fd302edba 100644 --- a/test/specs/plugin.legend.tests.js +++ b/test/specs/plugin.legend.tests.js @@ -877,6 +877,78 @@ describe('Legend block tests', function() { }]); }); + it('should respect dataset borderDash when usePointStyle is true and pointStyle is line', function() { + var chart = window.acquireChart({ + type: 'line', + data: { + datasets: [{ + label: 'dataset1', + borderColor: '#1a5276', + borderWidth: 2, + data: [] + }, { + label: 'dataset2', + borderColor: '#e74c3c', + borderWidth: 2, + borderDash: [6, 3], + borderDashOffset: 2, + borderCapStyle: 'round', + borderJoinStyle: 'bevel', + data: [] + }], + labels: [] + }, + options: { + plugins: { + legend: { + labels: { + usePointStyle: true, + pointStyle: 'line' + } + } + } + } + }); + + expect(chart.legend.legendItems).toEqual([{ + text: 'dataset1', + borderRadius: undefined, + fillStyle: 'rgba(0,0,0,0.1)', + fontColor: '#666', + hidden: false, + lineCap: 'butt', + lineDash: [], + lineDashOffset: 0, + lineJoin: 'miter', + lineWidth: 1, + strokeStyle: '#1a5276', + pointStyle: 'line', + rotation: 0, + textAlign: undefined, + datasetIndex: 0 + }, { + text: 'dataset2', + borderRadius: undefined, + fillStyle: 'rgba(0,0,0,0.1)', + fontColor: '#666', + hidden: false, + lineCap: 'round', + lineDash: [6, 3], + lineDashOffset: 2, + lineJoin: 'bevel', + lineWidth: 1, + strokeStyle: '#e74c3c', + pointStyle: 'line', + rotation: 0, + textAlign: undefined, + datasetIndex: 1 + }]); + + spyOn(chart.ctx, 'setLineDash'); + chart.legend.draw(); + expect(chart.ctx.setLineDash).toHaveBeenCalledWith([6, 3]); + }); + it('should not crash when the legend defaults are false', function() { const oldDefaults = Chart.defaults.plugins.legend;