feat(KB-071): rebrand dashboard to Fusion
- Rename dashboard header to Fusion\n- Update page title to Fusion\n- Replace logo with geometric four-dot design\n- Update Header tests for new branding\n- Add changeset for dashboard rename
This commit is contained in:
@@ -28,9 +28,9 @@ export function Header({
|
||||
return (
|
||||
<header className="header">
|
||||
<div className="header-left">
|
||||
<img src="/logo.svg" alt="kb logo" className="header-logo" width={24} height={24} />
|
||||
<h1 className="logo">kb</h1>
|
||||
<span className="logo-sub">board</span>
|
||||
<img src="/logo.svg" alt="Fusion logo" className="header-logo" width={24} height={24} />
|
||||
<h1 className="logo">Fusion</h1>
|
||||
<span className="logo-sub">tasks</span>
|
||||
</div>
|
||||
<div className="header-actions">
|
||||
{/* View Toggle */}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Header } from "../Header";
|
||||
describe("Header", () => {
|
||||
it("renders a logo image with correct src and alt", () => {
|
||||
render(<Header />);
|
||||
const logo = screen.getByAltText("kb logo");
|
||||
const logo = screen.getByAltText("Fusion logo");
|
||||
expect(logo).toBeDefined();
|
||||
expect(logo.tagName).toBe("IMG");
|
||||
expect((logo as HTMLImageElement).src).toContain("/logo.svg");
|
||||
@@ -13,7 +13,7 @@ describe("Header", () => {
|
||||
|
||||
it("renders the logo before the h1 element", () => {
|
||||
render(<Header />);
|
||||
const logo = screen.getByAltText("kb logo");
|
||||
const logo = screen.getByAltText("Fusion logo");
|
||||
const h1 = screen.getByRole("heading", { level: 1 });
|
||||
// Logo should be a preceding sibling of the h1
|
||||
expect(logo.compareDocumentPosition(h1) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
|
||||
@@ -188,110 +188,26 @@ describe("Header", () => {
|
||||
expect(boardBtn.getAttribute("aria-pressed")).toBe("false");
|
||||
});
|
||||
|
||||
// ── Theme Toggle ─────────────────────────────────────────────────
|
||||
|
||||
it("renders theme toggle button when onToggleTheme is provided", () => {
|
||||
const onToggleTheme = vi.fn();
|
||||
render(<Header themeMode="dark" onToggleTheme={onToggleTheme} />);
|
||||
const btn = screen.getByTestId("theme-toggle-btn");
|
||||
expect(btn).toBeDefined();
|
||||
});
|
||||
|
||||
it("does not render theme toggle when onToggleTheme is not provided", () => {
|
||||
render(<Header />);
|
||||
const btn = screen.queryByTestId("theme-toggle-btn");
|
||||
expect(btn).toBeNull();
|
||||
});
|
||||
|
||||
it("calls onToggleTheme when theme toggle button is clicked", () => {
|
||||
const onToggleTheme = vi.fn();
|
||||
render(<Header themeMode="dark" onToggleTheme={onToggleTheme} />);
|
||||
const btn = screen.getByTestId("theme-toggle-btn");
|
||||
fireEvent.click(btn);
|
||||
expect(onToggleTheme).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it("shows Moon icon for dark mode", () => {
|
||||
const onToggleTheme = vi.fn();
|
||||
render(<Header themeMode="dark" onToggleTheme={onToggleTheme} />);
|
||||
const btn = screen.getByTestId("theme-toggle-btn");
|
||||
expect(btn.querySelector("svg")).toBeDefined();
|
||||
});
|
||||
|
||||
it("shows Sun icon for light mode", () => {
|
||||
const onToggleTheme = vi.fn();
|
||||
render(<Header themeMode="light" onToggleTheme={onToggleTheme} />);
|
||||
const btn = screen.getByTestId("theme-toggle-btn");
|
||||
expect(btn.querySelector("svg")).toBeDefined();
|
||||
});
|
||||
|
||||
it("shows Monitor icon for system mode", () => {
|
||||
const onToggleTheme = vi.fn();
|
||||
render(<Header themeMode="system" onToggleTheme={onToggleTheme} />);
|
||||
const btn = screen.getByTestId("theme-toggle-btn");
|
||||
expect(btn.querySelector("svg")).toBeDefined();
|
||||
});
|
||||
|
||||
it("shows correct title for dark mode", () => {
|
||||
const onToggleTheme = vi.fn();
|
||||
render(<Header themeMode="dark" onToggleTheme={onToggleTheme} />);
|
||||
const btn = screen.getByTitle("Toggle theme (Dark)");
|
||||
expect(btn).toBeDefined();
|
||||
});
|
||||
|
||||
it("shows correct title for light mode", () => {
|
||||
const onToggleTheme = vi.fn();
|
||||
render(<Header themeMode="light" onToggleTheme={onToggleTheme} />);
|
||||
const btn = screen.getByTitle("Toggle theme (Light)");
|
||||
expect(btn).toBeDefined();
|
||||
});
|
||||
|
||||
it("shows correct title for system mode", () => {
|
||||
const onToggleTheme = vi.fn();
|
||||
render(<Header themeMode="system" onToggleTheme={onToggleTheme} />);
|
||||
const btn = screen.getByTitle("Toggle theme (System)");
|
||||
expect(btn).toBeDefined();
|
||||
});
|
||||
|
||||
// ── Terminal Button ─────────────────────────────────────────────
|
||||
// Terminal button is now always enabled (interactive shell feature)
|
||||
|
||||
it("renders terminal button with correct title", () => {
|
||||
const onToggle = vi.fn();
|
||||
render(<Header onToggleTerminal={onToggle} inProgressCount={0} />);
|
||||
render(<Header onToggleTerminal={onToggle} />);
|
||||
const btn = screen.getByTitle("Open Terminal");
|
||||
expect(btn).toBeDefined();
|
||||
});
|
||||
|
||||
it("calls onToggleTerminal when terminal button is clicked", () => {
|
||||
const onToggle = vi.fn();
|
||||
render(<Header onToggleTerminal={onToggle} inProgressCount={1} />);
|
||||
render(<Header onToggleTerminal={onToggle} />);
|
||||
const btn = screen.getByTitle("Open Terminal");
|
||||
fireEvent.click(btn);
|
||||
expect(onToggle).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it("is enabled regardless of in-progress task count", () => {
|
||||
// Terminal is now always accessible as an interactive shell
|
||||
const { rerender } = render(<Header onToggleTerminal={vi.fn()} inProgressCount={0} />);
|
||||
let btn = screen.getByTitle("Open Terminal");
|
||||
it("is enabled", () => {
|
||||
render(<Header onToggleTerminal={vi.fn()} />);
|
||||
const btn = screen.getByTitle("Open Terminal");
|
||||
expect((btn as HTMLButtonElement).disabled).toBe(false);
|
||||
|
||||
rerender(<Header onToggleTerminal={vi.fn()} inProgressCount={3} />);
|
||||
btn = screen.getByTitle("Open Terminal");
|
||||
expect((btn as HTMLButtonElement).disabled).toBe(false);
|
||||
});
|
||||
|
||||
it("shows badge when in-progress tasks exist", () => {
|
||||
render(<Header onToggleTerminal={vi.fn()} inProgressCount={3} />);
|
||||
const badge = screen.getByTestId("terminal-badge");
|
||||
expect(badge.textContent).toBe("3");
|
||||
});
|
||||
|
||||
it("does not show badge when no in-progress tasks", () => {
|
||||
render(<Header onToggleTerminal={vi.fn()} inProgressCount={0} />);
|
||||
const badge = screen.queryByTestId("terminal-badge");
|
||||
expect(badge).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>kb | board</title>
|
||||
<title>Fusion</title>
|
||||
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
|
||||
<script>
|
||||
// Theme initialization - runs before React to prevent flash
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" fill="none">
|
||||
<rect x="14" y="32" width="18" height="64" rx="9" fill="#d29922" opacity="0.9"/>
|
||||
<rect x="40" y="24" width="18" height="80" rx="9" fill="#58a6ff" opacity="0.9"/>
|
||||
<rect x="66" y="16" width="18" height="96" rx="9" fill="#bc8cff" opacity="0.9"/>
|
||||
<rect x="92" y="28" width="18" height="72" rx="9" fill="#3fb950" opacity="0.9"/>
|
||||
<circle cx="23" cy="52" r="4" fill="#0d1117" opacity="0.6"/>
|
||||
<circle cx="49" cy="40" r="4" fill="#0d1117" opacity="0.6"/>
|
||||
<circle cx="75" cy="36" r="4" fill="#0d1117" opacity="0.6"/>
|
||||
<circle cx="101" cy="48" r="4" fill="#0d1117" opacity="0.6"/>
|
||||
<circle cx="44" cy="44" r="20" fill="#58a6ff"/>
|
||||
<circle cx="84" cy="44" r="20" fill="#58a6ff"/>
|
||||
<circle cx="44" cy="84" r="20" fill="#58a6ff"/>
|
||||
<circle cx="84" cy="84" r="20" fill="#58a6ff"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 667 B After Width: | Height: | Size: 282 B |
Reference in New Issue
Block a user