Skip to content

Commit

Permalink
fix(stark-ui): handles paste and drop event on the restrict-input dir…
Browse files Browse the repository at this point in the history
…ective

ISSUES CLOSED: #1905
  • Loading branch information
mhenkens committed Sep 15, 2022
1 parent 6734bcd commit 28791f3
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { StarkRestrictInputDirective } from "./restrict-input.directive";
import Spy = jasmine.Spy;
import createSpy = jasmine.createSpy;

// tslint:disable-next-line:no-big-function
describe("RestrictInputDirective", () => {
@Component({
selector: "test-component",
Expand Down Expand Up @@ -43,6 +44,34 @@ describe("RestrictInputDirective", () => {
return !(<HTMLInputElement>inputElement.nativeElement).dispatchEvent(keypressEvent);
}

function triggerPasteEvent(inputElement: DebugElement, value: string): boolean {
const clipboardData: DataTransfer = new DataTransfer();
clipboardData.setData("text/plain", value);
const pasteEvent: ClipboardEvent = new ClipboardEvent("paste", { clipboardData: clipboardData, cancelable: true });

return !(<HTMLInputElement>inputElement.nativeElement).dispatchEvent(pasteEvent);
}

function triggerDropEvent(inputElement: DebugElement, value: string): boolean {
const dataTransfer: DataTransfer = new DataTransfer();
dataTransfer.setData("text/plain", value);
const dragEvent: DragEvent = new DragEvent("drop", { dataTransfer: dataTransfer, cancelable: true });

return !(<HTMLInputElement>inputElement.nativeElement).dispatchEvent(dragEvent);
}

function triggerAndAssert(
inputElement: DebugElement,
values: string[],
shouldBeValid: boolean,
triggerFunction: (inputElement: DebugElement, value: string) => boolean
): void {
for (const value of values) {
const eventDefaultPrevented = triggerFunction(inputElement, value);
expect(eventDefaultPrevented).toBe(shouldBeValid);
}
}

beforeEach(() => {
TestBed.configureTestingModule({
declarations: [StarkRestrictInputDirective, TestComponent],
Expand All @@ -64,21 +93,28 @@ describe("RestrictInputDirective", () => {
expect(fixture).toBeDefined();
const inputElement: DebugElement = fixture.debugElement.query(By.css("input"));

let keyPressEventDefaultPrevented: boolean = triggerKeyPressEvent(inputElement, "1");
expect(keyPressEventDefaultPrevented).toBe(false);
triggerAndAssert(inputElement, ["1", "a", "-"], false, triggerKeyPressEvent);
});

it("should NOT prevent any value from being pasted in the input when no input restriction was provided", () => {
expect(fixture).toBeDefined();
const inputElement: DebugElement = fixture.debugElement.query(By.css("input"));

keyPressEventDefaultPrevented = triggerKeyPressEvent(inputElement, "9");
expect(keyPressEventDefaultPrevented).toBe(false);
triggerAndAssert(inputElement, ["1", "a", "-"], false, triggerPasteEvent);
});

it("should NOT prevent any value from being drop in the input when no input restriction was provided", () => {
expect(fixture).toBeDefined();
const inputElement: DebugElement = fixture.debugElement.query(By.css("input"));

keyPressEventDefaultPrevented = triggerKeyPressEvent(inputElement, "0");
expect(keyPressEventDefaultPrevented).toBe(false);
triggerAndAssert(inputElement, ["1", "a", "-"], false, triggerDropEvent);
});
});

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!
// the directive should not be used with square brackets "[]" because the input is a string literal!
const newTemplate: string = getTemplate("starkRestrictInput='\\d'");

TestBed.overrideTemplate(TestComponent, newTemplate);
Expand All @@ -94,27 +130,40 @@ describe("RestrictInputDirective", () => {
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 keyPressEventDefaultPrevented: boolean = triggerKeyPressEvent(inputElement, "a");
expect(keyPressEventDefaultPrevented).toBe(true);
triggerAndAssert(inputElement, ["a", "B", "-"], true, triggerKeyPressEvent);
});

keyPressEventDefaultPrevented = triggerKeyPressEvent(inputElement, "B");
expect(keyPressEventDefaultPrevented).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"));

keyPressEventDefaultPrevented = triggerKeyPressEvent(inputElement, "-");
expect(keyPressEventDefaultPrevented).toBe(true);
triggerAndAssert(inputElement, ["9", "1", "0"], false, triggerKeyPressEvent);
});

it("should NOT prevent any of the values given in the configuration from being typed in the input", () => {
it("should prevent any of the values given in the configuration from being pasted in the input", () => {
expect(fixture).toBeDefined();
const inputElement: DebugElement = fixture.debugElement.query(By.css("input"));

let keyPressEventDefaultPrevented: boolean = triggerKeyPressEvent(inputElement, "1");
expect(keyPressEventDefaultPrevented).toBe(false);
triggerAndAssert(inputElement, ["a", "B", "-"], true, triggerPasteEvent);
});

keyPressEventDefaultPrevented = triggerKeyPressEvent(inputElement, "9");
expect(keyPressEventDefaultPrevented).toBe(false);
// tslint:disable-next-line:no-identical-functions
it("should NOT prevent any of the values given in the configuration from being pasted in the input", () => {
expect(fixture).toBeDefined();
const inputElement: DebugElement = fixture.debugElement.query(By.css("input"));
triggerAndAssert(inputElement, ["9", "1", "0"], false, triggerPasteEvent);
});

keyPressEventDefaultPrevented = triggerKeyPressEvent(inputElement, "0");
expect(keyPressEventDefaultPrevented).toBe(false);
it("should prevent any of the values given in the configuration from being drop in the input", () => {
expect(fixture).toBeDefined();
const inputElement: DebugElement = fixture.debugElement.query(By.css("input"));
triggerAndAssert(inputElement, ["a", "B", "-"], true, triggerDropEvent);
});

// tslint:disable-next-line:no-identical-functions
it("should NOT prevent any of the values given in the configuration from being drop in the input", () => {
expect(fixture).toBeDefined();
const inputElement: DebugElement = fixture.debugElement.query(By.css("input"));
triggerAndAssert(inputElement, ["9", "1", "0"], false, triggerDropEvent);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,50 @@ export class StarkRestrictInputDirective implements OnInit {
*/
@HostListener("keypress", ["$event"])
public eventHandler(event: KeyboardEvent): boolean {
const regularExpression: string = this.inputRestriction || "";
// some browsers return the special key value (i.e. keys in the numeric keypad), in such cases we use the 'char'
// see: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values
/* tslint:disable-next-line:deprecation */
const key: string = event.key.length > 1 ? event.char : event.key;

return this.testValue(event, key);
}

/**
* Event handler to be invoked on a "paste" event in the field
*/
@HostListener("paste", ["$event"])
public onPaste(event: ClipboardEvent): boolean {
if (!event.clipboardData) {
return true;
}
const value = event.clipboardData.getData("text/plain");

return this.testValue(event, value);
}

/**
* Event handler to be invoked on a "drop" event in the field
*/
@HostListener("drop", ["$event"])
public onDrop(event: DragEvent): boolean {
if (!event.dataTransfer) {
return true;
}
const value = event.dataTransfer.getData("text/plain");
return this.testValue(event, value);
}

private testValue(event: Event, value: string): boolean {
const regularExpression: string = this.inputRestriction || "";
if (regularExpression) {
// some browsers return the special key value (i.e. keys in the numeric keypad), in such cases we use the 'char'
// see: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values
/* tslint:disable-next-line:deprecation */
const key: string = event.key.length > 1 ? event.char : event.key;
const regex: RegExp = new RegExp(regularExpression);

if (!regex.test(key)) {
if (!regex.test(value)) {
event.preventDefault();
return false;
}
} else {
this.logger.warn(directiveName + ": no input restriction defined");
}

return true;
}

Expand Down

0 comments on commit 28791f3

Please sign in to comment.