FN-5765: move settings version check from header to footer
Relocate Settings version/update UI into the modal footer to reduce header crowding. - Remove the version/update-check control block from the Settings modal header. - Add a dedicated footer version container and keep the update-check action/result rendering there. - Update Settings modal CSS for the new footer layout and mobile wrapping behavior. - Extend mobile tests to verify footer placement and update-check button clickability. Files changed: packages/dashboard/app/components/SettingsModal.css | 25 +++++++-- packages/dashboard/app/components/SettingsModal.tsx | 64 +++++++++++----------- packages/dashboard/app/components/__tests__/settings-mobile.test.tsx | 27 ++++++++- 3 files changed, 77 insertions(+), 39 deletions(-) Fusion-Task-Id: FN-5765 Fusion-Task-Lineage: 97923f08-e787-4274-a4ce-8b615b909468
This commit is contained in:
@@ -41,11 +41,6 @@
|
|||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-update-check {
|
|
||||||
flex-wrap: wrap;
|
|
||||||
row-gap: var(--space-xs);
|
|
||||||
}
|
|
||||||
|
|
||||||
.settings-header-actions {
|
.settings-header-actions {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: var(--space-sm);
|
margin-right: var(--space-sm);
|
||||||
@@ -164,10 +159,30 @@
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.settings-modal-footer-version {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-right: var(--space-sm);
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.settings-update-check {
|
.settings-update-check {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: var(--space-sm);
|
gap: var(--space-sm);
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.settings-modal-footer-version {
|
||||||
|
flex: 1 1 100%;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-update-check {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
row-gap: var(--space-xs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-version-check-btn {
|
.settings-version-check-btn {
|
||||||
|
|||||||
@@ -7443,37 +7443,6 @@ export function SettingsModal({
|
|||||||
<div className="modal-header">
|
<div className="modal-header">
|
||||||
<div className="settings-modal-heading">
|
<div className="settings-modal-heading">
|
||||||
<h3>Settings</h3>
|
<h3>Settings</h3>
|
||||||
<div className="settings-update-check">
|
|
||||||
{appVersion && (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="settings-version-check-btn"
|
|
||||||
onClick={() => {
|
|
||||||
void handleCheckForUpdates();
|
|
||||||
}}
|
|
||||||
disabled={updateCheckLoading}
|
|
||||||
aria-label="Check for updates"
|
|
||||||
title="Check for updates"
|
|
||||||
>
|
|
||||||
<span className="settings-modal-version">Version {appVersion}</span>
|
|
||||||
<RefreshCw size={12} className={updateCheckLoading ? "spinning" : undefined} />
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
{updateCheckResult && (
|
|
||||||
<span
|
|
||||||
aria-live="polite"
|
|
||||||
className={`settings-update-result ${
|
|
||||||
updateCheckResult.error
|
|
||||||
? "settings-update-result--error"
|
|
||||||
: updateCheckResult.updateAvailable
|
|
||||||
? "settings-update-result--available"
|
|
||||||
: "settings-update-result--up-to-date"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{renderUpdateCheckResultContent()}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="settings-header-actions">
|
<div className="settings-header-actions">
|
||||||
{form.showGitHubStarButton !== false && (
|
{form.showGitHubStarButton !== false && (
|
||||||
@@ -7588,6 +7557,39 @@ export function SettingsModal({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="modal-actions">
|
<div className="modal-actions">
|
||||||
|
<div className="settings-modal-footer-version">
|
||||||
|
<div className="settings-update-check">
|
||||||
|
{appVersion && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="settings-version-check-btn"
|
||||||
|
onClick={() => {
|
||||||
|
void handleCheckForUpdates();
|
||||||
|
}}
|
||||||
|
disabled={updateCheckLoading}
|
||||||
|
aria-label="Check for updates"
|
||||||
|
title="Check for updates"
|
||||||
|
>
|
||||||
|
<span className="settings-modal-version">Version {appVersion}</span>
|
||||||
|
<RefreshCw size={12} className={updateCheckLoading ? "spinning" : undefined} />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
{updateCheckResult && (
|
||||||
|
<span
|
||||||
|
aria-live="polite"
|
||||||
|
className={`settings-update-result ${
|
||||||
|
updateCheckResult.error
|
||||||
|
? "settings-update-result--error"
|
||||||
|
: updateCheckResult.updateAvailable
|
||||||
|
? "settings-update-result--available"
|
||||||
|
: "settings-update-result--up-to-date"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{renderUpdateCheckResultContent()}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div className="modal-actions-left">
|
<div className="modal-actions-left">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import fs from "node:fs";
|
|||||||
import { loadAllAppCss } from "../../test/cssFixture";
|
import { loadAllAppCss } from "../../test/cssFixture";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||||
import { render, waitFor } from "@testing-library/react";
|
import { render, waitFor, within } from "@testing-library/react";
|
||||||
import userEvent from "@testing-library/user-event";
|
import userEvent from "@testing-library/user-event";
|
||||||
import { SettingsModal } from "../SettingsModal";
|
import { SettingsModal } from "../SettingsModal";
|
||||||
import type { Settings } from "@fusion/core";
|
import type { Settings } from "@fusion/core";
|
||||||
@@ -192,10 +192,31 @@ describe("SettingsModal mobile adaptations", () => {
|
|||||||
|
|
||||||
it("renders the app version label in mobile layout", async () => {
|
it("renders the app version label in mobile layout", async () => {
|
||||||
mockSettingsViewport(true);
|
mockSettingsViewport(true);
|
||||||
const { findByText } = render(<SettingsModal onClose={vi.fn()} addToast={vi.fn()} />);
|
const { findByText, container } = render(<SettingsModal onClose={vi.fn()} addToast={vi.fn()} />);
|
||||||
await waitFor(() => expect(fetchSettings).toHaveBeenCalled());
|
await waitFor(() => expect(fetchSettings).toHaveBeenCalled());
|
||||||
|
|
||||||
expect(await findByText("Version 1.2.3")).toBeTruthy();
|
const version = await findByText("Version 1.2.3");
|
||||||
|
const modalActions = container.querySelector(".modal-actions");
|
||||||
|
const modalHeader = container.querySelector(".modal-header");
|
||||||
|
|
||||||
|
expect(version).toBeTruthy();
|
||||||
|
expect(modalActions?.contains(version)).toBe(true);
|
||||||
|
expect(modalHeader?.contains(version)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("keeps update-check button clickable from the footer", async () => {
|
||||||
|
mockSettingsViewport(true);
|
||||||
|
const user = userEvent.setup();
|
||||||
|
const { container } = render(<SettingsModal onClose={vi.fn()} addToast={vi.fn()} />);
|
||||||
|
await waitFor(() => expect(fetchSettings).toHaveBeenCalled());
|
||||||
|
|
||||||
|
const modalActions = container.querySelector(".modal-actions");
|
||||||
|
expect(modalActions).toBeTruthy();
|
||||||
|
|
||||||
|
const updateButton = within(modalActions as HTMLElement).getByRole("button", { name: "Check for updates" });
|
||||||
|
await user.click(updateButton);
|
||||||
|
|
||||||
|
expect(updateButton).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("excludes research sections from mobile picker when researchView is disabled", async () => {
|
it("excludes research sections from mobile picker when researchView is disabled", async () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user