FN-7048: enable mobile detail tab swiping
Allow task detail tabs to keep horizontal touch scrolling on mobile surfaces. - Add pan-x touch-action overrides on detail tab buttons for modal, embedded, mobile, and tablet contexts. - Extend responsive CSS coverage to assert each task-detail tab target allows horizontal panning. - Add a patch changeset documenting the mobile task-detail tab scroll fix. Files changed: .changeset/fn-7048-task-detail-mobile-tab-touch-action.md | 7 +++++++ packages/dashboard/app/components/TaskDetailModal.css | 14 ++++++++++++++ .../TaskDetailModal.responsive-and-dependencies.test.tsx | 14 +++++++++++++- 3 files changed, 34 insertions(+), 1 deletion(-) Fusion-Task-Id: FN-7048 Fusion-Task-Lineage: bb07b72f-8843-4ed4-a45b-ea8172bc4957
This commit is contained in:
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
"@runfusion/fusion": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
summary: Restore mobile swipe scrolling when touching task-detail tab buttons.
|
||||||
|
category: fix
|
||||||
|
dev: Adds detail-tab touch-action pan-x coverage to override the global mobile pan-y lock.
|
||||||
@@ -1025,6 +1025,10 @@ FN-6500 fixes a tablet regression from FN-5599: the task-detail overlay offset a
|
|||||||
touch-action: pan-x pan-y;
|
touch-action: pan-x pan-y;
|
||||||
-webkit-overflow-scrolling: touch;
|
-webkit-overflow-scrolling: touch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.detail-tab {
|
||||||
|
touch-action: pan-x pan-y;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
@@ -1133,6 +1137,10 @@ Embedded in the full-width board-card panel the detail content must be width-bou
|
|||||||
-webkit-overflow-scrolling: touch;
|
-webkit-overflow-scrolling: touch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.task-detail-content--embedded .detail-tab {
|
||||||
|
touch-action: pan-x pan-y;
|
||||||
|
}
|
||||||
|
|
||||||
/* The gray header row must wrap (task id left, Back-to-board right) instead of overflowing on narrow panels. */
|
/* The gray header row must wrap (task id left, Back-to-board right) instead of overflowing on narrow panels. */
|
||||||
.task-detail-content--embedded .modal-header {
|
.task-detail-content--embedded .modal-header {
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@@ -1940,6 +1948,9 @@ Remove the divider between the tab selector and the detail area below. Keep the
|
|||||||
|
|
||||||
FNXC:TaskDetailTabs 2026-06-25-16:55:
|
FNXC:TaskDetailTabs 2026-06-25-16:55:
|
||||||
Task-detail tabs must remain reachable as a one-row horizontal scroller on mobile across the Board modal and List embedded pane. The tab strip is the scroller, opts out of the global mobile pan-y lock with pan-x pan-y, and keeps tab children non-shrinking so conditional and plugin tabs overflow-scroll instead of clipping or compressing.
|
Task-detail tabs must remain reachable as a one-row horizontal scroller on mobile across the Board modal and List embedded pane. The tab strip is the scroller, opts out of the global mobile pan-y lock with pan-x pan-y, and keeps tab children non-shrinking so conditional and plugin tabs overflow-scroll instead of clipping or compressing.
|
||||||
|
|
||||||
|
FNXC:TaskDetailTabs 2026-06-26-00:35:
|
||||||
|
`touch-action` is not inherited, so the tab buttons must also opt into pan-x because they are the real touch targets. A container-only opt-in leaves touches that start on a `.detail-tab` under the global mobile `* { touch-action: pan-y; }` lock and blocks horizontal swipes.
|
||||||
*/
|
*/
|
||||||
.detail-tabs {
|
.detail-tabs {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -1958,6 +1969,7 @@ Task-detail tabs must remain reachable as a one-row horizontal scroller on mobil
|
|||||||
|
|
||||||
.detail-tab {
|
.detail-tab {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
touch-action: pan-x pan-y;
|
||||||
padding: var(--space-sm) var(--space-lg);
|
padding: var(--space-sm) var(--space-lg);
|
||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
@@ -2042,6 +2054,7 @@ Task-detail tabs must remain reachable as a one-row horizontal scroller on mobil
|
|||||||
|
|
||||||
.detail-tab {
|
.detail-tab {
|
||||||
min-height: 36px;
|
min-height: 36px;
|
||||||
|
touch-action: pan-x pan-y;
|
||||||
}
|
}
|
||||||
|
|
||||||
.detail-source-grid,
|
.detail-source-grid,
|
||||||
@@ -2150,6 +2163,7 @@ Task-detail tabs must remain reachable as a one-row horizontal scroller on mobil
|
|||||||
}
|
}
|
||||||
|
|
||||||
.detail-tab {
|
.detail-tab {
|
||||||
|
touch-action: pan-x pan-y;
|
||||||
padding: var(--space-sm) var(--space-md);
|
padding: var(--space-sm) var(--space-md);
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,6 +84,10 @@ function expectHorizontalTabScroller(ruleBlock: string, surface: string): void {
|
|||||||
expect(ruleBlock, `${surface} momentum-scroll`).toContain("-webkit-overflow-scrolling: touch;");
|
expect(ruleBlock, `${surface} momentum-scroll`).toContain("-webkit-overflow-scrolling: touch;");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function expectTabTouchAction(ruleBlock: string, surface: string): void {
|
||||||
|
expect(ruleBlock, `${surface} touch-action`).toContain("touch-action: pan-x pan-y;");
|
||||||
|
}
|
||||||
|
|
||||||
describe("TaskDetailModal", () => {
|
describe("TaskDetailModal", () => {
|
||||||
describe("mobile responsive structure", () => {
|
describe("mobile responsive structure", () => {
|
||||||
it("keeps detail metadata as a single wrapping flex row without mobile column fallbacks", () => {
|
it("keeps detail metadata as a single wrapping flex row without mobile column fallbacks", () => {
|
||||||
@@ -157,12 +161,20 @@ describe("TaskDetailModal", () => {
|
|||||||
const embeddedTabsBlock = getExactCssRuleBlock(css, ".task-detail-content--embedded .detail-tabs");
|
const embeddedTabsBlock = getExactCssRuleBlock(css, ".task-detail-content--embedded .detail-tabs");
|
||||||
const detailContentBlock = getCssRuleBlock(css, ".task-detail-content");
|
const detailContentBlock = getCssRuleBlock(css, ".task-detail-content");
|
||||||
const detailBodyBlock = getCssRuleBlock(css, ".detail-body");
|
const detailBodyBlock = getCssRuleBlock(css, ".detail-body");
|
||||||
const detailTabBlock = getCssRuleBlock(css, ".detail-tab");
|
const baseDetailTabsSection = css.slice(css.indexOf("/* === Detail Tabs === */"));
|
||||||
|
const detailTabBlock = getExactCssRuleBlock(baseDetailTabsSection, ".detail-tab");
|
||||||
|
const mobileTabBlock = getExactCssRuleBlock(mobileBlock, ".detail-tab");
|
||||||
|
const tabletTabBlock = getExactCssRuleBlock(tabletBlock, ".detail-tab");
|
||||||
|
const embeddedTabBlock = getExactCssRuleBlock(css, ".task-detail-content--embedded .detail-tab");
|
||||||
|
|
||||||
expectHorizontalTabScroller(baseTabsBlock, "base .detail-tabs");
|
expectHorizontalTabScroller(baseTabsBlock, "base .detail-tabs");
|
||||||
expectHorizontalTabScroller(mobileTabsBlock, "mobile .detail-tabs");
|
expectHorizontalTabScroller(mobileTabsBlock, "mobile .detail-tabs");
|
||||||
expectHorizontalTabScroller(tabletTabsBlock, "tablet .detail-tabs");
|
expectHorizontalTabScroller(tabletTabsBlock, "tablet .detail-tabs");
|
||||||
expectHorizontalTabScroller(embeddedTabsBlock, "embedded .detail-tabs");
|
expectHorizontalTabScroller(embeddedTabsBlock, "embedded .detail-tabs");
|
||||||
|
expectTabTouchAction(detailTabBlock, "base .detail-tab");
|
||||||
|
expectTabTouchAction(mobileTabBlock, "mobile .detail-tab");
|
||||||
|
expectTabTouchAction(tabletTabBlock, "tablet .detail-tab");
|
||||||
|
expectTabTouchAction(embeddedTabBlock, "embedded .detail-tab");
|
||||||
expect(baseTabsBlock).toContain("min-width: 0;");
|
expect(baseTabsBlock).toContain("min-width: 0;");
|
||||||
expect(mobileTabsBlock).toContain("min-width: 0;");
|
expect(mobileTabsBlock).toContain("min-width: 0;");
|
||||||
expect(detailTabBlock).toContain("flex-shrink: 0;");
|
expect(detailTabBlock).toContain("flex-shrink: 0;");
|
||||||
|
|||||||
Reference in New Issue
Block a user