From ce6697d38d2a2a4a21bbf29318bfedb04281abaa Mon Sep 17 00:00:00 2001 From: Baptiste Lyet <44911483+Babali42@users.noreply.github.com> Date: Tue, 7 Jan 2025 02:42:27 +0100 Subject: [PATCH] 95 change genre and subgenre labels to select (#109) * create select input component * add test on selection change * improved user interface for the select * navigate using the select box * correct ci linter --- frontend/package-lock.json | 19 ++++++++++ frontend/package.json | 1 + frontend/src/app/app.module.ts | 4 +-- .../select-input/select-input.component.html | 4 +++ .../select-input/select-input.component.scss | 5 +++ .../select-input.component.spec.ts | 35 +++++++++++++++++++ .../select-input/select-input.component.ts | 21 +++++++++++ .../sequencer/sequencer.component.html | 12 +++++-- .../sequencer/sequencer.component.ts | 24 +++++++++++-- 9 files changed, 119 insertions(+), 6 deletions(-) create mode 100644 frontend/src/app/components/select-input/select-input.component.html create mode 100644 frontend/src/app/components/select-input/select-input.component.scss create mode 100644 frontend/src/app/components/select-input/select-input.component.spec.ts create mode 100644 frontend/src/app/components/select-input/select-input.component.ts diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 68e7bd5..b5a7fba 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -13,6 +13,7 @@ "@angular/common": "^17.3.12", "@angular/compiler": "^17.3.12", "@angular/core": "^17.3.12", + "@angular/forms": "^17.3.12", "@angular/platform-browser": "^17.3.12", "@angular/platform-browser-dynamic": "^17.3.12", "@angular/router": "^17.3.12", @@ -555,6 +556,24 @@ "zone.js": "~0.14.0" } }, + "node_modules/@angular/forms": { + "version": "17.3.12", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-17.3.12.tgz", + "integrity": "sha512-tV6r12Q3yEUlXwpVko4E+XscunTIpPkLbaiDn/MTL3Vxi2LZnsLgHyd/i38HaHN+e/H3B0a1ToSOhV5wf3ay4Q==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.13.0 || >=20.9.0" + }, + "peerDependencies": { + "@angular/common": "17.3.12", + "@angular/core": "17.3.12", + "@angular/platform-browser": "17.3.12", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, "node_modules/@angular/platform-browser": { "version": "17.3.12", "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-17.3.12.tgz", diff --git a/frontend/package.json b/frontend/package.json index b54aee3..d8e6368 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -18,6 +18,7 @@ "@angular/platform-browser": "^17.3.12", "@angular/platform-browser-dynamic": "^17.3.12", "@angular/router": "^17.3.12", + "@angular/forms": "^17.3.12", "@ngx-loading-bar/core": "^7.0.0", "angular-in-memory-web-api": "^0.17.0", "effect": "^3.10.14", diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index 1369f3d..fb844ad 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -12,6 +12,7 @@ import {LoadingInterceptor} from './interceptors/loading.interceptor'; import {environment} from "../environments/environment"; import {provideRouter, RouterOutlet, Routes} from "@angular/router"; import {BeatCreatorComponent} from "./components/beat-creator/beat-creator.component"; +import { FormsModule } from '@angular/forms'; export const routes: Routes = [ { path: '', component: SequencerComponent }, @@ -24,14 +25,13 @@ import {IManageGenresToken} from "./domain/ports/secondary/i-manage-genres"; BrowserModule, HttpClientModule, LoadingBarModule, - + FormsModule, // The HttpClientInMemoryWebApiModule module intercepts HTTP requests // and returns simulated server responses. // Remove it when a real server is ready to receive requests. environment.httpClientInMemory ? HttpClientInMemoryWebApiModule.forRoot( InMemoryDataService, {dataEncapsulation: false} ) : [], - SequencerComponent, RouterOutlet ], declarations: [AppComponent], diff --git a/frontend/src/app/components/select-input/select-input.component.html b/frontend/src/app/components/select-input/select-input.component.html new file mode 100644 index 0000000..bf061f5 --- /dev/null +++ b/frontend/src/app/components/select-input/select-input.component.html @@ -0,0 +1,4 @@ + diff --git a/frontend/src/app/components/select-input/select-input.component.scss b/frontend/src/app/components/select-input/select-input.component.scss new file mode 100644 index 0000000..00c2179 --- /dev/null +++ b/frontend/src/app/components/select-input/select-input.component.scss @@ -0,0 +1,5 @@ +select{ + border-color: var(--borderColor); + background-color: var(--backgroundColor); + color: var(--textColor); +} diff --git a/frontend/src/app/components/select-input/select-input.component.spec.ts b/frontend/src/app/components/select-input/select-input.component.spec.ts new file mode 100644 index 0000000..719b71f --- /dev/null +++ b/frontend/src/app/components/select-input/select-input.component.spec.ts @@ -0,0 +1,35 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SelectInputComponent } from './select-input.component'; + +describe('SelectInputComponent', () => { + let component: SelectInputComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [SelectInputComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(SelectInputComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should emit petChange when a new pet is selected', () => { + //Arrange + spyOn(component.selectChange, 'emit'); + + //Act + component.selectedElement = 'gabber'; + component.onSelectChange(); + + //Assert + expect(component.selectChange.emit).toHaveBeenCalledWith('gabber'); + }); +}); diff --git a/frontend/src/app/components/select-input/select-input.component.ts b/frontend/src/app/components/select-input/select-input.component.ts new file mode 100644 index 0000000..7b2c210 --- /dev/null +++ b/frontend/src/app/components/select-input/select-input.component.ts @@ -0,0 +1,21 @@ +import {Component, EventEmitter, Input, Output} from '@angular/core'; +import {FormsModule} from "@angular/forms"; +import {NgForOf} from "@angular/common"; + +@Component({ + selector: 'app-select-input', + standalone: true, + imports: [FormsModule, NgForOf], + templateUrl: './select-input.component.html', + styleUrl: './select-input.component.scss' +}) +export class SelectInputComponent { + @Input() elements: string[] = []; + @Input() selectedElement: string = ""; + @Input() placeHolder: string = ""; + @Output() selectChange = new EventEmitter(); + + onSelectChange() { + this.selectChange.emit(this.selectedElement); + } +} diff --git a/frontend/src/app/components/sequencer/sequencer.component.html b/frontend/src/app/components/sequencer/sequencer.component.html index e9300c5..2719b5b 100644 --- a/frontend/src/app/components/sequencer/sequencer.component.html +++ b/frontend/src/app/components/sequencer/sequencer.component.html @@ -7,11 +7,19 @@
Genre : - {{ genre.label }} + +
Beat : - {{ beat.label }} + +
Tempo : diff --git a/frontend/src/app/components/sequencer/sequencer.component.ts b/frontend/src/app/components/sequencer/sequencer.component.ts index 89149d6..3deed8b 100644 --- a/frontend/src/app/components/sequencer/sequencer.component.ts +++ b/frontend/src/app/components/sequencer/sequencer.component.ts @@ -8,18 +8,24 @@ import {ActivatedRoute} from '@angular/router'; import IManageGenres, {IManageGenresToken} from "../../domain/ports/secondary/i-manage-genres"; import {Subject} from "rxjs"; import {BpmInputComponent} from "../bpm-input/bpm-input.component"; +import {SelectInputComponent} from "../select-input/select-input.component"; @Component({ selector: 'sequencer', templateUrl: './sequencer.component.html', styleUrls: ['./sequencer.component.scss'], standalone: true, - imports: [NgFor, BpmInputComponent] + imports: [NgFor, BpmInputComponent, SelectInputComponent] }) export class SequencerComponent implements OnInit { beat = {} as Beat; genre = {} as Genre; beatBehaviourSubject: Subject; + genresLabel: string[] = []; + selectedGenreLabel: string = ""; + beats: string[] = []; + selectedBeatLabel: string = ""; + private genres: Genre[] = []; constructor(@Inject(IManageGenresToken) private _genresManager: IManageGenres, public soundService: SoundService, @@ -29,6 +35,8 @@ export class SequencerComponent implements OnInit { ngOnInit() { this._genresManager.getGenres().then(genres => { + this.genres = genres; + this.genresLabel = genres.map(x => x.label); this.route.queryParamMap.subscribe((params) => { this.selectGenre(genres, params.get('genre'), params.get('beat')); }); @@ -66,6 +74,8 @@ export class SequencerComponent implements OnInit { if (!firstGenre) return; this.genre = firstGenre; + this.selectedGenreLabel = firstGenre.label; + this.beats = firstGenre.beats.map(x => x.label); const beatToSelect = beat ? firstGenre.beats.find(x => x.id === beat) : firstGenre.beats[0]; this.selectBeat(beatToSelect); @@ -74,7 +84,8 @@ export class SequencerComponent implements OnInit { selectBeat(beatToSelect: Beat | undefined): void { if (beatToSelect == undefined) return; this.beat = beatToSelect; - this.beatBehaviourSubject.next(this.beat) + this.beatBehaviourSubject.next(this.beat); + this.selectedBeatLabel = this.beat.label; } protected readonly StepLengths = StepLengths; @@ -92,5 +103,14 @@ export class SequencerComponent implements OnInit { } ); } + + genreChange($event: string) { + this.selectGenre(this.genres, $event, null); + } + + beatChange($event: string) { + const beatToSelect = this.genres.find(x => x.label === this.selectedGenreLabel)?.beats.find(x => x.label === $event); + this.selectBeat(beatToSelect); + } }