A box dragging example
++ Drag some boxes around with the mouse, then open your Developer + Tools, turn on mobile emulation, and try to do the same with touch + input enabled. Things should still work. +
+window A
+ ++ + + +
+ +window B
+ + + + diff --git a/tests/integration/issue-77b/test.js b/tests/integration/issue-77b/test.js new file mode 100644 index 0000000..71b62ae --- /dev/null +++ b/tests/integration/issue-77b/test.js @@ -0,0 +1,23 @@ +import { enableDragDropTouch } from "../../../dist/drag-drop-touch.esm.min.js"; + +import * as simulatedTouch from "../touch-simulation.js"; +globalThis.simulatedTouch = simulatedTouch; + +globalThis.enablePressHold = (threshold = 0) => { + enableDragDropTouch(document, document, { + isPressHoldMode: true, + pressHoldThresholdPixels: threshold, + }); +}; + +document.addEventListener(`dragover`, (e) => e.preventDefault()); + +document.addEventListener(`drop`, (e) => { + e.preventDefault(); + document.getElementById(`result`).textContent = + e.dataTransfer.getData(`text/plain`); +}); + +document.addEventListener(`dragstart`, (e) => + e.dataTransfer.setData(`text/plain`, `we dragged a ${e.target.tagName}`) +); diff --git a/demo/touch-simulation.js b/tests/integration/touch-simulation.js similarity index 86% rename from demo/touch-simulation.js rename to tests/integration/touch-simulation.js index 4327582..755b4f6 100644 --- a/demo/touch-simulation.js +++ b/tests/integration/touch-simulation.js @@ -21,11 +21,13 @@ function simulate(eventType, element, { x, y }) { element.dispatchEvent(event); } -export /* async */ function drag(from, to) { +export /* async */ function drag(from, to, options = {}) { const { left, width, top } = from.getBoundingClientRect(); const touch = { x: left + width / 2, y: top + 1, target: from }; simulate("touchstart", from, touch); + const timeout = options.dragDelay || false; + // simulate a dragging track const steps = 10; const [dx, dy] = (function (l, t) { @@ -34,17 +36,20 @@ export /* async */ function drag(from, to) { })(left, top); return new Promise((resolve) => { - (function drag(i = 0) { + function drag(i = 0) { if (i === steps - 1) { simulate("touchend", to, touch); - setTimeout(resolve, 10); + return setTimeout(resolve, 10); } touch.x += dx; touch.y += dy; touch.target = document; simulate("touchmove", to, touch); setTimeout(() => drag(i + 1), 100 / steps); - })(); + } + + if (!timeout) drag(); + else setTimeout(drag, timeout); }); } diff --git a/tests/touch.spec.js b/tests/touch.spec.js index e3a5df5..50a4b40 100644 --- a/tests/touch.spec.js +++ b/tests/touch.spec.js @@ -4,14 +4,19 @@ async function bootstrapPage(browser, options = {}) { const context = await browser.newContext(options); const page = await context.newPage(); page.on("console", (msg) => console.log(msg.text())); - await page.goto(`http://localhost:8000`); + if (options.page) { + await page.goto(`http://localhost:8000/${options.page}`); + } else { + await page.goto(`http://localhost:8000`); + } return page; } test.describe(`touch events`, () => { - let page; + let browser, page; - test.beforeEach(async ({ browser }) => { + test.beforeEach(async ({ browser: b }) => { + browser = b; page = await bootstrapPage(browser, { hasTouch: true, }); @@ -21,7 +26,6 @@ test.describe(`touch events`, () => { return page.evaluate((eventType) => { return new Promise((resolve) => { document.querySelector(qs).addEventListener(eventType, ({ type }) => { - // fill this it resolve({ type }); }); }); @@ -38,14 +42,14 @@ test.describe(`touch events`, () => { ); } - async function touchDragEntry(sourceSelector, targetSelector) { + async function touchDragEntry(sourceSelector, targetSelector, options) { await page.evaluate( - async ({ sourceSelector, targetSelector }) => { + async ({ sourceSelector, targetSelector, options }) => { const from = document.querySelector(sourceSelector); const to = document.querySelector(targetSelector); - await globalThis.simulatedTouch.drag(from, to); + await globalThis.simulatedTouch.drag(from, to, options); }, - { sourceSelector, targetSelector } + { sourceSelector, targetSelector, options} ); } @@ -98,4 +102,38 @@ test.describe(`touch events`, () => { expect(await e1.textContent()).toBe(`Input`); expect(await e2.textContent()).toBe(`Image`); }); + + test(`longpress with 0px drag threshold`, async () => { + page = await bootstrapPage(browser, { + hasTouch: true, + page: `tests/integration/issue-77b/index.html`, + }); + + const textContent = await page.locator(`text`).textContent(); + expect(textContent.trim()).toBe(`testing`); + + await page.evaluate(() => globalThis.enablePressHold()); + const from = `#from`; + const to = `#to`; + await touchDragEntry(from, to, { dragDelay: 500 }); + + expect(await page.locator(`#result`).textContent()).toBe(`we dragged a A`); + }); + + test(`longpress with 25px drag threshold`, async () => { + page = await bootstrapPage(browser, { + hasTouch: true, + page: `tests/integration/issue-77b/index.html`, + }); + + const textContent = await page.locator(`text`).textContent(); + expect(textContent.trim()).toBe(`testing`); + + await page.evaluate(() => globalThis.enablePressHold(25)); + const from = `#from`; + const to = `#to`; + await touchDragEntry(from, to, { dragDelay: 500 }); + + expect(await page.locator(`#result`).textContent()).toBe(`we dragged a A`); + }); }); diff --git a/ts/drag-drop-touch.ts b/ts/drag-drop-touch.ts index 4e7cdfa..b7e7a15 100644 --- a/ts/drag-drop-touch.ts +++ b/ts/drag-drop-touch.ts @@ -229,8 +229,13 @@ class DragDropTouch { }, this.configuration.contextMenuDelayMS); if (this.configuration.isPressHoldMode) { - DEBUG: console.log(`setting a press-hold timeout`); + DEBUG: console.log( + `setting a press-hold timeout for ${this.configuration.pressHoldDelayMS}ms`, + ); this._pressHoldIntervalId = setTimeout(() => { + DEBUG: console.log( + `this._isDragEnabled = true, calling touchMove`, + ); this._isDragEnabled = true; this._touchmove(e); }, this.configuration.pressHoldDelayMS); @@ -399,6 +404,14 @@ class DragDropTouch { * @returns */ _shouldCancelPressHoldMove(e: TouchEvent) { + DEBUG: { + console.log({ + isPressHoldMode: this.configuration.isPressHoldMode, + _isDragEnabled: this._isDragEnabled, + delta: this._getDelta(e), + pressHoldMargin: this.configuration.pressHoldMargin, + }); + } return ( this.configuration.isPressHoldMode && !this._isDragEnabled &&