FN-7703: fix search icon overlapping text in file browser search input

Fixes the search icon overlapping placeholder/typed text in the Files — Project search input under the compact spacing theme.

- Anchor .file-browser-search-input padding-left to the icon's own --space-sm offset + 16px icon width + a real gap, instead of the unrelated calc(--space-lg + --space-md) formula that collided exactly with the icon's occupied width under compact spacing
- Add FNXC:FileBrowser comment documenting the collision math and why the padding is now theme-invariant
- Add a regression test asserting padding-left exceeds icon offset + width against the compact spacing scale
- Add a patch changeset for @runfusion/fusion

Files changed:
 .changeset/fn-7703-search-icon-overlap.md          |  7 +++
 packages/dashboard/app/components/FileBrowser.css  | 11 ++++-
 .../app/components/__tests__/FileBrowser.test.tsx  | 52 ++++++++++++++++++++++
 3 files changed, 69 insertions(+), 1 deletion(-)

Fusion-Task-Id: FN-7703

Fusion-Task-Lineage: d7de7f82-f0e7-4086-a8ef-2fed2b4704ec

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
gsxdsm
2026-07-08 19:30:00 -07:00
parent 3e7e4a865b
commit 22e7d75a07
3 changed files with 69 additions and 1 deletions

View File

@@ -0,0 +1,7 @@
---
"@runfusion/fusion": patch
---
summary: Fix the search icon overlapping typed/placeholder text in the Files — Project search input.
category: fix
dev: `.file-browser-search-input` `padding-left` was `calc(var(--space-lg) + var(--space-md))`, which collided exactly with the leading `.file-browser-search-icon`'s occupied width (`var(--space-sm)` offset + 16px icon) under the compact spacing theme. Padding is now anchored to the same `--space-sm` offset the icon uses, plus the icon's box width, plus a real gap, so clearance holds across all spacing scales for both the FileBrowser view and modal.

View File

@@ -209,10 +209,19 @@
pointer-events: none;
}
/*
FNXC:FileBrowser 2026-07-08-00:00:
Search input left padding must exceed the leading icon's offset + width so the
magnifier never overlaps placeholder/typed text. The prior calc(--space-lg + --space-md)
collided exactly with the icon's right edge (--space-sm offset + 16px icon) under the
compact spacing theme (space-sm:4px, space-md:8px, space-lg:12px -> 12+8=20 == 4+16),
leaving zero gap. Anchor the padding to the same --space-sm offset the icon uses, plus
the icon's box width, plus a visible gap, so the relationship stays theme-invariant.
*/
.file-browser-search-input {
width: 100%;
min-width: 0;
padding-left: calc(var(--space-lg) + var(--space-md));
padding-left: calc(var(--space-sm) + 16px + var(--space-sm));
}
.file-browser-create-button {

View File

@@ -611,6 +611,58 @@ describe("FileBrowser", () => {
expect(css).toMatch(/\.file-browser-context-menu__item\s*\{[^}]*min-height:\s*36px;/);
});
// FN-7703: the leading magnifier icon in the "Search project files" input
// was overlapping the placeholder/typed text because the input's
// padding-left did not reserve enough clearance for the absolutely
// positioned icon. Under the compact spacing theme (--space-sm: 4px),
// the old formula calc(var(--space-lg) + var(--space-md)) == 12px + 8px
// == 20px collided exactly with the icon's occupied width
// (--space-sm offset 4px + 16px icon == 20px), leaving zero gap. This
// test encodes the invariant directly against the declared CSS formulas
// (rather than jsdom computed styles, which do not resolve calc()/var())
// so it fails against the pre-fix rule and passes once padding-left is
// anchored to the icon's own offset + width + a real gap.
it("reserves search input padding-left beyond the leading icon's offset + width under the compact spacing theme", () => {
const css = loadAllAppCss();
const iconRuleMatch = css.match(/\.file-browser-search-icon\s*\{([^}]*)\}/);
const inputRuleMatch = css.match(/\.file-browser-search-input\s*\{([^}]*)\}/);
expect(iconRuleMatch).toBeTruthy();
expect(inputRuleMatch).toBeTruthy();
const iconLeftMatch = iconRuleMatch![1].match(/left:\s*([^;]+);/);
const paddingLeftMatch = inputRuleMatch![1].match(/padding-left:\s*([^;]+);/);
expect(iconLeftMatch).toBeTruthy();
expect(paddingLeftMatch).toBeTruthy();
// Compact spacing scale from public/theme-data.css (the failing case).
const compactSpaceTokens: Record<string, number> = {
"--space-sm": 4,
"--space-md": 8,
"--space-lg": 12,
};
function resolvePx(expr: string): number {
let normalized = expr.trim();
if (normalized.startsWith("calc(") && normalized.endsWith(")")) {
normalized = normalized.slice(5, -1);
}
for (const [token, value] of Object.entries(compactSpaceTokens)) {
normalized = normalized.split(`var(${token})`).join(String(value));
}
normalized = normalized.replace(/px/g, "");
expect(normalized).toMatch(/^[0-9+\-*/.\s]+$/);
return Function(`"use strict"; return (${normalized});`)();
}
const iconLeftPx = resolvePx(iconLeftMatch![1]);
const paddingLeftPx = resolvePx(paddingLeftMatch![1]);
const iconWidthPx = 16; // Search size={16} in FileBrowser.tsx
// The regression: text must clear the icon's full occupied width, not just its offset.
expect(paddingLeftPx).toBeGreaterThan(iconLeftPx + iconWidthPx);
});
// ── Download Actions ────────────────────────────────────────────────
it("opens download URL for file when Download is clicked", () => {