FN-6160: fix mobile quick-entry touch handling

Let mobile Quick Entry actions rely on native tap-to-click behavior again.

- remove the manual quick-entry action button click dispatch on touchend
- switch mobile quick-entry action buttons to touch-action: manipulation
- update QuickEntryBox mobile touch tests and add a patch changeset for @runfusion/fusion

Files changed:
 .changeset/tiny-mobile-clicks.md                           |  5 +++++
 packages/dashboard/app/components/QuickEntryBox.css        |  4 ++--
 packages/dashboard/app/components/QuickEntryBox.tsx        |  9 ---------
 packages/dashboard/app/components/__tests__/QuickEntryBox.test.tsx        | 14 +++++++++++++-
 4 files changed, 20 insertions(+), 12 deletions(-)

Fusion-Task-Id: FN-6160

Fusion-Task-Lineage: a8c13f7e-fb86-4087-a516-a6e16dac2092
This commit is contained in:
gsxdsm
2026-06-09 22:13:54 -07:00
parent e883a8df01
commit c27c321a8b
4 changed files with 20 additions and 12 deletions

View File

@@ -0,0 +1,5 @@
---
"@runfusion/fusion": patch
---
Fix mobile Quick Entry action buttons so taps rely on native browser click synthesis instead of a manual touchend click.

View File

@@ -428,11 +428,11 @@
} }
} }
/* === Quick Entry Mobile Touch + Overflow Fixes (FN-1140, FN-6153) === */ /* === Quick Entry Mobile Touch + Overflow Fixes (FN-1140, FN-6153, FN-6160) === */
@media (max-width: 768px) { @media (max-width: 768px) {
.quick-entry-actions, .quick-entry-actions,
.quick-entry-actions * { .quick-entry-actions * {
touch-action: none; touch-action: manipulation;
} }
.quick-entry-actions .btn { .quick-entry-actions .btn {

View File

@@ -1492,16 +1492,7 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
} }
}} }}
onTouchEnd={() => { onTouchEnd={() => {
const button = touchButtonRef.current;
touchButtonRef.current = null; touchButtonRef.current = null;
if (button && !button.disabled) {
button.click();
window.setTimeout(() => {
window.setTimeout(() => {
textareaRef.current?.focus({ preventScroll: true });
}, 0);
}, 0);
}
}} }}
onTouchCancel={() => { onTouchCancel={() => {
touchButtonRef.current = null; touchButtonRef.current = null;

View File

@@ -505,6 +505,7 @@ describe("QuickEntryBox", () => {
expect(preventDefaultSpy).toHaveBeenCalled(); expect(preventDefaultSpy).toHaveBeenCalled();
await act(async () => { await act(async () => {
fireEvent(button, new Event("touchend", { bubbles: true, cancelable: true })); fireEvent(button, new Event("touchend", { bubbles: true, cancelable: true }));
fireEvent.click(button);
vi.runOnlyPendingTimers(); vi.runOnlyPendingTimers();
vi.runOnlyPendingTimers(); vi.runOnlyPendingTimers();
}); });
@@ -530,6 +531,7 @@ describe("QuickEntryBox", () => {
expect(preventDefaultSpy).toHaveBeenCalled(); expect(preventDefaultSpy).toHaveBeenCalled();
await act(async () => { await act(async () => {
fireEvent(svg!, new Event("touchend", { bubbles: true, cancelable: true })); fireEvent(svg!, new Event("touchend", { bubbles: true, cancelable: true }));
fireEvent.click(priorityButton);
vi.runOnlyPendingTimers(); vi.runOnlyPendingTimers();
vi.runOnlyPendingTimers(); vi.runOnlyPendingTimers();
}); });
@@ -569,6 +571,7 @@ describe("QuickEntryBox", () => {
expect(preventDefaultSpy).toHaveBeenCalled(); expect(preventDefaultSpy).toHaveBeenCalled();
await act(async () => { await act(async () => {
fireEvent(svg!, new Event("touchend", { bubbles: true, cancelable: true })); fireEvent(svg!, new Event("touchend", { bubbles: true, cancelable: true }));
fireEvent.click(githubToggle);
vi.runOnlyPendingTimers(); vi.runOnlyPendingTimers();
vi.runOnlyPendingTimers(); vi.runOnlyPendingTimers();
}); });
@@ -653,10 +656,19 @@ describe("QuickEntryBox", () => {
expect(document.activeElement).toBe(textarea); expect(document.activeElement).toBe(textarea);
await act(async () => { await act(async () => {
fireEvent(button, new Event("touchend", { bubbles: true, cancelable: true })); fireEvent(button, new Event("touchend", { bubbles: true, cancelable: true }));
fireEvent.click(button);
vi.runOnlyPendingTimers(); vi.runOnlyPendingTimers();
vi.runOnlyPendingTimers(); vi.runOnlyPendingTimers();
}); });
expect(document.activeElement).toBe(textarea); expect(document.activeElement).toBe(textarea);
const outsideElement = document.createElement("div");
document.body.appendChild(outsideElement);
try {
fireEvent.mouseDown(outsideElement);
} finally {
document.body.removeChild(outsideElement);
}
}); });
it("does not fire disabled button actions via touch", async () => { it("does not fire disabled button actions via touch", async () => {
@@ -3218,7 +3230,7 @@ describe("QuickEntryBox", () => {
const touchRule = quickEntryMobileActionsTouchRule(); const touchRule = quickEntryMobileActionsTouchRule();
expect(touchRule).toMatch(/\.quick-entry-actions,\s*\.quick-entry-actions \*/); expect(touchRule).toMatch(/\.quick-entry-actions,\s*\.quick-entry-actions \*/);
expect(touchRule).toMatch(/touch-action:\s*none;/); expect(touchRule).toMatch(/touch-action:\s*manipulation;/);
}); });
it("keeps inline deps/models controls in touch-target button classes on mobile", () => { it("keeps inline deps/models controls in touch-target button classes on mobile", () => {