FN-8624: fix task detail activity right padding
Keep Task Detail activity insets symmetric while reserving overlay clearance only for first-row content. - Move Live/Feed overlay clearance from the activity container to first-row elements. - Preserve responsive overlay spacing and the full-width Interventions timeline. - Cover modal, pop-out, embedded, and responsive activity layouts with updated tests. - Add a patch changeset for the Task Detail padding fix. Files changed: .changeset/fn-8624-task-detail-inset.md | 7 ++++ .../dashboard/app/components/TaskDetailModal.css | 41 ++++++++++------------ .../TaskDetailModal.attachments-and-tabs.test.tsx | 6 ++-- .../__tests__/TaskDetailModal.css.test.ts | 21 ++++++----- ...etailModal.responsive-and-dependencies.test.tsx | 25 +++++++------ 5 files changed, 54 insertions(+), 46 deletions(-) Fusion-Task-Id: FN-8624 Fusion-Task-Lineage: 578cfe86-3021-421d-841b-2670efc10a5d Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
7
.changeset/fn-8624-task-detail-inset.md
Normal file
7
.changeset/fn-8624-task-detail-inset.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
summary: Fix asymmetric right padding in Task Detail activity content.
|
||||
category: fix
|
||||
dev: Overlay clearance now applies only to Activity first rows at every breakpoint.
|
||||
@@ -3258,20 +3258,27 @@ The body-portaled menu must paint above the owning task-detail modal/popup while
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/*
|
||||
FNXC:TaskDetailActivity 2026-07-27-02:15:
|
||||
FN-8624 requires Task Detail content insets to stay symmetric at every breakpoint,
|
||||
including modal, pop-out, and embedded surfaces. The fixed Live/Feed overlay can cover
|
||||
only the first row, so its tokenized clearance belongs there rather than on this container;
|
||||
this generalizes FN-8166's mobile-only correction to desktop and tablet.
|
||||
*/
|
||||
.detail-activity {
|
||||
position: relative;
|
||||
padding-inline-end: 0;
|
||||
}
|
||||
|
||||
.detail-activity:not(.detail-activity--interventions) > h4,
|
||||
.detail-activity:not(.detail-activity--interventions) > .detail-log-truncated,
|
||||
.detail-activity:not(.detail-activity--interventions) > .detail-log-loading,
|
||||
.detail-activity:not(.detail-activity--interventions) > .detail-log-empty,
|
||||
.detail-activity:not(.detail-activity--interventions) > .detail-activity-list > .detail-log-entry:first-child {
|
||||
padding-inline-end: calc(var(--space-2xl) + var(--space-md));
|
||||
}
|
||||
|
||||
/*
|
||||
FNXC:PlannerOversight 2026-07-05-00:00:
|
||||
The Interventions Activity segment (FN-7571) renders no `.activity-expand-toggle--overlay`
|
||||
button, so it must not reserve the shared `.detail-activity` padding-inline-end that exists
|
||||
solely to keep that overlay toggle from covering Live/Feed content. Without this override the
|
||||
FN-7519 Intervention Timeline is inset from the right edge (worse on mobile, where the base
|
||||
reserved padding grows). FN-7581 zeroes the reserved padding for this segment only; Live/Feed/
|
||||
Raw keep the base rule unchanged.
|
||||
*/
|
||||
/* FNXC:PlannerOversight 2026-07-27-02:15: Retain the explicit zero inset for the overlay-free Interventions segment; it documents the FN-7581 full-width timeline contract even though the shared container is now symmetric. */
|
||||
.detail-activity--interventions {
|
||||
padding-inline-end: 0;
|
||||
}
|
||||
@@ -3315,15 +3322,10 @@ Live and Feed Activity expansion overlays the content instead of reserving a too
|
||||
}
|
||||
|
||||
/*
|
||||
FNXC:TaskDetailActivity 2026-07-16-00:00:
|
||||
FN-8166 removes the residual mobile Feed container right inset so `.detail-body`
|
||||
provides equal left and right insets. Reserve overlay-toggle clearance only on
|
||||
top-of-panel first-row elements, keeping the first visible row uncovered.
|
||||
FNXC:TaskDetailActivity 2026-07-27-02:15:
|
||||
FN-8624 keeps the base container symmetric; at mobile sizes the smaller fixed
|
||||
overlay needs correspondingly smaller first-row clearance without covering log text.
|
||||
*/
|
||||
.detail-activity {
|
||||
padding-inline-end: 0;
|
||||
}
|
||||
|
||||
.detail-activity:not(.detail-activity--interventions) > h4,
|
||||
.detail-activity:not(.detail-activity--interventions) > .detail-log-truncated,
|
||||
.detail-activity:not(.detail-activity--interventions) > .detail-log-loading,
|
||||
@@ -3332,11 +3334,6 @@ Live and Feed Activity expansion overlays the content instead of reserving a too
|
||||
padding-inline-end: calc(var(--space-2xl) + var(--space-sm));
|
||||
}
|
||||
|
||||
/* FNXC:PlannerOversight 2026-07-05-00:00: mobile reserves more padding for the overlay toggle; neutralize it for Interventions too so FN-7519's timeline stays full width on mobile per FN-7581. */
|
||||
.detail-activity--interventions {
|
||||
padding-inline-end: 0;
|
||||
}
|
||||
|
||||
.activity-expand-toggle {
|
||||
min-inline-size: calc(var(--space-2xl) + var(--space-sm));
|
||||
min-block-size: calc(var(--space-2xl) + var(--space-sm));
|
||||
|
||||
@@ -928,12 +928,14 @@ describe("TaskDetailModal", () => {
|
||||
|
||||
expect(css).not.toContain(".activity-toolbar");
|
||||
expect(css).not.toContain("activity-toolbar--expand-only");
|
||||
expect(css).toContain(".detail-activity {\n position: relative;\n padding-inline-end: calc(var(--space-2xl) + var(--space-md));\n}");
|
||||
expect(css).toContain(".detail-activity {\n position: relative;\n padding-inline-end: 0;\n}");
|
||||
expect(css).toContain(".detail-activity:not(.detail-activity--interventions) > h4,");
|
||||
expect(css).toContain("padding-inline-end: calc(var(--space-2xl) + var(--space-md));");
|
||||
expect(activityOverlayRule).toContain("position: absolute");
|
||||
expect(activityOverlayRule).toContain("top: var(--space-md)");
|
||||
expect(activityOverlayRule).toContain("right: var(--space-md)");
|
||||
expect(activityOverlayRule).toContain("z-index: 3");
|
||||
expect(mobileCss).toContain(" .detail-activity {\n padding-inline-end: 0;\n }");
|
||||
expect(mobileCss).not.toContain(" .detail-activity {\n padding-inline-end:");
|
||||
expect(mobileCss).toContain(" .activity-expand-toggle--overlay {\n top: var(--space-sm);\n right: var(--space-sm);\n }");
|
||||
expect(expandedTitleRule).not.toContain("display: none");
|
||||
expect(expandedMetaRule).toContain("display: none");
|
||||
|
||||
@@ -45,13 +45,12 @@ describe("TaskDetailModal CSS contract", () => {
|
||||
});
|
||||
|
||||
/*
|
||||
FNXC:TaskDetailActivity 2026-07-18-07:25:
|
||||
FN-8166 zeroed mobile `.detail-activity` padding-inline-end (equal insets from
|
||||
`.detail-body`); overlay clearance lives only on first-row selectors. Keep the
|
||||
contract aligned with TaskDetailModal.css so full-suite does not assert the
|
||||
pre-8166 `var(--space-md)` residual inset.
|
||||
FNXC:TaskDetailActivity 2026-07-27-02:15:
|
||||
FN-8624 extends FN-8166's symmetric Activity container inset from mobile to
|
||||
desktop and tablet. Only possible overlay-covered first rows may reserve tokenized
|
||||
clearance, so modal, pop-out, and embedded task-detail renders share this CSS contract.
|
||||
*/
|
||||
it("FN-8154 keeps the mobile Feed inset narrow while clearing its overlay toggle from first rows", async () => {
|
||||
it("FN-8624 keeps Activity container insets symmetric while clearing the overlay from first rows", async () => {
|
||||
const css = await loadAllAppCss();
|
||||
const baseCss = await loadAllAppCssBaseOnly();
|
||||
const mobileCss = css.slice(css.indexOf("@media (max-width: 768px)"));
|
||||
@@ -63,13 +62,13 @@ describe("TaskDetailModal CSS contract", () => {
|
||||
".detail-activity:not(.detail-activity--interventions) > .detail-activity-list > .detail-log-entry:first-child",
|
||||
];
|
||||
|
||||
expect(baseCss).toContain(".detail-activity {\n position: relative;\n padding-inline-end: calc(var(--space-2xl) + var(--space-md));\n}");
|
||||
expect(mobileCss).toContain(" .detail-activity {\n padding-inline-end: 0;\n }");
|
||||
expect(mobileCss).not.toContain(" .detail-activity {\n padding-inline-end: calc(var(--space-2xl) + var(--space-lg));\n }");
|
||||
expect(mobileCss).not.toContain(" .detail-activity {\n padding-inline-end: var(--space-md);\n }");
|
||||
expect(mobileCss).toContain(" .detail-activity--interventions {\n padding-inline-end: 0;\n }");
|
||||
expect(baseCss).toContain(".detail-activity {\n position: relative;\n padding-inline-end: 0;\n}");
|
||||
expect(baseCss).toContain(".detail-activity--interventions {\n padding-inline-end: 0;\n}");
|
||||
expect(baseCss).not.toContain(".detail-activity {\n position: relative;\n padding-inline-end: calc(var(--space-2xl) + var(--space-md));\n}");
|
||||
expect(mobileCss).not.toContain(" .detail-activity {\n padding-inline-end:");
|
||||
for (const selector of firstRowSelectors) {
|
||||
const escapedSelector = selector.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
expect(baseCss, selector).toMatch(new RegExp(`${escapedSelector}(?:\\s*,\\s*[^{}]+)*\\s*\\{[^}]*padding-inline-end: calc\\(var\\(--space-2xl\\) \\+ var\\(--space-md\\)\\);`));
|
||||
expect(mobileCss, selector).toMatch(new RegExp(`${escapedSelector}(?:\\s*,\\s*[^{}]+)*\\s*\\{[^}]*padding-inline-end: calc\\(var\\(--space-2xl\\) \\+ var\\(--space-sm\\)\\);`));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -729,23 +729,24 @@ describe("TaskDetailModal", () => {
|
||||
|
||||
expect(css).not.toContain(".activity-toolbar");
|
||||
expect(css).not.toContain("activity-toolbar--expand-only");
|
||||
expect(css).toContain(".detail-activity {\n position: relative;\n padding-inline-end: calc(var(--space-2xl) + var(--space-md));\n}");
|
||||
expect(css).toContain(".detail-activity {\n position: relative;\n padding-inline-end: 0;\n}");
|
||||
expect(css).toContain(".detail-activity:not(.detail-activity--interventions) > h4,");
|
||||
expect(css).toContain("padding-inline-end: calc(var(--space-2xl) + var(--space-md));");
|
||||
expect(overlayBlock).toContain("position: absolute;");
|
||||
expect(overlayBlock).toContain("top: var(--space-md);");
|
||||
expect(overlayBlock).toContain("right: var(--space-md);");
|
||||
expect(mobileBlock).toContain(" .detail-activity {\n padding-inline-end: 0;\n }");
|
||||
expect(mobileBlock).not.toContain(" .detail-activity {\n padding-inline-end:");
|
||||
expect(mobileOverlayBlock).toContain("top: var(--space-sm);");
|
||||
expect(mobileOverlayBlock).toContain("right: var(--space-sm);");
|
||||
|
||||
/*
|
||||
FNXC:TaskDetailActivity 2026-07-16-00:00:
|
||||
FN-8166 requires Feed to inherit the symmetric mobile `.detail-body` inset,
|
||||
while only its first visible row reserves space for the opaque overlay toggle.
|
||||
This contract also prevents non-Feed task-detail surfaces from adding a
|
||||
container-level right-only inset or mobile horizontal overflow.
|
||||
FNXC:TaskDetailActivity 2026-07-27-02:15:
|
||||
FN-8624 requires Feed to inherit the symmetric `.detail-body` inset at every
|
||||
breakpoint, while only its first visible row reserves space for the opaque overlay.
|
||||
This contract covers modal, pop-out, embedded, and mobile task-detail surfaces.
|
||||
*/
|
||||
const mobileDetailBodyBlock = getExactCssRuleBlock(mobileBlock, ".detail-body");
|
||||
const mobileInterventionsBlock = getExactCssRuleBlock(mobileBlock, ".detail-activity--interventions");
|
||||
const baseInterventionsBlock = getExactCssRuleBlock(css, ".detail-activity--interventions");
|
||||
const mobilePrBlock = getExactCssRuleBlock(
|
||||
getCssAtRuleBlockContainingExactRule(css, "@media (max-width: 768px)", ".detail-pr-tab"),
|
||||
".detail-pr-tab",
|
||||
@@ -758,7 +759,7 @@ describe("TaskDetailModal", () => {
|
||||
|
||||
expect(mobileDetailBodyBlock).toContain("padding: calc(var(--space-md) + var(--space-xs) / 2);");
|
||||
expect(mobileDetailBodyBlock).toContain("overflow-x: hidden;");
|
||||
expect(mobileInterventionsBlock).toContain("padding-inline-end: 0;");
|
||||
expect(baseInterventionsBlock).toContain("padding-inline-end: 0;");
|
||||
expect(mobileBlock).toContain(".detail-activity:not(.detail-activity--interventions) > h4,");
|
||||
expect(mobileBlock).toContain(".detail-activity:not(.detail-activity--interventions) > .detail-log-loading,");
|
||||
expect(mobileBlock).toContain(".detail-activity:not(.detail-activity--interventions) > .detail-log-empty,");
|
||||
@@ -797,6 +798,7 @@ describe("TaskDetailModal", () => {
|
||||
const mobileScrollbarBlock = getExactCssRuleBlock(mobileBlock, ".detail-body::-webkit-scrollbar");
|
||||
const mobileActivityBlock = getExactCssRuleBlock(mobileBlock, ".detail-activity");
|
||||
const mobileInterventionsBlock = getExactCssRuleBlock(mobileBlock, ".detail-activity--interventions");
|
||||
const baseInterventionsBlock = getExactCssRuleBlock(css, ".detail-activity--interventions");
|
||||
const mobilePrBlock = getExactCssRuleBlock(
|
||||
getCssAtRuleBlockContainingExactRule(css, "@media (max-width: 768px)", ".detail-pr-tab"),
|
||||
".detail-pr-tab",
|
||||
@@ -815,9 +817,10 @@ describe("TaskDetailModal", () => {
|
||||
expect(mobileDetailBodyBlock).toContain("scrollbar-width: none;");
|
||||
expect(mobileScrollbarBlock).toContain("display: none;");
|
||||
|
||||
expect(mobileActivityBlock).toContain("padding-inline-end: 0;");
|
||||
expect(mobileActivityBlock).toBe("");
|
||||
expect(mobileBlock).toContain("padding-inline-end: calc(var(--space-2xl) + var(--space-sm));");
|
||||
expect(mobileInterventionsBlock).toContain("padding-inline-end: 0;");
|
||||
expect(mobileInterventionsBlock).toBe("");
|
||||
expect(baseInterventionsBlock).toContain("padding-inline-end: 0;");
|
||||
expect(mobilePrBlock.trim()).toBe("gap: var(--space-md);");
|
||||
expect(allMobileCss).not.toMatch(/\.task-changes-tab\s*\{[^}]*\bpadding(?:-[\w-]+)?\s*:/);
|
||||
expect(embeddedBodyBlock).toContain("width: 100%;");
|
||||
|
||||
Reference in New Issue
Block a user