6cf8a82889
- Settings → Appearance: mode Dark / Light / System (follows the OS live) and a named theme remembered per mode — dark: Default, Darcula, Apple Dark; light: Default, Apple Light, IntelliJ Light, Canvas. Each theme is a CSS-variable override block keyed by `data-theme` on <html>; the sidebar quick toggle gains System and swaps between the two remembered themes. - An inline boot script in index.html applies the stored mode and theme before first paint, so a reload no longer flashes the default colours. - Diff +/- rows are easier to tell apart on every theme: a /20 status tint, a 3px status bar and a bold coloured marker; the changed-span emphasis no longer vanishes on light themes. - #42: the Add-lane modal (Repo mode / "Current branch") asks POST /api/lanes/ensure with `exact: true`. Without it a folder nested inside an existing lane resolved to that parent lane with created:false, so the modal closed and no new card appeared. When the folder really already has a lane, the modal now says which one instead of closing. Closes #42
60 lines
2.4 KiB
TypeScript
60 lines
2.4 KiB
TypeScript
/**
|
|
* @file Sidebar.theme.test.tsx
|
|
* @description Tests for the Sidebar's theme controls: the expanded panel's Dark / Light / System buttons and the collapsed one-click toggle, both writing through useTheme.
|
|
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
|
*/
|
|
|
|
import { describe, it, expect, beforeEach } from "vitest";
|
|
import { render, screen } from "@testing-library/react";
|
|
import userEvent from "@testing-library/user-event";
|
|
import { MemoryRouter } from "react-router-dom";
|
|
import { Sidebar } from "../Sidebar";
|
|
|
|
function renderSidebar(collapsed = false) {
|
|
return render(
|
|
<MemoryRouter>
|
|
<Sidebar wsConnected collapsed={collapsed} onToggle={() => {}} />
|
|
</MemoryRouter>
|
|
);
|
|
}
|
|
|
|
describe("Sidebar theme controls", () => {
|
|
beforeEach(() => {
|
|
localStorage.clear();
|
|
document.documentElement.classList.remove("dark");
|
|
});
|
|
|
|
it("expanded panel offers Dark, Light and System with Dark pressed by default", () => {
|
|
renderSidebar();
|
|
expect(screen.getByRole("button", { name: "Dark" })).toHaveAttribute("aria-pressed", "true");
|
|
expect(screen.getByRole("button", { name: "Light" })).toHaveAttribute("aria-pressed", "false");
|
|
expect(screen.getByRole("button", { name: "System" })).toHaveAttribute("aria-pressed", "false");
|
|
});
|
|
|
|
it("clicking System sets the mode and presses only System", async () => {
|
|
const user = userEvent.setup();
|
|
renderSidebar();
|
|
await user.click(screen.getByRole("button", { name: "System" }));
|
|
expect(localStorage.getItem("theme")).toBe("system");
|
|
expect(screen.getByRole("button", { name: "System" })).toHaveAttribute("aria-pressed", "true");
|
|
expect(screen.getByRole("button", { name: "Dark" })).toHaveAttribute("aria-pressed", "false");
|
|
});
|
|
|
|
it("clicking Light applies light mode to <html>", async () => {
|
|
const user = userEvent.setup();
|
|
renderSidebar();
|
|
await user.click(screen.getByRole("button", { name: "Light" }));
|
|
expect(localStorage.getItem("theme")).toBe("light");
|
|
expect(document.documentElement.classList.contains("dark")).toBe(false);
|
|
});
|
|
|
|
it("collapsed toggle flips to the opposite of the resolved mode", async () => {
|
|
const user = userEvent.setup();
|
|
localStorage.setItem("theme", "light");
|
|
renderSidebar(true);
|
|
await user.click(screen.getByRole("button", { name: "Switch to Dark" }));
|
|
expect(localStorage.getItem("theme")).toBe("dark");
|
|
expect(screen.getByRole("button", { name: "Switch to Light" })).toBeInTheDocument();
|
|
});
|
|
});
|