From b23061fbd958a7f52105e1bc852946a11514cdcd Mon Sep 17 00:00:00 2001 From: thelastbackspace <23155651+thelastbackspace@users.noreply.github.com> Date: Fri, 6 Nov 2026 10:02:19 +0100 Subject: [PATCH 1/2] Fix modebar icon color for custom SVG icons with multiple paths The hover handlers and updateActiveButton only styled the first '.icon path' of a button, so custom SVG icons made of several paths were only partly recolored. Apply the fill to every matching path. Fixes #7669 --- src/components/modebar/modebar.js | 12 +++------ src/lib/dom.js | 21 +++++++--------- test/jasmine/tests/modebar_test.js | 39 ++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 20 deletions(-) diff --git a/src/components/modebar/modebar.js b/src/components/modebar/modebar.js index 4a2ce19ffcc..51dd5c920e4 100644 --- a/src/components/modebar/modebar.js +++ b/src/components/modebar/modebar.js @@ -261,14 +261,10 @@ proto.updateActiveButton = function(buttonClicked) { // (e.g. during the handling when a modebar button is clicked) var updateButtonStyle = function(button, isActive) { var style = fullLayout.modebar; - var childEl = button.querySelector('.icon path'); - if(childEl) { - if(isActive || button.matches(':hover')) { - childEl.style.fill = style.activecolor; - } else { - childEl.style.fill = style.color; - } - } + var fill = (isActive || button.matches(':hover')) ? style.activecolor : style.color; + button.querySelectorAll('.icon path').forEach(function(childEl) { + childEl.style.fill = fill; + }); }; // Use 'data-toggle' and 'buttonClicked' to toggle buttons diff --git a/src/lib/dom.js b/src/lib/dom.js index 1beabd1b076..01b36544d47 100644 --- a/src/lib/dom.js +++ b/src/lib/dom.js @@ -97,7 +97,7 @@ function deleteRelatedStyleRule(uid) { * in which case, the active style remains even when it's no longer hovered. * @param {string} selector selector for button elements to be styled when hovered * @param {string} activeSelector selector used to determine if selected element is active - * @param {string} childSelector the child element on which the styling needs to be updated + * @param {string} childSelector the child elements on which the styling needs to be updated * @param {string} activeStyle style that has to be applied when 'hovered' or 'active' * @param {string} inactiveStyle style that has to be applied when not 'hovered' nor 'active' */ @@ -119,21 +119,18 @@ function setStyleOnHover(selector, activeSelector, childSelector, activeStyle, i // style in a strict CSP-compliant manner. el.addEventListener('mouseenter', function() { var hoverStyle = this._hoverStyle; - var childEl = this.querySelector(childSelector); - if(childEl) { + this.querySelectorAll(childSelector).forEach(function(childEl) { childEl.style[hoverStyle.activeStyleParts[0]] = hoverStyle.activeStyleParts[1]; - } + }); }); el.addEventListener('mouseleave', function() { var hoverStyle = this._hoverStyle; - var childEl = this.querySelector(childSelector); - if(childEl) { - if(activeSelector && this.matches(activeSelector)) { - childEl.style[hoverStyle.activeStyleParts[0]] = hoverStyle.activeStyleParts[1]; - } else { - childEl.style[hoverStyle.inactiveStyleParts[0]] = hoverStyle.inactiveStyleParts[1]; - } - } + var styleParts = (activeSelector && this.matches(activeSelector)) ? + hoverStyle.activeStyleParts : + hoverStyle.inactiveStyleParts; + this.querySelectorAll(childSelector).forEach(function(childEl) { + childEl.style[styleParts[0]] = styleParts[1]; + }); }); el.setAttribute(eventAddedAttrName, true); } diff --git a/test/jasmine/tests/modebar_test.js b/test/jasmine/tests/modebar_test.js index 64ce1f20be1..a7596da997f 100644 --- a/test/jasmine/tests/modebar_test.js +++ b/test/jasmine/tests/modebar_test.js @@ -1644,6 +1644,45 @@ describe('ModeBar', function() { .then(done, done.fail); }); + it('applies icon colors to every path of a multi-path custom svg icon', function(done) { + var icon = { + svg: '' + + '' + + '' + + '' + }; + + function checkAllPaths(btn, color) { + var paths = btn.querySelectorAll('.icon path'); + expect(paths.length).toBe(2); + paths.forEach(function(p) { + expect(window.getComputedStyle(p).fill).toBe(color); + }); + } + + Plotly.newPlot(gd, [], { + modebar: { + color: colors[0], + activecolor: colors[1] + } + }, { + modeBarButtonsToAdd: [{ + name: 'multi-path', + icon: icon, + click: function() {} + }] + }) + .then(function() { + var btn = gd._fullLayout._modeBar.element.querySelector('[data-title="multi-path"]'); + checkAllPaths(btn, colors[0]); + btn.dispatchEvent(new window.MouseEvent('mouseenter')); + checkAllPaths(btn, colors[1]); + btn.dispatchEvent(new window.MouseEvent('mouseleave')); + checkAllPaths(btn, colors[0]); + }) + .then(done, done.fail); + }); + it('changes background color (displayModeBar: hover)', function(done) { Plotly.newPlot(gd, [], {modebar: { bgcolor: colors[0]}}) .then(function() { From a5f0b3c532c0d5a4b52f71dac035b77fea9f4754 Mon Sep 17 00:00:00 2001 From: thelastbackspace <23155651+thelastbackspace@users.noreply.github.com> Date: Fri, 6 Nov 2026 11:38:22 +0100 Subject: [PATCH 2/2] Add draftlog for #8164 --- draftlogs/8164_fix.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 draftlogs/8164_fix.md diff --git a/draftlogs/8164_fix.md b/draftlogs/8164_fix.md new file mode 100644 index 00000000000..515d143cf10 --- /dev/null +++ b/draftlogs/8164_fix.md @@ -0,0 +1 @@ + - Fix modebar hover and active colors for custom SVG icons with multiple paths [[#8164](https://github.com/plotly/plotly.js/pull/8164)]