feat(ui): the six native selects become the house controls

Owner's pick from the S7 mockups (2026-09-19): a short fixed set is a SegmentedToggle — the live
search's result count, the table's rows per page, a download profile's mode (on its own row, so the
three worded choices fit a phone) and audio format — and a longer set or the user's own list is a
FilterSelect: the profile's quality (seven heights) and the download dialog's profile.

A lint rule refuses a native <select> from now on (the canonical "FE control" row). The px-frozen
size rules also caught up with a fraction: `text-[9.5px]` had slipped past the integer-only pattern
in the Glass tuner, which is rem now.
This commit is contained in:
2026-09-19 17:53:22 +02:00
parent 17a26894b0
commit 0ef25a8037
9 changed files with 171 additions and 84 deletions
@@ -0,0 +1,34 @@
// @vitest-environment jsdom
import { describe, it, expect, vi, afterEach } from "vitest";
import { render, screen, fireEvent, cleanup } from "@testing-library/react";
vi.mock("react-i18next", () => ({ useTranslation: () => ({ t: (k: string) => k }) }));
import Pager from "./Pager";
afterEach(cleanup);
// Rows per page is a short fixed set, so it is the house's segmented control (debt burn 2026-09),
// not a native <select>: one named group, the current size pressed, "All" as size 0.
describe("Pager · rows per page", () => {
it("offers each size and All as one named group, and reports the pick", () => {
const onSizeChange = vi.fn();
render(
<Pager
page={0}
size={25}
total={120}
pageSizeOptions={[10, 25, 50, 100]}
onPageChange={() => {}}
onSizeChange={onSizeChange}
sizeLabel="Rows per page"
/>,
);
const group = screen.getByRole("group", { name: "Rows per page" });
expect(group.querySelector('[aria-pressed="true"]')?.textContent).toBe("25");
fireEvent.click(screen.getByRole("button", { name: "50" }));
fireEvent.click(screen.getByRole("button", { name: "datatable.all" }));
expect(onSizeChange.mock.calls).toEqual([[50], [0]]);
});
});