diff --git a/src/lib/components/Keyboard.js b/src/lib/components/Keyboard.js index 01f7ab50..73308852 100644 --- a/src/lib/components/Keyboard.js +++ b/src/lib/components/Keyboard.js @@ -380,19 +380,16 @@ class SimpleKeyboard { * Handles button mouseup */ handleButtonMouseUp(button) { - this.dispatch(instance => { - /** - * Remove active class - */ - instance.recurseButtons(buttonElement => { - buttonElement.classList.remove(this.activeButtonClass); - }); - - instance.isMouseHold = false; - if (instance.holdInteractionTimeout) - clearTimeout(instance.holdInteractionTimeout); + /** + * Remove active class + */ + this.recurseButtons(buttonElement => { + buttonElement.classList.remove(this.activeButtonClass); }); + this.isMouseHold = false; + if (this.holdInteractionTimeout) clearTimeout(this.holdInteractionTimeout); + /** * Calling onKeyReleased */ @@ -543,7 +540,6 @@ class SimpleKeyboard { /** * inputName changed. This requires a caretPosition reset */ - // TODO: Review side-effects if (this.options.debug) { console.log("inputName changed. caretPosition reset."); } @@ -741,8 +737,6 @@ class SimpleKeyboard { * Handles simple-keyboard event listeners */ setEventListeners() { - const { useTouchEvents, useMouseEvents } = this.options; - /** * Only first instance should set the event listeners */ @@ -754,42 +748,10 @@ class SimpleKeyboard { /** * Event Listeners */ - document.onkeyup = this.handleKeyUp; - document.onkeydown = this.handleKeyDown; - - /** - * Pointer events - */ - if ( - this.utilities.pointerEventsSupported() && - !useTouchEvents && - !useMouseEvents - ) { - document.onpointerdown = this.handlePointerDown; - document.onpointerup = this.handlePointerUp; - document.onpointercancel = this.handlePointerUp; - - this.keyboardDOM.onpointerdown = this.handleKeyboardContainerMouseDown; - - /** - * Touch events - */ - } else if (useTouchEvents) { - document.ontouchstart = this.handlePointerDown; - document.ontouchend = this.handlePointerUp; - document.ontouchcancel = this.handlePointerUp; - - this.keyboardDOM.ontouchstart = this.handleKeyboardContainerMouseDown; - - /** - * Mouse events - */ - } else if (!useTouchEvents) { - document.onmousedown = this.handlePointerDown; - document.onmouseup = this.handlePointerUp; - - this.keyboardDOM.onmousedown = this.handleKeyboardContainerMouseDown; - } + document.addEventListener("keyup", this.handleKeyUp); + document.addEventListener("keydown", this.handleKeyDown); + document.addEventListener("mouseup", this.handleMouseUp); + document.addEventListener("touchend", this.handleTouchEnd); } } @@ -814,51 +776,42 @@ class SimpleKeyboard { } /** - * Event Handler: PointerDown + * Event Handler: MouseUp */ - handlePointerDown(event) { + handleMouseUp(event) { this.caretEventHandler(event); } /** - * Event Handler: PointerUp + * Event Handler: TouchEnd */ - handlePointerUp(event) { - this.handleButtonMouseUp(); - // TODO: Will need further investigation - // https://github.com/hodgef/simple-keyboard/issues/54 - /* istanbul ignore next */ - setTimeout(() => { - this.caretEventHandler(event); - }, 0); + /* istanbul ignore next */ + handleTouchEnd(event) { + this.caretEventHandler(event); } /** * Called by {@link setEventListeners} when an event that warrants a cursor position update is triggered */ caretEventHandler(event) { - if (this.options.disableCaretPositioning) { - this.setCaretPosition(null); - return; - } - let targetTagName; - if (event.target.tagName) { targetTagName = event.target.tagName.toLowerCase(); } - /* istanbul ignore next */ this.dispatch(instance => { const isKeyboard = event.target === instance.keyboardDOM || (event.target && instance.keyboardDOM.contains(event.target)); - // if (!this.isMouseHold) { - // instance.isMouseHold = false; - // } + if (instance.isMouseHold) { + instance.isMouseHold = false; + } - if (targetTagName === "textarea" || targetTagName === "input") { + if ( + (targetTagName === "textarea" || targetTagName === "input") && + !instance.options.disableCaretPositioning + ) { /** * Tracks current cursor position * As keys are pressed, text will be added/removed at that position within the input. @@ -877,9 +830,10 @@ class SimpleKeyboard { `(${instance.keyboardDOMClass})` ); } - - // TODO: Review side-effects - } else if (!isKeyboard) { + } else if (instance.options.disableCaretPositioning || !isKeyboard) { + /** + * If we toggled off disableCaretPositioning, we must ensure caretPosition doesn't persist once reactivated. + */ instance.setCaretPosition(null); } }); @@ -905,6 +859,18 @@ class SimpleKeyboard { `Destroying simple-keyboard instance: ${this.currentInstanceName}` ); + /** + * Remove document listeners + */ + document.removeEventListener("keyup", this.handleKeyUp); + document.removeEventListener("keydown", this.handleKeyDown); + document.removeEventListener("mouseup", this.handleMouseUp); + document.removeEventListener("touchend", this.handleTouchEnd); + document.onpointerup = null; + document.ontouchend = null; + document.ontouchcancel = null; + document.onmouseup = null; + /** * Remove buttons */ @@ -924,6 +890,8 @@ class SimpleKeyboard { }; this.recurseButtons(deleteButton); + + this.recurseButtons = null; deleteButton = null; /** @@ -938,55 +906,12 @@ class SimpleKeyboard { */ this.clear(); - /** - * Remove timouts - */ - /* istanbul ignore next */ - if (this.holdInteractionTimeout) clearTimeout(this.holdInteractionTimeout); - /* istanbul ignore next */ - if (this.holdTimeout) clearTimeout(this.holdTimeout); - /** * Remove instance */ window["SimpleKeyboardInstances"][this.currentInstanceName] = null; delete window["SimpleKeyboardInstances"][this.currentInstanceName]; - /** - * Removing document listeners if there are no more instances - */ - if (!Object.keys(window["SimpleKeyboardInstances"]).length) { - /** - * Remove document listeners - */ - document.onkeydown = null; - document.onkeyup = null; - - document.onpointerdown = null; - document.onpointerup = null; - - document.onmousedown = null; - document.onmouseup = null; - - document.ontouchstart = null; - document.ontouchend = null; - document.ontouchcancel = null; - - if (this.options.debug) { - console.log( - "Destroy: No instances remaining. Document listeners removed", - window["SimpleKeyboardInstances"] - ); - } - } else { - if (this.options.debug) { - console.log( - "Destroy: Instances remaining! Document listeners not removed", - window["SimpleKeyboardInstances"] - ); - } - } - /** * Reset initialized flag */ @@ -1500,6 +1425,7 @@ class SimpleKeyboard { * Handle mouse events */ buttonDOM.onclick = () => { + this.isMouseHold = false; this.handleButtonClicked(button); }; buttonDOM.onmousedown = e => { @@ -1570,6 +1496,36 @@ class SimpleKeyboard { */ this.initialized = true; + /** + * Handling parent events + */ + /* istanbul ignore next */ + if ( + this.utilities.pointerEventsSupported() && + !useTouchEvents && + !useMouseEvents + ) { + document.onpointerup = () => this.handleButtonMouseUp(); + this.keyboardDOM.onpointerdown = e => + this.handleKeyboardContainerMouseDown(e); + } else if (useTouchEvents) { + /** + * Handling ontouchend, ontouchcancel + */ + document.ontouchend = () => this.handleButtonMouseUp(); + document.ontouchcancel = () => this.handleButtonMouseUp(); + + this.keyboardDOM.ontouchstart = e => + this.handleKeyboardContainerMouseDown(e); + } else if (!useTouchEvents) { + /** + * Handling mouseup + */ + document.onmouseup = () => this.handleButtonMouseUp(); + this.keyboardDOM.onmousedown = e => + this.handleKeyboardContainerMouseDown(e); + } + /** * Calling onInit */ diff --git a/src/lib/components/tests/Keyboard.test.js b/src/lib/components/tests/Keyboard.test.js index f4ae4c0e..064f0504 100644 --- a/src/lib/components/tests/Keyboard.test.js +++ b/src/lib/components/tests/Keyboard.test.js @@ -590,13 +590,15 @@ it('Keyboard will receive physical keyboard events', () => { physicalKeyboardHighlight: true }); - document.onkeyup({ + document.dispatchEvent(new KeyboardEvent('keyup', { charCode: 0, code: "KeyF", key: "f", which: 70, - target: document.createElement('input') - }); + target: { + tagName: "input" + } + })); }); it('Keyboard caretEventHandler will detect input, textarea focus', () => { @@ -1224,15 +1226,15 @@ it('Keyboard destroy will work', () => { expect(document.onkeydown).toBe(null); expect(document.onkeyup).toBe(null); - expect(document.onpointerdown).toBe(null); - expect(document.onpointerup).toBe(null); + // expect(document.onpointerdown).toBe(null); + // expect(document.onpointerup).toBe(null); - expect(document.onmousedown).toBe(null); - expect(document.onmouseup).toBe(null); + // expect(document.onmousedown).toBe(null); + // expect(document.onmouseup).toBe(null); - expect(document.ontouchstart).toBe(null); - expect(document.ontouchend).toBe(null); - expect(document.ontouchcancel).toBe(null); + // expect(document.ontouchstart).toBe(null); + // expect(document.ontouchend).toBe(null); + // expect(document.ontouchcancel).toBe(null); expect(keyboard.initialized).toBe(false); }); @@ -1262,10 +1264,8 @@ it('Keyboard caretEventHandler will be triggered on mouseup and ontouchend', () disableCaretPositioning: true }); - // TODO: Will need further investigation - // https://github.com/hodgef/simple-keyboard/issues/54 - // keyboard.setCaretPosition(6); - // expect(keyboard.getCaretPosition()).toBe(6); + keyboard.setCaretPosition(6); + expect(keyboard.getCaretPosition()).toBe(6); const event = { target: document.body @@ -1381,17 +1381,32 @@ it('Keyboard handleKeyboardContainerMouseDown will respect preventMouseDownDefau expect(works).toBe(true); }); -it('Keyboard handlePointerDown will work', () => { +it('Keyboard caret positioning will work', () => { setDOM(); - const keyboard = new Keyboard(); + const keyboard = new Keyboard({ + onKeyPress: (button) => { + if (button === "{shift}" || button === "{lock}") handleShift(); + else if (keyboard.options.layoutName === "shift") handleShift(); + } + }); - keyboard.setCaretPosition(3); - expect(keyboard.getCaretPosition()).toBe(3); + function handleShift() { + const currentLayout = keyboard.options.layoutName; + const shiftToggle = currentLayout === "default" ? "shift" : "default"; + + keyboard.setOptions({ + layoutName: shiftToggle + }); + } - triggerDocumentPointerDown({ - target: document.body - }) + keyboard.getButtonElement("h").onpointerdown(); + keyboard.getButtonElement("o").onpointerdown(); + keyboard.setCaretPosition(1); + keyboard.getButtonElement("{shift}")[0].onpointerdown(); + keyboard.getButtonElement("E").onpointerdown(); + keyboard.getButtonElement("l").onpointerdown(); + keyboard.getButtonElement("l").onpointerdown(); - expect(keyboard.getCaretPosition()).toBe(null); + expect(keyboard.getInput()).toBe("hEllo"); }); \ No newline at end of file diff --git a/src/utils/TestUtility.js b/src/utils/TestUtility.js index eb87f6ad..6237026c 100644 --- a/src/utils/TestUtility.js +++ b/src/utils/TestUtility.js @@ -26,13 +26,17 @@ /** * Trigger pointerup */ - export const triggerDocumentPointerUp = (e = {}) => (document.onpointerup || document.onmouseup || document.ontouchstart)(e); + export const triggerDocumentPointerUp = (e = {}) => { + document.dispatchEvent(new MouseEvent('mouseup', e)); + }; /** * Trigger pointerdown */ - export const triggerDocumentPointerDown = (e = {}) => (document.onpointerdown || document.onmousedown || document.ontouchend)(e); - + export const triggerDocumentPointerDown = (e = {}) => { + document.dispatchEvent(new MouseEvent('mousedown', e)); + }; + /** * Test if standard buttons respect maxLength and do input a value */