Memoize Fluent chart token resolution

This commit is contained in:
2026-04-20 19:47:10 +02:00
parent 20412dd94b
commit 27369b43d6
9 changed files with 140 additions and 18 deletions

View File

@@ -3,6 +3,7 @@ import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { FluentProvider, webLightTheme } from "@fluentui/react-components";
import { BanTrendChart } from "../BanTrendChart";
import * as chartTheme from "../../utils/chartTheme";
import * as useBanTrendModule from "../../hooks/useBanTrend";
import type { UseBanTrendResult } from "../../hooks/useBanTrend";
import type { BanTrendBucket } from "../../types/ban";
@@ -87,4 +88,22 @@ describe("BanTrendChart", () => {
wrap(<BanTrendChart timeRange="24h" origin="all" />);
expect(screen.getByTestId("area-chart")).toBeInTheDocument();
});
it("memoizes token resolution across rerenders", () => {
const spy = vi.spyOn(chartTheme, "resolveFluentToken");
const props = { timeRange: "24h" as const, origin: "all" as const };
mockHook({ buckets: [{ timestamp: "2024-01-01T00:00:00Z", count: 5 }] });
const { rerender } = wrap(<BanTrendChart {...props} />);
expect(spy).toHaveBeenCalledTimes(6);
rerender(
<FluentProvider theme={webLightTheme}>
<BanTrendChart {...props} />
</FluentProvider>,
);
expect(spy).toHaveBeenCalledTimes(6);
spy.mockRestore();
});
});

View File

@@ -3,6 +3,7 @@ import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { FluentProvider, webLightTheme } from "@fluentui/react-components";
import { JailDistributionChart } from "../JailDistributionChart";
import * as chartTheme from "../../utils/chartTheme";
import * as useJailDistributionModule from "../../hooks/useJailDistribution";
import type { UseJailDistributionResult } from "../../hooks/useJailDistribution";
@@ -84,4 +85,21 @@ describe("JailDistributionChart", () => {
wrap(<JailDistributionChart timeRange="24h" origin="all" />);
expect(screen.getByTestId("bar-chart")).toBeInTheDocument();
});
it("memoizes token resolution across rerenders", () => {
const spy = vi.spyOn(chartTheme, "resolveFluentToken");
const props = { timeRange: "24h" as const, origin: "all" as const };
const { rerender } = wrap(<JailDistributionChart {...props} />);
expect(spy).toHaveBeenCalledTimes(3);
rerender(
<FluentProvider theme={webLightTheme}>
<JailDistributionChart {...props} />
</FluentProvider>,
);
expect(spy).toHaveBeenCalledTimes(3);
spy.mockRestore();
});
});

View File

@@ -2,6 +2,7 @@ import { describe, it, expect, vi } from "vitest";
import { render, screen } from "@testing-library/react";
import { FluentProvider, webLightTheme } from "@fluentui/react-components";
import { TopCountriesBarChart } from "../TopCountriesBarChart";
import * as chartTheme from "../../utils/chartTheme";
vi.mock("recharts", () => ({
ResponsiveContainer: ({ children }: { children: React.ReactNode }) => (
@@ -41,6 +42,26 @@ describe("TopCountriesBarChart", () => {
expect(screen.getByTestId("bar-chart")).toBeInTheDocument();
});
it("memoizes token resolution across rerenders", () => {
const spy = vi.spyOn(chartTheme, "resolveFluentToken");
const props = {
countries: { DE: 50, US: 30, FR: 15 },
countryNames: { DE: "Germany", US: "United States", FR: "France" },
};
const { rerender } = wrap(<TopCountriesBarChart {...props} />);
expect(spy).toHaveBeenCalledTimes(3);
rerender(
<FluentProvider theme={webLightTheme}>
<TopCountriesBarChart {...props} />
</FluentProvider>,
);
expect(spy).toHaveBeenCalledTimes(3);
spy.mockRestore();
});
it("does not render more than 20 bars (TOP_N limit)", () => {
// Build 30 countries — only top 20 should appear in the chart
const countries: Record<string, number> = {};

View File

@@ -2,6 +2,7 @@ import { describe, it, expect, vi } from "vitest";
import { render, screen } from "@testing-library/react";
import { FluentProvider, webLightTheme } from "@fluentui/react-components";
import { TopCountriesPieChart } from "../TopCountriesPieChart";
import * as chartTheme from "../../utils/chartTheme";
vi.mock("recharts", () => ({
ResponsiveContainer: ({ children }: { children: React.ReactNode }) => (
@@ -38,4 +39,24 @@ describe("TopCountriesPieChart", () => {
);
expect(screen.getByTestId("pie-chart")).toBeInTheDocument();
});
it("memoizes palette token resolution across rerenders", () => {
const spy = vi.spyOn(chartTheme, "resolveFluentToken");
const props = {
countries: { DE: 30, US: 20, CN: 10 },
countryNames: { DE: "Germany", US: "United States", CN: "China" },
};
const { rerender } = wrap(<TopCountriesPieChart {...props} />);
expect(spy).toHaveBeenCalledTimes(5);
rerender(
<FluentProvider theme={webLightTheme}>
<TopCountriesPieChart {...props} />
</FluentProvider>,
);
expect(spy).toHaveBeenCalledTimes(5);
spy.mockRestore();
});
});