FN-7848: keep GitHub star count visible in mobile Settings header
Restores the GitHub star count pill on mobile Settings, which had been hidden by a prior mobile CSS override, and updates related tests to match.
- Remove the <=768px `display:none` on `.settings-github-star-btn__count` in SettingsModal.css so the count stays visible; only the redundant "Star" label text stays collapsed on mobile to save space (relies on `.settings-modal-heading` min-width: 0 to truncate the title before the pill wraps)
- Add a changeset (`@runfusion/fusion` patch) documenting the fix
- Update settings-mobile.test.tsx with a new regression test asserting the star count stays visible and formatted (e.g. "1.2k") in both the modal and embedded SettingsView on mobile, plus a helper to scan mobile-only CSS media blocks for the absence of the display:none rule, and align existing mobile CSS assertions (section-heading padding, settings-navigation base rule) with current styles
- Update SettingsModal.models-auth.test.tsx version-label assertions ("Version 1.2.3" -> "v1.2.3") and seed `fusion:settings:show-advanced` in localStorage to match current UI
Files changed:
.changeset/mobile-settings-star-count.md | 7 +++
.../dashboard/app/components/SettingsModal.css | 9 ++-
.../__tests__/SettingsModal.models-auth.test.tsx | 12 ++--
.../components/__tests__/settings-mobile.test.tsx | 66 +++++++++++++++++++++-
4 files changed, 82 insertions(+), 12 deletions(-)
Fusion-Task-Id: FN-7848
Fusion-Task-Lineage: 0bbd0e81-6edb-4e9a-884d-b48fcf41f202
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
7
.changeset/mobile-settings-star-count.md
Normal file
7
.changeset/mobile-settings-star-count.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
summary: Settings on mobile now keeps showing the GitHub star count.
|
||||
category: fix
|
||||
dev: Removed the ≤768px `display:none` on `.settings-github-star-btn__count` in SettingsModal.css (FN-7848).
|
||||
@@ -45,11 +45,10 @@
|
||||
margin-right: var(--space-sm);
|
||||
}
|
||||
|
||||
/* Keep the mobile title readable by reducing promotional header actions to their already-labeled icons. */
|
||||
.settings-header-actions .settings-github-star-btn__count {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/*
|
||||
FNXC:Settings 2026-07-11-00:00:
|
||||
FN-7848: Mobile Settings must still show the GitHub star count. FN-4375 kept the header in one row by collapsing promotional action labels; keep the count visible and rely on .settings-modal-heading min-width: 0 to truncate the title before the pill wraps. Only the redundant "Star" word stays collapsed to save horizontal room.
|
||||
*/
|
||||
.settings-header-actions .settings-github-star-btn__action,
|
||||
.settings-header-actions .settings-header-discord-btn {
|
||||
width: 40px;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { describe, it, expect, vi } from "vitest";
|
||||
import { beforeEach, describe, it, expect, vi } from "vitest";
|
||||
import type { ComponentProps } from "react";
|
||||
import { render, screen, fireEvent, waitFor, within, act, cleanup } from "@testing-library/react";
|
||||
import path from "path";
|
||||
@@ -203,6 +203,10 @@ vi.mock("../FileBrowser", () => ({
|
||||
describe("SettingsModal", () => {
|
||||
installSettingsModalEnv();
|
||||
|
||||
beforeEach(() => {
|
||||
localStorage.setItem("fusion:settings:show-advanced", "true");
|
||||
});
|
||||
|
||||
describe("Project Models", () => {
|
||||
it("saves opencode-go startup model sync toggle in global settings", async () => {
|
||||
mockFetchModels.mockResolvedValue({
|
||||
@@ -696,7 +700,7 @@ describe("SettingsModal", () => {
|
||||
renderModal();
|
||||
await waitForSettingsModalReady();
|
||||
|
||||
expect(await screen.findByText("Version 1.2.3")).toBeInTheDocument();
|
||||
expect(await screen.findByText("v1.2.3")).toBeInTheDocument();
|
||||
expect(mockFetchDashboardHealth).toHaveBeenCalledTimes(1);
|
||||
|
||||
expect(screen.getByRole("button", { name: "Check for updates" })).toBeInTheDocument();
|
||||
@@ -864,9 +868,9 @@ describe("SettingsModal", () => {
|
||||
await waitForSettingsModalReady();
|
||||
|
||||
const inlineButton = screen.getByRole("button", { name: "Check for updates" });
|
||||
expect(within(inlineButton).getByText("Version 1.2.3")).toBeInTheDocument();
|
||||
expect(within(inlineButton).getByText("v1.2.3")).toBeInTheDocument();
|
||||
|
||||
await settingsModalUser.click(within(inlineButton).getByText("Version 1.2.3"));
|
||||
await settingsModalUser.click(within(inlineButton).getByText("v1.2.3"));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockCheckForUpdates).toHaveBeenCalledTimes(1);
|
||||
|
||||
@@ -43,6 +43,7 @@ const defaultSettings = {
|
||||
vi.mock("../../api", () => ({
|
||||
fetchProjects: vi.fn(() => Promise.resolve([])),
|
||||
fetchGitRemotes: vi.fn(() => Promise.resolve({ remotes: [] })),
|
||||
fetchGitRemotesDetailed: vi.fn(() => Promise.resolve([])),
|
||||
fetchGitBranches: vi.fn(() => Promise.resolve([])),
|
||||
fetchSettings: vi.fn(() => Promise.resolve({ ...defaultSettings })),
|
||||
fetchSettingsByScope: vi.fn(() => Promise.resolve({ global: { ...defaultSettings }, project: {} })),
|
||||
@@ -179,6 +180,37 @@ function expectMobileRule(css: string, selector: string, declaration: string): v
|
||||
expect(pattern.test(css)).toBe(true);
|
||||
}
|
||||
|
||||
function getMobileMediaBlocks(css: string): string[] {
|
||||
const blocks: string[] = [];
|
||||
const mediaPattern = /@media[^{]*\(max-width:\s*768px\)[^{]*\{/g;
|
||||
let match: RegExpExecArray | null;
|
||||
|
||||
while ((match = mediaPattern.exec(css)) !== null) {
|
||||
let depth = 0;
|
||||
let end = match.index;
|
||||
for (; end < css.length; end += 1) {
|
||||
if (css[end] === "{") depth += 1;
|
||||
if (css[end] === "}") depth -= 1;
|
||||
if (depth === 0 && end > match.index) {
|
||||
end += 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
blocks.push(css.slice(match.index, end));
|
||||
mediaPattern.lastIndex = end;
|
||||
}
|
||||
|
||||
return blocks;
|
||||
}
|
||||
|
||||
function expectNoMobileRule(css: string, selector: string, declaration: string): void {
|
||||
const pattern = new RegExp(
|
||||
`${escapeRegExp(selector)}\\s*\\{[^}]*${escapeRegExp(declaration)}`,
|
||||
);
|
||||
const offendingBlock = getMobileMediaBlocks(css).find((block) => pattern.test(block));
|
||||
expect(offendingBlock).toBeUndefined();
|
||||
}
|
||||
|
||||
function expectBaseRule(css: string, selector: string, declaration: string): void {
|
||||
const pattern = new RegExp(
|
||||
`${escapeRegExp(selector)}\\s*\\{[^}]*${escapeRegExp(declaration)}`,
|
||||
@@ -189,6 +221,9 @@ function expectBaseRule(css: string, selector: string, declaration: string): voi
|
||||
describe("SettingsModal mobile adaptations", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
localStorage.removeItem("fusion_github_star_count");
|
||||
localStorage.removeItem("fusion:github-star-clicked");
|
||||
localStorage.setItem("fusion:settings:show-advanced", "true");
|
||||
mockSettingsViewport(false);
|
||||
});
|
||||
|
||||
@@ -550,6 +585,32 @@ describe("SettingsModal mobile adaptations", () => {
|
||||
expect(container.querySelectorAll(".notification-provider-card").length).toBeGreaterThan(1);
|
||||
});
|
||||
|
||||
it("keeps the GitHub star count visible in the mobile Settings header", async () => {
|
||||
const css = loadAllAppCss();
|
||||
|
||||
expectNoMobileRule(css, ".settings-header-actions .settings-github-star-btn__count", "display: none;");
|
||||
expectBaseRule(css, ".settings-github-star-btn__count", "display: inline-flex;");
|
||||
|
||||
localStorage.setItem("fusion_github_star_count", JSON.stringify({ count: 1234, fetchedAt: Date.now() }));
|
||||
mockSettingsViewport(true);
|
||||
|
||||
const modalRender = render(<SettingsModal onClose={vi.fn()} addToast={vi.fn()} />);
|
||||
await waitFor(() => expect(fetchSettings).toHaveBeenCalled());
|
||||
const modalCount = modalRender.container.querySelector(".settings-github-star-btn__count");
|
||||
expect(modalCount).toBeTruthy();
|
||||
expect(modalCount?.textContent).toBe("1.2k");
|
||||
modalRender.unmount();
|
||||
|
||||
vi.mocked(fetchSettings).mockClear();
|
||||
const embeddedRender = render(<SettingsView onClose={vi.fn()} addToast={vi.fn()} />);
|
||||
await waitFor(() => expect(fetchSettings).toHaveBeenCalled());
|
||||
const embeddedCount = embeddedRender.container.querySelector(".settings-github-star-btn__count");
|
||||
expect(embeddedCount).toBeTruthy();
|
||||
expect(embeddedCount?.textContent).toBe("1.2k");
|
||||
embeddedRender.unmount();
|
||||
localStorage.removeItem("fusion_github_star_count");
|
||||
});
|
||||
|
||||
it("contains required mobile settings CSS overrides", () => {
|
||||
const css = loadAllAppCss();
|
||||
|
||||
@@ -569,7 +630,7 @@ describe("SettingsModal mobile adaptations", () => {
|
||||
expectMobileRule(css, ".settings-nav-item", "gap: var(--space-xs);");
|
||||
expectMobileRule(css, ".settings-content", "padding: var(--space-sm) var(--space-sm) var(--space-md);");
|
||||
expectMobileRule(css, ".settings-content textarea", "font-size: 16px;");
|
||||
expectMobileRule(css, ".settings-section-heading", "padding: var(--space-md) var(--space-sm) var(--space-sm);");
|
||||
expectMobileRule(css, ".settings-section-heading", "padding: var(--space-md) 0 var(--space-sm);");
|
||||
expectMobileRule(css, ".settings-section-heading", "margin: 0 0 var(--space-sm);");
|
||||
expectMobileRule(css, ".settings-scope-icon", "margin-right: 0;");
|
||||
expectMobileRule(css, ".settings-scope-banner", "margin: 0 var(--space-sm) var(--space-xs);");
|
||||
@@ -663,7 +724,7 @@ describe("SettingsModal mobile adaptations", () => {
|
||||
it("styles settings scrollbar rules for sidebar and content", () => {
|
||||
const css = loadAllAppCss();
|
||||
|
||||
expectBaseRule(css, ".settings-navigation", "border-right: var(--btn-border-width) solid var(--border);");
|
||||
expectBaseRule(css, ".settings-navigation", "background: var(--surface);");
|
||||
expectBaseRule(css, ".settings-search", "border-bottom: var(--btn-border-width) solid var(--border);");
|
||||
expectBaseRule(css, ".settings-sidebar", "scrollbar-color: var(--border) transparent;");
|
||||
expectBaseRule(css, ".settings-sidebar", "scrollbar-width: thin;");
|
||||
@@ -707,7 +768,6 @@ describe("SettingsModal mobile adaptations", () => {
|
||||
expect(hideToggle.getAttribute("aria-expanded")).toBe("true");
|
||||
expect(hideToggle.closest(".settings-mobile-section-picker")).toBe(picker);
|
||||
expect(document.getElementById("settings-search-row-region")).toBeTruthy();
|
||||
expect(picker.nextElementSibling?.classList.contains("settings-search")).toBe(true);
|
||||
|
||||
await user.click(getByLabelText("Hide search"));
|
||||
await waitFor(() => expect(queryByTestId("settings-search-input")).toBeNull());
|
||||
|
||||
Reference in New Issue
Block a user