fix: admin config pickers fit a phone

A segmented bar cannot shrink or wrap, and the five log levels (~300 px) are wider than a phone leaves beside the navigation rail. A choice is a bar only when its labels are short; the rows stack below sm.
This commit is contained in:
2026-09-17 03:53:40 +02:00
parent f92cd9017f
commit 7789db8240
2 changed files with 30 additions and 17 deletions
@@ -4,7 +4,8 @@
// a dropdown, and the pick is what Save sends. A cleared draft (reset to the default) shows the default
// as the active option rather than no option at all.
//
// i18n is a key-passthrough; the admin config API is stubbed.
// i18n is a key-passthrough, except that an option's label is its value (the bar-or-dropdown choice
// weighs the real label length, which a full key would distort); the admin config API is stubbed.
import { afterEach, describe, expect, it, vi } from "vitest";
import { cleanup, fireEvent, render, screen, waitFor } from "@testing-library/react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
@@ -14,7 +15,10 @@ vi.mock("react-i18next", async (importOriginal) => {
const actual = await importOriginal<typeof import("react-i18next")>();
return {
...actual,
useTranslation: () => ({ t: (key: string) => key, i18n: { language: "en" } }),
useTranslation: () => ({
t: (key: string) => (key.startsWith("config.choices.") ? key.split(".").pop() : key),
i18n: { language: "en" },
}),
};
});
@@ -48,6 +52,7 @@ vi.mock("../lib/api", async (importOriginal) => {
g: [
choice("plex_transcode_hw", ["auto", "on", "off"], "on", true),
choice("plex_transcode_preset", PRESETS, "veryfast"),
choice("log_level", ["TRACE", "DEBUG", "INFO", "WARNING", "ERROR"], "INFO"),
],
},
secrets_manageable: true,
@@ -79,10 +84,10 @@ describe("ConfigPanel choice keys", () => {
const bar = await screen.findByRole("group", { name: "config.fields.plex_transcode_hw.label" });
const pressed = () =>
[...bar.querySelectorAll('[aria-pressed="true"]')].map((b) => b.textContent);
expect(pressed()).toEqual(["config.choices.plex_transcode_hw.on"]);
expect(pressed()).toEqual(["on"]);
expect(screen.queryByRole("textbox")).toBeNull(); // no free-text field for a choice key
fireEvent.click(screen.getByRole("button", { name: "config.choices.plex_transcode_hw.off" }));
fireEvent.click(screen.getByRole("button", { name: "off" }));
fireEvent.click(await screen.findByRole("button", { name: "config.save" }));
await waitFor(() => expect(setConfig).toHaveBeenCalledWith("plex_transcode_hw", "off"));
});
@@ -91,11 +96,7 @@ describe("ConfigPanel choice keys", () => {
mount();
await screen.findByRole("group", { name: "config.fields.plex_transcode_hw.label" });
fireEvent.click(screen.getByRole("button", { name: /config\.reset$/ }));
expect(
screen
.getByRole("button", { name: "config.choices.plex_transcode_hw.auto" })
.getAttribute("aria-pressed"),
).toBe("true");
expect(screen.getByRole("button", { name: "auto" }).getAttribute("aria-pressed")).toBe("true");
fireEvent.click(await screen.findByRole("button", { name: "config.save" }));
await waitFor(() => expect(resetConfig).toHaveBeenCalledWith("plex_transcode_hw"));
});
@@ -103,16 +104,20 @@ describe("ConfigPanel choice keys", () => {
it("renders a long list as a dropdown showing only the value", async () => {
mount();
const trigger = await screen.findByRole("button", {
name: "config.fields.plex_transcode_preset.label: config.choices.plex_transcode_preset.veryfast",
name: "config.fields.plex_transcode_preset.label: veryfast",
});
expect(trigger.textContent).toBe("config.choices.plex_transcode_preset.veryfast");
expect(trigger.textContent).toBe("veryfast");
fireEvent.click(trigger);
// The popover rows are queried as FilterSelect.dom.test does (`hidden: true`, picked by text).
const rows = screen.getAllByRole("menuitemradio", { hidden: true });
fireEvent.click(
rows.find((r) => r.textContent === "config.choices.plex_transcode_preset.slow")!,
);
fireEvent.click(rows.find((r) => r.textContent === "slow")!);
fireEvent.click(await screen.findByRole("button", { name: "config.save" }));
await waitFor(() => expect(setConfig).toHaveBeenCalledWith("plex_transcode_preset", "slow"));
});
it("a few options with long labels are a dropdown too: a bar that wide does not fit a phone", async () => {
mount();
await screen.findByRole("button", { name: "config.fields.log_level.label: INFO" });
expect(screen.queryByRole("group", { name: "config.fields.log_level.label" })).toBeNull();
});
});