Basisimplementierung Songlist
This commit is contained in:
3
.angulardoc.json
Normal file
3
.angulardoc.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"repoId": "a3607a0e-3686-4664-8e33-0671e13cd437"
|
||||||
|
}
|
||||||
5
.firebaserc
Normal file
5
.firebaserc
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"projects": {
|
||||||
|
"default": "worshipgenerator"
|
||||||
|
}
|
||||||
|
}
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -314,3 +314,4 @@ testem.log
|
|||||||
# System Files
|
# System Files
|
||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
firebase.ts
|
||||||
|
|||||||
@@ -30,7 +30,8 @@
|
|||||||
],
|
],
|
||||||
"styles": [
|
"styles": [
|
||||||
"src/custom-theme.scss",
|
"src/custom-theme.scss",
|
||||||
"src/styles.less"
|
"src/styles/styles.less",
|
||||||
|
"src/styles/shadow.less"
|
||||||
],
|
],
|
||||||
"scripts": []
|
"scripts": []
|
||||||
},
|
},
|
||||||
@@ -97,9 +98,7 @@
|
|||||||
"src/assets",
|
"src/assets",
|
||||||
"src/manifest.webmanifest"
|
"src/manifest.webmanifest"
|
||||||
],
|
],
|
||||||
"styles": [
|
"styles": [],
|
||||||
"src/styles.less"
|
|
||||||
],
|
|
||||||
"scripts": []
|
"scripts": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
20
firebase.json
Normal file
20
firebase.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"firestore": {
|
||||||
|
"rules": "firestore.rules",
|
||||||
|
"indexes": "firestore.indexes.json"
|
||||||
|
},
|
||||||
|
"hosting": {
|
||||||
|
"public": "dist/wgenerator",
|
||||||
|
"ignore": [
|
||||||
|
"firebase.json",
|
||||||
|
"**/.*",
|
||||||
|
"**/node_modules/**"
|
||||||
|
],
|
||||||
|
"rewrites": [
|
||||||
|
{
|
||||||
|
"source": "**",
|
||||||
|
"destination": "/index.html"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
4
firestore.indexes.json
Normal file
4
firestore.indexes.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"indexes": [],
|
||||||
|
"fieldOverrides": []
|
||||||
|
}
|
||||||
16
firestore.rules
Normal file
16
firestore.rules
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
rules_version = '2';
|
||||||
|
service cloud.firestore {
|
||||||
|
match /databases/{database}/documents {
|
||||||
|
match /user/{user} {
|
||||||
|
allow read: if true;
|
||||||
|
}
|
||||||
|
match /songs/{song} {
|
||||||
|
allow read: if true;
|
||||||
|
allow write: if true;
|
||||||
|
}
|
||||||
|
match /lastmodified/{lastmodified} {
|
||||||
|
allow read: if true;
|
||||||
|
allow write: if true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
"ng": "ng",
|
"ng": "ng",
|
||||||
"start": "ng serve",
|
"start": "ng serve",
|
||||||
"build": "ng build",
|
"build": "ng build",
|
||||||
|
"deploy": "ng build --prod && firebase deploy",
|
||||||
"test": "ng test",
|
"test": "ng test",
|
||||||
"lint": "ng lint",
|
"lint": "ng lint",
|
||||||
"e2e": "ng e2e"
|
"e2e": "ng e2e"
|
||||||
|
|||||||
@@ -1,11 +1,22 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import {NgModule} from '@angular/core';
|
||||||
import { Routes, RouterModule } from '@angular/router';
|
import {RouterModule, Routes} from '@angular/router';
|
||||||
|
|
||||||
|
|
||||||
const routes: Routes = [];
|
const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
redirectTo: 'songs',
|
||||||
|
pathMatch: 'full'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'songs',
|
||||||
|
loadChildren: () => import('./songs/songs.module').then(m => m.SongsModule)
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [RouterModule.forRoot(routes)],
|
imports: [RouterModule.forRoot(routes)],
|
||||||
exports: [RouterModule]
|
exports: [RouterModule]
|
||||||
})
|
})
|
||||||
export class AppRoutingModule { }
|
export class AppRoutingModule {
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<app-navigation></app-navigation>
|
||||||
|
<div class="content">
|
||||||
|
<router-outlet></router-outlet>
|
||||||
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
h1 {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
margin-top: 80px;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { TestBed, async } from '@angular/core/testing';
|
import {async, TestBed} from '@angular/core/testing';
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
import {RouterTestingModule} from '@angular/router/testing';
|
||||||
import { AppComponent } from './app.component';
|
import {AppComponent} from './app.component';
|
||||||
|
|
||||||
describe('AppComponent', () => {
|
describe('AppComponent', () => {
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
@@ -19,17 +19,4 @@ describe('AppComponent', () => {
|
|||||||
const app = fixture.debugElement.componentInstance;
|
const app = fixture.debugElement.componentInstance;
|
||||||
expect(app).toBeTruthy();
|
expect(app).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should have as title 'wgenerator'`, () => {
|
|
||||||
const fixture = TestBed.createComponent(AppComponent);
|
|
||||||
const app = fixture.debugElement.componentInstance;
|
|
||||||
expect(app.title).toEqual('wgenerator');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render title', () => {
|
|
||||||
const fixture = TestBed.createComponent(AppComponent);
|
|
||||||
fixture.detectChanges();
|
|
||||||
const compiled = fixture.debugElement.nativeElement;
|
|
||||||
expect(compiled.querySelector('.content span').textContent).toContain('wgenerator app is running!');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Component } from '@angular/core';
|
import {Component} from '@angular/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
import { BrowserModule } from '@angular/platform-browser';
|
import {BrowserModule} from '@angular/platform-browser';
|
||||||
import { NgModule } from '@angular/core';
|
import {NgModule} from '@angular/core';
|
||||||
|
|
||||||
import { AppRoutingModule } from './app-routing.module';
|
import {AppRoutingModule} from './app-routing.module';
|
||||||
import { AppComponent } from './app.component';
|
import {AppComponent} from './app.component';
|
||||||
import { ServiceWorkerModule } from '@angular/service-worker';
|
import {ServiceWorkerModule} from '@angular/service-worker';
|
||||||
import { environment } from '../environments/environment';
|
import {environment} from '../environments/environment';
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||||
|
import {ApplicationFrameModule} from "./application-frame/application-frame.module";
|
||||||
|
import {AngularFireModule} from "@angular/fire";
|
||||||
|
import {AngularFirestoreModule} from "@angular/fire/firestore";
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@@ -14,10 +17,18 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
|||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
AppRoutingModule,
|
AppRoutingModule,
|
||||||
ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }),
|
ServiceWorkerModule.register('ngsw-worker.js', {enabled: environment.production}),
|
||||||
BrowserAnimationsModule
|
BrowserAnimationsModule,
|
||||||
|
|
||||||
|
ApplicationFrameModule,
|
||||||
|
|
||||||
|
|
||||||
|
AngularFireModule.initializeApp(environment.firebase),
|
||||||
|
AngularFirestoreModule.enablePersistence(),
|
||||||
|
|
||||||
],
|
],
|
||||||
providers: [],
|
providers: [],
|
||||||
bootstrap: [AppComponent]
|
bootstrap: [AppComponent]
|
||||||
})
|
})
|
||||||
export class AppModule { }
|
export class AppModule {
|
||||||
|
}
|
||||||
|
|||||||
20
src/app/application-frame/application-frame.module.ts
Normal file
20
src/app/application-frame/application-frame.module.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import {NgModule} from '@angular/core';
|
||||||
|
import {CommonModule} from '@angular/common';
|
||||||
|
import {NavigationComponent} from './navigation/navigation.component';
|
||||||
|
import {RouterModule} from "@angular/router";
|
||||||
|
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
NavigationComponent
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
RouterModule
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
NavigationComponent
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class ApplicationFrameModule {
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<nav class="head">
|
||||||
|
<a href="#" routerLink="/songs" routerLinkActive="active">Inhalt</a>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
@import "../../../styles/styles";
|
||||||
|
@import "../../../styles/shadow";
|
||||||
|
|
||||||
|
nav {
|
||||||
|
&.head {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 60px;
|
||||||
|
background: @navigation-background;
|
||||||
|
.card-3;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
|
||||||
|
a {
|
||||||
|
display: block;
|
||||||
|
height: 60px;
|
||||||
|
color: @navigation-link-color;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 20px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: transparent;
|
||||||
|
transition: @transition;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background: @primary-color;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
|
import {NavigationComponent} from './navigation.component';
|
||||||
|
|
||||||
|
describe('NavigationComponent', () => {
|
||||||
|
let component: NavigationComponent;
|
||||||
|
let fixture: ComponentFixture<NavigationComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [NavigationComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(NavigationComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
16
src/app/application-frame/navigation/navigation.component.ts
Normal file
16
src/app/application-frame/navigation/navigation.component.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import {Component, OnInit} from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-navigation',
|
||||||
|
templateUrl: './navigation.component.html',
|
||||||
|
styleUrls: ['./navigation.component.less']
|
||||||
|
})
|
||||||
|
export class NavigationComponent implements OnInit {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
10
src/app/songs/models/song.ts
Normal file
10
src/app/songs/models/song.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
export interface Song {
|
||||||
|
comment: string;
|
||||||
|
final: boolean;
|
||||||
|
key: string;
|
||||||
|
number: number;
|
||||||
|
tempo: number;
|
||||||
|
text: string;
|
||||||
|
title: string;
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
41
src/app/songs/services/song-data.service.spec.ts
Normal file
41
src/app/songs/services/song-data.service.spec.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import {async, TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
|
import {SongDataService} from './song-data.service';
|
||||||
|
import {AngularFirestore} from "@angular/fire/firestore";
|
||||||
|
import {of} from "rxjs";
|
||||||
|
|
||||||
|
describe('SongDataService', () => {
|
||||||
|
|
||||||
|
const songs = [
|
||||||
|
{title: 'title1'}
|
||||||
|
];
|
||||||
|
|
||||||
|
const angularFirestoreCollection = {
|
||||||
|
valueChanges: () => of(songs)
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockAngularFirestore = {
|
||||||
|
collection: path => angularFirestoreCollection
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => TestBed.configureTestingModule({
|
||||||
|
providers: [
|
||||||
|
{provide: AngularFirestore, useValue: mockAngularFirestore}
|
||||||
|
]
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
const service: SongDataService = TestBed.get(SongDataService);
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should list songs', async(() => {
|
||||||
|
const service: SongDataService = TestBed.get(SongDataService);
|
||||||
|
service.list().subscribe(songs => {
|
||||||
|
expect(songs).toEqual(<any>[
|
||||||
|
{title: 'title1'}
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}));
|
||||||
|
});
|
||||||
20
src/app/songs/services/song-data.service.ts
Normal file
20
src/app/songs/services/song-data.service.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import {Injectable} from '@angular/core';
|
||||||
|
import {AngularFirestore, AngularFirestoreCollection} from "@angular/fire/firestore";
|
||||||
|
import {Song} from "../models/song";
|
||||||
|
import {Observable} from "rxjs";
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class SongDataService {
|
||||||
|
private songCollection: AngularFirestoreCollection<Song>;
|
||||||
|
private songs: Observable<Song[]>;
|
||||||
|
|
||||||
|
constructor(private afs: AngularFirestore) {
|
||||||
|
this.songCollection = afs.collection<Song>('songs');
|
||||||
|
this.songs = this.songCollection.valueChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
public list = (): Observable<Song[]> => this.songs;
|
||||||
|
|
||||||
|
}
|
||||||
36
src/app/songs/services/song.service.spec.ts
Normal file
36
src/app/songs/services/song.service.spec.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import {async, TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
|
import {SongService} from './song.service';
|
||||||
|
import {SongDataService} from "./song-data.service";
|
||||||
|
import {of} from "rxjs";
|
||||||
|
|
||||||
|
describe('SongService', () => {
|
||||||
|
|
||||||
|
const songs = [
|
||||||
|
{title: 'title1'}
|
||||||
|
];
|
||||||
|
|
||||||
|
const mockSongDataService = {
|
||||||
|
list: () => of(songs)
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => TestBed.configureTestingModule({
|
||||||
|
providers: [
|
||||||
|
{provide: SongDataService, useValue: mockSongDataService}
|
||||||
|
]
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
const service: SongService = TestBed.get(SongService);
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should list songs', async(() => {
|
||||||
|
const service: SongService = TestBed.get(SongService);
|
||||||
|
service.list().subscribe(songs => {
|
||||||
|
expect(songs).toEqual(<any>[
|
||||||
|
{title: 'title1'}
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
});
|
||||||
16
src/app/songs/services/song.service.ts
Normal file
16
src/app/songs/services/song.service.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import {Injectable} from '@angular/core';
|
||||||
|
import {Observable} from "rxjs";
|
||||||
|
import {Song} from "../models/song";
|
||||||
|
import {SongDataService} from "./song-data.service";
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class SongService {
|
||||||
|
|
||||||
|
constructor(private songDataService: SongDataService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public list = (): Observable<Song[]> => this.songDataService.list();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<div class="list-item">
|
||||||
|
<div class="number">{{song.number}}</div>
|
||||||
|
<div>{{song.title}}</div>
|
||||||
|
<div>{{song.key}}</div>
|
||||||
|
<div>{{song.type | songType}}</div>
|
||||||
|
</div>
|
||||||
23
src/app/songs/song-list/list-item/list-item.component.less
Normal file
23
src/app/songs/song-list/list-item/list-item.component.less
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
@import "../../../../styles/styles";
|
||||||
|
|
||||||
|
.list-item {
|
||||||
|
padding: 10px 20px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 50px auto 30px 100px;
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: @primary-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.number {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
|
import {ListItemComponent} from './list-item.component';
|
||||||
|
|
||||||
|
describe('ListItemComponent', () => {
|
||||||
|
let component: ListItemComponent;
|
||||||
|
let fixture: ComponentFixture<ListItemComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ListItemComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ListItemComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
18
src/app/songs/song-list/list-item/list-item.component.ts
Normal file
18
src/app/songs/song-list/list-item/list-item.component.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import {Component, Input, OnInit} from '@angular/core';
|
||||||
|
import {Song} from "../../models/song";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-list-item',
|
||||||
|
templateUrl: './list-item.component.html',
|
||||||
|
styleUrls: ['./list-item.component.less']
|
||||||
|
})
|
||||||
|
export class ListItemComponent implements OnInit {
|
||||||
|
@Input() public song: Song;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
3
src/app/songs/song-list/song-list.component.html
Normal file
3
src/app/songs/song-list/song-list.component.html
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<app-card [padding]="false">
|
||||||
|
<app-list-item *ngFor="let song of songs" [song]="song"></app-list-item>
|
||||||
|
</app-card>
|
||||||
2
src/app/songs/song-list/song-list.component.less
Normal file
2
src/app/songs/song-list/song-list.component.less
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
.song-list {
|
||||||
|
}
|
||||||
47
src/app/songs/song-list/song-list.component.spec.ts
Normal file
47
src/app/songs/song-list/song-list.component.spec.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import {async, ComponentFixture, fakeAsync, TestBed, tick} from '@angular/core/testing';
|
||||||
|
|
||||||
|
import {SongListComponent} from './song-list.component';
|
||||||
|
import {of} from "rxjs";
|
||||||
|
import {SongService} from "../services/song.service";
|
||||||
|
import {NO_ERRORS_SCHEMA} from "@angular/core";
|
||||||
|
|
||||||
|
describe('SongListComponent', () => {
|
||||||
|
let component: SongListComponent;
|
||||||
|
let fixture: ComponentFixture<SongListComponent>;
|
||||||
|
|
||||||
|
const songs = [
|
||||||
|
{title: 'title1'}
|
||||||
|
];
|
||||||
|
|
||||||
|
const mockSongService = {
|
||||||
|
list: () => of(songs)
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [SongListComponent],
|
||||||
|
providers: [
|
||||||
|
{provide: SongService, useValue: mockSongService}
|
||||||
|
],
|
||||||
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(SongListComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should read songs from SongService', fakeAsync(() => {
|
||||||
|
tick();
|
||||||
|
expect(component.songs).toEqual(<any>[
|
||||||
|
{title: 'title1'}
|
||||||
|
]);
|
||||||
|
}));
|
||||||
|
});
|
||||||
22
src/app/songs/song-list/song-list.component.ts
Normal file
22
src/app/songs/song-list/song-list.component.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import {Component, OnInit} from '@angular/core';
|
||||||
|
import {SongService} from "../services/song.service";
|
||||||
|
import {Song} from "../models/song";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-songs',
|
||||||
|
templateUrl: './song-list.component.html',
|
||||||
|
styleUrls: ['./song-list.component.less']
|
||||||
|
})
|
||||||
|
export class SongListComponent implements OnInit {
|
||||||
|
public songs: Song[];
|
||||||
|
|
||||||
|
constructor(private songService: SongService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.songService.list().subscribe(songs => {
|
||||||
|
this.songs = songs.sort((a, b) => a.number - b.number);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
20
src/app/songs/song-list/song-list.module.ts
Normal file
20
src/app/songs/song-list/song-list.module.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import {NgModule} from '@angular/core';
|
||||||
|
import {CommonModule} from '@angular/common';
|
||||||
|
import {SongListComponent} from "./song-list.component";
|
||||||
|
import {ListItemComponent} from './list-item/list-item.component';
|
||||||
|
import {CardModule} from "../../widget-modules/components/card/card.module";
|
||||||
|
import {SongTypeTranslaterModule} from "../../widget-modules/pipes/song-type-translater/song-type-translater.module";
|
||||||
|
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [SongListComponent, ListItemComponent],
|
||||||
|
exports: [SongListComponent],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
|
||||||
|
CardModule,
|
||||||
|
SongTypeTranslaterModule
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class SongListModule {
|
||||||
|
}
|
||||||
1
src/app/songs/song/song.component.html
Normal file
1
src/app/songs/song/song.component.html
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<p>song works with songId: {{songId}}!</p>
|
||||||
0
src/app/songs/song/song.component.less
Normal file
0
src/app/songs/song/song.component.less
Normal file
39
src/app/songs/song/song.component.spec.ts
Normal file
39
src/app/songs/song/song.component.spec.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import {async, ComponentFixture, fakeAsync, TestBed, tick} from '@angular/core/testing';
|
||||||
|
|
||||||
|
import {SongComponent} from './song.component';
|
||||||
|
import {of} from "rxjs";
|
||||||
|
import {ActivatedRoute} from "@angular/router";
|
||||||
|
|
||||||
|
describe('SongComponent', () => {
|
||||||
|
let component: SongComponent;
|
||||||
|
let fixture: ComponentFixture<SongComponent>;
|
||||||
|
|
||||||
|
const mockActivatedRoute = {
|
||||||
|
params: of({songId: '4711'})
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [SongComponent],
|
||||||
|
providers: [
|
||||||
|
{provide: ActivatedRoute, useValue: mockActivatedRoute}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(SongComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should provide songId', fakeAsync(() => {
|
||||||
|
tick();
|
||||||
|
expect(component.songId).toBe('4711');
|
||||||
|
}));
|
||||||
|
});
|
||||||
19
src/app/songs/song/song.component.ts
Normal file
19
src/app/songs/song/song.component.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import {Component, OnInit} from '@angular/core';
|
||||||
|
import {ActivatedRoute} from "@angular/router";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-song',
|
||||||
|
templateUrl: './song.component.html',
|
||||||
|
styleUrls: ['./song.component.less']
|
||||||
|
})
|
||||||
|
export class SongComponent implements OnInit {
|
||||||
|
public songId: string;
|
||||||
|
|
||||||
|
constructor(private activatedRoute: ActivatedRoute) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public ngOnInit(): void {
|
||||||
|
this.activatedRoute.params.subscribe(params => this.songId = params.songId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
24
src/app/songs/songs-routing.module.ts
Normal file
24
src/app/songs/songs-routing.module.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import {NgModule} from '@angular/core';
|
||||||
|
import {RouterModule, Routes} from '@angular/router';
|
||||||
|
import {SongComponent} from "./song/song.component";
|
||||||
|
import {SongListComponent} from "./song-list/song-list.component";
|
||||||
|
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: SongListComponent,
|
||||||
|
pathMatch: 'full'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ':songId',
|
||||||
|
component: SongComponent
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild(routes)],
|
||||||
|
exports: [RouterModule]
|
||||||
|
})
|
||||||
|
export class SongsRoutingModule {
|
||||||
|
}
|
||||||
19
src/app/songs/songs.module.ts
Normal file
19
src/app/songs/songs.module.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import {NgModule} from '@angular/core';
|
||||||
|
import {CommonModule} from '@angular/common';
|
||||||
|
|
||||||
|
import {SongsRoutingModule} from './songs-routing.module';
|
||||||
|
import {SongComponent} from './song/song.component';
|
||||||
|
import {SongListModule} from "./song-list/song-list.module";
|
||||||
|
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [SongComponent],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
SongsRoutingModule,
|
||||||
|
SongListModule,
|
||||||
|
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class SongsModule {
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<div [class.padding]="padding" class="card">
|
||||||
|
<ng-content></ng-content>
|
||||||
|
</div>
|
||||||
12
src/app/widget-modules/components/card/card.component.less
Normal file
12
src/app/widget-modules/components/card/card.component.less
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
@import "../../../../styles/shadow";
|
||||||
|
|
||||||
|
.card {
|
||||||
|
.card-3;
|
||||||
|
margin: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #fffe;
|
||||||
|
|
||||||
|
&.padding {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
|
import {CardComponent} from './card.component';
|
||||||
|
|
||||||
|
describe('CardComponent', () => {
|
||||||
|
let component: CardComponent;
|
||||||
|
let fixture: ComponentFixture<CardComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [CardComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(CardComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
17
src/app/widget-modules/components/card/card.component.ts
Normal file
17
src/app/widget-modules/components/card/card.component.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import {Component, Input, OnInit} from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-card',
|
||||||
|
templateUrl: './card.component.html',
|
||||||
|
styleUrls: ['./card.component.less']
|
||||||
|
})
|
||||||
|
export class CardComponent implements OnInit {
|
||||||
|
@Input() padding = true;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
14
src/app/widget-modules/components/card/card.module.ts
Normal file
14
src/app/widget-modules/components/card/card.module.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import {NgModule} from '@angular/core';
|
||||||
|
import {CommonModule} from '@angular/common';
|
||||||
|
import {CardComponent} from './card.component';
|
||||||
|
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [CardComponent],
|
||||||
|
exports: [CardComponent],
|
||||||
|
imports: [
|
||||||
|
CommonModule
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class CardModule {
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import {NgModule} from '@angular/core';
|
||||||
|
import {CommonModule} from '@angular/common';
|
||||||
|
import {SongTypePipe} from './song-type.pipe';
|
||||||
|
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [SongTypePipe],
|
||||||
|
exports: [
|
||||||
|
SongTypePipe
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CommonModule
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class SongTypeTranslaterModule {
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import {SongTypePipe} from './song-type.pipe';
|
||||||
|
|
||||||
|
describe('SongTypePipe', () => {
|
||||||
|
it('create an instance', () => {
|
||||||
|
const pipe = new SongTypePipe();
|
||||||
|
expect(pipe).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import {Pipe, PipeTransform} from '@angular/core';
|
||||||
|
|
||||||
|
@Pipe({
|
||||||
|
name: 'songType'
|
||||||
|
})
|
||||||
|
export class SongTypePipe implements PipeTransform {
|
||||||
|
|
||||||
|
transform(songTypeKey: string): string {
|
||||||
|
switch (songTypeKey) {
|
||||||
|
case "Worship":
|
||||||
|
return "Anbetung";
|
||||||
|
case "Praise":
|
||||||
|
return "Lobpreis";
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
// Custom Theming for Angular Material
|
// Custom Theming for Angular Material
|
||||||
// For more information: https://material.angular.io/guide/theming
|
// For more information: https://material.angular.io/guide/theming
|
||||||
@import '~@angular/material/theming';
|
@import '~@angular/material/theming';
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
import {firebase} from "./firebase";
|
||||||
|
|
||||||
export const environment = {
|
export const environment = {
|
||||||
production: true
|
production: true,
|
||||||
|
firebase: firebase
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,8 +2,11 @@
|
|||||||
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
|
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
|
||||||
// The list of file replacements can be found in `angular.json`.
|
// The list of file replacements can be found in `angular.json`.
|
||||||
|
|
||||||
|
import {firebase} from "./firebase";
|
||||||
|
|
||||||
export const environment = {
|
export const environment = {
|
||||||
production: false
|
production: false,
|
||||||
|
firebase: firebase
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -4,15 +4,15 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>Wgenerator</title>
|
<title>Wgenerator</title>
|
||||||
<base href="/">
|
<base href="/">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta content="width=device-width, initial-scale=1" name="viewport">
|
||||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
<link href="favicon.ico" rel="icon" type="image/x-icon">
|
||||||
<link rel="manifest" href="manifest.webmanifest">
|
<link href="manifest.webmanifest" rel="manifest">
|
||||||
<meta name="theme-color" content="#1976d2">
|
<meta content="#1976d2" name="theme-color">
|
||||||
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500&display=swap" rel="stylesheet">
|
||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<app-root></app-root>
|
<app-root></app-root>
|
||||||
<noscript>Please enable JavaScript to continue using this application.</noscript>
|
<noscript>Please enable JavaScript to continue using this application.</noscript>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import 'hammerjs';
|
import 'hammerjs';
|
||||||
import { enableProdMode } from '@angular/core';
|
import {enableProdMode} from '@angular/core';
|
||||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
|
||||||
|
|
||||||
import { AppModule } from './app/app.module';
|
import {AppModule} from './app/app.module';
|
||||||
import { environment } from './environments/environment';
|
import {environment} from './environments/environment';
|
||||||
|
|
||||||
if (environment.production) {
|
if (environment.production) {
|
||||||
enableProdMode();
|
enableProdMode();
|
||||||
|
|||||||
@@ -55,7 +55,7 @@
|
|||||||
/***************************************************************************************************
|
/***************************************************************************************************
|
||||||
* Zone JS is required by default for Angular itself.
|
* Zone JS is required by default for Angular itself.
|
||||||
*/
|
*/
|
||||||
import 'zone.js/dist/zone'; // Included with Angular CLI.
|
import 'zone.js/dist/zone'; // Included with Angular CLI.
|
||||||
|
|
||||||
|
|
||||||
/***************************************************************************************************
|
/***************************************************************************************************
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
/* You can add global styles to this file, and also import other style files */
|
|
||||||
|
|
||||||
html, body { height: 100%; }
|
|
||||||
body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
|
|
||||||
19
src/styles/shadow.less
Normal file
19
src/styles/shadow.less
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
.card-1 {
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-2 {
|
||||||
|
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-3 {
|
||||||
|
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-4 {
|
||||||
|
box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25), 0 10px 10px rgba(0, 0, 0, 0.22);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-5 {
|
||||||
|
box-shadow: 0 19px 38px rgba(0, 0, 0, 0.30), 0 15px 12px rgba(0, 0, 0, 0.22);
|
||||||
|
}
|
||||||
21
src/styles/styles.less
Normal file
21
src/styles/styles.less
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
@primary-color: #5285ff;
|
||||||
|
|
||||||
|
@navigation-background: #fffffff1;
|
||||||
|
@navigation-link-color: #555;
|
||||||
|
|
||||||
|
@transition: all 300ms ease-in-out;
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: Roboto, "Helvetica Neue", sans-serif;
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
background: #373b44; /* fallback for old browsers */
|
||||||
|
background: -webkit-linear-gradient(to top, #373b44, #4286f4); /* Chrome 10-25, Safari 5.1-6 */
|
||||||
|
background: linear-gradient(to top, #373b44, #4286f4); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
|
||||||
|
background-attachment: fixed;
|
||||||
|
}
|
||||||
@@ -1,11 +1,8 @@
|
|||||||
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
|
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
|
||||||
|
|
||||||
import 'zone.js/dist/zone-testing';
|
import 'zone.js/dist/zone-testing';
|
||||||
import { getTestBed } from '@angular/core/testing';
|
import {getTestBed} from '@angular/core/testing';
|
||||||
import {
|
import {BrowserDynamicTestingModule, platformBrowserDynamicTesting} from '@angular/platform-browser-dynamic/testing';
|
||||||
BrowserDynamicTestingModule,
|
|
||||||
platformBrowserDynamicTesting
|
|
||||||
} from '@angular/platform-browser-dynamic/testing';
|
|
||||||
|
|
||||||
declare const require: any;
|
declare const require: any;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user