feat(FN-687): relocate ProjectSelector to header-left group

- Move ProjectSelector from standalone header section into header-left div next to logo/title
- Change dropdown positioning from right-aligned to left-aligned with higher z-index
- Update CSS to use inline-flex and relative positioning for proper containment
- Adjust mobile styles for narrower max-width within header-left
- Add test verifying ProjectSelector renders inside header-left container
This commit is contained in:
gsxdsm
2026-04-01 09:26:44 -07:00
parent 57d996fb9b
commit f6ba388536
3 changed files with 41 additions and 21 deletions

View File

@@ -170,6 +170,20 @@ export function Header({
<h1 className="logo">Fusion</h1>
<span className="logo-sub">tasks</span>
{/* Project Selector - shown when 2+ projects, placed next to logo/title */}
{projects.length > 1 && (
<div className="header-project-selector">
<ProjectSelector
projects={projects}
currentProject={currentProject || null}
onSelect={(project) => {
onSelectProject?.(project);
}}
onViewAll={onViewAllProjects || (() => {})}
/>
</div>
)}
{/* Back to All Projects button when viewing a specific project */}
{currentProject && onViewAllProjects && (
<button
@@ -184,20 +198,6 @@ export function Header({
)}
</div>
{/* Project Selector - shown when 2+ projects */}
{projects.length > 1 && (
<div className="header-project-selector">
<ProjectSelector
projects={projects}
currentProject={currentProject || null}
onSelect={(project) => {
onSelectProject?.(project);
}}
onViewAll={onViewAllProjects || (() => {})}
/>
</div>
)}
<div className="header-actions">
{/* Desktop Search - only show in board view */}
{onSearchChange && view === "board" && !isMobile && (

View File

@@ -559,6 +559,27 @@ describe("Header", () => {
expect(screen.getByTestId("project-selector-trigger")).toBeDefined();
});
it("renders ProjectSelector within header-left when multiple projects exist", () => {
const projects = [
{ id: "proj_1", name: "Project One", path: "/path/1", status: "active" as const, isolationMode: "in-process" as const, createdAt: "", updatedAt: "" },
{ id: "proj_2", name: "Project Two", path: "/path/2", status: "active" as const, isolationMode: "in-process" as const, createdAt: "", updatedAt: "" },
];
const { container } = render(
<Header
projects={projects}
currentProject={projects[0]}
onSelectProject={vi.fn()}
onViewAllProjects={vi.fn()}
/>
);
// ProjectSelector should be inside header-left, not a sibling
const headerLeft = container.querySelector(".header-left");
expect(headerLeft).not.toBeNull();
const selectorInLeft = headerLeft!.querySelector(".header-project-selector");
expect(selectorInLeft).not.toBeNull();
expect(selectorInLeft!.querySelector("[data-testid='project-selector-trigger']")).not.toBeNull();
});
it("does not show ProjectSelector with single project", () => {
const projects = [
{ id: "proj_1", name: "Project One", path: "/path/1", status: "active" as const, isolationMode: "in-process" as const, createdAt: "", updatedAt: "" },

View File

@@ -131,11 +131,11 @@ body {
position: relative;
}
/* Project selector in header */
/* Project selector in header (inside header-left) */
.header-project-selector {
display: flex;
display: inline-flex;
align-items: center;
margin: 0 var(--space-md);
position: relative;
flex-shrink: 1;
min-width: 0;
}
@@ -1522,8 +1522,8 @@ body {
.project-selector__dropdown {
position: absolute;
top: calc(100% + 8px);
right: 0;
z-index: 50;
left: 0;
z-index: 100;
min-width: 320px;
max-width: min(420px, 90vw);
max-height: min(70vh, 520px);
@@ -4175,8 +4175,7 @@ body {
/* Project selector mobile adjustments */
.header-project-selector {
margin: 0 8px;
max-width: 140px;
max-width: 120px;
flex-shrink: 1;
}