From 0c9ae3c400137c4d54a432d9edc7864d75bcbf27 Mon Sep 17 00:00:00 2001 From: amtins Date: Sun, 12 Nov 2023 15:50:27 +0100 Subject: [PATCH] feat(svg-icons): handle custom svg icons Replace the player's default icons with custom icons. The `experimentalSvgIcons` option accepts a `boolean` or a `string`. If the string represents a valid svg, it will replace the default icons. **Usage** ```javascript import playerIcons from './asset/vjs-sprite-icons.svg'; const player = videojs('my-player', { experimentalSvgIcons: playerIcons, // ... }); ``` Or: ```javascript const player = videojs('my-player', { experimentalSvgIcons: ` ... `, // ... }); ``` - extract a `initSvgIcons_` function to activate svg icons - add test cases --- src/js/player.js | 53 ++++++++++++++++++++++++++-------------- test/unit/player.test.js | 33 +++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 19 deletions(-) diff --git a/src/js/player.js b/src/js/player.js index e872644b9d..8f2b6f37da 100644 --- a/src/js/player.js +++ b/src/js/player.js @@ -518,25 +518,7 @@ class Player extends Component { this.playbackRates(options.playbackRates); - if (options.experimentalSvgIcons) { - // Add SVG Sprite to the DOM - const parser = new window.DOMParser(); - const parsedSVG = parser.parseFromString(icons, 'image/svg+xml'); - const errorNode = parsedSVG.querySelector('parsererror'); - - if (errorNode) { - log.warn('Failed to load SVG Icons. Falling back to Font Icons.'); - this.options_.experimentalSvgIcons = null; - } else { - const sprite = parsedSVG.documentElement; - - sprite.style.display = 'none'; - this.el_.appendChild(sprite); - - this.addClass('vjs-svg-icons-enabled'); - } - } - + this.initSvgIcons_(options.experimentalSvgIcons); this.initChildren(); // Set isAudio based on whether or not an audio tag was used @@ -5093,6 +5075,39 @@ class Player extends Component { return baseOptions; } + /** + * Initialize SVG icons. + * + * @param {boolean|string} svgIconsOption + * Takes a Boolean or a string as parameter, if true will activate + * the default icons, if the string represents an svg it will replace + * the default icons. + */ + initSvgIcons_(svgIconsOption) { + if (!svgIconsOption) { + return; + } + + const hasCustomSvgIcons = typeof svgIconsOption === 'string'; + const svgIcons = hasCustomSvgIcons ? svgIconsOption : icons; + // Add SVG Sprite to the DOM + const parser = new window.DOMParser(); + const parsedSVG = parser.parseFromString(svgIcons, 'image/svg+xml'); + const errorNode = parsedSVG.querySelector('parsererror'); + + if (errorNode) { + log.warn('Failed to load SVG Icons. Falling back to Font Icons.'); + this.options_.experimentalSvgIcons = null; + } else { + const sprite = parsedSVG.documentElement; + + sprite.style.display = 'none'; + this.el_.appendChild(sprite); + + this.addClass('vjs-svg-icons-enabled'); + } + } + /** * Set debug mode to enable/disable logs at info level. * diff --git a/test/unit/player.test.js b/test/unit/player.test.js index 3b77360594..aa9b0e5cd3 100644 --- a/test/unit/player.test.js +++ b/test/unit/player.test.js @@ -1000,6 +1000,39 @@ QUnit.test('should revert to font icons if the SVG sprite cannot be loaded', fun player.dispose(); }); +QUnit.test('should add a svg-icons-enabled classname when svg icons string is valid', function(assert) { + // Stub a successful parsing of the SVG sprite. + sinon.stub(window.DOMParser.prototype, 'parseFromString').returns({ + querySelector: () => false, + documentElement: document.createElement('span') + }); + + assert.expect(1); + + const player = TestHelpers.makePlayer({experimentalSvgIcons: ''}); + + assert.ok(player.hasClass('vjs-svg-icons-enabled'), 'svg-icons-enabled classname added'); + + window.DOMParser.prototype.parseFromString.restore(); + player.dispose(); +}); + +QUnit.test('should revert to font icons if the SVG sprite string is not valid', function(assert) { + // Stub an unsuccessful parsing of the SVG sprite. + sinon.stub(window.DOMParser.prototype, 'parseFromString').returns({ + querySelector: () => true + }); + + assert.expect(1); + + const player = TestHelpers.makePlayer({experimentalSvgIcons: ''}); + + assert.ok(!player.hasClass('vjs-svg-icons-enabled'), 'svg-icons-enabled classname was not added'); + + window.DOMParser.prototype.parseFromString.restore(); + player.dispose(); +}); + QUnit.test('should not add a touch-enabled classname when touch is not supported', function(assert) { assert.expect(1);