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 27, 2018
1 parent 892c7fe commit 032e6a1
Show file tree
Hide file tree
Showing 19 changed files with 342 additions and 4 deletions.
1 change: 1 addition & 0 deletions packages/stark-ui/src/modules.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from "./modules/action-bar";
export * from "./modules/app-logo";
export * from "./modules/keyboard-directives";
export * from "./modules/pretty-print";
export * from "./modules/slider";
export * from "./modules/table";
Expand Down
2 changes: 2 additions & 0 deletions packages/stark-ui/src/modules/keyboard-directives.ts
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";
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 StarkKeyboardDirectivesModule {}
3 changes: 3 additions & 0 deletions showcase/src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@
<a mat-list-item uiSref="demo-example-viewer" uiSrefActive="active">
<span matLine>Example Viewer</span>
</a>
<a mat-list-item uiSref="demo-keyboard-directives" uiSrefActive="active">
<span matLine>Keyboard Directives</span>
</a>
<a mat-list-item uiSref="demo-table" uiSrefActive="active">
<span matLine>Table</span>
</a>
Expand Down
3 changes: 2 additions & 1 deletion showcase/src/app/app.routes.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ActionBarComponent, ButtonComponent, ExampleViewerComponent, TableComponent } from "./demo";
import { ActionBarComponent, ButtonComponent, ExampleViewerComponent, KeyboardDirectivesComponent, TableComponent } from "./demo";
import { HomeComponent } from "./home";
import { NoContentComponent } from "./no-content";
import { Ng2StateDeclaration } from "@uirouter/angular";
Expand All @@ -8,6 +8,7 @@ export const APP_STATES: Ng2StateDeclaration[] = [
{ name: "demo-action-bar", url: "/demo/action-bar", component: ActionBarComponent },
{ name: "demo-button", url: "/demo/button", component: ButtonComponent },
{ name: "demo-example-viewer", url: "/demo/example-viewer", component: ExampleViewerComponent },
{ name: "demo-keyboard-directives", url: "/demo/keyboard-directives", component: KeyboardDirectivesComponent },
{ name: "demo-table", url: "/demo/table", component: TableComponent },
{ name: "otherwise", url: "/otherwise", component: NoContentComponent }
];
27 changes: 24 additions & 3 deletions showcase/src/app/demo/demo.module.ts
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,
MatInputModule,
MatFormFieldModule
} 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],
declarations: [ActionBarComponent, ButtonComponent, ExampleViewerComponent, KeyboardDirectivesComponent, TableComponent],
exports: [ActionBarComponent, ButtonComponent, ExampleViewerComponent, TableComponent]
})
export class DemoModule {}
1 change: 1 addition & 0 deletions showcase/src/app/demo/index.ts
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";
1 change: 1 addition & 0 deletions showcase/src/app/demo/keyboard-directives/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./keyboard-directives.component";
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<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>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.restrict-input-directive-form {
display: flex;
flex-direction: column;
}

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) {}
}
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>
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) {}
}
Loading

0 comments on commit 032e6a1

Please sign in to comment.