forked from NationalBankBelgium/stark
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #545 from christophercr/feature/onEnterKey-directive
feat(stark-ui): implement OnEnterKey directive and module
- Loading branch information
Showing
19 changed files
with
427 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from "./keyboard-directives/keyboard-directives.module"; | ||
export * from "./keyboard-directives/directives"; |
1 change: 1 addition & 0 deletions
1
packages/stark-ui/src/modules/keyboard-directives/directives.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from "./directives/on-enter-key.directive"; |
109 changes: 109 additions & 0 deletions
109
packages/stark-ui/src/modules/keyboard-directives/directives/on-enter-key.directive.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
/*tslint:disable:completed-docs*/ | ||
import { ComponentFixture, fakeAsync, TestBed } from "@angular/core/testing"; | ||
import { Component, DebugElement } from "@angular/core"; | ||
import { By } from "@angular/platform-browser"; | ||
import { STARK_LOGGING_SERVICE } from "@nationalbankbelgium/stark-core"; | ||
import { MockStarkLoggingService } from "@nationalbankbelgium/stark-core/testing"; | ||
import { StarkOnEnterKeyDirective } from "./on-enter-key.directive"; | ||
import Spy = jasmine.Spy; | ||
import createSpy = jasmine.createSpy; | ||
|
||
describe("OnEnterKeyDirective", () => { | ||
@Component({ | ||
selector: "test-component", | ||
template: getTemplate("starkOnEnterKey") | ||
}) | ||
class TestComponent { | ||
public onEnterKeyHandler: Spy = createSpy("onEnterKeyHandlerSpy"); | ||
|
||
public onEnterKeyParam: object = { id: "123" }; | ||
} | ||
|
||
let fixture: ComponentFixture<TestComponent>; | ||
|
||
function getTemplate(onEnterKeyDirective: string): string { | ||
return "<input " + "type='text' " + onEnterKeyDirective + ">"; | ||
} | ||
|
||
function initializeComponentFixture(): void { | ||
fixture = TestBed.createComponent(TestComponent); | ||
// trigger initial data binding | ||
fixture.detectChanges(); | ||
} | ||
|
||
function triggerKeyPressEvent(inputElement: DebugElement, keyCode: number): void { | ||
const keypressEvent: Event = document.createEvent("Event"); | ||
keypressEvent.initEvent("keypress", true, true); | ||
keypressEvent["which"] = keyCode; | ||
inputElement.triggerEventHandler("keypress", keypressEvent); | ||
} | ||
|
||
beforeEach(() => { | ||
TestBed.configureTestingModule({ | ||
declarations: [StarkOnEnterKeyDirective, TestComponent], | ||
providers: [{ provide: STARK_LOGGING_SERVICE, useValue: new MockStarkLoggingService() }] | ||
}); | ||
}); | ||
|
||
describe("when onEnterKeyHandler is not defined", () => { | ||
beforeEach( | ||
fakeAsync(() => { | ||
// compile template and css | ||
return TestBed.compileComponents(); | ||
}) | ||
); | ||
|
||
beforeEach(() => { | ||
initializeComponentFixture(); | ||
}); | ||
|
||
it("should not do anything when the enter key is pressed and the enter key handler was not provided", () => { | ||
expect(fixture).toBeDefined(); | ||
const hostComponent: TestComponent = fixture.componentInstance; | ||
|
||
const inputElement: DebugElement = fixture.debugElement.query(By.css("input")); | ||
triggerKeyPressEvent(inputElement, 13); | ||
|
||
expect(hostComponent.onEnterKeyHandler).not.toHaveBeenCalled(); | ||
}); | ||
}); | ||
|
||
describe("when onEnterKeyHandler is given", () => { | ||
// overriding the components's template | ||
beforeEach( | ||
fakeAsync(() => { | ||
const newTemplate: string = getTemplate( | ||
"[starkOnEnterKey]='onEnterKeyHandler' [starkOnEnterKeyParams]='[onEnterKeyParam]'" | ||
); | ||
|
||
TestBed.overrideTemplate(TestComponent, newTemplate); | ||
|
||
// compile template and css | ||
return TestBed.compileComponents(); | ||
}) | ||
); | ||
|
||
beforeEach(() => { | ||
initializeComponentFixture(); | ||
}); | ||
|
||
it("should call the given function when Enter key is pressed", () => { | ||
const hostComponent: TestComponent = fixture.componentInstance; | ||
|
||
const inputElement: DebugElement = fixture.debugElement.query(By.css("input")); | ||
triggerKeyPressEvent(inputElement, 13); | ||
|
||
expect(hostComponent.onEnterKeyHandler).toHaveBeenCalledTimes(1); | ||
expect(hostComponent.onEnterKeyHandler).toHaveBeenCalledWith(hostComponent.onEnterKeyParam); | ||
}); | ||
|
||
it("should not call the given function when a key other than Enter is pressed", () => { | ||
const hostComponent: TestComponent = fixture.componentInstance; | ||
|
||
const inputElement: DebugElement = fixture.debugElement.query(By.css("input")); | ||
triggerKeyPressEvent(inputElement, 1); | ||
|
||
expect(hostComponent.onEnterKeyHandler).not.toHaveBeenCalled(); | ||
}); | ||
}); | ||
}); |
69 changes: 69 additions & 0 deletions
69
packages/stark-ui/src/modules/keyboard-directives/directives/on-enter-key.directive.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import { Directive, HostListener, Inject, Input, OnInit } from "@angular/core"; | ||
import { STARK_LOGGING_SERVICE, StarkLoggingService } from "@nationalbankbelgium/stark-core"; | ||
|
||
/** | ||
* Name of the directive | ||
*/ | ||
const directiveName: string = "[starkOnEnterKey]"; | ||
|
||
/** | ||
* Directive that allows the execution of a certain callback whenever the user presses the Enter key in a field. | ||
* | ||
* This directive is used essentially for input tags. | ||
* | ||
* `IMPORTANT:` This directive will have its own context so any properties/methods of the host component where this directive is used will not be accessible. | ||
* In case you want to access any variable or method of your component where you include this directive from the callback function, | ||
* you should bind the component's "this" context to the callback function passed to the directive with the "bind()" method. | ||
* | ||
* For example: | ||
* ```html | ||
* <input name="test" [starkOnEnterKey]="yourCallbackFn.bind(this)" [starkOnEnterKeyParams]="['someValue']"> | ||
* ``` | ||
* | ||
* Then in your component your callback could be defined like this: | ||
* ```typescript | ||
* public yourCallbackFn(paramValue: string): void { | ||
* this.someProperty; // is the value of the component's "someProperty" due to the ".bind(this)" in the callback passed to the directive, otherwise it would be "undefined"! | ||
* paramValue; // is "someValue" since it was a literal param passed via the [starkOnEnterKeyParams] input | ||
* } | ||
* ``` | ||
*/ | ||
@Directive({ | ||
selector: directiveName | ||
}) | ||
export class StarkOnEnterKeyDirective implements OnInit { | ||
/** | ||
* Callback function to be triggered on every Enter key press in the field | ||
*/ | ||
/* tslint:disable:no-input-rename */ | ||
@Input("starkOnEnterKey") public onEnterKeyHandler: Function; | ||
|
||
/** | ||
* Parameters to be passed to the specified callback function | ||
*/ | ||
@Input("starkOnEnterKeyParams") public onEnterKeyParams: any[]; | ||
|
||
/** | ||
* Event handler to be invoked on a "keypress" event in the field | ||
*/ | ||
@HostListener("keypress", ["$event"]) | ||
public eventHandler(event: KeyboardEvent): void { | ||
if (this.onEnterKeyHandler && event.which === 13) { | ||
this.onEnterKeyHandler(...this.onEnterKeyParams); | ||
event.preventDefault(); | ||
} | ||
} | ||
|
||
/** | ||
* Class constructor | ||
* @param logger - The logger of the application | ||
*/ | ||
public constructor(@Inject(STARK_LOGGING_SERVICE) private logger: StarkLoggingService) {} | ||
|
||
/** | ||
* Directive lifecycle hook | ||
*/ | ||
public ngOnInit(): void { | ||
this.logger.debug(directiveName + ": directive initialized"); | ||
} | ||
} |
8 changes: 8 additions & 0 deletions
8
packages/stark-ui/src/modules/keyboard-directives/keyboard-directives.module.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { NgModule } from "@angular/core"; | ||
import { StarkOnEnterKeyDirective } from "./directives"; | ||
|
||
@NgModule({ | ||
declarations: [StarkOnEnterKeyDirective], | ||
exports: [StarkOnEnterKeyDirective] | ||
}) | ||
export class StarkKeyboardDirectivesModule {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,31 +1,52 @@ | ||
import { MatButtonModule, MatCardModule, MatIconModule, MatTabsModule, MatTooltipModule, MatSnackBarModule } from "@angular/material"; | ||
import { | ||
MatButtonModule, | ||
MatCardModule, | ||
MatIconModule, | ||
MatTabsModule, | ||
MatTooltipModule, | ||
MatSnackBarModule, | ||
MatFormFieldModule, | ||
MatInputModule | ||
} from "@angular/material"; | ||
import { CommonModule } from "@angular/common"; | ||
import { NgModule } from "@angular/core"; | ||
import { FormsModule } from "@angular/forms"; | ||
import { TranslateModule } from "@ngx-translate/core"; | ||
import { ActionBarComponent } from "./action-bar/action-bar.component"; | ||
import { ButtonComponent } from "./button/button.component"; | ||
import { ExampleViewerComponent } from "./example-viewer/example-viewer.component"; | ||
import { KeyboardDirectivesComponent } from "./keyboard-directives/keyboard-directives.component"; | ||
import { TableComponent } from "./table/table.component"; | ||
import { SharedModule } from "../shared/shared.module"; | ||
import { StarkActionBarModule, StarkSliderModule, StarkTableModule, StarkSvgViewBoxModule } from "@nationalbankbelgium/stark-ui"; | ||
import { | ||
StarkActionBarModule, | ||
StarkSliderModule, | ||
StarkTableModule, | ||
StarkSvgViewBoxModule, | ||
StarkKeyboardDirectivesModule | ||
} from "@nationalbankbelgium/stark-ui"; | ||
|
||
@NgModule({ | ||
imports: [ | ||
MatButtonModule, | ||
MatCardModule, | ||
MatFormFieldModule, | ||
MatIconModule, | ||
MatInputModule, | ||
MatTooltipModule, | ||
MatSnackBarModule, | ||
MatTabsModule, | ||
CommonModule, | ||
FormsModule, | ||
TranslateModule, | ||
SharedModule, | ||
StarkActionBarModule, | ||
StarkSliderModule, | ||
StarkSvgViewBoxModule, | ||
StarkKeyboardDirectivesModule, | ||
StarkTableModule | ||
], | ||
declarations: [ActionBarComponent, ButtonComponent, ExampleViewerComponent, TableComponent], | ||
exports: [ActionBarComponent, ButtonComponent, ExampleViewerComponent, TableComponent] | ||
declarations: [ActionBarComponent, ButtonComponent, ExampleViewerComponent, KeyboardDirectivesComponent, TableComponent], | ||
exports: [ActionBarComponent, ButtonComponent, ExampleViewerComponent, KeyboardDirectivesComponent, TableComponent] | ||
}) | ||
export class DemoModule {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
export * from "./action-bar"; | ||
export * from "./button"; | ||
export * from "./example-viewer"; | ||
export * from "./keyboard-directives"; | ||
export * from "./table"; | ||
export * from "./demo.module"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from "./keyboard-directives.component"; |
27 changes: 27 additions & 0 deletions
27
showcase/src/app/demo/keyboard-directives/keyboard-directives.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
<example-viewer [extensions]="['HTML', 'TS']" filesPath="keyboard-directives/on-enter-key-directive" | ||
title="SHOWCASE.DEMO.KEYBOARD_DIRECTIVES.ON_ENTER_KEY_DIRECTIVE"> | ||
<form class="on-enter-key-directive-form"> | ||
<p translate>SHOWCASE.DEMO.KEYBOARD_DIRECTIVES.DESCRIPTION</p> | ||
<mat-form-field> | ||
<mat-label translate>SHOWCASE.DEMO.KEYBOARD_DIRECTIVES.INPUT_WITH_CONTEXT</mat-label> | ||
<input name="test" [(ngModel)]="inputValue1" matInput placeholder="{{ 'SHOWCASE.DEMO.KEYBOARD_DIRECTIVES.TYPE_AND_PRESS_ENTER' | translate }}" | ||
[starkOnEnterKey]="onEnterKeyCallback.bind(this)" | ||
[starkOnEnterKeyParams]="['input1', inputValue1, 123, { prop: 'someValue' }]"> | ||
</mat-form-field> | ||
<mat-form-field> | ||
<mat-label translate>SHOWCASE.DEMO.KEYBOARD_DIRECTIVES.INPUT_WITHOUT_CONTEXT</mat-label> | ||
<input name="test" [(ngModel)]="inputValue2" matInput | ||
placeholder="{{ 'SHOWCASE.DEMO.KEYBOARD_DIRECTIVES.TYPE_PRESS_ENTER_AND_CHECK_CONSOLE' | translate }}" | ||
[starkOnEnterKey]="onEnterKeyCallback" | ||
[starkOnEnterKeyParams]="['input2', inputValue2, 123, { prop: 'someValue' }]"> | ||
</mat-form-field> | ||
<mat-form-field> | ||
<mat-label translate>SHOWCASE.DEMO.KEYBOARD_DIRECTIVES.INPUT_WITHOUT_CONTEXT_ALTERNATIVE</mat-label> | ||
<input name="test" [(ngModel)]="inputValue3" matInput placeholder="{{ 'SHOWCASE.DEMO.KEYBOARD_DIRECTIVES.TYPE_AND_PRESS_ENTER' | translate }}" | ||
[starkOnEnterKey]="onEnterKeyCallback" | ||
[starkOnEnterKeyParams]="['input3', inputValue3, 123, { prop: 'someValue' }, this]"> | ||
</mat-form-field> | ||
</form> | ||
<label>Logging:</label> | ||
<pre><code>{{ logging }}</code></pre> | ||
</example-viewer> |
11 changes: 11 additions & 0 deletions
11
showcase/src/app/demo/keyboard-directives/keyboard-directives.component.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
.on-enter-key-directive-form { | ||
display: flex; | ||
flex-direction: column; | ||
} | ||
|
||
pre code { | ||
display: block; | ||
height: 200px; | ||
background-color: #cfcfcf; | ||
overflow: auto; | ||
} |
67 changes: 67 additions & 0 deletions
67
showcase/src/app/demo/keyboard-directives/keyboard-directives.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import { Component, Inject, OnInit } from "@angular/core"; | ||
import { STARK_LOGGING_SERVICE, StarkLoggingService } from "@nationalbankbelgium/stark-core"; | ||
|
||
@Component({ | ||
selector: "showcase-demo-on-enter-key", | ||
styleUrls: ["./keyboard-directives.component.scss"], | ||
templateUrl: "./keyboard-directives.component.html" | ||
}) | ||
export class KeyboardDirectivesComponent implements OnInit { | ||
public latestInputValue: string; | ||
public inputValue1: string; | ||
public inputValue2: string; | ||
public inputValue3: string; | ||
|
||
public logging: string; | ||
|
||
public constructor(@Inject(STARK_LOGGING_SERVICE) private logger: StarkLoggingService) {} | ||
|
||
public ngOnInit(): void { | ||
this.latestInputValue = ""; | ||
this.inputValue1 = ""; | ||
this.inputValue2 = ""; | ||
this.inputValue3 = ""; | ||
this.logging = ""; | ||
} | ||
|
||
public onEnterKeyCallback(...paramValues: any[]): void { | ||
switch (paramValues[0]) { | ||
case "input1": | ||
this.updateLogging(this.inputValue1, paramValues); | ||
break; | ||
case "input2": | ||
// this.updateLogging() is not accessible since this onEnterKeyCallback is called without context | ||
let callbackLogging: string = `Callback triggered from ${paramValues[0]}!`; | ||
callbackLogging += ` - Passed params: ${paramValues}`; | ||
callbackLogging += ` - Component this.latestInputValue property: '${this.latestInputValue}'\n`; | ||
// however this.logger is accessible because the OnEnterKey directive has also the "logger" service injected | ||
this.logger.debug(callbackLogging); | ||
break; | ||
case "input3": | ||
// this.updateLogging() is not accessible since this onEnterKeyCallback is called without context | ||
if (paramValues.length === 5) { | ||
// the context of the host component was passed in the last parameter | ||
// so we can access all properties/methods from the component | ||
const parentComponentContext: KeyboardDirectivesComponent = paramValues[4]; | ||
parentComponentContext.updateLogging(parentComponentContext.inputValue3, paramValues); | ||
} | ||
break; | ||
default: | ||
break; | ||
} | ||
} | ||
|
||
public updateLogging(inputValue: string, paramValues: any[]): void { | ||
if (typeof this.latestInputValue !== "undefined") { | ||
this.latestInputValue = inputValue; | ||
} | ||
|
||
let callbackLogging: string = `Callback triggered from ${paramValues[0]}!`; | ||
callbackLogging += ` - Passed params: ${paramValues}`; | ||
callbackLogging += ` - Component this.latestInputValue property: '${this.latestInputValue}'\n`; | ||
|
||
// this won't appear in the view when no context is passed to this callback function | ||
this.logging += callbackLogging; | ||
this.logger.debug(callbackLogging); | ||
} | ||
} |
Oops, something went wrong.