Skip to content

Commit

Permalink
feat(stark-ui): add directive for transforming input value before pas…
Browse files Browse the repository at this point in the history
…sing it to a ngControl

  - added directive `[starkTransformInput]`
  - created new module
  - added demo page / documentation
  - small refactor demo page keyboard-directives
  - added tests for directive

ISSUES CLOSED: NationalBankBelgium#1099
  • Loading branch information
carlo-nomes committed Feb 7, 2019
1 parent 1f75d0d commit c515910
Show file tree
Hide file tree
Showing 20 changed files with 702 additions and 90 deletions.
1 change: 1 addition & 0 deletions packages/stark-ui/src/modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ export * from "./modules/slider";
export * from "./modules/svg-view-box";
export * from "./modules/table";
export * from "./modules/toast-notification";
export * from "./modules/transform-input-directive";
2 changes: 2 additions & 0 deletions packages/stark-ui/src/modules/transform-input-directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./transform-input-directive/directives";
export * from "./transform-input-directive/transform-input-directive.module";
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./directives/transform-input.directive";
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
/*tslint:disable:completed-docs no-identical-functions no-duplicate-string no-big-function*/
import { Component } from "@angular/core";
import { ComponentFixture, TestBed } from "@angular/core/testing";
import { FormControl, FormsModule, ReactiveFormsModule } from "@angular/forms";
import { StarkInputTransformationType, StarkTransformInputDirective } from "./transform-input.directive";
import { Observer } from "rxjs";

/**
* Mocks an InputEvent on the element with the given value.
* @param value - the value to set the element to.
* @param element - the HTMLInputElement to mock the event on
*/
const mockInputEvent: (value: string, element: HTMLInputElement) => void = (value: string, element: HTMLInputElement): void => {
const inputEvent: Event = document.createEvent("Event");
inputEvent.initEvent("input", true, true);

element.value = value;
element.dispatchEvent(inputEvent);
};

describe("TransformInputDirective", () => {
describe("with ngModel", () => {
@Component({
selector: "test-component",
template: "<input [(ngModel)]='value' [starkTransformInput]='starkTransformInputValue'/>"
})
class TestComponent {
public starkTransformInputValue: StarkInputTransformationType = () => {
/*noop*/
};
public value: string = "";
}

let fixture: ComponentFixture<TestComponent>;
let component: TestComponent;
let htmlInputElement: HTMLInputElement;

beforeEach(() => {
TestBed.configureTestingModule({
declarations: [StarkTransformInputDirective, TestComponent],
imports: [FormsModule]
});

fixture = TestBed.createComponent(TestComponent);
component = fixture.componentInstance;
htmlInputElement = fixture.nativeElement.querySelector("input");

// trigger initial data binding
fixture.detectChanges();

expect(component.value).toBe("", "field 'value' should start as empty string ");
});

it("should set value to uppercase", () => {
const input: string = "upper";
const expected: string = "UPPER";

// Set directive to correct implementation
component.starkTransformInputValue = "uppercase";
fixture.detectChanges();

mockInputEvent(input, htmlInputElement);
fixture.detectChanges();

expect(component.value).toBe(expected);
});

it("should set value to lowercase", () => {
const input: string = "LOWER";
const expected: string = "lower";

// Set directive to correct implementation
component.starkTransformInputValue = "lowercase";
fixture.detectChanges();

mockInputEvent(input, htmlInputElement);
fixture.detectChanges();

expect(component.value).toBe(expected);
});

it("should replace dirty word", () => {
const input: string = "fudge you!";
const expected: string = "***** you!";

// Set directive to correct implementation
component.starkTransformInputValue = (v: string) =>
v.replace("fudge", (match: string) =>
match
.split("")
.map(() => "*")
.join("")
);
fixture.detectChanges();

mockInputEvent(input, htmlInputElement);
fixture.detectChanges();

expect(component.value).toBe(expected);
});
});

describe("with formControl", () => {
@Component({
selector: "test-component",
template: "<input [formControl]='formControl' [starkTransformInput]='starkTransformInputValue'/>"
})
class TestComponent {
public starkTransformInputValue: StarkInputTransformationType = () => {
/*noop*/
};
public formControl: FormControl = new FormControl("");

public constructor() {
this.formControl.valueChanges.subscribe(this.changeObserver);
}

public changeObserver(): void {
/*Placeholder for spy*/
}
}

let fixture: ComponentFixture<TestComponent>;
let component: TestComponent;
let mockValueChangeObserver: jasmine.SpyObj<Observer<any>>;
let htmlInputElement: HTMLInputElement;

beforeEach(() => {
TestBed.configureTestingModule({
declarations: [StarkTransformInputDirective, TestComponent],
imports: [ReactiveFormsModule]
});

fixture = TestBed.createComponent(TestComponent);
component = fixture.componentInstance;
htmlInputElement = fixture.nativeElement.querySelector("input");

// Register mock subscription
mockValueChangeObserver = jasmine.createSpyObj<Observer<any>>("observerSpy", ["next", "error", "complete"]);
component.formControl.valueChanges.subscribe(mockValueChangeObserver);

// trigger initial data binding
fixture.detectChanges();

expect(component.formControl.value).toBe("", "field 'value' should start as empty string ");
});

it("should set value to uppercase", () => {
const input: string = "upper";
const expected: string = "UPPER";

// Set directive to correct implementation
component.starkTransformInputValue = "uppercase";
fixture.detectChanges();

mockInputEvent(input, htmlInputElement);
fixture.detectChanges();

expect(component.formControl.value).toBe(expected);
expect(mockValueChangeObserver.next).toHaveBeenCalledTimes(1);
expect(mockValueChangeObserver.error).not.toHaveBeenCalled();
expect(mockValueChangeObserver.complete).not.toHaveBeenCalledTimes(1);
});

it("should set value to lowercase", () => {
const input: string = "LOWER";
const expected: string = "lower";

// Set directive to correct implementation
component.starkTransformInputValue = "lowercase";
fixture.detectChanges();

mockInputEvent(input, htmlInputElement);
fixture.detectChanges();

expect(component.formControl.value).toBe(expected);
expect(mockValueChangeObserver.next).toHaveBeenCalledTimes(1);
expect(mockValueChangeObserver.error).not.toHaveBeenCalled();
expect(mockValueChangeObserver.complete).not.toHaveBeenCalledTimes(1);
});

it("should replace dirty word", () => {
const input: string = "fudge you!";
const expected: string = "***** you!";

// Set directive to correct implementation
component.starkTransformInputValue = (v: string) =>
v.replace("fudge", (match: string) =>
match
.split("")
.map(() => "*")
.join("")
);
fixture.detectChanges();

mockInputEvent(input, htmlInputElement);
fixture.detectChanges();

expect(component.formControl.value).toBe(expected);
expect(mockValueChangeObserver.next).toHaveBeenCalledTimes(1);
expect(mockValueChangeObserver.error).not.toHaveBeenCalled();
expect(mockValueChangeObserver.complete).not.toHaveBeenCalledTimes(1);
});
});

describe("without ngControl", () => {
@Component({
selector: "test-component",
template: "<input [starkTransformInput]='starkTransformInputValue'/>"
})
class TestComponent {
public starkTransformInputValue: StarkInputTransformationType = () => {
/*noop*/
};
}

let fixture: ComponentFixture<TestComponent>;
let component: TestComponent;
let htmlInputElement: HTMLInputElement;

beforeEach(() => {
TestBed.configureTestingModule({
declarations: [StarkTransformInputDirective, TestComponent]
});

fixture = TestBed.createComponent(TestComponent);
component = fixture.componentInstance;
htmlInputElement = fixture.nativeElement.querySelector("input");
htmlInputElement.value = "";

// trigger initial data binding
fixture.detectChanges();

expect(htmlInputElement.value).toBe("", "field 'value' should start as empty string ");
});

it("should set value to uppercase", () => {
const input: string = "upper";
const expected: string = "UPPER";

// Set directive to correct implementation
component.starkTransformInputValue = "uppercase";
fixture.detectChanges();

mockInputEvent(input, htmlInputElement);
fixture.detectChanges();

expect(htmlInputElement.value).toBe(expected);
});

it("should set value to lowercase", () => {
const input: string = "LOWER";
const expected: string = "lower";

// Set directive to correct implementation
component.starkTransformInputValue = "lowercase";
fixture.detectChanges();

mockInputEvent(input, htmlInputElement);
fixture.detectChanges();

expect(htmlInputElement.value).toBe(expected);
});

it("should replace dirty word", () => {
const input: string = "fudge you!";
const expected: string = "***** you!";

// Set directive to correct implementation
component.starkTransformInputValue = (v: string) =>
v.replace("fudge", (match: string) =>
match
.split("")
.map(() => "*")
.join("")
);
fixture.detectChanges();

mockInputEvent(input, htmlInputElement);
fixture.detectChanges();

expect(htmlInputElement.value).toBe(expected);
});
});
});
Loading

0 comments on commit c515910

Please sign in to comment.