feat(FN-2398): restore compact project switch in tablet header
- Render the compact project switch trigger for tablet layouts by gating on compact mode instead of mobile-only mode - Extend ProjectSelector compact-switch styles to the tablet media range so the trigger and dropdown render correctly beside the logo - Update tablet header control regression coverage to expect the compact switch affordance and absence of the desktop selector trigger - Add Header tablet interaction tests covering Escape key, outside click, and project selection closing behavior for the compact switch dropdown
This commit is contained in:
@@ -392,7 +392,7 @@ describe("tablet header controls", () => {
|
||||
expect(screen.queryByPlaceholderText("Search tasks...")).toBeNull();
|
||||
});
|
||||
|
||||
// ── Project selector hidden on tablet ──────────────────────────
|
||||
// ── Tablet project-switch affordances ──────────────────────────
|
||||
|
||||
it("does not render project selector on tablet", () => {
|
||||
const projects = [
|
||||
@@ -434,7 +434,7 @@ describe("tablet header controls", () => {
|
||||
expect(btn.textContent).toContain("Projects");
|
||||
});
|
||||
|
||||
it("does not render mobile project switch trigger on tablet", () => {
|
||||
it("renders compact project switch trigger beside logo on tablet", () => {
|
||||
const projects = [
|
||||
{ id: "1", name: "Project One", path: "/path/one", status: "active" as const },
|
||||
{ id: "2", name: "Project Two", path: "/path/two", status: "active" as const },
|
||||
@@ -444,7 +444,8 @@ describe("tablet header controls", () => {
|
||||
currentProject: projects[0],
|
||||
onSelectProject: vi.fn(),
|
||||
});
|
||||
expect(screen.queryByTestId("mobile-project-switch-trigger")).toBeNull();
|
||||
expect(screen.getByTestId("mobile-project-switch-trigger")).toBeDefined();
|
||||
expect(screen.queryByTestId("project-selector-trigger")).toBeNull();
|
||||
});
|
||||
|
||||
// ── Desktop still shows everything inline ──────────────────────
|
||||
|
||||
@@ -526,8 +526,8 @@ export function Header({
|
||||
<h1 className="logo">Fusion</h1>
|
||||
</div>
|
||||
|
||||
{/* Mobile Project Switch - dropdown trigger next to logo when 2+ projects (mobile only) */}
|
||||
{isMobile && projects.length > 1 && onSelectProject && (
|
||||
{/* Compact Project Switch - dropdown trigger next to logo when 2+ projects (mobile + tablet) */}
|
||||
{isCompact && projects.length > 1 && onSelectProject && (
|
||||
<div className="mobile-project-switch" ref={mobileProjectSwitchRef}>
|
||||
<button
|
||||
className={`mobile-project-switch-trigger${isMobileProjectSwitchOpen ? " mobile-project-switch-trigger--open" : ""}`}
|
||||
|
||||
@@ -273,8 +273,8 @@
|
||||
color: var(--todo);
|
||||
}
|
||||
|
||||
/* Mobile styles for mobile-project-switch */
|
||||
@media (max-width: 768px) {
|
||||
/* Compact styles for mobile-project-switch (mobile + tablet) */
|
||||
@media (max-width: 768px), (min-width: 769px) and (max-width: 1024px) {
|
||||
.mobile-project-switch {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -972,7 +972,7 @@ describe("Header", () => {
|
||||
expect(screen.queryByTestId("mobile-project-switch-trigger")).toBeNull();
|
||||
});
|
||||
|
||||
it("does not render mobile project switch trigger on tablet", () => {
|
||||
it("renders mobile project switch trigger on tablet with 2+ projects", () => {
|
||||
const projects = [
|
||||
{ id: "1", name: "Project One", path: "/path/one", status: "active" as const },
|
||||
{ id: "2", name: "Project Two", path: "/path/two", status: "active" as const },
|
||||
@@ -982,7 +982,7 @@ describe("Header", () => {
|
||||
currentProject: projects[0],
|
||||
onSelectProject: vi.fn(),
|
||||
}, "tablet");
|
||||
expect(screen.queryByTestId("mobile-project-switch-trigger")).toBeNull();
|
||||
expect(screen.getByTestId("mobile-project-switch-trigger")).toBeDefined();
|
||||
});
|
||||
|
||||
it("renders mobile project switch trigger on mobile with 2+ projects", () => {
|
||||
@@ -1009,6 +1009,69 @@ describe("Header", () => {
|
||||
}, "mobile");
|
||||
expect(screen.queryByTestId("mobile-project-switch-trigger")).toBeNull();
|
||||
});
|
||||
|
||||
it("closes compact project switch dropdown on Escape in tablet mode", async () => {
|
||||
const projects = [
|
||||
{ id: "1", name: "Project One", path: "/path/one", status: "active" as const },
|
||||
{ id: "2", name: "Project Two", path: "/path/two", status: "paused" as const },
|
||||
];
|
||||
renderHeader({
|
||||
projects,
|
||||
currentProject: projects[0],
|
||||
onSelectProject: vi.fn(),
|
||||
}, "tablet");
|
||||
|
||||
fireEvent.click(screen.getByTestId("mobile-project-switch-trigger"));
|
||||
expect(screen.getByTestId("mobile-project-switch-dropdown")).toBeDefined();
|
||||
|
||||
fireEvent.keyDown(document, { key: "Escape" });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByTestId("mobile-project-switch-dropdown")).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
it("closes compact project switch dropdown on outside click in tablet mode", async () => {
|
||||
const projects = [
|
||||
{ id: "1", name: "Project One", path: "/path/one", status: "active" as const },
|
||||
{ id: "2", name: "Project Two", path: "/path/two", status: "paused" as const },
|
||||
];
|
||||
renderHeader({
|
||||
projects,
|
||||
currentProject: projects[0],
|
||||
onSelectProject: vi.fn(),
|
||||
}, "tablet");
|
||||
|
||||
fireEvent.click(screen.getByTestId("mobile-project-switch-trigger"));
|
||||
expect(screen.getByTestId("mobile-project-switch-dropdown")).toBeDefined();
|
||||
|
||||
fireEvent.mouseDown(document.body);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByTestId("mobile-project-switch-dropdown")).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
it("closes compact project switch dropdown after selecting a project in tablet mode", async () => {
|
||||
const projects = [
|
||||
{ id: "1", name: "Project One", path: "/path/one", status: "active" as const },
|
||||
{ id: "2", name: "Project Two", path: "/path/two", status: "paused" as const },
|
||||
];
|
||||
const onSelectProject = vi.fn();
|
||||
renderHeader({
|
||||
projects,
|
||||
currentProject: projects[0],
|
||||
onSelectProject,
|
||||
}, "tablet");
|
||||
|
||||
fireEvent.click(screen.getByTestId("mobile-project-switch-trigger"));
|
||||
fireEvent.click(screen.getByTestId("mobile-project-switch-item-2"));
|
||||
|
||||
expect(onSelectProject).toHaveBeenCalledWith(projects[1]);
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByTestId("mobile-project-switch-dropdown")).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Manage Projects action", () => {
|
||||
|
||||
Reference in New Issue
Block a user