feat: add shared toolbox header and appearance

This commit is contained in:
2026-07-23 00:11:13 +02:00
parent 5e206baf42
commit a058858b50
17 changed files with 982 additions and 216 deletions

View File

@@ -1,6 +1,9 @@
import { render, screen, waitFor } from "@testing-library/react";
import type { ToolboxAppManifest } from "@add-ideas/toolbox-contract";
import { describe, expect, it, vi } from "vitest";
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import {
TOOLBOX_PREFERENCES_KEY,
type ToolboxAppManifest,
} from "@add-ideas/toolbox-contract";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { AppShell } from "../src/index.js";
@@ -76,11 +79,15 @@ function catalogFetch() {
}
describe("AppShell", () => {
beforeEach(() => localStorage.clear());
it("renders identity, privacy, version, actions, and content standalone", async () => {
const onHelp = vi.fn();
const fetch = vi.fn();
render(
<AppShell
app={currentApp}
helpAction={{ onClick: onHelp }}
appActions={<a href="/help">Help</a>}
contextOptions={{
location: "https://tools.example.test/pdf/",
@@ -107,7 +114,14 @@ describe("AppShell", () => {
).toBeInTheDocument();
expect(
screen.queryByRole("navigation", { name: "Toolbox applications" }),
).not.toBeInTheDocument();
).toHaveTextContent("Open this app from Toolbox");
expect(
screen.getByRole("link", { name: /Source for PDF Workbench/u }),
).toHaveAttribute("href", "https://git.example.test/pdf-tools");
fireEvent.click(screen.getByRole("button", { name: "Help" }));
expect(onHelp).toHaveBeenCalledOnce();
expect(screen.getByText("Personalize")).toBeInTheDocument();
expect(screen.getByText("Apps")).toBeInTheDocument();
});
it("loads same-origin context and creates full-page contextual switch links", async () => {
@@ -171,7 +185,7 @@ describe("AppShell", () => {
expect(fetch).not.toHaveBeenCalled();
expect(
screen.queryByRole("navigation", { name: "Toolbox applications" }),
).not.toBeInTheDocument();
).toHaveTextContent("Open this app from Toolbox");
});
it.each([
@@ -199,4 +213,53 @@ describe("AppShell", () => {
document.querySelector("[data-toolbox-context='standalone']"),
).toBeInTheDocument();
});
it("uses and updates the shared light, dark, and system preference", async () => {
localStorage.setItem(
TOOLBOX_PREFERENCES_KEY,
JSON.stringify({
version: 1,
pinned: [],
order: [],
hidden: [],
theme: "dark",
}),
);
render(
<AppShell
app={currentApp}
contextOptions={{ location: "https://tools.example.test/pdf/" }}
>
PDF
</AppShell>,
);
const shell = document.querySelector(".toolbox-shell");
expect(shell).toHaveAttribute("data-toolbox-theme", "dark");
fireEvent.click(screen.getByText("Personalize"));
fireEvent.click(screen.getByRole("button", { name: "Light" }));
expect(shell).toHaveAttribute("data-toolbox-theme", "light");
expect(
JSON.parse(localStorage.getItem(TOOLBOX_PREFERENCES_KEY) ?? "{}"),
).toMatchObject({ theme: "light" });
});
it("resolves a relative explicit manifest URL against the deployed page", async () => {
render(
<AppShell
app={currentApp}
manifestUrl="./toolbox-app.json"
contextOptions={{
location: "https://tools.example.test/nested/pdf/index.html",
}}
>
PDF
</AppShell>,
);
expect(document.querySelector(".toolbox-shell__icon")).toHaveAttribute(
"src",
"https://tools.example.test/nested/pdf/icon.svg",
);
});
});