Merge pull request #38778 from BerriAI/litellm_/json-readability-logs-661421

fix(ui): make the logs JSON viewer follow the theme in dark mode
This commit is contained in:
yuneng-jiang 2026-08-29 11:42:04 -07:00 committed by GitHub
commit 207893db9c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 35 additions and 6 deletions

View file

@ -1,17 +1,26 @@
import { render, screen } from "@testing-library/react";
import { ThemeProvider } from "next-themes";
import { darkStyles, defaultStyles } from "react-json-view-lite";
import { describe, expect, it } from "vitest";
import { JsonViewer } from "./JsonViewer";
const renderWithTheme = (theme: "light" | "dark", data: unknown) =>
render(
<ThemeProvider attribute="class" defaultTheme={theme} enableSystem={false}>
<JsonViewer data={data} mode="formatted" />
</ThemeProvider>,
);
describe("JsonViewer", () => {
it("should render a placeholder and no tree when the log entry carries no payload", () => {
render(<JsonViewer data={null} mode="formatted" />);
renderWithTheme("light", null);
expect(screen.getByText("No data")).toBeInTheDocument();
expect(screen.queryByRole("tree")).not.toBeInTheDocument();
});
it("should render the payload as a tree exposing its keys", () => {
render(<JsonViewer data={{ model: "claude-opus-4-5", stream: true }} mode="formatted" />);
renderWithTheme("light", { model: "claude-opus-4-5", stream: true });
expect(screen.getByRole("tree")).toBeInTheDocument();
expect(screen.getByText(/model/)).toBeInTheDocument();
@ -20,9 +29,26 @@ describe("JsonViewer", () => {
});
it("should treat an empty payload as data rather than showing the placeholder", () => {
render(<JsonViewer data={{}} mode="formatted" />);
renderWithTheme("light", {});
expect(screen.getByRole("tree")).toBeInTheDocument();
expect(screen.queryByText("No data")).not.toBeInTheDocument();
});
it("should style the tree with the light palette when the dashboard theme is light", () => {
renderWithTheme("light", { model: "claude-opus-4-5" });
expect(screen.getByRole("tree")).toHaveClass(...defaultStyles.container.split(" "));
});
it("should style the tree with the dark palette when the dashboard theme is dark", () => {
renderWithTheme("dark", { model: "claude-opus-4-5" });
const tree = screen.getByRole("tree");
expect(tree).toHaveClass(...darkStyles.container.split(" "));
defaultStyles.container
.split(" ")
.filter((className) => !darkStyles.container.split(" ").includes(className))
.forEach((lightOnlyClassName) => expect(tree).not.toHaveClass(lightOnlyClassName));
});
});

View file

@ -1,4 +1,5 @@
import { JsonView, defaultStyles } from "react-json-view-lite";
import { useTheme } from "next-themes";
import { JsonView, darkStyles, defaultStyles } from "react-json-view-lite";
import "react-json-view-lite/dist/index.css";
import { JSON_MAX_HEIGHT, SPACING_LARGE } from "./constants";
@ -12,6 +13,8 @@ interface JsonViewerProps {
* Uses an interactive tree component for easy navigation.
*/
export function JsonViewer({ data }: JsonViewerProps) {
const { resolvedTheme } = useTheme();
if (!data) return <span className="text-muted-foreground">No data</span>;
return (
@ -24,8 +27,8 @@ export function JsonViewer({ data }: JsonViewerProps) {
borderRadius: 4,
}}
>
<div className="**:[[role='tree']]:bg-background! **:[[role='tree']]:text-foreground">
<JsonView data={data} style={defaultStyles} clickToExpandNode={true} />
<div className="**:[[role='tree']]:bg-transparent!">
<JsonView data={data} style={resolvedTheme === "dark" ? darkStyles : defaultStyles} clickToExpandNode={true} />
</div>
</div>
);