igniteui-angular-sovn
Version: 
Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps
476 lines (429 loc) • 17.9 kB
text/typescript
import { first } from 'rxjs/operators';
import { HorizontalAlignment, VerticalAlignment, Point } from '../services/public_api';
import { DebugElement } from '@angular/core';
export const wait = (ms = 0) => new Promise(resolve => setTimeout(resolve, ms));
// export const waitForGridScroll = grid => new Promise<void>(resolve => grid.gridScroll.pipe(first()).subscribe(() => {
//     grid.cdr.detectChanges();
//     resolve();
// }));
export const waitForActiveNodeChange = grid => new Promise<void>(resolve => grid.activeNodeChange.pipe(first()).subscribe(() => {
    grid.cdr.detectChanges();
    resolve();
}));
export const waitForSelectionChange = grid => new Promise<void>(resolve => grid.selected.pipe(first()).subscribe(() => {
    grid.cdr.detectChanges();
    resolve();
}));
declare let Touch: {
    prototype: Touch;
    new(prop): Touch;
};
export class UIInteractions {
    /**
     * Clears all opened overlays and resets document scrollTop and scrollLeft
     */
    public static clearOverlay() {
        const overlays = document.getElementsByClassName('igx-overlay') as HTMLCollectionOf<Element>;
        Array.from(overlays).forEach(element => {
            element.remove();
        });
        document.documentElement.scrollTop = 0;
        document.documentElement.scrollLeft = 0;
        document.body.style.overflow = 'hidden';
    }
    /**
     * Clicks an element - native or debug, by dispatching pointerdown, pointerup and click events.
     *
     * @param element - Native or debug element.
     * @param shift - if the shift key is pressed.
     * @param ctrl - if the ctrl key is pressed.
     */
    public static simulateClickAndSelectEvent(element, shift = false, ctrl = false) {
        const nativeElement = element.nativeElement ?? element;
        UIInteractions.simulatePointerOverElementEvent('pointerdown', nativeElement, shift, ctrl);
        UIInteractions.simulatePointerOverElementEvent('pointerup', nativeElement);
        nativeElement.dispatchEvent(new MouseEvent('click', { bubbles: true }));
    }
    /**
     * Double click an element - native or debug, by dispatching pointerdown, pointerup and dblclick events.
     * // TODO - typing of element - whe npassing cell/row, should be CellType/RowTpe
     *
     * @param element - Native or debug element.
     */
    public static simulateDoubleClickAndSelectEvent(element: any) {
        const nativeElement = element.nativeElement ?? element;
        UIInteractions.simulatePointerOverElementEvent('pointerdown', nativeElement);
        UIInteractions.simulatePointerOverElementEvent('pointerup', nativeElement);
        nativeElement.dispatchEvent(new MouseEvent('dblclick'));
    }
    /**
     * click with non primary button on an element - native or debug, by dispatching pointerdown, pointerup and click events.
     *
     * @param element - Native or debug element.
     */
    public static simulateNonPrimaryClick(element) {
        const nativeElement = element.nativeElement ?? element;
        nativeElement.dispatchEvent(new PointerEvent('pointerdown', { button: 2 }));
        nativeElement.dispatchEvent(new PointerEvent('pointerup', { button: 2 }));
        nativeElement.dispatchEvent(new Event('click'));
    }
    /**
     * gets a keyboard event
     *
     * @param eventType - name of the event
     * @param keyPressed- pressed key
     */
    public static getKeyboardEvent(eventType: string, keyPressed: string, altKey = false, shiftKey = false, ctrlKey = false) {
        const keyboardEvent = {
            key: keyPressed,
            altKey,
            shiftKey,
            ctrlKey,
            stopPropagation: () => { },
            stopImmediatePropagation: () => { },
            preventDefault: () => { }
        };
        return new KeyboardEvent(eventType, keyboardEvent);
    }
    /**
     * gets a mouse event
     *
     * @param eventType - name of the event
     */
    public static getMouseEvent(eventType, altKey = false, shiftKey = false, ctrlKey = false) {
        const clickEvent = {
            altKey,
            shiftKey,
            ctrlKey,
            stopPropagation: () => { },
            stopImmediatePropagation: () => { },
            preventDefault: () => { }
        };
        return new MouseEvent(eventType, clickEvent);
    }
    /**
     * Press a key on an element - debug, by triggerEventHandler.
     *
     * @param keyPressed - pressed key
     * @param elem - debug element
     */
    public static triggerEventHandlerKeyDown(key: string, elem: any, altKey = false, shiftKey = false, ctrlKey = false) {
        const event = {
            target: elem.nativeElement,
            key,
            altKey,
            shiftKey,
            ctrlKey,
            stopPropagation: () => { },
            stopImmediatePropagation: () => { },
            preventDefault: () => { }
        };
        if (elem.hasOwnProperty('triggerEventHandler')) {
            elem.triggerEventHandler('keydown', event);
        } else {
            (elem.nativeElement as HTMLElement).dispatchEvent(new KeyboardEvent('keydown', { ...event }));
        }
    }
    /**
     * Trigger key up on an element - debug, by triggerEventHandler.
     *
     * @param keyPressed - pressed key
     * @param elem - debug element
     */
    public static triggerEventHandlerKeyUp(key: string, elem: DebugElement, altKey = false, shiftKey = false, ctrlKey = false) {
        const event = {
            key,
            altKey,
            shiftKey,
            ctrlKey,
            stopPropagation: () => { },
            stopImmediatePropagation: () => { },
            preventDefault: () => { }
        };
        elem.triggerEventHandler('keyup', event);
    }
    /**
     * Sets an input value- native or debug, by dispatching keydown, input and keyup events.
     *
     * @param element - Native or debug element.
     * @param text - text to be set.
     * @param fix - if fixture is set it will detect changes on it.
     */
    public static clickAndSendInputElementValue(element, text, fix = null) {
        const nativeElement = element.nativeElement ?? element;
        nativeElement.value = text;
        nativeElement.dispatchEvent(new Event('keydown'));
        nativeElement.dispatchEvent(new Event('input'));
        nativeElement.dispatchEvent(new Event('keyup'));
        if (fix) {
            fix.detectChanges();
        }
    }
    /**
     * Sets an input value- native or debug, by dispatching only input events.
     *
     * @param element - Native or debug element.
     * @param text - text to be set.
     * @param fix - if fixture is set it will detect changes on it.
     */
    public static setInputElementValue(element, text, fix = null) {
        const nativeElement = element.nativeElement ?? element;
        nativeElement.value = text;
        nativeElement.dispatchEvent(new Event('input'));
        if (fix) {
            fix.detectChanges();
        }
    }
    /**
     * Sets an input value- debug element.
     *
     * @param inputElement - debug element.
     * @param inputValue - text to be set.
     */
    public static triggerInputEvent(inputElement: DebugElement, inputValue: string) {
        inputElement.nativeElement.value = inputValue;
        inputElement.triggerEventHandler('input', { target: inputElement.nativeElement });
    }
    public static triggerInputKeyInteraction(inputValue: string, target: DebugElement) {
        const startPos = target.nativeElement.selectionStart;
        const endPos = target.nativeElement.selectionEnd;
        target.nativeElement.value =
            target.nativeElement.value.substring(0, startPos) +
            inputValue +
            target.nativeElement.value.substring(endPos);
        // move the caret
        if (startPos !== endPos) {
            // replaced selection, cursor goes to end
            target.nativeElement.selectionStart = target.nativeElement.selectionEnd = startPos + inputValue.length;
        } else {
            // typing move the cursor after the typed value
            target.nativeElement.selectionStart = target.nativeElement.selectionEnd = endPos + inputValue.length;
        }
        target.triggerEventHandler('input', { target: target.nativeElement });
    }
    public static simulateTyping(characters: string, target: DebugElement, selectionStart = 0, selectionEnd = 0) {
        if (characters) {
            if (selectionStart > selectionEnd) {
                return Error('Selection start should be less than selection end position');
            }
            const inputEl = target.nativeElement as HTMLInputElement;
            inputEl.setSelectionRange(selectionStart, selectionEnd);
            for (const char of characters) {
                this.triggerEventHandlerKeyDown(char, target);
                this.triggerInputKeyInteraction(char, target);
                this.triggerEventHandlerKeyUp(char, target);
            }
        }
    }
    public static simulatePaste(pasteText: string, target: DebugElement, selectionStart = 0, selectionEnd = 0) {
        if (selectionStart > selectionEnd) {
            return Error('Selection start should be less than selection end position');
        }
        const inputEl = target.nativeElement as HTMLInputElement;
        inputEl.setSelectionRange(selectionStart, selectionEnd);
        UIInteractions.triggerPasteEvent(target, pasteText);
        UIInteractions.triggerInputKeyInteraction(pasteText, target);
    }
    public static triggerPasteEvent(inputElement: DebugElement, inputValue: string) {
        const pasteData = new DataTransfer();
        pasteData.setData('text/plain', inputValue);
        const event = new ClipboardEvent('paste', { clipboardData: pasteData });
        inputElement.triggerEventHandler('paste', event);
    }
    public static simulateCompositionEvent(characters: string, target: DebugElement, selectionStart = 0, selectionEnd = 0, isBlur = true) {
        if (characters) {
            if (selectionStart > selectionEnd) {
                return Error('Selection start should be less than selection end position');
            }
            const inputEl = target.nativeElement as HTMLInputElement;
            inputEl.setSelectionRange(selectionStart, selectionEnd);
            target.triggerEventHandler('compositionstart', { target: target.nativeElement });
            for (const char of characters) {
                this.triggerEventHandlerKeyDown(char, target);
                this.triggerInputKeyInteraction(char, target);
                this.triggerEventHandlerKeyUp(char, target);
            }
            target.triggerEventHandler('compositionend', { target: target.nativeElement });
            if (isBlur) {
                this.triggerInputKeyInteraction(characters, target);
            }
        }
    }
    public static triggerKeyDownEvtUponElem(key, elem, bubbles = true, altKey = false, shiftKey = false, ctrlKey = false) {
        const keyboardEvent = new KeyboardEvent('keydown', {
            key,
            bubbles,
            shiftKey,
            ctrlKey,
            altKey
        });
        elem.dispatchEvent(keyboardEvent);
    }
    public static simulateClickEvent(element, shift = false, ctrl = false) {
        const event = new MouseEvent('click', {
            bubbles: true,
            shiftKey: shift,
            ctrlKey: ctrl
        });
        element.dispatchEvent(event);
    }
    public static simulateMouseEvent(eventName: string, element, clientX, clientY) {
        const options: MouseEventInit = {
            view: window,
            bubbles: true,
            cancelable: true,
            clientX,
            clientY
        };
        element.dispatchEvent(new MouseEvent(eventName, options));
    }
    public static createPointerEvent(eventName: string, point: Point) {
        const options: PointerEventInit = {
            view: window,
            bubbles: true,
            cancelable: true,
            pointerId: 1
        };
        const pointerEvent = new PointerEvent(eventName, options);
        Object.defineProperty(pointerEvent, 'pageX', { value: point.x, enumerable: true });
        Object.defineProperty(pointerEvent, 'pageY', { value: point.y, enumerable: true });
        return pointerEvent;
    }
    public static simulatePointerEvent(eventName: string, element, x, y) {
        const options: PointerEventInit = {
            view: window,
            bubbles: true,
            cancelable: true,
            pointerId: 1
        };
        const pointerEvent = new PointerEvent(eventName, options);
        Object.defineProperty(pointerEvent, 'pageX', { value: x, enumerable: true });
        Object.defineProperty(pointerEvent, 'pageY', { value: y, enumerable: true });
        element.dispatchEvent(pointerEvent);
        return pointerEvent;
    }
    public static simulatePointerOverElementEvent(eventName: string, element, shiftKey = false, ctrlKey = false) {
        const options: PointerEventInit = {
            view: window,
            bubbles: true,
            cancelable: true,
            pointerId: 1,
            buttons: 1,
            button: eventName === 'pointerenter' ? -1 : 0,
            shiftKey,
            ctrlKey
        };
        element.dispatchEvent(new PointerEvent(eventName, options));
    }
    public static simulateDropEvent(nativeElement: HTMLElement, data: any, format: string) {
        const dataTransfer = new DataTransfer();
        dataTransfer.setData(format, data);
        nativeElement.dispatchEvent(new DragEvent('drop', { dataTransfer }));
    }
    public static simulateWheelEvent(element, deltaX, deltaY, shiftKey = false) {
        const event = new WheelEvent('wheel', { deltaX, deltaY, shiftKey });
        Object.defineProperty(event, 'wheelDeltaX', { value: deltaX });
        Object.defineProperty(event, 'wheelDeltaY', { value: deltaY });
        return new Promise<void>(resolve => {
            element.dispatchEvent(event);
            resolve();
        });
    }
    public static simulateTouchStartEvent(target, pageX, pageY) {
        const touchInit = {
            identifier: 0,
            target,
            pageX,
            pageY
        };
        const t = new Touch(touchInit);
        const touchEventObject = new TouchEvent('touchstart', { touches: [t] });
        return new Promise<void>(resolve => {
            target.dispatchEvent(touchEventObject);
            resolve();
        });
    }
    public static simulateTouchMoveEvent(element, movedX, movedY) {
        const touchInit = {
            identifier: 0,
            target: element,
            pageX: movedX,
            pageY: movedY
        };
        const t = new Touch(touchInit);
        const touchEventObject = new TouchEvent('touchmove', { touches: [t] });
        return new Promise<void>(resolve => {
            element.dispatchEvent(touchEventObject);
            resolve();
        });
    }
    public static simulateTouchEndEvent(element, movedX, movedY) {
        const touchInit = {
            identifier: 0,
            target: element,
            pageX: movedX,
            pageY: movedY
        };
        const t = new Touch(touchInit);
        const touchEventObject = new TouchEvent('touchend', { touches: [t] });
        return new Promise<void>(resolve => {
            element.dispatchEvent(touchEventObject);
            resolve();
        });
    }
    /**
     * Calculate point within element
     *
     * @param element Element to calculate point for
     * @param hAlign The horizontal position of the point within the element (defaults to center)
     * @param vAlign The vertical position of the point within the element (defaults to middle)
     */
    public static getPointFromElement(
        element: Element,
        hAlign: HorizontalAlignment = HorizontalAlignment.Center,
        vAlign: VerticalAlignment = VerticalAlignment.Middle): Point {
        const elementRect = element.getBoundingClientRect();
        return {
            x: elementRect.right + hAlign * elementRect.width,
            y: elementRect.bottom + vAlign * elementRect.height
        };
    }
    public static hoverElement(element: HTMLElement, bubbles = false) {
        element.dispatchEvent(new MouseEvent('mouseenter', { bubbles }));
    }
    public static unhoverElement(element: HTMLElement, bubbles = false) {
        element.dispatchEvent(new MouseEvent('mouseleave', { bubbles }));
    }
    public static clickDragDirective(fix, dragDir) {
        dragDir.onPointerDown(new PointerEvent('pointerdown', { pointerId: 1 }));
        dragDir.onPointerUp(new PointerEvent('pointerup'));
        fix.detectChanges();
    }
    public static moveDragDirective(fix, dragDir, moveX, moveY, triggerPointerUp = false) {
        const dragElem = dragDir.element.nativeElement;
        const startingTop = dragElem.getBoundingClientRect().top;
        const startingLeft = dragElem.getBoundingClientRect().left;
        const startingBottom = dragElem.getBoundingClientRect().bottom;
        const startingRight = dragElem.getBoundingClientRect().right;
        const startingX = (startingLeft + startingRight) / 2;
        const startingY = (startingTop + startingBottom) / 2;
        dragDir.onPointerDown({ pointerId: 1, pageX: startingX, pageY: startingY });
        fix.detectChanges();
        dragDir.onPointerMove({ pointerId: 1, pageX: startingX + 10, pageY: startingY + 10 });
        fix.detectChanges();
        dragDir.onPointerMove({
            pointerId: 1,
            pageX: startingX + moveX,
            pageY: startingY + moveY
        });
        fix.detectChanges();
        if (triggerPointerUp) {
            dragDir.onPointerUp({
                pointerId: 1,
                pageX: startingX + moveX,
                pageY: startingY + moveY
            });
            fix.detectChanges();
        }
    }
}