Skip to content

Commit 963b31d

Browse files
author
Nathan Tate
committed
feat(youtube-player): initialize component and infrastructure
1 parent 701302d commit 963b31d

26 files changed

+561
-1
lines changed

.github/CODEOWNERS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@
5757
/src/material/core/typography/** @crisbeto
5858
/src/material/core/util/** @jelbourn
5959

60+
# Miscellaneous components
61+
/src/youtube-player/** @nathantate
62+
6063
# CDK
6164
/src/cdk/* @jelbourn
6265
/src/cdk/a11y/** @jelbourn @devversion

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
"@angular/elements": "^8.1.0",
4646
"@angular/forms": "^8.1.0",
4747
"@angular/platform-browser": "^8.1.0",
48+
"@types/youtube": "^0.0.38",
4849
"@webcomponents/custom-elements": "^1.1.0",
4950
"core-js": "^2.6.1",
5051
"material-components-web": "^3.0.0",

packages.bzl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ ROLLUP_GLOBALS = {
121121
"@angular/cdk-experimental": "ng.cdkExperimental",
122122
"@angular/material": "ng.material",
123123
"@angular/material-experimental": "ng.materialExperimental",
124+
"@angular/youtube-player": "ng.youtubePlayer",
124125
}
125126

126127
# Rollup globals for cdk subpackages in the form of, e.g., {"@angular/cdk/table": "ng.cdk.table"}

src/youtube-player/BUILD.bazel

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package(default_visibility = ["//visibility:public"])
2+
3+
load("//:packages.bzl", "ROLLUP_GLOBALS")
4+
load(
5+
"//tools:defaults.bzl",
6+
"markdown_to_html",
7+
"ng_module",
8+
"ng_package",
9+
"ng_test_library",
10+
"ng_web_test_suite",
11+
)
12+
13+
ng_module(
14+
name = "youtube-player",
15+
srcs = glob(
16+
["**/*.ts"],
17+
exclude = ["**/*.spec.ts"],
18+
),
19+
assets = glob(["**/*.html"]),
20+
module_name = "@angular/youtube-player",
21+
deps = [
22+
"@npm//@angular/common",
23+
"@npm//@angular/core",
24+
"@npm//@types/youtube",
25+
"@npm//rxjs",
26+
],
27+
)
28+
29+
ng_package(
30+
name = "npm_package",
31+
srcs = ["package.json"],
32+
entry_point = ":public-api.ts",
33+
entry_point_name = "youtube-player",
34+
globals = ROLLUP_GLOBALS,
35+
deps = [":youtube-player"],
36+
)
37+
38+
ng_test_library(
39+
name = "unit_test_sources",
40+
srcs = glob(
41+
["**/*.spec.ts"],
42+
exclude = ["**/*.e2e.spec.ts"],
43+
),
44+
deps = [
45+
":youtube-player",
46+
"@npm//@angular/platform-browser",
47+
],
48+
)
49+
50+
ng_web_test_suite(
51+
name = "unit_tests",
52+
deps = [":unit_test_sources"],
53+
)
54+
55+
markdown_to_html(
56+
name = "overview",
57+
srcs = [":youtube-player.md"],
58+
)
59+
60+
filegroup(
61+
name = "source-files",
62+
srcs = glob(["**/*.ts"]),
63+
)

src/youtube-player/README.md

Whitespace-only changes.

src/youtube-player/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './public-api';

src/youtube-player/package.json

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"name": "@angular/youtube-player",
3+
"version": "0.0.0-PLACEHOLDER",
4+
"description": "Angular YouTube Player",
5+
"main": "./bundles/youtube-player.umd.js",
6+
"module": "./esm5/youtube-player.es5.js",
7+
"es2015": "./esm2015/youtube-player.js",
8+
"typings": "./youtube-player.d.ts",
9+
"repository": {
10+
"type": "git",
11+
"url": "https://github.com/angular/components.git"
12+
},
13+
"keywords": [
14+
"angular",
15+
"components",
16+
"youtube"
17+
],
18+
"license": "MIT",
19+
"bugs": {
20+
"url": "https://github.com/angular/components/issues"
21+
},
22+
"homepage": "https://github.com/angular/components/tree/master/src/youtube-player#readme",
23+
"dependencies": {
24+
"@types/youtube": "^0.0.38"
25+
},
26+
"peerDependencies": {
27+
"@angular/core": "0.0.0-NG",
28+
"@angular/common": "0.0.0-NG"
29+
},
30+
"sideEffects": false
31+
}

src/youtube-player/public-api.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './youtube-module';
2+
export * from './youtube-player';
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// TypeScript config file that is used to compile the Material package through Gulp. As the
2+
// long term goal is to switch to Bazel, and we already want to run tests with Bazel, we need to
3+
// ensure the TypeScript build options are the same for Gulp and Bazel. We achieve this by
4+
// extending the generic Bazel build tsconfig which will be used for each entry-point.
5+
{
6+
"extends": "../bazel-tsconfig-build.json",
7+
"compilerOptions": {
8+
"baseUrl": ".",
9+
"outDir": "../../dist/packages/youtube-player",
10+
"rootDir": ".",
11+
"rootDirs": [
12+
".",
13+
"../../dist/packages/youtube-player"
14+
],
15+
"types": ["youtube"]
16+
},
17+
"files": [
18+
"public-api.ts"
19+
],
20+
"angularCompilerOptions": {
21+
"annotateForClosureCompiler": true,
22+
"strictMetadataEmit": true,
23+
"flatModuleOutFile": "index.js",
24+
"flatModuleId": "@angular/youtube-player",
25+
"skipTemplateCodegen": true,
26+
"fullTemplateTypeCheck": true
27+
}
28+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// TypeScript config file that extends the default tsconfig file for the library. This config is
2+
// used to compile the tests for Karma. Since the code will run inside of the browser, the target
3+
// needs to be ES5. The format needs to be CommonJS since Karma only supports that module format.
4+
{
5+
"extends": "./tsconfig-build",
6+
"compilerOptions": {
7+
"importHelpers": false,
8+
"module": "commonjs",
9+
"target": "es5",
10+
"types": ["jasmine", "youtube"]
11+
},
12+
"angularCompilerOptions": {
13+
"strictMetadataEmit": true,
14+
"skipTemplateCodegen": true,
15+
"emitDecoratorMetadata": true,
16+
"fullTemplateTypeCheck": true,
17+
18+
// Unset options inherited from tsconfig-build
19+
"annotateForClosureCompiler": false,
20+
"flatModuleOutFile": null,
21+
"flatModuleId": null
22+
},
23+
"include": [
24+
"*.ts"
25+
]
26+
}

src/youtube-player/tsconfig.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Configuration for IDEs only.
2+
{
3+
"extends": "../../tsconfig.json",
4+
"compilerOptions": {
5+
"rootDir": "..",
6+
"baseUrl": ".",
7+
"paths": {},
8+
"types": ["jasmine", "youtube"]
9+
},
10+
"include": ["*.ts"]
11+
}

src/youtube-player/youtube-module.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import {CommonModule} from '@angular/common';
2+
import {NgModule} from '@angular/core';
3+
4+
import {YouTubePlayer} from './youtube-player';
5+
6+
const COMPONENTS = [YouTubePlayer];
7+
8+
@NgModule(
9+
{declarations: COMPONENTS, exports: COMPONENTS, imports: [CommonModule]})
10+
export class YouTubePlayerModule {
11+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<div #youtube_container></div>

src/youtube-player/youtube-player.md

Whitespace-only changes.
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/// <reference types="youtube"/>
2+
3+
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
4+
import {Component, ViewChild} from '@angular/core';
5+
import {By} from '@angular/platform-browser';
6+
import {YouTubePlayerModule} from './index';
7+
import {YouTubePlayer} from './youtube-player';
8+
9+
const VIDEO_ID = 'a12345';
10+
11+
const playerState = {
12+
UNSTARTED: -1,
13+
ENDED: 0,
14+
PLAYING: 1,
15+
PAUSED: 2,
16+
BUFFERING: 3,
17+
CUED: 5,
18+
};
19+
20+
declare global {
21+
interface Window { YT: typeof YT | undefined; }
22+
}
23+
24+
describe('YoutubePlayer', () => {
25+
let playerCtor: jasmine.Spy;
26+
let player: jasmine.SpyObj<YT.Player>;
27+
let onPlayerReady: () => void;
28+
let fixture: ComponentFixture<TestApp>;
29+
let testComponent: TestApp;
30+
31+
beforeEach(async(() => {
32+
playerCtor = jasmine.createSpy('Player Constructor');
33+
player = jasmine.createSpyObj('Player', [
34+
'getPlayerState', 'destroy', 'cueVideoById', 'loadVideoById', 'pauseVideo', 'stopVideo',
35+
'seekTo', 'isMuted', 'mute', 'unMute', 'getVolume', 'getPlaybackRate',
36+
'getAvailablePlaybackRates', 'getVideoLoadedFraction', 'getPlayerState', 'getCurrentTime',
37+
'getPlaybackQuality', 'getAvailableQualityLevels', 'getDuration', 'getVideoUrl',
38+
'getVideoEmbedCode', 'playVideo', 'setSize', 'setVolume', 'setPlaybackQuality',
39+
'setPlaybackRate', 'addEventListener', 'removeEventListener',
40+
]);
41+
playerCtor.and.callFake((_el: Element, config: YT.PlayerOptions) => {
42+
onPlayerReady = () => {
43+
if (config.events && config.events.onReady) {
44+
config.events.onReady({target: player});
45+
}
46+
for (const [event, callback] of player.addEventListener.calls.allArgs()) {
47+
if (event === 'onReady') {
48+
callback({target: player});
49+
}
50+
}
51+
};
52+
return player;
53+
});
54+
window.YT = {
55+
'Player': playerCtor as unknown as typeof YT.Player,
56+
'PlayerState': playerState,
57+
} as typeof YT;
58+
59+
TestBed.configureTestingModule({
60+
imports: [YouTubePlayerModule],
61+
declarations: [TestApp],
62+
});
63+
64+
TestBed.compileComponents();
65+
fixture = TestBed.createComponent(TestApp);
66+
testComponent = fixture.debugElement.componentInstance;
67+
fixture.detectChanges();
68+
}));
69+
70+
afterEach(() => {
71+
window.YT = undefined;
72+
});
73+
74+
it('initializes a youtube player', () => {
75+
let containerElement = fixture.debugElement.query(By.css('div'));
76+
77+
expect(playerCtor).toHaveBeenCalledWith(
78+
containerElement.nativeElement, jasmine.objectContaining({
79+
videoId: VIDEO_ID,
80+
}));
81+
});
82+
83+
it('destroys the iframe when the component is destroyed', () => {
84+
onPlayerReady();
85+
86+
testComponent.visible = false;
87+
fixture.detectChanges();
88+
89+
expect(player.destroy).toHaveBeenCalled();
90+
});
91+
92+
it('responds to changes in video id', () => {
93+
let containerElement = fixture.debugElement.query(By.css('div'));
94+
95+
testComponent.videoId = 'otherId';
96+
fixture.detectChanges();
97+
98+
expect(player.cueVideoById).not.toHaveBeenCalled();
99+
100+
onPlayerReady();
101+
102+
expect(player.cueVideoById).toHaveBeenCalledWith(
103+
jasmine.objectContaining({videoId: 'otherId'}));
104+
105+
testComponent.videoId = undefined;
106+
fixture.detectChanges();
107+
108+
expect(player.destroy).toHaveBeenCalled();
109+
110+
testComponent.videoId = 'otherId2';
111+
fixture.detectChanges();
112+
113+
expect(playerCtor).toHaveBeenCalledWith(
114+
containerElement.nativeElement, jasmine.objectContaining({videoId: 'otherId2'}));
115+
});
116+
});
117+
118+
/** Test component that contains a YouTubePlayer. */
119+
@Component({
120+
selector: 'test-app',
121+
template: `
122+
<youtube-player #player [videoId]="videoId" *ngIf="visible">
123+
</youtube-player>
124+
`
125+
})
126+
class TestApp {
127+
videoId: string | undefined = VIDEO_ID;
128+
visible = true;
129+
@ViewChild('player', {static: true}) youtubePlayer: YouTubePlayer;
130+
}

0 commit comments

Comments
 (0)