Chore: rename Jellyseerr widget to Seerr and update references (#6322)

This commit is contained in:
shamoon
2026-02-14 16:11:00 -08:00
committed by GitHub
parent cdc96438cd
commit fe0b214334
9 changed files with 34 additions and 31 deletions
+39
View File
@@ -0,0 +1,39 @@
import Block from "components/services/widget/block";
import Container from "components/services/widget/container";
import useWidgetAPI from "utils/proxy/use-widget-api";
export const seerrDefaultFields = ["pending", "approved", "available"];
export default function Component({ service }) {
const { widget } = service;
widget.fields = widget?.fields?.length ? widget.fields : seerrDefaultFields;
const isIssueEnabled = widget.fields.includes("issues");
const { data: statsData, error: statsError } = useWidgetAPI(widget, "request/count");
const { data: issueData, error: issueError } = useWidgetAPI(widget, isIssueEnabled ? "issue/count" : "");
if (statsError || (isIssueEnabled && issueError)) {
return <Container service={service} error={statsError ? statsError : issueError} />;
}
if (!statsData || (isIssueEnabled && !issueData)) {
return (
<Container service={service}>
<Block label="seerr.pending" />
<Block label="seerr.approved" />
<Block label="seerr.available" />
<Block label="seerr.issues" />
</Container>
);
}
return (
<Container service={service}>
<Block label="seerr.pending" value={statsData.pending} />
<Block label="seerr.approved" value={statsData.approved} />
<Block label="seerr.available" value={statsData.available} />
<Block label="seerr.issues" value={`${issueData?.open} / ${issueData?.total}`} />
</Container>
);
}
+62
View File
@@ -0,0 +1,62 @@
// @vitest-environment jsdom
import { screen } from "@testing-library/react";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { renderWithProviders } from "test-utils/render-with-providers";
const { useWidgetAPI } = vi.hoisted(() => ({ useWidgetAPI: vi.fn() }));
vi.mock("utils/proxy/use-widget-api", () => ({ default: useWidgetAPI }));
import Component, { seerrDefaultFields } from "./component";
describe("widgets/seerr/component", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("defaults fields and filters to 3 blocks while loading when issues are not enabled", () => {
useWidgetAPI
.mockReturnValueOnce({ data: undefined, error: undefined }) // request/count
.mockReturnValueOnce({ data: undefined, error: undefined }); // issue/count disabled (endpoint = "")
const service = { widget: { type: "seerr", url: "http://x" } };
const { container } = renderWithProviders(<Component service={service} />, { settings: { hideErrors: false } });
expect(service.widget.fields).toEqual(seerrDefaultFields);
expect(useWidgetAPI.mock.calls[1][1]).toBe("");
expect(container.querySelectorAll(".service-block")).toHaveLength(3);
expect(screen.getByText("seerr.pending")).toBeInTheDocument();
expect(screen.getByText("seerr.approved")).toBeInTheDocument();
expect(screen.getByText("seerr.available")).toBeInTheDocument();
expect(screen.queryByText("seerr.issues")).toBeNull();
});
it("renders issues when enabled (and calls the issue/count endpoint)", () => {
useWidgetAPI
.mockReturnValueOnce({ data: { pending: 1, approved: 2, available: 3 }, error: undefined })
.mockReturnValueOnce({ data: { open: 1, total: 2 }, error: undefined });
const service = {
widget: { type: "seerr", url: "http://x", fields: ["pending", "approved", "available", "issues"] },
};
const { container } = renderWithProviders(<Component service={service} />, { settings: { hideErrors: false } });
expect(useWidgetAPI.mock.calls[1][1]).toBe("issue/count");
expect(container.querySelectorAll(".service-block")).toHaveLength(4);
expect(screen.getByText("1 / 2")).toBeInTheDocument();
});
it("renders error UI when issues are enabled and issue/count errors", () => {
useWidgetAPI
.mockReturnValueOnce({ data: { pending: 0, approved: 0, available: 0 }, error: undefined })
.mockReturnValueOnce({ data: undefined, error: { message: "nope" } });
renderWithProviders(<Component service={{ widget: { type: "seerr", url: "http://x", fields: ["issues"] } }} />, {
settings: { hideErrors: false },
});
expect(screen.getAllByText(/widget\.api_error/i).length).toBeGreaterThan(0);
expect(screen.getByText("nope")).toBeInTheDocument();
});
});
+19
View File
@@ -0,0 +1,19 @@
import credentialedProxyHandler from "utils/proxy/handlers/credentialed";
const widget = {
api: "{url}/api/v1/{endpoint}",
proxyHandler: credentialedProxyHandler,
mappings: {
"request/count": {
endpoint: "request/count",
validate: ["pending", "approved", "available"],
},
"issue/count": {
endpoint: "issue/count",
validate: ["open", "total"],
},
},
};
export default widget;
+11
View File
@@ -0,0 +1,11 @@
import { describe, it } from "vitest";
import { expectWidgetConfigShape } from "test-utils/widget-config";
import widget from "./widget";
describe("jellyseerr widget config", () => {
it("exports a valid widget config", () => {
expectWidgetConfigShape(widget);
});
});