Browse Source
Minimal pill-style tab bar for switching between primary and fallback model parameter views inside the InlineParameterPanel. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>handoff-20260429-1057
2 changed files with 113 additions and 0 deletions
@ -0,0 +1,46 @@ |
|||
import React from "react"; |
|||
|
|||
interface SettingsTabBarProps { |
|||
activeTab: "primary" | "fallback"; |
|||
onTabChange: (tab: "primary" | "fallback") => void; |
|||
primaryLabel: string; |
|||
fallbackLabel: string; |
|||
} |
|||
|
|||
export function SettingsTabBar({ |
|||
activeTab, |
|||
onTabChange, |
|||
primaryLabel, |
|||
fallbackLabel, |
|||
}: SettingsTabBarProps) { |
|||
return ( |
|||
<div className="flex gap-1 mb-2"> |
|||
<button |
|||
type="button" |
|||
className={`text-[10px] px-2 py-0.5 rounded transition-colors ${ |
|||
activeTab === "primary" |
|||
? "bg-neutral-700 text-neutral-200" |
|||
: "bg-transparent text-neutral-500 hover:text-neutral-400" |
|||
}`}
|
|||
onClick={() => { |
|||
if (activeTab !== "primary") onTabChange("primary"); |
|||
}} |
|||
> |
|||
{primaryLabel} |
|||
</button> |
|||
<button |
|||
type="button" |
|||
className={`text-[10px] px-2 py-0.5 rounded transition-colors ${ |
|||
activeTab === "fallback" |
|||
? "bg-neutral-700 text-neutral-200" |
|||
: "bg-transparent text-neutral-500 hover:text-neutral-400" |
|||
}`}
|
|||
onClick={() => { |
|||
if (activeTab !== "fallback") onTabChange("fallback"); |
|||
}} |
|||
> |
|||
{fallbackLabel} |
|||
</button> |
|||
</div> |
|||
); |
|||
} |
|||
@ -0,0 +1,67 @@ |
|||
import { describe, it, expect, vi } from "vitest"; |
|||
import { render, screen, fireEvent } from "@testing-library/react"; |
|||
import { SettingsTabBar } from "../SettingsTabBar"; |
|||
|
|||
describe("SettingsTabBar", () => { |
|||
it("renders both tab labels", () => { |
|||
render( |
|||
<SettingsTabBar |
|||
activeTab="primary" |
|||
onTabChange={vi.fn()} |
|||
primaryLabel="Nano Banana" |
|||
fallbackLabel="Flux Dev" |
|||
/> |
|||
); |
|||
|
|||
expect(screen.getByText("Nano Banana")).toBeInTheDocument(); |
|||
expect(screen.getByText("Flux Dev")).toBeInTheDocument(); |
|||
}); |
|||
|
|||
it("highlights active tab", () => { |
|||
render( |
|||
<SettingsTabBar |
|||
activeTab="primary" |
|||
onTabChange={vi.fn()} |
|||
primaryLabel="Nano Banana" |
|||
fallbackLabel="Flux Dev" |
|||
/> |
|||
); |
|||
|
|||
const primaryTab = screen.getByText("Nano Banana"); |
|||
const fallbackTab = screen.getByText("Flux Dev"); |
|||
|
|||
// Active tab should have bg-neutral-700 class
|
|||
expect(primaryTab.className).toContain("bg-neutral-700"); |
|||
expect(fallbackTab.className).not.toContain("bg-neutral-700"); |
|||
}); |
|||
|
|||
it("calls onTabChange when clicking inactive tab", () => { |
|||
const onTabChange = vi.fn(); |
|||
render( |
|||
<SettingsTabBar |
|||
activeTab="primary" |
|||
onTabChange={onTabChange} |
|||
primaryLabel="Nano Banana" |
|||
fallbackLabel="Flux Dev" |
|||
/> |
|||
); |
|||
|
|||
fireEvent.click(screen.getByText("Flux Dev")); |
|||
expect(onTabChange).toHaveBeenCalledWith("fallback"); |
|||
}); |
|||
|
|||
it("does not call onTabChange when clicking active tab", () => { |
|||
const onTabChange = vi.fn(); |
|||
render( |
|||
<SettingsTabBar |
|||
activeTab="primary" |
|||
onTabChange={onTabChange} |
|||
primaryLabel="Nano Banana" |
|||
fallbackLabel="Flux Dev" |
|||
/> |
|||
); |
|||
|
|||
fireEvent.click(screen.getByText("Nano Banana")); |
|||
expect(onTabChange).not.toHaveBeenCalled(); |
|||
}); |
|||
}); |
|||
Loading…
Reference in new issue