From 2ce79a3f1e77bc3be000359658d880ca413204e4 Mon Sep 17 00:00:00 2001 From: d3v07 Date: Wed, 22 Apr 2026 12:21:02 -0400 Subject: [PATCH 1/2] fix: keep reversed scale titles aligned --- src/core/core.scale.js | 8 ++-- test/specs/core.scale.tests.js | 77 ++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 4 deletions(-) diff --git a/src/core/core.scale.js b/src/core/core.scale.js index e81b6b933bf..db63f0552c3 100644 --- a/src/core/core.scale.js +++ b/src/core/core.scale.js @@ -119,10 +119,10 @@ function createTickContext(parent, index, tick) { }); } -function titleAlign(align, position, reverse) { +function titleAlign(align, position) { /** @type {CanvasTextAlign} */ let ret = _toLeftRightCenter(align); - if ((reverse && position !== 'right') || (!reverse && position === 'right')) { + if (position === 'right') { ret = reverseAlign(ret); } return ret; @@ -1586,7 +1586,7 @@ export default class Scale extends Element { * @protected */ drawTitle() { - const {ctx, options: {position, title, reverse}} = this; + const {ctx, options: {position, title}} = this; if (!title.display) { return; @@ -1612,7 +1612,7 @@ export default class Scale extends Element { color: title.color, maxWidth, rotation, - textAlign: titleAlign(align, position, reverse), + textAlign: titleAlign(align, position), textBaseline: 'middle', translation: [titleX, titleY], strokeColor: title.strokeColor, diff --git a/test/specs/core.scale.tests.js b/test/specs/core.scale.tests.js index a388e7c9ca5..ed3ecc79f85 100644 --- a/test/specs/core.scale.tests.js +++ b/test/specs/core.scale.tests.js @@ -818,6 +818,83 @@ describe('Core.scale', function() { }); }); describe('Scale Title stroke', ()=>{ + function getScaleTitleCalls(position, align, reverse) { + const axis = position === 'left' || position === 'right' ? 'y' : 'x'; + const chart = window.acquireChart({ + type: 'scatter', + data: { + datasets: [{ + data: [{x: 0, y: 0}, {x: 10, y: 10}] + }] + }, + options: { + events: [], + animation: false, + plugins: { + legend: false + }, + scales: { + x: { + display: axis === 'x', + position: axis === 'x' ? position : 'bottom', + min: 0, + max: 10, + reverse: axis === 'x' ? reverse : false, + ticks: { + display: false + }, + grid: { + display: false + }, + border: { + display: false + }, + title: { + display: axis === 'x', + text: 'title', + align + } + }, + y: { + display: axis === 'y', + position: axis === 'y' ? position : 'left', + min: 0, + max: 10, + reverse: axis === 'y' ? reverse : false, + ticks: { + display: false + }, + grid: { + display: false + }, + border: { + display: false + }, + title: { + display: axis === 'y', + text: 'title', + align + } + } + } + } + }); + const scale = chart.scales[axis]; + + scale.ctx = window.createMockContext(); + chart.draw(); + + return scale.ctx.getCalls().filter(({name}) => ['translate', 'setTextAlign', 'fillText'].includes(name)); + } + + ['top', 'bottom', 'left', 'right'].forEach(function(position) { + ['start', 'end'].forEach(function(align) { + it('should not reposition the ' + position + ' scale title when reverse=true and align=' + align, function() { + expect(getScaleTitleCalls(position, align, true)).toEqual(getScaleTitleCalls(position, align, false)); + }); + }); + }); + function getChartWithScaleTitleStroke() { return window.acquireChart({ type: 'line', From a95eeedccd489c0900d1d8b04cb7a9444e4b4fae Mon Sep 17 00:00:00 2001 From: d3v07 Date: Wed, 22 Apr 2026 13:15:29 -0400 Subject: [PATCH 2/2] fix: handle centered reversed scale titles --- src/core/core.scale.js | 7 ++++--- test/specs/core.scale.tests.js | 16 +++++++++++++--- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/core/core.scale.js b/src/core/core.scale.js index db63f0552c3..e69f813e557 100644 --- a/src/core/core.scale.js +++ b/src/core/core.scale.js @@ -119,10 +119,10 @@ function createTickContext(parent, index, tick) { }); } -function titleAlign(align, position) { +function titleAlign(align, position, horizontal) { /** @type {CanvasTextAlign} */ let ret = _toLeftRightCenter(align); - if (position === 'right') { + if (!horizontal && position !== 'left') { ret = reverseAlign(ret); } return ret; @@ -1587,6 +1587,7 @@ export default class Scale extends Element { */ drawTitle() { const {ctx, options: {position, title}} = this; + const horizontal = this.isHorizontal(); if (!title.display) { return; @@ -1612,7 +1613,7 @@ export default class Scale extends Element { color: title.color, maxWidth, rotation, - textAlign: titleAlign(align, position), + textAlign: titleAlign(align, position, horizontal), textBaseline: 'middle', translation: [titleX, titleY], strokeColor: title.strokeColor, diff --git a/test/specs/core.scale.tests.js b/test/specs/core.scale.tests.js index ed3ecc79f85..dc6af202d15 100644 --- a/test/specs/core.scale.tests.js +++ b/test/specs/core.scale.tests.js @@ -818,8 +818,7 @@ describe('Core.scale', function() { }); }); describe('Scale Title stroke', ()=>{ - function getScaleTitleCalls(position, align, reverse) { - const axis = position === 'left' || position === 'right' ? 'y' : 'x'; + function getScaleTitleCalls(axis, position, align, reverse) { const chart = window.acquireChart({ type: 'scatter', data: { @@ -857,6 +856,7 @@ describe('Core.scale', function() { }, y: { display: axis === 'y', + axis: 'y', position: axis === 'y' ? position : 'left', min: 0, max: 10, @@ -890,7 +890,17 @@ describe('Core.scale', function() { ['top', 'bottom', 'left', 'right'].forEach(function(position) { ['start', 'end'].forEach(function(align) { it('should not reposition the ' + position + ' scale title when reverse=true and align=' + align, function() { - expect(getScaleTitleCalls(position, align, true)).toEqual(getScaleTitleCalls(position, align, false)); + const axis = position === 'left' || position === 'right' ? 'y' : 'x'; + expect(getScaleTitleCalls(axis, position, align, true)).toEqual(getScaleTitleCalls(axis, position, align, false)); + }); + }); + }); + + ['center', {x: 5}].forEach(function(position) { + ['start', 'end'].forEach(function(align) { + const positionLabel = typeof position === 'string' ? position : '{x: 5}'; + it('should not reposition the vertical ' + positionLabel + ' scale title when reverse=true and align=' + align, function() { + expect(getScaleTitleCalls('y', position, align, true)).toEqual(getScaleTitleCalls('y', position, align, false)); }); }); });