Skip to content

Commit

Permalink
feat(stark-ui): implement RestrictInput directive and module
Browse files Browse the repository at this point in the history
ISSUES CLOSED: #546
  • Loading branch information
christophercr committed Jul 24, 2018
1 parent 892c7fe commit 4b59020
Show file tree
Hide file tree
Showing 5 changed files with 189 additions and 0 deletions.
2 changes: 2 additions & 0 deletions packages/stark-ui/src/modules/restrict-input.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./restrict-input/restrict-input.module";
export * from "./restrict-input/directives";
1 change: 1 addition & 0 deletions packages/stark-ui/src/modules/restrict-input/directives.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./directives/restrict-input.directive";
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);
});
});
});
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");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { NgModule } from "@angular/core";
import { StarkRestrictInputDirective } from "./directives";

@NgModule({
declarations: [StarkRestrictInputDirective],
exports: [StarkRestrictInputDirective]
})
export class StarkRestrictInputModule {}

0 comments on commit 4b59020

Please sign in to comment.