feat(FN-1442): replace emoji scope icons with theme-aware Lucide icons

- Replace emoji icons (🌐, 📁) with Lucide Globe and Folder icons in Settings sidebar scope indicators
- Icons now automatically adapt to light/dark theme colors for better visual consistency
- Update SettingsModal tests to validate Lucide icon rendering
- Add mobile-specific tests for settings scope indicators
This commit is contained in:
gsxdsm
2026-04-10 09:19:16 -07:00
parent 22370960a2
commit c94e2eaa37
3 changed files with 39 additions and 14 deletions

View File

@@ -579,12 +579,12 @@ export function SettingsModal({
setPresetDraft(null);
};
/** Render a scope indicator banner for the current section */
/** Render a scope indicator banner for the current section with theme-aware Lucide icons */
const renderScopeBanner = () => {
if (activeSectionScope === "global") {
return (
<div className="settings-scope-banner settings-scope-global">
<span>🌐</span>
<span className="settings-scope-icon"><Globe size={14} /></span>
<span>These settings are shared across all your Fusion projects.</span>
</div>
);
@@ -592,7 +592,7 @@ export function SettingsModal({
if (activeSectionScope === "project") {
return (
<div className="settings-scope-banner settings-scope-project">
<span>📁</span>
<span className="settings-scope-icon"><Folder size={14} /></span>
<span>These settings only affect this project.</span>
</div>
);
@@ -600,7 +600,8 @@ export function SettingsModal({
if (activeSectionScope === "mixed") {
return (
<div className="settings-scope-banner settings-scope-mixed">
<span>🌐📁</span>
<span className="settings-scope-icon"><Globe size={14} /></span>
<span className="settings-scope-icon"><Folder size={14} /></span>
<span>
This section contains both global settings (default &amp; fallback models) and project
settings (planning, validator, presets, and AI summarization).

View File

@@ -2040,7 +2040,7 @@ describe("SettingsModal", () => {
expect(payload.maxStuckKills).toBeUndefined();
});
it("scope banners render for global and project sections", async () => {
it("scope banners render for global and project sections with theme-aware icons", async () => {
const { container } = render(<SettingsModal onClose={onClose} addToast={addToast} />);
await waitFor(() => expect(fetchSettings).toHaveBeenCalled());
@@ -2048,22 +2048,34 @@ describe("SettingsModal", () => {
expect(container.querySelector(".settings-scope-project")).toBeNull();
expect(container.querySelector(".settings-scope-global")).toBeNull();
// Switch to Appearance → should show global banner
// Switch to Appearance → should show global banner with Globe icon (SVG, not emoji)
fireEvent.click(screen.getAllByText("Appearance")[0]);
expect(container.querySelector(".settings-scope-global")).toBeTruthy();
expect(container.querySelector(".settings-scope-global")?.textContent).toContain("Fusion");
const globalBanner = container.querySelector(".settings-scope-global");
expect(globalBanner).toBeTruthy();
expect(globalBanner?.textContent).toContain("Fusion");
expect(container.querySelector(".settings-scope-project")).toBeNull();
// Verify banner uses SVG icon, not emoji
const globalIcon = globalBanner!.querySelector(".settings-scope-icon svg");
expect(globalIcon).toBeTruthy();
// Switch to General → should show project banner
// Switch to General → should show project banner with Folder icon (SVG, not emoji)
fireEvent.click(screen.getAllByText("General")[0]);
expect(container.querySelector(".settings-scope-project")).toBeTruthy();
const projectBanner = container.querySelector(".settings-scope-project");
expect(projectBanner).toBeTruthy();
expect(container.querySelector(".settings-scope-global")).toBeNull();
// Verify banner uses SVG icon, not emoji
const projectIcon = projectBanner!.querySelector(".settings-scope-icon svg");
expect(projectIcon).toBeTruthy();
// Switch to Models → should show mixed scope banner (contains both global and project settings)
// Switch to Models → should show mixed scope banner with both icons (SVG, not emoji)
fireEvent.click(screen.getAllByText("Models")[0]);
expect(container.querySelector(".settings-scope-mixed")).toBeTruthy();
expect(container.querySelector(".settings-scope-mixed")?.textContent).toContain("global");
expect(container.querySelector(".settings-scope-mixed")?.textContent).toContain("project");
const mixedBanner = container.querySelector(".settings-scope-mixed");
expect(mixedBanner).toBeTruthy();
expect(mixedBanner?.textContent).toContain("global");
expect(mixedBanner?.textContent).toContain("project");
// Verify mixed banner uses both icons as SVG elements, not emoji
const mixedIcons = mixedBanner!.querySelectorAll(".settings-scope-icon svg");
expect(mixedIcons.length).toBe(2);
});
// --- Settings save error handling tests ---

View File

@@ -108,9 +108,21 @@ describe("SettingsModal mobile adaptations", () => {
// Authentication is first with no scope banner by default - click General to see project scope
expect(container.querySelectorAll(".settings-scope-icon").length).toBeGreaterThan(0);
await user.click(getAllByText("General")[0]);
// Verify project scope banner contains icon elements (SVG from Lucide, not emoji)
const projectBanner = container.querySelector(".settings-scope-project");
expect(projectBanner).toBeTruthy();
const projectBannerIcon = projectBanner!.querySelector(".settings-scope-icon svg");
expect(projectBannerIcon).toBeTruthy();
expect(getByText("These settings only affect this project.")).toBeTruthy();
await user.click(getByText("Appearance"));
// Verify global scope banner contains icon elements (SVG from Lucide, not emoji)
const globalBanner = container.querySelector(".settings-scope-global");
expect(globalBanner).toBeTruthy();
const globalBannerIcon = globalBanner!.querySelector(".settings-scope-icon svg");
expect(globalBannerIcon).toBeTruthy();
expect(getByText("These settings are shared across all your Fusion projects.")).toBeTruthy();
});