From 79ee28d9b156590f353520abae6795ab8cb29659 Mon Sep 17 00:00:00 2001 From: Parman Mohammadalizadeh Date: Sat, 3 Oct 2026 21:25:34 +0200 Subject: [PATCH 1/8] fix: Apply hoverlabel.align to unified hover label items Unified hover labels draw their items through the legend, which always anchors item text at the start. The resolved hoverlabel.align of each point was never read there, so layout and trace values had no effect. Items with align 'right' now anchor at the right edge of the label. Closes #8100 --- draftlogs/XXXX_fix.md | 1 + src/components/legend/draw.js | 11 ++++++++ test/jasmine/tests/hover_test.js | 45 ++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+) create mode 100644 draftlogs/XXXX_fix.md diff --git a/draftlogs/XXXX_fix.md b/draftlogs/XXXX_fix.md new file mode 100644 index 00000000000..ed082cf140c --- /dev/null +++ b/draftlogs/XXXX_fix.md @@ -0,0 +1 @@ + - Fix `hoverlabel.align` so that it aligns the item text in `x unified` and `y unified` hover labels [[#XXXX](https://github.com/plotly/plotly.js/pull/XXXX)] diff --git a/src/components/legend/draw.js b/src/components/legend/draw.js index 7ed481e2393..15b62929541 100644 --- a/src/components/legend/draw.js +++ b/src/components/legend/draw.js @@ -1064,6 +1064,17 @@ function computeLegendDimensions(gd, groups, traces, legendObj, scrollBox) { Drawing.setRect(traceToggle, 0, -h / 2, w, h); }); + if (legendObj._inHover) { + const textRight = legendObj._width - bw2 - itemGap - titleSize[0]; + traces.each(function (d) { + if (d[0].textAlign !== 'right') return; + d3.select(this) + .select('.' + legendId + 'text') + .attr('text-anchor', 'end') + .call(svgTextUtils.positionText, textRight); + }); + } + // align legend title horizontally var titleEl = scrollBox.select('.' + legendId + 'titletext'); if(titleEl.node()) { diff --git a/test/jasmine/tests/hover_test.js b/test/jasmine/tests/hover_test.js index 544bf2f0e29..33ffce088e0 100644 --- a/test/jasmine/tests/hover_test.js +++ b/test/jasmine/tests/hover_test.js @@ -7395,6 +7395,51 @@ describe('hovermode: (x|y)unified', function () { .then(done, done.fail); }); + it('should align item text with hoverlabel.align', (done) => { + const getItems = () => { + const items = []; + getHoverLabel() + .selectAll('g.traces text.legendtext') + .each(function () { + const bBox = this.getBoundingClientRect(); + items.push({ anchor: this.getAttribute('text-anchor'), left: bBox.left, right: bBox.right }); + }); + return items; + }; + + Plotly.newPlot(gd, [{ y: [1, 2, 3] }, { y: [1000000, 2000000, 3000000] }], { + hovermode: 'x unified', + hoverlabel: { align: 'right' }, + showlegend: false, + width: 500, + height: 500 + }) + .then(() => { + _hover(gd, { xval: 1 }); + const [short, long] = getItems(); + expect([short.anchor, long.anchor]).toEqual(['end', 'end']); + expect(short.right).toBeCloseTo(long.right, 0); + expect(short.left).toBeGreaterThan(long.left); + + return Plotly.relayout(gd, 'hoverlabel.align', 'left'); + }) + .then(() => { + _hover(gd, { xval: 1 }); + const [short, long] = getItems(); + expect([short.anchor, long.anchor]).toEqual(['start', 'start']); + expect(short.left).toBeCloseTo(long.left, 0); + + return Plotly.restyle(gd, 'hoverlabel.align', 'right', [0]); + }) + .then(() => { + _hover(gd, { xval: 1 }); + const [short, long] = getItems(); + expect([short.anchor, long.anchor]).toEqual(['end', 'start']); + expect(short.right).toBeCloseTo(long.right, 0); + }) + .then(done, done.fail); + }); + it('should work with hovertemplate', function (done) { var mockCopy = Lib.extendDeep({}, mock); mockCopy.data[0].hovertemplate = 'hovertemplate: %{y:0.2f}'; From 0366890784a332380c8d336b3195b820dd5efde8 Mon Sep 17 00:00:00 2001 From: Parman Mohammadalizadeh Date: Sat, 3 Oct 2026 21:44:14 +0200 Subject: [PATCH 2/8] Rename draftlog to the PR number --- draftlogs/{XXXX_fix.md => 8104_fix.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename draftlogs/{XXXX_fix.md => 8104_fix.md} (100%) diff --git a/draftlogs/XXXX_fix.md b/draftlogs/8104_fix.md similarity index 100% rename from draftlogs/XXXX_fix.md rename to draftlogs/8104_fix.md From 8f5eeaab5f5c633d753bc7d89a00ecd1d36fe51c Mon Sep 17 00:00:00 2001 From: Parman Mohammadalizadeh Date: Sat, 3 Oct 2026 21:48:31 +0200 Subject: [PATCH 3/8] Link the draftlog to the PR --- draftlogs/8104_fix.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/draftlogs/8104_fix.md b/draftlogs/8104_fix.md index ed082cf140c..bbda60a01f1 100644 --- a/draftlogs/8104_fix.md +++ b/draftlogs/8104_fix.md @@ -1 +1 @@ - - Fix `hoverlabel.align` so that it aligns the item text in `x unified` and `y unified` hover labels [[#XXXX](https://github.com/plotly/plotly.js/pull/XXXX)] + - Fix `hoverlabel.align` so that it aligns the item text in `x unified` and `y unified` hover labels [[#8104](https://github.com/plotly/plotly.js/pull/8104)] From 6d29071141fe08051629d2c42e00dbc2f3cc4ca3 Mon Sep 17 00:00:00 2001 From: Parman Mohammadalizadeh Date: Sun, 4 Oct 2026 17:29:43 +0200 Subject: [PATCH 4/8] Fix the hoverlabel.align description and loosen the right-edge check The description read "spans more two or more lines". The right-edge check compared positions to the nearest pixel, and font rendering differs between machines, so it failed in CI. --- src/components/fx/layout_attributes.js | 2 +- src/types/generated/schema.d.ts | 8 +-- test/jasmine/tests/hover_test.js | 2 +- test/plot-schema.json | 90 +++++++++++++------------- 4 files changed, 51 insertions(+), 51 deletions(-) diff --git a/src/components/fx/layout_attributes.js b/src/components/fx/layout_attributes.js index 963ad29a9c9..fd7fae8dbb1 100644 --- a/src/components/fx/layout_attributes.js +++ b/src/components/fx/layout_attributes.js @@ -188,7 +188,7 @@ module.exports = { editType: 'none', description: [ 'Sets the horizontal alignment of the text content within hover label box.', - 'Has an effect only if the hover label text spans more two or more lines' + 'Has an effect only if the hover label text spans two or more lines' ].join(' ') }, namelength: { diff --git a/src/types/generated/schema.d.ts b/src/types/generated/schema.d.ts index 7f146506849..7ec49b9e5cb 100644 --- a/src/types/generated/schema.d.ts +++ b/src/types/generated/schema.d.ts @@ -438,7 +438,7 @@ export interface ColorBar { export interface HoverLabel { /** - * Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines + * Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines * @default 'auto' */ align?: 'left' | 'right' | 'auto' | ('left' | 'right' | 'auto')[]; @@ -1577,7 +1577,7 @@ export interface CandlestickData { hoverinfo?: 'x' | 'y' | 'z' | 'text' | 'name' | 'all' | 'none' | 'skip' | (string & {}) | ('x' | 'y' | 'z' | 'text' | 'name' | 'all' | 'none' | 'skip' | (string & {}))[]; hoverlabel?: { /** - * Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines + * Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines * @default 'auto' */ align?: 'left' | 'right' | 'auto' | ('left' | 'right' | 'auto')[]; @@ -6074,7 +6074,7 @@ export interface OhlcData { hoverinfo?: 'x' | 'y' | 'z' | 'text' | 'name' | 'all' | 'none' | 'skip' | (string & {}) | ('x' | 'y' | 'z' | 'text' | 'name' | 'all' | 'none' | 'skip' | (string & {}))[]; hoverlabel?: { /** - * Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines + * Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines * @default 'auto' */ align?: 'left' | 'right' | 'auto' | ('left' | 'right' | 'auto')[]; @@ -16176,7 +16176,7 @@ export interface Layout { hoverdistance?: number; hoverlabel?: { /** - * Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines + * Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines * @default 'auto' */ align?: 'left' | 'right' | 'auto'; diff --git a/test/jasmine/tests/hover_test.js b/test/jasmine/tests/hover_test.js index 33ffce088e0..b664da7a78a 100644 --- a/test/jasmine/tests/hover_test.js +++ b/test/jasmine/tests/hover_test.js @@ -7435,7 +7435,7 @@ describe('hovermode: (x|y)unified', function () { _hover(gd, { xval: 1 }); const [short, long] = getItems(); expect([short.anchor, long.anchor]).toEqual(['end', 'start']); - expect(short.right).toBeCloseTo(long.right, 0); + expect(short.right).toBeWithin(long.right, 1); }) .then(done, done.fail); }); diff --git a/test/plot-schema.json b/test/plot-schema.json index 3f8728d1b01..7abcf5c88f9 100644 --- a/test/plot-schema.json +++ b/test/plot-schema.json @@ -2830,7 +2830,7 @@ }, "hoverlabel": { "align": { - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", @@ -16035,7 +16035,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", @@ -17904,7 +17904,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", @@ -19300,7 +19300,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", @@ -20670,7 +20670,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", @@ -23505,7 +23505,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", @@ -24645,7 +24645,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", @@ -25814,7 +25814,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", @@ -27229,7 +27229,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", @@ -29859,7 +29859,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", @@ -30397,7 +30397,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", @@ -32140,7 +32140,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", @@ -33656,7 +33656,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", @@ -34681,7 +34681,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", @@ -37143,7 +37143,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", @@ -38713,7 +38713,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", @@ -39528,7 +39528,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", @@ -41259,7 +41259,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", @@ -43621,7 +43621,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", @@ -45006,7 +45006,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", @@ -45676,7 +45676,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", @@ -48913,7 +48913,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", @@ -49961,7 +49961,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", @@ -51394,7 +51394,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "calc", "valType": "enumerated", @@ -51749,7 +51749,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "calc", "valType": "enumerated", @@ -52021,7 +52021,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "calc", "valType": "enumerated", @@ -52744,7 +52744,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", @@ -55237,7 +55237,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", @@ -57472,7 +57472,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", @@ -59551,7 +59551,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", @@ -61758,7 +61758,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", @@ -63866,7 +63866,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", @@ -65198,7 +65198,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", @@ -67293,7 +67293,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", @@ -69242,7 +69242,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", @@ -71321,7 +71321,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", @@ -73421,7 +73421,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", @@ -75857,7 +75857,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", @@ -76526,7 +76526,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", @@ -79006,7 +79006,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", @@ -80014,7 +80014,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", @@ -80432,7 +80432,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", @@ -82238,7 +82238,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", @@ -84226,7 +84226,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", @@ -85035,7 +85035,7 @@ "hoverlabel": { "align": { "arrayOk": true, - "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans more two or more lines", + "description": "Sets the horizontal alignment of the text content within hover label box. Has an effect only if the hover label text spans two or more lines", "dflt": "auto", "editType": "none", "valType": "enumerated", From 3915055c7ab599dab9d94da8c361b7973d813cd4 Mon Sep 17 00:00:00 2001 From: Cameron DeCoster Date: Mon, 5 Oct 2026 09:33:27 -0600 Subject: [PATCH 5/8] Add CI debugging code --- test/jasmine/tests/hover_test.js | 35 ++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/test/jasmine/tests/hover_test.js b/test/jasmine/tests/hover_test.js index b664da7a78a..84a828aefc4 100644 --- a/test/jasmine/tests/hover_test.js +++ b/test/jasmine/tests/hover_test.js @@ -7396,6 +7396,8 @@ describe('hovermode: (x|y)unified', function () { }); it('should align item text with hoverlabel.align', (done) => { + // DEBUG: remove before merge + let debugStep = 0; const getItems = () => { const items = []; getHoverLabel() @@ -7403,7 +7405,40 @@ describe('hovermode: (x|y)unified', function () { .each(function () { const bBox = this.getBoundingClientRect(); items.push({ anchor: this.getAttribute('text-anchor'), left: bBox.left, right: bBox.right }); + + // DEBUG: compare the cached width that sized the box with fresh measurements + const savedBBoxes = Drawing.savedBBoxes; + Drawing.savedBBoxes = {}; + const freshWidth = Drawing.bBox(this).width; + Drawing.savedBBoxes = savedBBoxes; + const style = window.getComputedStyle(this); + console.log( + '[DEBUG 8104]', + JSON.stringify({ + step: debugStep, + text: this.textContent, + anchor: this.getAttribute('text-anchor'), + x: this.getAttribute('x'), + cachedWidth: this.parentNode.__data__[0].width, + freshWidth, + renderedWidth: bBox.width, + textLength: this.getComputedTextLength(), + left: bBox.left, + right: bBox.right, + font: style.fontFamily + ' ' + style.fontSize + }) + ); }); + const bg = getHoverLabel().select('rect.bg').node(); + console.log( + '[DEBUG 8104]', + JSON.stringify({ + step: debugStep++, + boxWidth: bg && bg.getAttribute('width'), + userAgent: navigator.userAgent, + fonts: [...document.fonts].map((f) => f.family + ' ' + f.weight + ' ' + f.status) + }) + ); return items; }; From 1f96bfa8e520ab1223c236daea4fae7e26a25be7 Mon Sep 17 00:00:00 2001 From: Cameron DeCoster Date: Mon, 5 Oct 2026 09:47:15 -0600 Subject: [PATCH 6/8] Revert "Add CI debugging code" This reverts commit 3915055c7ab599dab9d94da8c361b7973d813cd4. --- test/jasmine/tests/hover_test.js | 35 -------------------------------- 1 file changed, 35 deletions(-) diff --git a/test/jasmine/tests/hover_test.js b/test/jasmine/tests/hover_test.js index 84a828aefc4..b664da7a78a 100644 --- a/test/jasmine/tests/hover_test.js +++ b/test/jasmine/tests/hover_test.js @@ -7396,8 +7396,6 @@ describe('hovermode: (x|y)unified', function () { }); it('should align item text with hoverlabel.align', (done) => { - // DEBUG: remove before merge - let debugStep = 0; const getItems = () => { const items = []; getHoverLabel() @@ -7405,40 +7403,7 @@ describe('hovermode: (x|y)unified', function () { .each(function () { const bBox = this.getBoundingClientRect(); items.push({ anchor: this.getAttribute('text-anchor'), left: bBox.left, right: bBox.right }); - - // DEBUG: compare the cached width that sized the box with fresh measurements - const savedBBoxes = Drawing.savedBBoxes; - Drawing.savedBBoxes = {}; - const freshWidth = Drawing.bBox(this).width; - Drawing.savedBBoxes = savedBBoxes; - const style = window.getComputedStyle(this); - console.log( - '[DEBUG 8104]', - JSON.stringify({ - step: debugStep, - text: this.textContent, - anchor: this.getAttribute('text-anchor'), - x: this.getAttribute('x'), - cachedWidth: this.parentNode.__data__[0].width, - freshWidth, - renderedWidth: bBox.width, - textLength: this.getComputedTextLength(), - left: bBox.left, - right: bBox.right, - font: style.fontFamily + ' ' + style.fontSize - }) - ); }); - const bg = getHoverLabel().select('rect.bg').node(); - console.log( - '[DEBUG 8104]', - JSON.stringify({ - step: debugStep++, - boxWidth: bg && bg.getAttribute('width'), - userAgent: navigator.userAgent, - fonts: [...document.fonts].map((f) => f.family + ' ' + f.weight + ' ' + f.status) - }) - ); return items; }; From 6b5f2b8070e3f05dd30175f1ebf68fda7e97f321 Mon Sep 17 00:00:00 2001 From: Cameron DeCoster Date: Mon, 5 Oct 2026 11:05:30 -0600 Subject: [PATCH 7/8] Update test to check same rows --- test/jasmine/tests/hover_test.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/test/jasmine/tests/hover_test.js b/test/jasmine/tests/hover_test.js index b664da7a78a..bf80831cab6 100644 --- a/test/jasmine/tests/hover_test.js +++ b/test/jasmine/tests/hover_test.js @@ -7401,11 +7401,17 @@ describe('hovermode: (x|y)unified', function () { getHoverLabel() .selectAll('g.traces text.legendtext') .each(function () { - const bBox = this.getBoundingClientRect(); - items.push({ anchor: this.getAttribute('text-anchor'), left: bBox.left, right: bBox.right }); + const { left, right } = this.getBoundingClientRect(); + items.push({ + anchor: this.getAttribute('text-anchor'), + left, + right, + x: this.getAttribute('x') + }); }); return items; }; + let rightAlignedShort; Plotly.newPlot(gd, [{ y: [1, 2, 3] }, { y: [1000000, 2000000, 3000000] }], { hovermode: 'x unified', @@ -7418,8 +7424,10 @@ describe('hovermode: (x|y)unified', function () { _hover(gd, { xval: 1 }); const [short, long] = getItems(); expect([short.anchor, long.anchor]).toEqual(['end', 'end']); - expect(short.right).toBeCloseTo(long.right, 0); + // Compare anchor positions because the text rect can extend past the anchor by a font-dependent amount + expect(short.x).toBe(long.x); expect(short.left).toBeGreaterThan(long.left); + rightAlignedShort = short; return Plotly.relayout(gd, 'hoverlabel.align', 'left'); }) @@ -7435,7 +7443,9 @@ describe('hovermode: (x|y)unified', function () { _hover(gd, { xval: 1 }); const [short, long] = getItems(); expect([short.anchor, long.anchor]).toEqual(['end', 'start']); - expect(short.right).toBeWithin(long.right, 1); + // Same text and box as the first step, so the right-aligned item must not move + expect(short.x).toBe(rightAlignedShort.x); + expect(short.right).toBeCloseTo(rightAlignedShort.right, 0); }) .then(done, done.fail); }); From dc20017463d9719ec5312e1240456dfe233f772d Mon Sep 17 00:00:00 2001 From: Cameron DeCoster Date: Mon, 5 Oct 2026 12:07:08 -0600 Subject: [PATCH 8/8] Update draftlog --- draftlogs/8104_fix.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/draftlogs/8104_fix.md b/draftlogs/8104_fix.md index bbda60a01f1..f37635a93d4 100644 --- a/draftlogs/8104_fix.md +++ b/draftlogs/8104_fix.md @@ -1 +1 @@ - - Fix `hoverlabel.align` so that it aligns the item text in `x unified` and `y unified` hover labels [[#8104](https://github.com/plotly/plotly.js/pull/8104)] + - Fix `hoverlabel.align` so that it aligns the item text in `x unified` and `y unified` hover labels [[#8104](https://github.com/plotly/plotly.js/pull/8104)], with thanks to @MannXo for the contribution!