-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(stark-ui): implement RestrictInput directive and module
- Loading branch information
1 parent
d82a11e
commit b8bd5be
Showing
11 changed files
with
325 additions
and
32 deletions.
There are no files selected for viewing
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 |
---|---|---|
@@ -1 +1,2 @@ | ||
export * from "./directives/on-enter-key.directive"; | ||
export * from "./directives/restrict-input.directive"; |
122 changes: 122 additions & 0 deletions
122
...ages/stark-ui/src/modules/keyboard-directives/directives/restrict-input.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,122 @@ | ||
/*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 { StarkRestrictInputDirective } from "./restrict-input.directive"; | ||
import Spy = jasmine.Spy; | ||
import createSpy = jasmine.createSpy; | ||
|
||
describe("RestrictInputDirective", () => { | ||
@Component({ | ||
selector: "test-component", | ||
template: getTemplate("starkRestrictInput") | ||
}) | ||
class TestComponent { | ||
public onEnterKeyHandler: Spy = createSpy("onEnterKeyHandlerSpy"); | ||
} | ||
|
||
let fixture: ComponentFixture<TestComponent>; | ||
|
||
function getTemplate(restrictInputDirective: string): string { | ||
return "<input " + "type='text' " + restrictInputDirective + ">"; | ||
} | ||
|
||
function initializeComponentFixture(): void { | ||
fixture = TestBed.createComponent(TestComponent); | ||
// trigger initial data binding | ||
fixture.detectChanges(); | ||
} | ||
|
||
function triggerKeyPressEvent(inputElement: DebugElement, value: string): KeyboardEvent { | ||
(<HTMLInputElement>inputElement.nativeElement).value = value; | ||
|
||
const keypressEvent: Event = document.createEvent("Event"); | ||
keypressEvent.initEvent("keypress", true, true); | ||
keypressEvent["char"] = value; | ||
keypressEvent["charCode"] = value.charCodeAt(0); | ||
inputElement.triggerEventHandler("keypress", keypressEvent); | ||
|
||
return <KeyboardEvent>keypressEvent; | ||
} | ||
|
||
beforeEach(() => { | ||
TestBed.configureTestingModule({ | ||
declarations: [StarkRestrictInputDirective, TestComponent], | ||
providers: [{ provide: STARK_LOGGING_SERVICE, useValue: new MockStarkLoggingService() }] | ||
}); | ||
}); | ||
|
||
describe("when input restriction is not defined", () => { | ||
beforeEach( | ||
fakeAsync(() => { | ||
// compile template and css | ||
return TestBed.compileComponents(); | ||
}) | ||
); | ||
|
||
beforeEach(() => { | ||
initializeComponentFixture(); | ||
}); | ||
|
||
it("should NOT prevent any value from being typed in the input when no input restriction was provided", () => { | ||
expect(fixture).toBeDefined(); | ||
const inputElement: DebugElement = fixture.debugElement.query(By.css("input")); | ||
|
||
let keyPressEvent: Event = triggerKeyPressEvent(inputElement, "1"); | ||
expect(keyPressEvent.defaultPrevented).toBe(false); | ||
|
||
keyPressEvent = triggerKeyPressEvent(inputElement, "9"); | ||
expect(keyPressEvent.defaultPrevented).toBe(false); | ||
|
||
keyPressEvent = triggerKeyPressEvent(inputElement, "0"); | ||
expect(keyPressEvent.defaultPrevented).toBe(false); | ||
}); | ||
}); | ||
|
||
describe("when input restriction is given", () => { | ||
// overriding the components's template | ||
beforeEach( | ||
fakeAsync(() => { | ||
// the directive should not be used with square brackets "[]" because the input is an string literal! | ||
const newTemplate: string = getTemplate("starkRestrictInput='\\d'"); | ||
|
||
TestBed.overrideTemplate(TestComponent, newTemplate); | ||
|
||
// compile template and css | ||
return TestBed.compileComponents(); | ||
}) | ||
); | ||
|
||
beforeEach(() => { | ||
initializeComponentFixture(); | ||
}); | ||
|
||
it("should prevent any value other than the given ones in the configuration from being typed in the input", () => { | ||
const inputElement: DebugElement = fixture.debugElement.query(By.css("input")); | ||
|
||
let keyPressEvent: KeyboardEvent = triggerKeyPressEvent(inputElement, "a"); | ||
expect(keyPressEvent.defaultPrevented).toBe(true); | ||
|
||
keyPressEvent = triggerKeyPressEvent(inputElement, "B"); | ||
expect(keyPressEvent.defaultPrevented).toBe(true); | ||
|
||
keyPressEvent = triggerKeyPressEvent(inputElement, "-"); | ||
expect(keyPressEvent.defaultPrevented).toBe(true); | ||
}); | ||
|
||
it("should NOT prevent any of the values given in the configuration from being typed in the input", () => { | ||
const inputElement: DebugElement = fixture.debugElement.query(By.css("input")); | ||
|
||
let keyPressEvent: Event = triggerKeyPressEvent(inputElement, "1"); | ||
expect(keyPressEvent.defaultPrevented).toBe(false); | ||
|
||
keyPressEvent = triggerKeyPressEvent(inputElement, "9"); | ||
expect(keyPressEvent.defaultPrevented).toBe(false); | ||
|
||
keyPressEvent = triggerKeyPressEvent(inputElement, "0"); | ||
expect(keyPressEvent.defaultPrevented).toBe(false); | ||
}); | ||
}); | ||
}); |
56 changes: 56 additions & 0 deletions
56
packages/stark-ui/src/modules/keyboard-directives/directives/restrict-input.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,56 @@ | ||
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 = "[starkRestrictInput]"; | ||
|
||
/** | ||
* Directive to restrict the characters that can be typed in a field to allow only those matching a regex pattern. | ||
*/ | ||
@Directive({ | ||
selector: directiveName | ||
}) | ||
export class StarkRestrictInputDirective implements OnInit { | ||
/** | ||
* A valid regular expression that defines the allowed characters | ||
*/ | ||
/* tslint:disable:no-input-rename */ | ||
@Input("starkRestrictInput") public inputRestriction: string; | ||
|
||
/** | ||
* Event handler to be invoked on a "keypress" event in the field | ||
*/ | ||
@HostListener("keypress", ["$event"]) | ||
public eventHandler(event: KeyboardEvent): boolean { | ||
const regularExpression: string = this.inputRestriction || ""; | ||
|
||
if (regularExpression) { | ||
const key: string = String.fromCharCode(!event.charCode ? event.which : event.charCode); | ||
const regex: RegExp = new RegExp(regularExpression); | ||
|
||
if (!regex.test(key)) { | ||
event.preventDefault(); | ||
return false; | ||
} | ||
} else { | ||
this.logger.warn(directiveName + ": no input restriction defined"); | ||
} | ||
|
||
return true; | ||
} | ||
|
||
/** | ||
* 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"); | ||
} | ||
} |
6 changes: 3 additions & 3 deletions
6
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 |
---|---|---|
@@ -1,8 +1,8 @@ | ||
import { NgModule } from "@angular/core"; | ||
import { StarkOnEnterKeyDirective } from "./directives"; | ||
import { StarkOnEnterKeyDirective, StarkRestrictInputDirective } from "./directives"; | ||
|
||
@NgModule({ | ||
declarations: [StarkOnEnterKeyDirective], | ||
exports: [StarkOnEnterKeyDirective] | ||
declarations: [StarkOnEnterKeyDirective, StarkRestrictInputDirective], | ||
exports: [StarkOnEnterKeyDirective, StarkRestrictInputDirective] | ||
}) | ||
export class StarkKeyboardDirectivesModule {} |
52 changes: 44 additions & 8 deletions
52
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 |
---|---|---|
@@ -1,27 +1,63 @@ | ||
<example-viewer [extensions]="['HTML', 'TS']" filesPath="keyboard-directives/on-enter-key-directive" | ||
title="SHOWCASE.DEMO.KEYBOARD_DIRECTIVES.ON_ENTER_KEY_DIRECTIVE"> | ||
title="SHOWCASE.DEMO.KEYBOARD_DIRECTIVES.ON_ENTER_KEY.TITLE"> | ||
<form class="on-enter-key-directive-form"> | ||
<p translate>SHOWCASE.DEMO.KEYBOARD_DIRECTIVES.DESCRIPTION</p> | ||
<p translate>SHOWCASE.DEMO.KEYBOARD_DIRECTIVES.ON_ENTER_KEY.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 }}" | ||
<mat-label translate>SHOWCASE.DEMO.KEYBOARD_DIRECTIVES.ON_ENTER_KEY.INPUT_WITH_CONTEXT</mat-label> | ||
<input name="test" [(ngModel)]="inputValue1" matInput | ||
placeholder="{{ 'SHOWCASE.DEMO.KEYBOARD_DIRECTIVES.ON_ENTER_KEY.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> | ||
<mat-label translate>SHOWCASE.DEMO.KEYBOARD_DIRECTIVES.ON_ENTER_KEY.INPUT_WITHOUT_CONTEXT</mat-label> | ||
<input name="test" [(ngModel)]="inputValue2" matInput | ||
placeholder="{{ 'SHOWCASE.DEMO.KEYBOARD_DIRECTIVES.TYPE_PRESS_ENTER_AND_CHECK_CONSOLE' | translate }}" | ||
placeholder="{{ 'SHOWCASE.DEMO.KEYBOARD_DIRECTIVES.ON_ENTER_KEY.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 }}" | ||
<mat-label translate>SHOWCASE.DEMO.KEYBOARD_DIRECTIVES.ON_ENTER_KEY.INPUT_WITHOUT_CONTEXT_ALTERNATIVE | ||
</mat-label> | ||
<input name="test" [(ngModel)]="inputValue3" matInput | ||
placeholder="{{ 'SHOWCASE.DEMO.KEYBOARD_DIRECTIVES.ON_ENTER_KEY.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> | ||
|
||
<example-viewer [extensions]="['HTML', 'TS']" filesPath="keyboard-directives/restrict-input-directive" | ||
title="SHOWCASE.DEMO.KEYBOARD_DIRECTIVES.RESTRICT_INPUT.TITLE"> | ||
<form class="restrict-input-directive-form"> | ||
<p translate>SHOWCASE.DEMO.KEYBOARD_DIRECTIVES.RESTRICT_INPUT.DESCRIPTION</p> | ||
<mat-form-field> | ||
<mat-label translate>SHOWCASE.DEMO.KEYBOARD_DIRECTIVES.RESTRICT_INPUT.INPUT_ONLY_NUMBERS</mat-label> | ||
<input name="test" matInput | ||
placeholder="{{ 'SHOWCASE.DEMO.KEYBOARD_DIRECTIVES.RESTRICT_INPUT.TYPE_A_VALUE' | translate }}" | ||
starkRestrictInput="\d"> | ||
</mat-form-field> | ||
<mat-form-field> | ||
<mat-label translate>SHOWCASE.DEMO.KEYBOARD_DIRECTIVES.RESTRICT_INPUT.INPUT_ALPHANUMERICAL_CHARACTERS | ||
</mat-label> | ||
<input name="test" matInput | ||
placeholder="{{ 'SHOWCASE.DEMO.KEYBOARD_DIRECTIVES.RESTRICT_INPUT.TYPE_A_VALUE' | translate }}" | ||
starkRestrictInput="^[A-Za-z0-9]*$"> | ||
</mat-form-field> | ||
<mat-form-field> | ||
<mat-label translate>SHOWCASE.DEMO.KEYBOARD_DIRECTIVES.RESTRICT_INPUT.INPUT_NO_SPECIAL_CHARACTERS | ||
</mat-label> | ||
<input name="test" matInput | ||
placeholder="{{ 'SHOWCASE.DEMO.KEYBOARD_DIRECTIVES.RESTRICT_INPUT.TYPE_A_VALUE' | translate }}" | ||
starkRestrictInput="\w"> | ||
</mat-form-field> | ||
<mat-form-field> | ||
<mat-label translate>SHOWCASE.DEMO.KEYBOARD_DIRECTIVES.RESTRICT_INPUT.INPUT_UPPERCASE_CHARACTERS</mat-label> | ||
<input name="test" matInput | ||
placeholder="{{ 'SHOWCASE.DEMO.KEYBOARD_DIRECTIVES.RESTRICT_INPUT.TYPE_A_VALUE' | translate }}" | ||
starkRestrictInput="^[A-Z]*$"> | ||
</mat-form-field> | ||
</form> | ||
</example-viewer> |
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
29 changes: 29 additions & 0 deletions
29
showcase/src/assets/examples/keyboard-directives/restrict-input-directive.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,29 @@ | ||
<form class="restrict-input-directive-form"> | ||
<p>Type some value in the inputs and some characters will be prevented depending on the restriction set in every field</p> | ||
<mat-form-field> | ||
<mat-label>Accept only numbers</mat-label> | ||
<input name="test" matInput | ||
placeholder="Type a value" | ||
starkRestrictInput="\d"> | ||
</mat-form-field> | ||
<mat-form-field> | ||
<mat-label>Accept only alphanumeric characters | ||
</mat-label> | ||
<input name="test" matInput | ||
placeholder="Type a value" | ||
starkRestrictInput="^[A-Za-z0-9]*$"> | ||
</mat-form-field> | ||
<mat-form-field> | ||
<mat-label>Accept all except special characters | ||
</mat-label> | ||
<input name="test" matInput | ||
placeholder="Type a value" | ||
starkRestrictInput="\w"> | ||
</mat-form-field> | ||
<mat-form-field> | ||
<mat-label>Accept only uppercase characters</mat-label> | ||
<input name="test" matInput | ||
placeholder="Type a value" | ||
starkRestrictInput="^[A-Z]*$"> | ||
</mat-form-field> | ||
</form> |
11 changes: 11 additions & 0 deletions
11
showcase/src/assets/examples/keyboard-directives/restrict-input-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,11 @@ | ||
import { Component, Inject } 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 { | ||
public constructor(@Inject(STARK_LOGGING_SERVICE) public logger: StarkLoggingService) {} | ||
} |
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
Oops, something went wrong.