feat(FN-3133): restore mobile back button with CSS contract tests
This merge adds a standalone bottom-gap CSS token for PWA mobile compatibility, implements a responsive back button that hides on desktop and restores on mobile, refactors the test-changed script for improved workflow testing, and expands the test suite with new CI workflow tests and mobile CSS cont Fusion-Task-Id: FN-3133
This commit is contained in:
@@ -125,6 +125,12 @@
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* Hide back button on desktop — sidebar is always visible so navigation is unnecessary.
|
||||
Keep control mounted in DOM for jsdom test compatibility; mobile override restores visibility. */
|
||||
.mission-manager--desktop .mission-manager__back-btn {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* ── Body ── */
|
||||
.mission-manager__body {
|
||||
flex: 1;
|
||||
@@ -2197,6 +2203,11 @@
|
||||
padding: var(--space-md);
|
||||
}
|
||||
|
||||
/* Back button visible on mobile — stacked list→detail navigation needs it. */
|
||||
.mission-manager--desktop .mission-manager__back-btn {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
/* Restore dynamic header title text on mobile. */
|
||||
.mission-manager__title-text--mobile {
|
||||
display: inline;
|
||||
|
||||
@@ -3727,7 +3727,7 @@ export function MissionManager({ isOpen, isInline = false, onClose, addToast, pr
|
||||
const manager = (
|
||||
<div
|
||||
ref={modalRef}
|
||||
className={`mission-manager mission-manager--desktop-split${isInline ? " mission-manager--inline" : ""}`}
|
||||
className={`mission-manager mission-manager--desktop${isInline ? " mission-manager--inline" : ""}`}
|
||||
role={isInline ? undefined : "dialog"}
|
||||
aria-modal={isInline ? undefined : true}
|
||||
aria-label={isInline ? undefined : "Mission Manager"}
|
||||
@@ -3735,7 +3735,7 @@ export function MissionManager({ isOpen, isInline = false, onClose, addToast, pr
|
||||
>
|
||||
<div className={`mission-manager__header${isInline ? " mission-manager__header--inline" : ""}`}>
|
||||
<div className="mission-manager__header-title">
|
||||
{isMobile && selectedMission && (
|
||||
{selectedMission && (
|
||||
<button
|
||||
className="mission-manager__back-btn"
|
||||
onClick={handleBackToList}
|
||||
|
||||
@@ -58,6 +58,16 @@ describe("MissionManager mobile styles", () => {
|
||||
expect(section).toContain("display: block;");
|
||||
});
|
||||
|
||||
it("hides back button on desktop and restores it on mobile", () => {
|
||||
const css = loadAllAppCss();
|
||||
const desktopRule = css.match(/\.mission-manager--desktop \.mission-manager__back-btn\s*\{[^}]*\}/)?.[0];
|
||||
expect(desktopRule).toContain("display: none;");
|
||||
|
||||
const section = getMissionMobileSection(css);
|
||||
const mobileRule = section.match(/\.mission-manager--desktop \.mission-manager__back-btn\s*\{[^}]*\}/)?.[0];
|
||||
expect(mobileRule).toContain("display: inline-flex;");
|
||||
});
|
||||
|
||||
it("keeps desktop defaults and mobile overrides for header title spans", () => {
|
||||
const css = loadAllAppCss();
|
||||
const desktopMobileSpanBlock = css.match(/\.mission-manager__title-text--mobile\s*\{[^}]*\}/)?.[0];
|
||||
|
||||
@@ -1416,8 +1416,8 @@ describe("MissionManager", () => {
|
||||
|
||||
// Wait for detail view to render
|
||||
await waitFor(() => {
|
||||
// Desktop keeps sidebar visible and does not render the mobile back button.
|
||||
expect(screen.queryByTestId("mission-back-btn")).toBeNull();
|
||||
// Desktop keeps sidebar visible and back button stays mounted (CSS-hidden).
|
||||
expect(screen.getByTestId("mission-back-btn")).toBeInTheDocument();
|
||||
// Milestone should be visible (auto-expanded)
|
||||
expect(screen.getByText("Database Schema")).toBeDefined();
|
||||
});
|
||||
@@ -1434,7 +1434,7 @@ describe("MissionManager", () => {
|
||||
fireEvent.click(screen.getByText("Build Auth System"));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByTestId("mission-back-btn")).toBeNull();
|
||||
expect(screen.getByTestId("mission-back-btn")).toBeInTheDocument();
|
||||
expect(screen.getByText("API Redesign")).toBeDefined();
|
||||
});
|
||||
});
|
||||
@@ -1461,7 +1461,7 @@ describe("MissionManager", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("does not render back button on desktop in detail view", async () => {
|
||||
it("keeps back button mounted on desktop in detail view", async () => {
|
||||
globalThis.fetch = createDetailFetchMock();
|
||||
render(<MissionManager isOpen={true} onClose={vi.fn()} addToast={vi.fn()} />);
|
||||
|
||||
@@ -1472,7 +1472,7 @@ describe("MissionManager", () => {
|
||||
fireEvent.click(screen.getByText("Build Auth System"));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByLabelText("Back to missions list")).toBeNull();
|
||||
expect(screen.getByLabelText("Back to missions list")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3180,6 +3180,49 @@ describe("MissionManager", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("desktop back button behavior", () => {
|
||||
it("back button element exists when mission is selected and root uses desktop shell class", async () => {
|
||||
mockViewport("desktop");
|
||||
globalThis.fetch = createDetailFetchMock();
|
||||
render(<MissionManager isOpen={true} onClose={vi.fn()} addToast={vi.fn()} />);
|
||||
|
||||
await waitFor(() => expect(screen.getByText("Build Auth System")).toBeInTheDocument());
|
||||
fireEvent.click(screen.getByText("Build Auth System"));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId("mission-back-btn")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
expect(screen.getByTestId("mission-manager-dialog")).toHaveClass("mission-manager--desktop");
|
||||
});
|
||||
|
||||
it("clicking back button clears selected mission", async () => {
|
||||
mockViewport("desktop");
|
||||
globalThis.fetch = createDetailFetchMock();
|
||||
render(<MissionManager isOpen={true} onClose={vi.fn()} addToast={vi.fn()} />);
|
||||
|
||||
await waitFor(() => expect(screen.getByText("Build Auth System")).toBeInTheDocument());
|
||||
fireEvent.click(screen.getByText("Build Auth System"));
|
||||
|
||||
await waitFor(() => expect(document.querySelector(".mission-manager__detail-pane .mission-detail")).toBeTruthy());
|
||||
fireEvent.click(screen.getByTestId("mission-back-btn"));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(document.querySelector(".mission-manager__detail-pane .mission-detail")).toBeNull();
|
||||
expect(screen.getByText("API Redesign")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it("back button does not render when no mission is selected", async () => {
|
||||
mockViewport("desktop");
|
||||
globalThis.fetch = createDetailFetchMock();
|
||||
render(<MissionManager isOpen={true} onClose={vi.fn()} addToast={vi.fn()} />);
|
||||
|
||||
await waitFor(() => expect(screen.getByText("Build Auth System")).toBeInTheDocument());
|
||||
expect(screen.queryByTestId("mission-back-btn")).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("mobile stacked layout", () => {
|
||||
it("renders stacked body on mobile and hides desktop split", async () => {
|
||||
mockViewport("mobile");
|
||||
|
||||
Reference in New Issue
Block a user