FN-7155: keep goal mission picker natural-height

Keep the goal-to-mission link picker from stretching vertically in stacked layouts.

- Reset the linked mission picker flex sizing at tablet and mobile breakpoints while preserving full-width stacking.
- Add CSS regression coverage for the base flex value and breakpoint overrides.

Files changed:
 packages/dashboard/app/components/GoalsView.css    | 20 ++++++++-
 .../app/components/__tests__/GoalsView.css.test.ts | 48 ++++++++++++++++++++++
 2 files changed, 66 insertions(+), 2 deletions(-)

Fusion-Task-Id: FN-7155

Fusion-Task-Lineage: cad957bb-3efd-4435-9386-fcf84cb0d209

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
gsxdsm
2026-06-27 20:02:33 -07:00
parent bb89e1ec3c
commit c1216eb271
2 changed files with 66 additions and 2 deletions

View File

@@ -295,8 +295,16 @@ The goal card is a 3-column flex row: .goals-card-main | .goals-card-actions | .
align-items: stretch;
}
.goals-linked-missions-link-button,
/*
FNXC:Goals 2026-06-27-00:00:
FN-7155: In this tablet column layout, the picker flex-basis maps to height instead of width. Reset flex so the mission picker stays at native select height while preserving full-width stacking.
*/
.goals-linked-missions-picker {
flex: 0 0 auto;
width: 100%;
}
.goals-linked-missions-link-button {
width: 100%;
}
}
@@ -321,8 +329,16 @@ The goal card is a 3-column flex row: .goals-card-main | .goals-card-actions | .
border-top: calc(var(--space-xs) / 4) solid var(--border);
}
.goals-linked-missions-link-button,
/*
FNXC:Goals 2026-06-27-00:00:
FN-7155: In this mobile column layout, the picker flex-basis maps to height instead of width. Reset flex so the mission picker stays at native select height while preserving full-width stacking.
*/
.goals-linked-missions-picker {
flex: 0 0 auto;
width: 100%;
}
.goals-linked-missions-link-button {
width: 100%;
}

View File

@@ -6,6 +6,41 @@ function goalsBlocks(css: string): string {
return blocks.join("\n");
}
function extractRuleBlock(css: string, selector: string): string {
const escapedSelector = selector.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
const match = css.match(new RegExp(`${escapedSelector}\\s*\\{([\\s\\S]*?)\\}`));
return match?.[1] ?? "";
}
function extractAtRuleBlocks(css: string, atRule: string): string[] {
const blocks: string[] = [];
let start = css.indexOf(atRule);
while (start !== -1) {
const open = css.indexOf("{", start);
if (open === -1) break;
let depth = 1;
let cursor = open + 1;
while (cursor < css.length && depth > 0) {
if (css[cursor] === "{") depth++;
if (css[cursor] === "}") depth--;
cursor++;
}
blocks.push(css.slice(open + 1, cursor - 1));
start = css.indexOf(atRule, cursor);
}
return blocks;
}
function extractRuleBlockFromAtRule(css: string, atRule: string, selector: string): string {
return extractAtRuleBlocks(css, atRule)
.map((block) => extractRuleBlock(block, selector))
.find((block) => block !== "") ?? "";
}
describe("GoalsView CSS token guardrails", () => {
it("uses tokens and contains mobile/focus rules", async () => {
const css = await loadAllAppCss();
@@ -20,4 +55,17 @@ describe("GoalsView CSS token guardrails", () => {
expect(css).toMatch(/\.goals-[^\n{]*:focus-visible/g);
expect(css).toMatch(/@media \(max-width: 768px\)[\s\S]*\.goals-/);
});
it("keeps the linked mission picker natural-height in column breakpoints", async () => {
const css = await loadAllAppCss();
const basePickerBlock = extractRuleBlock(css, ".goals-linked-missions-picker");
const mobilePickerBlock = extractRuleBlockFromAtRule(css, "@media (max-width: 768px)", ".goals-linked-missions-picker");
const tabletPickerBlock = extractRuleBlockFromAtRule(css, "@media (min-width: 769px) and (max-width: 1024px)", ".goals-linked-missions-picker");
expect(basePickerBlock).toMatch(/flex\s*:\s*1\s+1\s+calc\(var\(--space-xl\)\s*\*\s*10\)/);
expect(mobilePickerBlock).toMatch(/flex\s*:\s*0\s+0\s+auto/);
expect(mobilePickerBlock).toMatch(/width\s*:\s*100%/);
expect(tabletPickerBlock).toMatch(/flex\s*:\s*0\s+0\s+auto/);
expect(tabletPickerBlock).toMatch(/width\s*:\s*100%/);
});
});