Skip to content

Commit

Permalink
Merge d795401 into 262ec20
Browse files Browse the repository at this point in the history
  • Loading branch information
fkloes committed Apr 11, 2019
2 parents 262ec20 + d795401 commit cfa12a5
Show file tree
Hide file tree
Showing 24 changed files with 576 additions and 943 deletions.
69 changes: 69 additions & 0 deletions libraries/common/collections/media-providers/MediaProvider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { logger } from '@shopgate/pwa-core/helpers';
import styles from './style';

/**
* The MediaProvider base class.
*/
class MediaProvider {
/**
* Constructor.
*/
constructor() {
this.containers = new Map();
}

/**
* Optimizes video container to make it responsive.
* @param {NodeList} container A DOM container.
* @private
* @returns {MediaProvider}
*/
responsify(container) {
// Remove fixed dimensions from the container.
container.setAttribute('height', '');
container.setAttribute('width', '');

// Create the wrapper and apply styling.
const wrapper = document.createElement('div');

wrapper.className = styles;

// Add the wrapper right before the container into the DOM.
container.parentNode.insertBefore(wrapper, container);
// Move the container into the wrapper.
wrapper.appendChild(container);

return this;
}

/**
* Add a DOM container with embedded videos.
* @param {NodeList} container A DOM container.
* @returns {MediaProvider}
*/
add() {
logger.error('MediaProvider.add() needs to be implemented within an inheriting class');
return this;
}

/**
* Remove a DOM container.
* @param {NodeList} container A DOM container.
* @returns {MediaProvider}
*/
remove(container) {
this.containers.delete(container);
return this;
}

/**
* Stops all playing videos within the DOM containers.
* @returns {MediaProvider}
*/
stop() {
logger.error('MediaProvider.stop() needs to be implemented within an inheriting class');
return this;
}
}

export default MediaProvider;
76 changes: 76 additions & 0 deletions libraries/common/collections/media-providers/MediaProvider.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { JSDOM } from 'jsdom';
import { logger } from '@shopgate/pwa-core/helpers';
import MediaProvider from './MediaProvider';

jest.mock('@shopgate/pwa-core/helpers', () => ({
logger: {
error: jest.fn(),
},
}));

/**
* Creates a DOM container with iframes.
* @param {Array} srcs A list of video URLs.
* @return {Object}
*/
const createContainer = (srcs) => {
const html = srcs.map(src => `<iframe src="${src}"></iframe>`).join('');
return new JSDOM(html).window.document;
};

describe('MediaProvider', () => {
beforeEach(() => {
jest.clearAllMocks();
});

describe('.constructor()', () => {
it('should construct as expected', () => {
const instance = new MediaProvider();
expect(instance.containers).toBeInstanceOf(Map);
expect(Array.from(instance.containers)).toHaveLength(0);
});
});

describe('.add()', () => {
it('should log an error when it is not overwritten', () => {
const instance = new MediaProvider();
instance.add();
expect(logger.error).toHaveBeenCalledTimes(1);
});
});

describe('.stop()', () => {
it('should log an error when it is not overwritten', () => {
const instance = new MediaProvider();
instance.stop();
expect(logger.error).toHaveBeenCalledTimes(1);
});
});

describe('.remove()', () => {
it('should remove containers as expected', () => {
const instance = new MediaProvider();
const containerOne = createContainer(['http://www.provider-one.com/video']);
const containerTwo = createContainer(['http://www.provider-two.com/video']);

instance.containers.set(containerOne, containerOne);
instance.containers.set(containerTwo, containerTwo);

instance.remove(containerTwo);

expect(instance.containers.size).toBe(1);
expect(instance.containers.get(containerTwo)).toBeUndefined();
expect(instance.containers.get(containerOne)).toEqual(containerOne);

instance.remove(containerOne);

expect(instance.containers.size).toBe(0);
});
});

describe.skip('.responsify()', () => {
it('should optimize a container to be responsive', () => {
// TODO: Implement the test when a solution for the insertBefore issue was found.
});
});
});
30 changes: 15 additions & 15 deletions libraries/common/collections/media-providers/Vimeo.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
/* eslint-disable extra-rules/potential-point-free */
import MediaProvider from './MediaProvider';

/**
* The Vimeo media provider class.
*/
class VimeoMediaProvider {
class VimeoMediaProvider extends MediaProvider {
/**
* Constructor.
*/
constructor() {
this.containers = new Map();
super();
this.playerReady = false;
this.deferred = [];

Expand All @@ -17,7 +17,8 @@ class VimeoMediaProvider {

/**
* Checks if the Video player script is already available.
* If not, it injects it into the DOM and adds defferred containers.
* If not, it injects it into the DOM and adds deferred containers.
* @private
*/
checkPlayer() {
if (typeof window.Vimeo !== 'undefined') {
Expand All @@ -42,49 +43,48 @@ class VimeoMediaProvider {

/**
* Add a DOM container with embedded videos.
* @override
* @param {NodeList} container A DOM container.
* @returns {VimeoMediaProvider}
*/
add(container) {
if (!this.playerReady) {
this.deferred.push(container);
return;
return this;
}

const iframes = container.querySelectorAll('iframe[src*="vimeo.com"]');

if (!iframes.length) {
return;
return this;
}

const players = [];

iframes.forEach((iframe) => {
this.responsify(iframe);
players.push(new window.Vimeo.Player(iframe));
});

this.containers.set(container, players);
}

/**
* Remove a DOM container.
* @param {NodeList} container A DOM container.
*/
remove(container) {
this.containers.delete(container);
return this;
}

/**
* Stops all playing videos within the DOM containers.
* @override
* @returns {VimeoMediaProvider}
*/
stop() {
this.containers.forEach((players) => {
players.forEach((player) => {
player.pause();
});
});

return this;
}
}

/* eslint-enable extra-rules/potential-point-free */

export default VimeoMediaProvider;
7 changes: 7 additions & 0 deletions libraries/common/collections/media-providers/Vimeo.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ describe('Vimeo media provider', () => {

document.getElementsByTagName('html')[0].innerHTML = '';
instance = new Vimeo();
// TODO Implement tests for the method when a solution for the insertBefore issue was found.
instance.responsify = jest.fn();

playerScript = document.querySelector('script[src*="vimeo.com"]');
};

Expand Down Expand Up @@ -91,6 +94,8 @@ describe('Vimeo media provider', () => {
it('should add multiple containers as expected', () => {
const containerOne = createContainer([videos[0]]);
const containerTwo = createContainer([videos[1]]);
const iframesOne = containerOne.querySelectorAll('iframe');
const iframesTwo = containerTwo.querySelectorAll('iframe');

instance.add(containerOne);
instance.add(containerTwo);
Expand All @@ -99,6 +104,8 @@ describe('Vimeo media provider', () => {
expect(instance.containers.size).toBe(2);
expect(instance.containers.get(containerOne)).toEqual([expect.any(window.Vimeo.Player)]);
expect(instance.containers.get(containerTwo)).toEqual([expect.any(window.Vimeo.Player)]);
expect(instance.responsify).toHaveBeenCalledWith(iframesOne[0]);
expect(instance.responsify).toHaveBeenCalledWith(iframesTwo[0]);
});

it('should defer addition of a container if the player is not ready', () => {
Expand Down
31 changes: 12 additions & 19 deletions libraries/common/collections/media-providers/YouTube.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,28 @@
/* eslint-disable extra-rules/potential-point-free */
import URLSearchParams from 'url-search-params';
import MediaProvider from './MediaProvider';

/**
* The YouTube media provider class.
*/
class YouTubeMediaProvider {
/**
* Constructor.
*/
constructor() {
this.containers = new Map();
}

class YouTubeMediaProvider extends MediaProvider {
/**
* Add a DOM container with embedded videos.
* @override
* @param {NodeList} container A DOM container.
* @returns {YouTubeMediaProvider}
*/
add(container) {
const iframes = container
.querySelectorAll('iframe[src*="youtube.com"], iframe[src*="youtube-nocookie.com"]');

if (!iframes.length) {
return;
return this;
}

// Update the video urls to enable stopping videos via the event API.
iframes.forEach((iframe, index) => {
this.responsify(iframe);

const { src } = iframe;

const [url, query] = src.split('?');
Expand All @@ -40,18 +37,14 @@ class YouTubeMediaProvider {
});

this.containers.set(container, iframes);
}

/**
* Remove a DOM container.
* @param {NodeList} container A DOM container.
*/
remove(container) {
this.containers.delete(container);
return this;
}

/**
* Stops all playing videos within the DOM containers.
* @override
* @returns {YouTubeMediaProvider}
*/
stop() {
this.containers.forEach((iframes) => {
Expand All @@ -61,9 +54,9 @@ class YouTubeMediaProvider {
}
});
});

return this;
}
}

/* eslint-enable extra-rules/potential-point-free */

export default YouTubeMediaProvider;
9 changes: 7 additions & 2 deletions libraries/common/collections/media-providers/YouTube.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ const videos = [
* @return {Object}
*/
const createContainer = (srcs) => {
const html = srcs.map(src => `<iframe src="${src}"></iframe>`).join('');
const iframes = srcs.map(src => `<iframe src="${src}"></iframe>`).join('');
const html = `<div>${iframes}</div>`;
return new JSDOM(html).window.document;
};

Expand All @@ -24,6 +25,8 @@ describe('YouTube media provider', () => {

beforeEach(() => {
instance = new YouTube();
// TODO Implement tests for the method when a solution for the insertBefore issue was found.
instance.responsify = jest.fn();
});

describe('.constructor()', () => {
Expand All @@ -47,6 +50,8 @@ describe('YouTube media provider', () => {
expect(instance.containers.size).toBe(2);
expect(instance.containers.get(containerOne)).toEqual(iframesOne);
expect(instance.containers.get(containerTwo)).toEqual(iframesTwo);
expect(instance.responsify).toHaveBeenCalledWith(iframesOne[0]);
expect(instance.responsify).toHaveBeenCalledWith(iframesTwo[0]);
});

it('should add a container with different types of YouTube videos', () => {
Expand Down Expand Up @@ -97,7 +102,7 @@ describe('YouTube media provider', () => {
});

describe('.stop()', () => {
it('should stop the videos within mutiple containers', () => {
it('should stop the videos within multiple containers', () => {
const postMessageMock = jest.fn();
const containerOne = createContainer([videos[0]]);
const containerTwo = createContainer([videos[1], videos[2]]);
Expand Down
1 change: 1 addition & 0 deletions libraries/common/collections/media-providers/index.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { default as MediaProvider } from './MediaProvider';
export { default as Vimeo } from './Vimeo';
export { default as YouTube } from './YouTube';
16 changes: 16 additions & 0 deletions libraries/common/collections/media-providers/style.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { css } from 'glamor';

export default css({
position: 'relative',
height: 0,
overflow: 'hidden',
padding: '0 0 56.25% 0',
' iframe, object, embed': {
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
border: 0,
},
}).toString();

0 comments on commit cfa12a5

Please sign in to comment.