The user page at /u/:username now opens with a compact identity block (40px avatar, name, handle, clamped bio) instead of a hero header with a share button, and the list beneath it follows the user's column setting like Explore does. In grid mode the header spans every column via a new ColumnGrid header slot, exposed through PagedMemoList.renderHeader. Loading shows a skeleton after the standard delay, not-found renders a body instead of a toast fired from render, and visitors get an explicit empty message. Saved views now apply on profile routes too. A single profile-route matcher in lib/memo-views replaces five separate spellings, isMemoCollectionRoute names the routes where views, calendar days and tags narrow the list in place, and the sidebar shows the Views section on every such route for signed-in users. The never-taken navigation-target branch in the collection sidebar is removed along with its props.
87 lines
3.6 KiB
TypeScript
87 lines
3.6 KiB
TypeScript
import { DirectionProvider } from "@base-ui/react/direction-provider";
|
|
import { render } from "@testing-library/react";
|
|
import { describe, expect, it, vi } from "vitest";
|
|
import ColumnGrid from "@/components/ColumnGrid";
|
|
|
|
// jsdom has no layout engine (offsetHeight/clientWidth are 0) and no ResizeObserver,
|
|
// both of which ColumnGrid guards for — so these assert render structure, not positioning.
|
|
interface Item {
|
|
id: string;
|
|
}
|
|
|
|
const item = (id: string): Item => ({ id });
|
|
const getKey = (i: Item) => i.id;
|
|
|
|
describe("<ColumnGrid>", () => {
|
|
it("renders one card per item", () => {
|
|
const { container } = render(
|
|
<ColumnGrid items={[item("a"), item("b"), item("c")]} getKey={getKey} renderItem={(i) => <div data-testid="card">{i.id}</div>} />,
|
|
);
|
|
|
|
expect(container.querySelectorAll('[data-testid="card"]')).toHaveLength(3);
|
|
});
|
|
|
|
it("renders the leading node as the first tile, before the items", () => {
|
|
const { container, getByTestId } = render(
|
|
<ColumnGrid
|
|
items={[item("a")]}
|
|
getKey={getKey}
|
|
renderItem={(i) => <div data-testid={`card-${i.id}`}>{i.id}</div>}
|
|
leading={<div data-testid="composer" />}
|
|
/>,
|
|
);
|
|
|
|
expect(getByTestId("composer")).toBeInTheDocument();
|
|
// The grid is the container's only child; its first wrapper holds the leading node.
|
|
const grid = container.firstElementChild as HTMLElement;
|
|
expect(grid.children[0].querySelector('[data-testid="composer"]')).not.toBeNull();
|
|
});
|
|
|
|
it("spans the header across the packed columns and starts every column below it", () => {
|
|
// 532px fits two 260px columns with a 12px gap; every measured height is 100px.
|
|
const clientWidth = vi.spyOn(HTMLElement.prototype, "clientWidth", "get").mockReturnValue(532);
|
|
const offsetHeight = vi.spyOn(HTMLElement.prototype, "offsetHeight", "get").mockReturnValue(100);
|
|
|
|
const { getByTestId } = render(
|
|
<ColumnGrid
|
|
items={[item("a"), item("b")]}
|
|
getKey={getKey}
|
|
renderItem={(i) => <div data-testid={`card-${i.id}`}>{i.id}</div>}
|
|
header={<div data-testid="identity" />}
|
|
/>,
|
|
);
|
|
|
|
const header = getByTestId("identity").parentElement as HTMLElement;
|
|
expect(header.style.width).toBe("532px");
|
|
expect(header.style.left).toBe("0px");
|
|
// Header height plus one grid gap: both columns begin on the same line beneath it.
|
|
expect(getByTestId("card-a").parentElement?.style.transform).toContain("translate3d(0px, 112px");
|
|
expect(getByTestId("card-b").parentElement?.style.transform).toContain("translate3d(272px, 112px");
|
|
|
|
clientWidth.mockRestore();
|
|
offsetHeight.mockRestore();
|
|
});
|
|
|
|
it("renders nothing for an empty list", () => {
|
|
const { container } = render(<ColumnGrid items={[]} getKey={getKey} renderItem={() => <div data-testid="card" />} />);
|
|
|
|
expect(container.querySelectorAll('[data-testid="card"]')).toHaveLength(0);
|
|
});
|
|
|
|
it("places the first packed column at inline start in RTL", () => {
|
|
const clientWidth = vi.spyOn(HTMLElement.prototype, "clientWidth", "get").mockReturnValue(532);
|
|
const offsetHeight = vi.spyOn(HTMLElement.prototype, "offsetHeight", "get").mockReturnValue(100);
|
|
|
|
const { getByTestId } = render(
|
|
<DirectionProvider direction="rtl">
|
|
<ColumnGrid items={[item("a"), item("b")]} getKey={getKey} renderItem={(i) => <div data-testid={`card-${i.id}`}>{i.id}</div>} />
|
|
</DirectionProvider>,
|
|
);
|
|
|
|
expect(getByTestId("card-a").parentElement?.style.transform).toContain("translate3d(272px");
|
|
expect(getByTestId("card-b").parentElement?.style.transform).toContain("translate3d(0px");
|
|
|
|
clientWidth.mockRestore();
|
|
offsetHeight.mockRestore();
|
|
});
|
|
});
|