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:
@@ -579,12 +579,12 @@ export function SettingsModal({
|
|||||||
setPresetDraft(null);
|
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 = () => {
|
const renderScopeBanner = () => {
|
||||||
if (activeSectionScope === "global") {
|
if (activeSectionScope === "global") {
|
||||||
return (
|
return (
|
||||||
<div className="settings-scope-banner settings-scope-global">
|
<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>
|
<span>These settings are shared across all your Fusion projects.</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -592,7 +592,7 @@ export function SettingsModal({
|
|||||||
if (activeSectionScope === "project") {
|
if (activeSectionScope === "project") {
|
||||||
return (
|
return (
|
||||||
<div className="settings-scope-banner settings-scope-project">
|
<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>
|
<span>These settings only affect this project.</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -600,7 +600,8 @@ export function SettingsModal({
|
|||||||
if (activeSectionScope === "mixed") {
|
if (activeSectionScope === "mixed") {
|
||||||
return (
|
return (
|
||||||
<div className="settings-scope-banner settings-scope-mixed">
|
<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>
|
<span>
|
||||||
This section contains both global settings (default & fallback models) and project
|
This section contains both global settings (default & fallback models) and project
|
||||||
settings (planning, validator, presets, and AI summarization).
|
settings (planning, validator, presets, and AI summarization).
|
||||||
|
|||||||
@@ -2040,7 +2040,7 @@ describe("SettingsModal", () => {
|
|||||||
expect(payload.maxStuckKills).toBeUndefined();
|
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} />);
|
const { container } = render(<SettingsModal onClose={onClose} addToast={addToast} />);
|
||||||
await waitFor(() => expect(fetchSettings).toHaveBeenCalled());
|
await waitFor(() => expect(fetchSettings).toHaveBeenCalled());
|
||||||
|
|
||||||
@@ -2048,22 +2048,34 @@ describe("SettingsModal", () => {
|
|||||||
expect(container.querySelector(".settings-scope-project")).toBeNull();
|
expect(container.querySelector(".settings-scope-project")).toBeNull();
|
||||||
expect(container.querySelector(".settings-scope-global")).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]);
|
fireEvent.click(screen.getAllByText("Appearance")[0]);
|
||||||
expect(container.querySelector(".settings-scope-global")).toBeTruthy();
|
const globalBanner = container.querySelector(".settings-scope-global");
|
||||||
expect(container.querySelector(".settings-scope-global")?.textContent).toContain("Fusion");
|
expect(globalBanner).toBeTruthy();
|
||||||
|
expect(globalBanner?.textContent).toContain("Fusion");
|
||||||
expect(container.querySelector(".settings-scope-project")).toBeNull();
|
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]);
|
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();
|
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]);
|
fireEvent.click(screen.getAllByText("Models")[0]);
|
||||||
expect(container.querySelector(".settings-scope-mixed")).toBeTruthy();
|
const mixedBanner = container.querySelector(".settings-scope-mixed");
|
||||||
expect(container.querySelector(".settings-scope-mixed")?.textContent).toContain("global");
|
expect(mixedBanner).toBeTruthy();
|
||||||
expect(container.querySelector(".settings-scope-mixed")?.textContent).toContain("project");
|
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 ---
|
// --- Settings save error handling tests ---
|
||||||
|
|||||||
@@ -108,9 +108,21 @@ describe("SettingsModal mobile adaptations", () => {
|
|||||||
// Authentication is first with no scope banner by default - click General to see project scope
|
// Authentication is first with no scope banner by default - click General to see project scope
|
||||||
expect(container.querySelectorAll(".settings-scope-icon").length).toBeGreaterThan(0);
|
expect(container.querySelectorAll(".settings-scope-icon").length).toBeGreaterThan(0);
|
||||||
await user.click(getAllByText("General")[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();
|
expect(getByText("These settings only affect this project.")).toBeTruthy();
|
||||||
|
|
||||||
await user.click(getByText("Appearance"));
|
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();
|
expect(getByText("These settings are shared across all your Fusion projects.")).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user