Skip to content

Commit

Permalink
95 change genre and subgenre labels to select (#109)
Browse files Browse the repository at this point in the history
* create select input component

* add test on selection change

* improved user interface for the select

* navigate using the select box

* correct ci linter
  • Loading branch information
Babali42 authored Jan 7, 2025
1 parent 5317daa commit ce6697d
Show file tree
Hide file tree
Showing 9 changed files with 119 additions and 6 deletions.
19 changes: 19 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
Expand All @@ -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],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<select [(ngModel)]="selectedElement" (change)="onSelectChange()">
<option value="" disabled selected>{{placeHolder}}</option>
<option *ngFor="let element of elements" [value]="element">{{ element }}</option>
</select>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
select{
border-color: var(--borderColor);
background-color: var(--backgroundColor);
color: var(--textColor);
}
Original file line number Diff line number Diff line change
@@ -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<SelectInputComponent>;

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');
});
});
21 changes: 21 additions & 0 deletions frontend/src/app/components/select-input/select-input.component.ts
Original file line number Diff line number Diff line change
@@ -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<string>();

onSelectChange() {
this.selectChange.emit(this.selectedElement);
}
}
12 changes: 10 additions & 2 deletions frontend/src/app/components/sequencer/sequencer.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,19 @@
<div class="sequencer-header">
<div class="label">
<span class="small">Genre : </span>
<span class="large">{{ genre.label }}</span>
<app-select-input [elements]="genresLabel"
[selectedElement]="selectedGenreLabel"
(selectChange)="genreChange($event)"
placeHolder="Choose genre">
</app-select-input>
</div>
<div class="label">
<span class="small">Beat : </span>
<span class="large">{{ beat.label }}</span>
<app-select-input [elements]="beats"
[selectedElement]="selectedBeatLabel"
(selectChange)="beatChange($event)"
placeHolder="Choose {{selectedGenreLabel}} beat">
</app-select-input>
</div>
<div class="flex label" style="align-items: center; gap: 5px; margin-top: 2px">
<span class="small">Tempo : </span>
Expand Down
24 changes: 22 additions & 2 deletions frontend/src/app/components/sequencer/sequencer.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<Beat>;
genresLabel: string[] = [];
selectedGenreLabel: string = "";
beats: string[] = [];
selectedBeatLabel: string = "";
private genres: Genre[] = [];

constructor(@Inject(IManageGenresToken) private _genresManager: IManageGenres,
public soundService: SoundService,
Expand All @@ -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'));
});
Expand Down Expand Up @@ -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);
Expand All @@ -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;
Expand All @@ -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);
}
}

0 comments on commit ce6697d

Please sign in to comment.