Skip to content

Commit

Permalink
feat(stark-ui): language-selector: implement component/module
Browse files Browse the repository at this point in the history
ISSUES CLOSED: #564
  • Loading branch information
RobbyDeLaet committed Sep 17, 2018
1 parent 6074dca commit 38cbd68
Show file tree
Hide file tree
Showing 27 changed files with 419 additions and 29 deletions.
1 change: 1 addition & 0 deletions packages/rollup.config.common-data.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const globals = {
"@angular/forms": "ng.forms",
"@angular/material": "ngMaterial",
"@angular/material/button": "ngMaterial.button",
"@angular/material/button-toggle": "ngMaterial.buttonToggle",
"@angular/material/checkbox": "ngMaterial.checkbox",
"@angular/material/core": "ngMaterial.core",
"@angular/material/dialog": "ngMaterial.dialog",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { select, Store } from "@ngrx/store";
import { StateObject } from "@uirouter/core";
import { validateSync } from "class-validator";
import { defer, Observable, Subject } from "rxjs";
import { map, take, distinct } from "rxjs/operators";
import { map, take, distinctUntilChanged } from "rxjs/operators";

import { STARK_LOGGING_SERVICE, StarkLoggingService } from "../../logging/services";
import { StarkSessionService, starkSessionServiceName } from "./session.service.intf";
Expand Down Expand Up @@ -326,14 +326,14 @@ export class StarkSessionServiceImpl implements StarkSessionService {
public getCurrentUser(): Observable<StarkUser | undefined> {
return this.session$.pipe(
map((session: StarkSession) => session.user),
distinct() // using distinct to make sure to only emit on unique values, to prevent infinite loops.
distinctUntilChanged() // using distinctUntilChanged to make sure to only emit on values different from the last one, to prevent infinite loops.
);
}

public getCurrentLanguage(): Observable<string> {
return this.session$.pipe(
map((session: StarkSession) => session.currentLanguage),
distinct() // using distinct to make sure to only emit on unique values, to prevent infinite loops.
distinctUntilChanged() // using distinctUntilChanged to make sure to only emit on values different from the last one, to prevent infinite loops.
);
}

Expand All @@ -342,7 +342,7 @@ export class StarkSessionServiceImpl implements StarkSessionService {
this.store.dispatch(new StarkChangeLanguage(newLanguage));

defer(() => this.translateService.use(newLanguage)).subscribe(
(languageId: string) => this.store.dispatch(new StarkChangeLanguageSuccess(languageId)),
(_translations: any) => this.store.dispatch(new StarkChangeLanguageSuccess(newLanguage)),
(error: any) => this.store.dispatch(new StarkChangeLanguageFailure(error))
);
}
Expand Down
27 changes: 15 additions & 12 deletions packages/stark-ui/assets/styles/_header.scss
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,13 @@
display: flex;
flex-direction: row-reverse;
width: 50%;

.stark-language-selector {
width: 45px;
}

> div {
align-items: center;
display: flex;
justify-content: flex-end;
margin-bottom: 5px;
Expand Down Expand Up @@ -98,8 +104,17 @@
.stark-app-bar-content-right {
flex-direction: column;
width: 33%;

.stark-language-selector {
width: 100px;
}

> div {
&.stark-app-bar-content-right-center {
margin-top: -10px;
}
&.stark-app-bar-content-right-actions-alt {
margin-top: -10px;
position: inherit;
}
}
Expand Down Expand Up @@ -139,19 +154,7 @@
width: 290px;
}

.dropdown-lang {
background-color: rgba($color: #000, $alpha: 0.4);
display: flex;
justify-content: center;
align-items: center;
height: 46px;
width: 45px;
}

@media #{$tablet-query} {
.dropdown-lang {
width: 100px;
}
}

.stark-app-header {
Expand Down
2 changes: 1 addition & 1 deletion packages/stark-ui/src/common/classes/abstract-component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export abstract class AbstractStarkUiComponent implements OnInit {
* Color theme
*/
@Input()
protected color?: string;
public color?: string; // Needs to be public for Angular to be able to read this property inside the template.

protected constructor(protected renderer: Renderer2, protected elementRef: ElementRef) {}

Expand Down
2 changes: 2 additions & 0 deletions packages/stark-ui/src/modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export * from "./modules/keyboard-directives";
export * from "./modules/date-picker";
export * from "./modules/date-range-picker";
export * from "./modules/dropdown";
export * from "./modules/keyboard-directives";
export * from "./modules/language-selector";
export * from "./modules/pretty-print";
export * from "./modules/slider";
export * from "./modules/svg-view-box";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,20 @@
margin: 0;
}

.mat-form-field-infix {
.mat-form-field {
width: 150px;
}

&.stark-full-width {
.mat-form-field-infix {
.mat-form-field {
width: 100%;
}
}
}

@media #{$tablet-only-query} {
.stark-dropdown {
.mat-form-field-infix {
.mat-form-field {
width: 300px;
}
}
Expand All @@ -30,7 +30,7 @@

@media #{$desktop-query} {
.stark-dropdown {
.mat-form-field-infix {
.mat-form-field {
width: 350px;
}
}
Expand Down
2 changes: 2 additions & 0 deletions packages/stark-ui/src/modules/language-selector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./language-selector/language-selector.module";
export * from "./language-selector/components";
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./components/language-selector.component";
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/* ============================================================================== */
/* S t a r k L a n g u a g e S e l e c t o r */
/* ============================================================================== */
/* stark: src/modules/language-selector/components/_language-selector.component.scss */

.stark-language-selector {
&.stark-full-width {
.stark-dropdown {
.mat-form-field {
width: 100%;
}
}
}
}

/* END stark: src/modules/language-selector/components/_language-selector.component.scss */
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<div *ngIf="mode === 'dropdown'">
<stark-dropdown
dropdownId="languageDropdown"
[options]="appMetadata.supportedLanguages"
[value]="selectedLanguage"
[color]="color"
placeholder=""
optionIdProperty="code"
optionLabelProperty="translationKey"
(dropdownSelectionChanged)="changeLanguage($event)">
</stark-dropdown>
</div>
<div *ngIf="mode === 'toolbar'">
<mat-button-toggle-group name="languageSelector" [value]="selectedLanguage">
<mat-button-toggle *ngFor="let language of appMetadata.supportedLanguages; trackBy: trackLanguage"
[id]="languageSelectorId + '-' + language.isoCode"
[value]="language.code"
(click)="changeLanguage(language.code)"
>{{ language.code }}</mat-button-toggle>
</mat-button-toggle-group>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/* tslint:disable:completed-docs */
/* angular imports */
import { Component, ViewChild } from "@angular/core";
import { async, ComponentFixture, TestBed } from "@angular/core/testing";
import { MatButtonToggleModule } from "@angular/material/button-toggle";
import { DateAdapter } from "@angular/material/core";
import { CommonModule } from "@angular/common";
import { TranslateModule } from "@ngx-translate/core";

import Spy = jasmine.Spy;

/* stark-core imports */
import {
STARK_APP_METADATA,
STARK_LOGGING_SERVICE,
STARK_SESSION_SERVICE,
StarkApplicationMetadata,
StarkApplicationMetadataImpl,
StarkLanguages,
StarkSessionService
} from "@nationalbankbelgium/stark-core";

import { MockStarkLoggingService, MockStarkSessionService } from "@nationalbankbelgium/stark-core/testing";

/* stark-ui imports */
import { StarkLanguageSelectorComponent, StarkLanguageSelectorMode } from "./language-selector.component";
import { StarkDropdownModule } from "../../dropdown";
import { of } from "rxjs";
import { throwError } from "rxjs/index";

/***
* To be able to test changes to the input fields, the Language-Selector component is hosted inside the TestComponentHost class.
*/
@Component({
selector: `host-component`,
template: `
<stark-language-selector mode="mode"></stark-language-selector>`
})
class TestHostComponent {
@ViewChild(StarkLanguageSelectorComponent)
public languageSelectorComponent: StarkLanguageSelectorComponent;

public mode: StarkLanguageSelectorMode;
}

describe("StarkLanguageSelectorComponent", () => {
let component: StarkLanguageSelectorComponent;
let hostComponent: TestHostComponent;
let hostFixture: ComponentFixture<TestHostComponent>;

let appMetadata: StarkApplicationMetadata;
appMetadata = new StarkApplicationMetadataImpl();
appMetadata.supportedLanguages = [StarkLanguages.EN_US, StarkLanguages.FR_BE, StarkLanguages.NL_BE];

describe("on initialization", () => {
const mockSessionService: StarkSessionService = new MockStarkSessionService();
(<Spy>mockSessionService.getCurrentLanguage).and.returnValue(of("fr"));

beforeEach(async(() => {
return compileComponent(mockSessionService);
}));

beforeEach(() => {
initializeComponent();
});

it("should set internal component properties", () => {
expect(hostFixture).toBeDefined();
expect(component).toBeDefined();

expect(component.logger).not.toBeNull();
expect(component.logger).toBeDefined();
});

it("should have inputs", () => {
expect(component.languageSelectorId).toBeDefined();
expect(component.mode).toBeDefined();
expect(component.selectedLanguage).toBeDefined();
});
});

describe("on failing initialization", () => {
const mockSessionService: StarkSessionService = new MockStarkSessionService();
(<Spy>mockSessionService.getCurrentLanguage).and.returnValue(throwError("dummy-error"));

beforeEach(async(() => {
return compileComponent(mockSessionService);
}));

beforeEach(() => {
initializeComponent();
});

it("should log an error when the sessionService.getCurrentLanguage fails", () => {
expect(component.logger).toBeDefined();
expect(component.logger.error).toHaveBeenCalledTimes(1);
});
});

describe("on changeLanguage", () => {
const mockSessionService: StarkSessionService = new MockStarkSessionService();
(<Spy>mockSessionService.getCurrentLanguage).and.returnValue(of("fr"));

beforeEach(async(() => {
return compileComponent(mockSessionService);
}));

beforeEach(() => {
initializeComponent();
});

it("should change the selected language", () => {
expect(component.selectedLanguage).toBe("fr");
component.changeLanguage("nl");
expect(component.selectedLanguage).toBe("nl");
component.changeLanguage("en");
expect(component.selectedLanguage).toBe("en");
component.changeLanguage("fr");
expect(component.selectedLanguage).toBe("fr");
component.changeLanguage("en");
expect(component.selectedLanguage).toBe("en");
});
});

/***
* This function contains the component compilation code
* Instead of repeating the code, it is placed in a separate function
* @param mockSessionService
*/
function compileComponent(mockSessionService: StarkSessionService): Promise<any> {
return TestBed.configureTestingModule({
imports: [CommonModule, MatButtonToggleModule, StarkDropdownModule, TranslateModule.forRoot()],
declarations: [StarkLanguageSelectorComponent, TestHostComponent],
providers: [
{ provide: STARK_APP_METADATA, useValue: appMetadata },
{ provide: STARK_LOGGING_SERVICE, useValue: new MockStarkLoggingService() },
{ provide: STARK_SESSION_SERVICE, useValue: mockSessionService },
DateAdapter
]
}).compileComponents();
}

/***
* This function contains the component initialization code
* Instead of repeating the code, it is placed in a separate function
*/
function initializeComponent(): void {
hostFixture = TestBed.createComponent(TestHostComponent);
hostComponent = hostFixture.componentInstance;
hostFixture.detectChanges(); // trigger initial data binding
component = hostComponent.languageSelectorComponent;
}
});
Loading

0 comments on commit 38cbd68

Please sign in to comment.