- The Downloads module, its rail icon and badge are gone. A bookmark, the remembered page and an old
history entry that name it land on Files (urlState.resolvePage); `g d` goes to Files too.
- Users page: a Storage tab — the disk in numbers, every user's use against their own limit with
their file count, every user's downloads; aligned tables, one menu per row (edit / reset quota,
open source, copy, the owner's quota). The admin storage rows carry quota + file count, the admin
job list its owner.
- Settings: a Downloads tab — the download formats in one list (duplicate a built-in, edit or delete
your own) and the site sign-ins, moved from Account.
- "View" on an added download, a clip or a converting upload opens the tray on that row (unfolded,
back if closed; a finished one opens as a file; under a player it opens without taking focus).
- The download dialog opens on the format used last.
- DataTable: `pager={false}` for a short list, `cardRow` to give a lone "…" its own card line.
- Welcome and README speak of Files; a new Files screenshot.
47 lines
1.7 KiB
TypeScript
47 lines
1.7 KiB
TypeScript
// @vitest-environment jsdom
|
|
//
|
|
// Below `md` a table is a list of cards, its columns flowing one after another. A column marked
|
|
// `cardRow` takes a line of its own there — a row's lone "…" otherwise landed at the end of one
|
|
// card's line and on the next line of another.
|
|
import { describe, it, expect, afterEach, vi } from "vitest";
|
|
import { render, cleanup, screen } from "@testing-library/react";
|
|
import DataTable, { type Column } from "./DataTable";
|
|
|
|
vi.mock("react-i18next", async (importOriginal) => ({
|
|
...(await importOriginal<typeof import("react-i18next")>()),
|
|
useTranslation: () => ({ i18n: { language: "en" }, t: (key: string) => key }),
|
|
}));
|
|
|
|
const narrow = (matches: boolean) =>
|
|
vi.stubGlobal(
|
|
"matchMedia",
|
|
(query: string) =>
|
|
({
|
|
matches,
|
|
media: query,
|
|
addEventListener: () => {},
|
|
removeEventListener: () => {},
|
|
}) as unknown as MediaQueryList,
|
|
);
|
|
|
|
afterEach(() => {
|
|
cleanup();
|
|
vi.unstubAllGlobals();
|
|
});
|
|
|
|
type Row = { id: string };
|
|
const columns: Column<Row>[] = [
|
|
{ key: "name", header: "Name", cardPrimary: true, render: (r) => r.id },
|
|
{ key: "size", header: "Size", render: () => "1 MB" },
|
|
{ key: "menu", header: "", cardLabel: false, cardRow: true, render: () => <button>more</button> },
|
|
];
|
|
|
|
describe("DataTable cards", () => {
|
|
it("gives a cardRow column a line of its own; the others flow", () => {
|
|
narrow(false);
|
|
render(<DataTable rows={[{ id: "a" }]} columns={columns} rowKey={(r) => r.id} />);
|
|
expect(screen.getByText("more").parentElement!.className).toContain("basis-full");
|
|
expect(screen.getByText("1 MB").parentElement!.className).not.toContain("basis-full");
|
|
});
|
|
});
|