Skip to content

Commit

Permalink
feat(svg-icons): handle custom svg icons
Browse files Browse the repository at this point in the history
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: `
  <svg>
  ...
  </svg>
  `,
  // ...
});
```

- extract a `initSvgIcons_` function to activate svg icons
- add test cases
  • Loading branch information
amtins committed Nov 12, 2023
1 parent d535e16 commit 0c9ae3c
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 19 deletions.
53 changes: 34 additions & 19 deletions src/js/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
*
Expand Down
33 changes: 33 additions & 0 deletions test/unit/player.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: '<svg></svg>'});

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);

Expand Down

0 comments on commit 0c9ae3c

Please # to comment.