FBToast tests: constructor and onEnter hooks

pull/1252/head
Rick Carlino 2019-06-26 15:47:26 -05:00
parent 74ee415069
commit a7363ae0f7
3 changed files with 154 additions and 130 deletions

View File

@ -0,0 +1,40 @@
import { FBToast } from "../fb_toast";
describe("FBToast", () => {
const parent = document.createElement("div");
const newToast = () => new FBToast(parent, "title", "message", "red");
it("instantiates", () => {
const instance = newToast();
expect(instance.leftLoaderEl.tagName).toEqual("DIV");
expect(instance.loaderEl.tagName).toEqual("DIV");
expect(instance.messageEl.tagName).toEqual("DIV");
expect(instance.rightLoaderEl.tagName).toEqual("DIV");
expect(instance.titleEl.tagName).toEqual("H4");
expect(instance.toastEl.tagName).toEqual("DIV");
expect(instance.spinnerLoaderEl.tagName).toEqual("DIV");
expect(instance.isHovered).toEqual(false);
expect(instance.intervalId).toEqual(0);
expect(instance.message).toEqual("message");
expect(instance.parent).toEqual(parent);
expect(instance.timeout).toEqual(7);
});
it("handles mouse enter events", () => {
const i = newToast();
i.isHovered = false;
const children = [
{ style: { animationPlayState: "X" } },
{ style: { animationPlayState: "Y" } },
{ style: { animationPlayState: "Z" } },
];
const fakeEvent: MouseEvent = {
currentTarget: { children: [{}, {}, { children }] }
// tslint:disable-next-line:no-any
} as any;
i.onEnter(fakeEvent);
const playState = children.map(x => x.style.animationPlayState);
expect(playState).toEqual(["paused", "paused", "paused"]);
});
});

View File

@ -0,0 +1,113 @@
export class FBToast {
/**
* Warnings and errors fire once, to avoid bombarding the user with repetition.
* Eg: "Can"t connect to server!" might get repetitive.
*/
static everyMessage: Record<string, boolean> = {};
public toastEl = document.createElement("div");
public titleEl = document.createElement("h4");
public messageEl = document.createElement("div");
public loaderEl = document.createElement("div");
public leftLoaderEl = document.createElement("div");
public rightLoaderEl = document.createElement("div");
public spinnerLoaderEl = document.createElement("div");
/** Used to stop a running interval timer */
public intervalId = 0;
/** Amount of time before each element is removed. */
public timeout = 7;
/** Declare if the user's mouse is hovering over the message. */
public isHovered = false;
constructor(public parent: Element,
title: string,
public message: string,
color: string) {
/** Fill contents. */
this.titleEl.innerText = title;
this.messageEl.innerText = message;
/** Add classes. */
this.toastEl.classList.add("toast");
this.toastEl.classList.add(color);
this.titleEl.classList.add("toast-title");
this.messageEl.classList.add("toast-message");
this.loaderEl.classList.add("toast-loader");
this.leftLoaderEl.classList.add("toast-loader-left");
this.leftLoaderEl.classList.add(color);
this.rightLoaderEl.classList.add("toast-loader-right");
this.spinnerLoaderEl.classList.add("toast-loader-spinner");
this.loaderEl.appendChild(this.leftLoaderEl);
this.loaderEl.appendChild(this.rightLoaderEl);
this.loaderEl.appendChild(this.spinnerLoaderEl);
this.toastEl.appendChild(this.titleEl);
this.toastEl.appendChild(this.messageEl);
this.toastEl.appendChild(this.loaderEl);
/** MouseLeave (resumes the timer). */
this.toastEl.addEventListener("mouseleave", this.onLeave);
/** MouseEnter (pauses the timer). */
this.toastEl.addEventListener("mouseenter", this.onEnter);
/** Click (makes the message go away entirely). */
this.toastEl.addEventListener("click", this.onClick);
}
onEnter = (e: MouseEvent) => {
const children = (e.currentTarget as HTMLElement).children[2].children;
for (let i = 0; i < children.length; i++) {
(children[i] as HTMLElement).style.animationPlayState = "paused";
}
this.isHovered = true;
};
onLeave = (e: MouseEvent) => {
const children = (e.currentTarget as HTMLElement).children[2].children;
for (let i = 0; i < children.length; i++) {
(children[i] as HTMLElement).style.animationPlayState = "running";
}
this.isHovered = false;
};
detach = () => {
this.parent.removeChild(this.toastEl);
delete FBToast.everyMessage[this.message];
};
onClick = (e: MouseEvent) => {
(e.currentTarget as Element).classList.add("poof");
setTimeout(this.detach, 200);
}
/** Start timer. */
doPolling = () => {
(this.timeout <= 7) && this.toastEl.classList.add("active");
(!this.isHovered && this.timeout <= 0.800) && this.toastEl.classList.add("poof");
if (!this.isHovered) {
this.timeout -= 0.100;
if (this.timeout <= 0) {
clearInterval(this.intervalId);
if (this.toastEl && this.toastEl.parentNode === this.parent) {
this.parent.removeChild(this.toastEl);
delete FBToast.everyMessage[this.message];
}
}
}
};
run = () => {
/** Append children. */
this.parent.appendChild(this.toastEl);
// TSC Thinks this is a node project :-\
// tslint:disable-next-line:no-any
this.intervalId = setInterval(this.doPolling, 100) as any;
}
}

View File

@ -1,133 +1,4 @@
class FBToast {
/**
* Warnings and errors fire once, to avoid bombarding the user with repetition.
* Eg: "Can"t connect to server!" might get repetitive.
*/
static everyMessage: Record<string, boolean> = {};
public toastEl = document.createElement("div");
public titleEl = document.createElement("h4");
public messageEl = document.createElement("div");
public loaderEl = document.createElement("div");
public leftLoaderEl = document.createElement("div");
public rightLoaderEl = document.createElement("div");
public spinnerLoaderEl = document.createElement("div");
/** Used to stop a running interval timer */
public intervalId = 0;
/**
* Amount of time before each element is removed.
*/
public timeout = 7;
/**
* Declare if the user's mouse is hovering over the message.
*/
public isHovered = false;
constructor(public parent: Element,
title: string,
public message: string,
color: string) {
/**
* Fill contents.
*/
this.titleEl.innerText = title;
this.messageEl.innerText = message;
/**
* Add classes.
*/
this.toastEl.classList.add("toast");
this.toastEl.classList.add(color);
this.titleEl.classList.add("toast-title");
this.messageEl.classList.add("toast-message");
this.loaderEl.classList.add("toast-loader");
this.leftLoaderEl.classList.add("toast-loader-left");
this.leftLoaderEl.classList.add(color);
this.rightLoaderEl.classList.add("toast-loader-right");
this.spinnerLoaderEl.classList.add("toast-loader-spinner");
this.loaderEl.appendChild(this.leftLoaderEl);
this.loaderEl.appendChild(this.rightLoaderEl);
this.loaderEl.appendChild(this.spinnerLoaderEl);
this.toastEl.appendChild(this.titleEl);
this.toastEl.appendChild(this.messageEl);
this.toastEl.appendChild(this.loaderEl);
/**
* MouseLeave (resumes the timer).
*/
this.toastEl.addEventListener("mouseleave", this.mouseLeave);
/**
* MouseEnter (pauses the timer).
*/
this.toastEl.addEventListener("mouseenter", this.mouseEnter);
/**
* Click (makes the message go away entirely).
*/
this.toastEl.addEventListener("click", this.onClick);
}
mouseEnter = (e: MouseEvent) => {
const children = (e.currentTarget as HTMLElement).children[2].children;
for (let i = 0; i < children.length; i++) {
(children[i] as HTMLElement).style.animationPlayState = "paused";
}
this.isHovered = true;
};
mouseLeave = (e: MouseEvent) => {
const children = (e.currentTarget as HTMLElement).children[2].children;
for (let i = 0; i < children.length; i++) {
(children[i] as HTMLElement).style.animationPlayState = "running";
}
this.isHovered = false;
};
detach = () => {
this.parent.removeChild(this.toastEl);
delete FBToast.everyMessage[this.message];
};
onClick = (e: MouseEvent) => {
(e.currentTarget as Element).classList.add("poof");
setTimeout(this.detach, 200);
}
/**
* Start timer.
*/
doPolling = () => {
(this.timeout <= 7) && this.toastEl.classList.add("active");
(!this.isHovered && this.timeout <= 0.800) && this.toastEl.classList.add("poof");
if (!this.isHovered) {
this.timeout -= 0.100;
if (this.timeout <= 0) {
clearInterval(this.intervalId);
if (this.toastEl && this.toastEl.parentNode === this.parent) {
this.parent.removeChild(this.toastEl);
delete FBToast.everyMessage[this.message];
}
}
}
};
run = () => {
/**
* Append children.
*/
this.parent.appendChild(this.toastEl);
// TSC Thinks this is a node project :-\
// tslint:disable-next-line:no-any
this.intervalId = setInterval(this.doPolling, 100) as any;
}
}
import { FBToast } from "./fb_toast";
/**
* The function responsible for attaching the messages to the container.