Skip to content

Commit

Permalink
feat: Player can be replaced with original el after dispose() (#7722)
Browse files Browse the repository at this point in the history
  • Loading branch information
mister-ben authored May 18, 2022
1 parent fbee000 commit 3ec2ac7
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 4 deletions.
11 changes: 9 additions & 2 deletions src/js/component.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,11 @@ class Component {
* Dispose of the `Component` and all child components.
*
* @fires Component#dispose
*
* @param {Object} options
* @param {Element} options.originalEl element with which to replace player element
*/
dispose() {
dispose(options = {}) {

// Bail out if the component has already been disposed.
if (this.isDisposed_) {
Expand Down Expand Up @@ -182,7 +185,11 @@ class Component {
if (this.el_) {
// Remove element from DOM
if (this.el_.parentNode) {
this.el_.parentNode.removeChild(this.el_);
if (options.restoreEl) {
this.el_.parentNode.replaceChild(options.restoreEl, this.el_);
} else {
this.el_.parentNode.removeChild(this.el_);
}
}

this.el_ = null;
Expand Down
4 changes: 2 additions & 2 deletions src/js/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -666,8 +666,8 @@ class Player extends Component {
}
});

// the actual .el_ is removed here
super.dispose();
// the actual .el_ is removed here, or replaced if
super.dispose({restoreEl: this.options_.restoreEl});
}

/**
Expand Down
6 changes: 6 additions & 0 deletions src/js/video.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,12 @@ function videojs(id, options, ready) {

options = options || {};

// Store a copy of the el before modification, if it is to be restored in destroy()
// If div ingest, store the parent div
if (options.restoreEl === true) {
options.restoreEl = (el.parentNode && el.parentNode.hasAttribute('data-vjs-player') ? el.parentNode : el).cloneNode(true);
}

hooks('beforesetup').forEach((hookFunction) => {
const opts = hookFunction(el, mergeOptions(options));

Expand Down
12 changes: 12 additions & 0 deletions test/unit/component.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1431,3 +1431,15 @@ QUnit.test('ready queue should not run after dispose', function(assert) {
assert.notOk(callback, 'ready callback not run');

});

QUnit.test('a component\'s el can be replaced on dispose', function(assert) {
const comp = this.player.addChild('Component', {}, {}, 2);
const prevIndex = Array.from(this.player.el_.childNodes).indexOf(comp.el_);
const replacementEl = document.createElement('div');

comp.dispose({restoreEl: replacementEl});

assert.strictEqual(replacementEl.parentNode, this.player.el_, 'replacement was inserted');
assert.strictEqual(Array.from(this.player.el_.childNodes).indexOf(replacementEl), prevIndex, 'replacement was inserted at same position');

});
14 changes: 14 additions & 0 deletions test/unit/player.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,20 @@ QUnit.test('dispose should not throw if playerEl is missing', function(assert) {
assert.notOk(error, 'Function did not throw an error on dispose');
});

QUnit.test('dispose should replace playerEl with restoreEl', function(assert) {
const videoTag = TestHelpers.makeTag();
const fixture = document.getElementById('qunit-fixture');
const replacement = document.createElement('div');

fixture.appendChild(videoTag);

const player = new Player(videoTag, {restoreEl: replacement});

player.dispose();

assert.ok(replacement.parentNode, fixture, 'Replacement node present after dispose');
});

// technically, all uses of videojs.options should be replaced with
// Player.prototype.options_ in this file and a equivalent test using
// videojs.options should be made in video.test.js. Keeping this here
Expand Down
44 changes: 44 additions & 0 deletions test/unit/video.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -579,3 +579,47 @@ QUnit.test('adds video-js class name with the video-js embed', function(assert)
assert.ok(player.hasClass('video-js'), 'video-js class was added to the first embed');
assert.ok(player2.hasClass('video-js'), 'video-js class was preserved to the second embed');
});

QUnit.test('stores placeholder el and restores on dispose', function(assert) {
const fixture = document.getElementById('qunit-fixture');

const embeds = [
{
type: 'video el',
src: '<video id="test1"><source src="http://example.com/video.mp4" type="video/mp4"></source></video>',
initSelector: 'test1',
testSelector: '#test1'
},
{
type: 'video-js el',
src: '<video-js id="test2"><source src="http://example.com/video.mp4" type="video/mp4"></source></video-js>',
initSelector: 'test2',
testSelector: '#test2'
},
{
type: 'div ingest',
src: '<div data-vjs-player><video id="test3"><source src="http://example.com/video.mp4" type="video/mp4"></source></video></div>',
initSelector: 'test3',
testSelector: 'div[data-vjs-player]'
}
];

embeds.forEach(embed => {
const comparisonEl = document.createRange().createContextualFragment(embed.src).children[0];

fixture.innerHTML += embed.src;

const player = videojs(embed.initSelector, {restoreEl: true});

assert.ok(comparisonEl.isEqualNode(player.options_.restoreEl), `${embed.type}: restoreEl option replaced by an element`);
assert.notOk(document.querySelector(embed.testSelector).isSameNode(player.options_.restoreEl), `${embed.type}: restoreEl is not the original element`);
assert.notOk(comparisonEl.isSameNode(player.options_.restoreEl), `${embed.type}: restoreEl is not the control element`);

player.dispose();

const expectedEl = document.querySelector(embed.testSelector);

assert.ok(comparisonEl.isEqualNode(expectedEl), `${embed.type}: element was restored`);

});
});

0 comments on commit 3ec2ac7

Please sign in to comment.