Extract seat allocation and elected candidates to separate components
This commit is contained in:
parent
641bba55e0
commit
93acd0935e
|
@ -0,0 +1,20 @@
|
|||
<table mat-table [dataSource]="dataSource"
|
||||
matSort (matSortChange)="announceSortChange($event)">
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell mat-sort-header *matHeaderCellDef i18n
|
||||
i18n-sortActionDescription sortActionDescription="Sort by name">Name</th>
|
||||
<td mat-cell *matCellDef="let row">{{row.name}}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="profession">
|
||||
<th mat-header-cell mat-sort-header *matHeaderCellDef i18n
|
||||
i18n-sortActionDescription sortActionDescription="Sort by profession">Profession</th>
|
||||
<td mat-cell *matCellDef="let row">{{row.profession}}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="elected">
|
||||
<th mat-header-cell *matHeaderCellDef i18n>Elected by</th>
|
||||
<td mat-cell *matCellDef="let row">{{row.elected}}</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="resultColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: resultColumns"></tr>
|
||||
</table>
|
|
@ -0,0 +1,21 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ElectedCandidatesComponent } from './elected-candidates.component';
|
||||
|
||||
describe('ElectedCandidatesComponent', () => {
|
||||
let component: ElectedCandidatesComponent;
|
||||
let fixture: ComponentFixture<ElectedCandidatesComponent>;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ElectedCandidatesComponent]
|
||||
});
|
||||
fixture = TestBed.createComponent(ElectedCandidatesComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,105 @@
|
|||
import {AfterViewInit, Component, Input, ViewChild} from '@angular/core';
|
||||
import {ElectedCandidates} from "../model/elected-candidates";
|
||||
import {MatSort, Sort} from "@angular/material/sort";
|
||||
import {MatTableDataSource} from "@angular/material/table";
|
||||
import {LiveAnnouncer} from "@angular/cdk/a11y";
|
||||
import {ElectionSource} from "../model/election-source";
|
||||
|
||||
export interface ViewModel {
|
||||
electedCandidates: ElectedCandidates;
|
||||
partyAbbreviation: string;
|
||||
}
|
||||
|
||||
interface PresentedCandidate {
|
||||
name: string;
|
||||
profession: string;
|
||||
elected: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-elected-candidates',
|
||||
templateUrl: './elected-candidates.component.html',
|
||||
styleUrls: ['./elected-candidates.component.scss']
|
||||
})
|
||||
export class ElectedCandidatesComponent implements AfterViewInit {
|
||||
@ViewChild(MatSort) sort: MatSort | null = null;
|
||||
resultColumns: string[] = ['name', 'profession', 'elected'];
|
||||
electedCandidatesOfParty: PresentedCandidate[] = [];
|
||||
dataSource: MatTableDataSource<PresentedCandidate> = new MatTableDataSource<PresentedCandidate>();
|
||||
|
||||
constructor(private liveAnnouncer: LiveAnnouncer) {
|
||||
}
|
||||
|
||||
private _viewModel: ViewModel | null = null;
|
||||
|
||||
get viewModel(): ViewModel | null {
|
||||
return this._viewModel;
|
||||
}
|
||||
|
||||
@Input() set viewModel(value: ViewModel | null) {
|
||||
if (value != null) {
|
||||
this._viewModel = value;
|
||||
const electedCandidates: PresentedCandidate[] = [];
|
||||
const overallCandidates = this._viewModel.electedCandidates.overallResult.electedCandidates;
|
||||
if (this._viewModel.partyAbbreviation in overallCandidates) {
|
||||
electedCandidates.push(
|
||||
...overallCandidates[this._viewModel.partyAbbreviation].map(
|
||||
candidate => {
|
||||
return {
|
||||
name: candidate.candidate.name,
|
||||
profession: candidate.candidate.profession,
|
||||
elected: this.getElectedMessage((<any>ElectionSource)[candidate.elected])
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
for (const constituency in this._viewModel.electedCandidates.constituencyResults) {
|
||||
const electedResult = this._viewModel.electedCandidates.constituencyResults[constituency];
|
||||
if (this._viewModel.partyAbbreviation in electedResult.electedCandidates) {
|
||||
electedCandidates.push(
|
||||
...electedResult.electedCandidates[this._viewModel.partyAbbreviation].map(
|
||||
candidate => {
|
||||
return {
|
||||
name: candidate.candidate.name,
|
||||
profession: candidate.candidate.profession,
|
||||
elected: this.getElectedMessage((<any>ElectionSource)[candidate.elected])
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
this.electedCandidatesOfParty = electedCandidates;
|
||||
this.dataSource.data = this.electedCandidatesOfParty;
|
||||
}
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.dataSource.sort = this.sort;
|
||||
}
|
||||
|
||||
announceSortChange(sortState: Sort) {
|
||||
if (sortState.direction) {
|
||||
this.liveAnnouncer.announce($localize`Sorted ${sortState.direction}ending`)
|
||||
} else {
|
||||
this.liveAnnouncer.announce($localize`Sorting cleared`)
|
||||
}
|
||||
}
|
||||
|
||||
getElectedMessage(elected: ElectionSource): string {
|
||||
switch(elected) {
|
||||
case ElectionSource.NOT_ELECTED:
|
||||
return $localize`Not elected`;
|
||||
case ElectionSource.CONSTITUENCY:
|
||||
return $localize`Constituency list vote order`;
|
||||
case ElectionSource.OVERALL_NOMINATION_ORDER:
|
||||
return $localize`District list position order`;
|
||||
case ElectionSource.OVERALL_VOTE_ORDER:
|
||||
return $localize`District list vote order`;
|
||||
default:
|
||||
console.error(elected);
|
||||
return 'oops';
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,15 +1,12 @@
|
|||
<table mat-table [dataSource]="dataSource" [hidden]="electedCandidates == null" matSort (matSortChange)="announceSortChange($event)">
|
||||
<ng-container matColumnDef="party">
|
||||
<th mat-header-cell mat-sort-header *matHeaderCellDef i18n
|
||||
i18n-sortActionDescription sortActionDescription="Sort by party">Party</th>
|
||||
<td mat-cell *matCellDef="let row">{{row.party}}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="seats">
|
||||
<th mat-header-cell mat-sort-header i18n-sortActionDescription sortActionDescription="Sort by seats"
|
||||
*matHeaderCellDef i18n>Seats</th>
|
||||
<td mat-cell *matCellDef="let row">{{row.seats}}</td>
|
||||
</ng-container>
|
||||
<div [hidden]="electedCandidates == null || parties == null">
|
||||
<h2 class="mat-headline-6" i18n>Seat allocation and elected candidates</h2>
|
||||
<mat-tab-group>
|
||||
<mat-tab i18n-label label="Seat allocation">
|
||||
<app-seat-allocation [parties]="parties" [electedCandidates]="electedCandidates"></app-seat-allocation>
|
||||
</mat-tab>
|
||||
<mat-tab *ngFor="let abbreviation of partyAbbreviations" i18n-label label="{{abbreviation}}">
|
||||
<app-elected-candidates [viewModel]="getViewModel(abbreviation)"></app-elected-candidates>
|
||||
</mat-tab>
|
||||
</mat-tab-group>
|
||||
</div>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="resultColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: resultColumns"></tr>
|
||||
</table>
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
[hidden] {
|
||||
display: none !important;
|
||||
}
|
|
@ -1,9 +1,8 @@
|
|||
import {AfterViewInit, Component, Input, ViewChild} from '@angular/core';
|
||||
import {Component, Input} from '@angular/core';
|
||||
import {Party} from "../model/party";
|
||||
import {ElectedCandidates} from "../model/elected-candidates";
|
||||
import {MatSort, Sort} from "@angular/material/sort";
|
||||
import {LiveAnnouncer} from "@angular/cdk/a11y";
|
||||
import {MatTableDataSource} from "@angular/material/table";
|
||||
import {ViewModel} from "../elected-candidates/elected-candidates.component";
|
||||
import {electedCandidates} from "../store";
|
||||
|
||||
export interface PartySeats {
|
||||
party: string;
|
||||
|
@ -15,50 +14,29 @@ export interface PartySeats {
|
|||
templateUrl: './election-result.component.html',
|
||||
styleUrls: ['./election-result.component.scss']
|
||||
})
|
||||
export class ElectionResultComponent implements AfterViewInit {
|
||||
@ViewChild(MatSort) sort: MatSort|null = null;
|
||||
resultColumns: string[] = ['party', 'seats'];
|
||||
seatsPerParty: PartySeats[] = [];
|
||||
dataSource: MatTableDataSource<PartySeats> = new MatTableDataSource<PartySeats>();
|
||||
export class ElectionResultComponent {
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.dataSource.sort = this.sort;
|
||||
}
|
||||
@Input() parties: Map<string, Party>|null = null;
|
||||
@Input() electedCandidates: ElectedCandidates | null = null;
|
||||
|
||||
@Input() parties: Map<string, Party> = new Map<string, Party>();
|
||||
|
||||
private _electedCandidates: ElectedCandidates | null = null;
|
||||
|
||||
get electedCandidates(): ElectedCandidates | null {
|
||||
return this._electedCandidates;
|
||||
}
|
||||
|
||||
@Input() set electedCandidates(value: ElectedCandidates | null) {
|
||||
this._electedCandidates = value;
|
||||
if (value != null) {
|
||||
const newSeatAllocations = [];
|
||||
for (const partyAbbreviation in value.seatAllocation) {
|
||||
if (!this.parties.has(partyAbbreviation)) {
|
||||
continue;
|
||||
}
|
||||
newSeatAllocations.push({
|
||||
party: this.parties.get(partyAbbreviation)!.abbreviation,
|
||||
seats: value.seatAllocation[partyAbbreviation]
|
||||
})
|
||||
}
|
||||
this.seatsPerParty = newSeatAllocations;
|
||||
this.dataSource.data = this.seatsPerParty;
|
||||
get partyAbbreviations() {
|
||||
if (this.electedCandidates == null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
constructor(private liveAnnouncer: LiveAnnouncer) {
|
||||
}
|
||||
|
||||
announceSortChange(sortState: Sort) {
|
||||
if (sortState.direction) {
|
||||
this.liveAnnouncer.announce($localize`Sorted ${sortState.direction}ending`)
|
||||
} else {
|
||||
this.liveAnnouncer.announce($localize`Sorting cleared`)
|
||||
const electedParties: string[] = [];
|
||||
for (const party in this.electedCandidates.seatAllocation) {
|
||||
electedParties.push(party);
|
||||
}
|
||||
return electedParties;
|
||||
}
|
||||
|
||||
getViewModel(partyAbbreviation: string): ViewModel|null {
|
||||
if (this.electedCandidates == null) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
partyAbbreviation,
|
||||
electedCandidates: this.electedCandidates
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -213,8 +213,6 @@ export class ElectionComponent implements OnInit {
|
|||
map.set(number, modifiedConstituencyResults[number]);
|
||||
}
|
||||
this.viewModel.electionResult.constituencyResults = map;
|
||||
|
||||
|
||||
this.valueChanges.emit(modifiedValue);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -17,6 +17,10 @@ import { ElectionContainerComponent } from './election-container/election-contai
|
|||
import {MatButtonModule} from "@angular/material/button";
|
||||
import { ElectionResultComponent } from './election-result/election-result.component';
|
||||
import {MatSortModule} from "@angular/material/sort";
|
||||
import {MatStepperModule} from "@angular/material/stepper";
|
||||
import { SeatAllocationComponent } from './seat-allocation/seat-allocation.component';
|
||||
import {MatIconModule} from "@angular/material/icon";
|
||||
import { ElectedCandidatesComponent } from './elected-candidates/elected-candidates.component';
|
||||
|
||||
|
||||
@NgModule({
|
||||
|
@ -24,7 +28,9 @@ import {MatSortModule} from "@angular/material/sort";
|
|||
LandingPageComponent,
|
||||
ElectionComponent,
|
||||
ElectionContainerComponent,
|
||||
ElectionResultComponent
|
||||
ElectionResultComponent,
|
||||
SeatAllocationComponent,
|
||||
ElectedCandidatesComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
|
@ -40,6 +46,8 @@ import {MatSortModule} from "@angular/material/sort";
|
|||
ReactiveFormsModule,
|
||||
MatButtonModule,
|
||||
MatSortModule,
|
||||
MatStepperModule,
|
||||
MatIconModule,
|
||||
],
|
||||
exports: [
|
||||
LandingPageComponent
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
<table mat-table [dataSource]="dataSource"
|
||||
matSort (matSortChange)="announceSortChange($event)">
|
||||
<ng-container matColumnDef="party">
|
||||
<th mat-header-cell mat-sort-header *matHeaderCellDef i18n
|
||||
i18n-sortActionDescription sortActionDescription="Sort by party">Party</th>
|
||||
<td mat-cell *matCellDef="let row">{{row.party}}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="seats">
|
||||
<th mat-header-cell mat-sort-header *matHeaderCellDef i18n
|
||||
i18n-sortActionDescription sortActionDescription="Sort by seats">Seats</th>
|
||||
<td mat-cell *matCellDef="let row">{{row.seats}}</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="resultColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: resultColumns"></tr>
|
||||
</table>
|
|
@ -0,0 +1,21 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SeatAllocationComponent } from './seat-allocation.component';
|
||||
|
||||
describe('SeatAllocationComponent', () => {
|
||||
let component: SeatAllocationComponent;
|
||||
let fixture: ComponentFixture<SeatAllocationComponent>;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [SeatAllocationComponent]
|
||||
});
|
||||
fixture = TestBed.createComponent(SeatAllocationComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,60 @@
|
|||
import {AfterViewInit, Component, Input, ViewChild} from '@angular/core';
|
||||
import {MatSort, Sort} from "@angular/material/sort";
|
||||
import {MatTableDataSource} from "@angular/material/table";
|
||||
import {PartySeats} from "../election-result/election-result.component";
|
||||
import {ElectedCandidates} from "../model/elected-candidates";
|
||||
import {Party} from "../model/party";
|
||||
import {LiveAnnouncer} from "@angular/cdk/a11y";
|
||||
|
||||
@Component({
|
||||
selector: 'app-seat-allocation',
|
||||
templateUrl: './seat-allocation.component.html',
|
||||
styleUrls: ['./seat-allocation.component.scss']
|
||||
})
|
||||
export class SeatAllocationComponent implements AfterViewInit {
|
||||
@ViewChild(MatSort) sort: MatSort|null = null;
|
||||
resultColumns: string[] = ['party', 'seats'];
|
||||
seatsPerParty: PartySeats[] = [];
|
||||
dataSource: MatTableDataSource<PartySeats> = new MatTableDataSource<PartySeats>();
|
||||
|
||||
@Input() parties: Map<string, Party>|null = null;
|
||||
|
||||
constructor(private liveAnnouncer: LiveAnnouncer) {
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.dataSource.sort = this.sort;
|
||||
}
|
||||
|
||||
announceSortChange(sortState: Sort) {
|
||||
if (sortState.direction) {
|
||||
this.liveAnnouncer.announce($localize`Sorted ${sortState.direction}ending`)
|
||||
} else {
|
||||
this.liveAnnouncer.announce($localize`Sorting cleared`)
|
||||
}
|
||||
}
|
||||
|
||||
private _electedCandidates: ElectedCandidates | null = null;
|
||||
|
||||
get electedCandidates(): ElectedCandidates | null {
|
||||
return this._electedCandidates;
|
||||
}
|
||||
|
||||
@Input() set electedCandidates(value: ElectedCandidates | null) {
|
||||
this._electedCandidates = value;
|
||||
if (value != null) {
|
||||
const newSeatAllocations = [];
|
||||
for (const partyAbbreviation in value.seatAllocation) {
|
||||
if (!this.parties!.has(partyAbbreviation)) {
|
||||
continue;
|
||||
}
|
||||
newSeatAllocations.push({
|
||||
party: this.parties!.get(partyAbbreviation)!.abbreviation,
|
||||
seats: value.seatAllocation[partyAbbreviation]
|
||||
})
|
||||
}
|
||||
this.seatsPerParty = newSeatAllocations;
|
||||
this.dataSource.data = this.seatsPerParty;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue