diff --git a/.changeset/fn-7703-search-icon-overlap.md b/.changeset/fn-7703-search-icon-overlap.md new file mode 100644 index 0000000000..800dd357bf --- /dev/null +++ b/.changeset/fn-7703-search-icon-overlap.md @@ -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. diff --git a/packages/dashboard/app/components/FileBrowser.css b/packages/dashboard/app/components/FileBrowser.css index 188e32ecb1..19a445ba41 100644 --- a/packages/dashboard/app/components/FileBrowser.css +++ b/packages/dashboard/app/components/FileBrowser.css @@ -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 { diff --git a/packages/dashboard/app/components/__tests__/FileBrowser.test.tsx b/packages/dashboard/app/components/__tests__/FileBrowser.test.tsx index 79277b5bed..ad9419b3fd 100644 --- a/packages/dashboard/app/components/__tests__/FileBrowser.test.tsx +++ b/packages/dashboard/app/components/__tests__/FileBrowser.test.tsx @@ -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 = { + "--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", () => {