Files
homarr/apps/nextjs/src/components/board/layout/test/reflow.spec.ts
T

101 lines
3.0 KiB
TypeScript

import { describe, expect, test } from "vitest";
import {
doGridPlacementsOverlap,
getCollapsedDisplayLayout,
placeGridItem,
reflowVerticalLayout,
sortGridPlacementsForReadingOrder,
} from "../index";
import type { GridPlacement } from "../index";
describe("dashboard layout reflow", () => {
test("compacts each horizontal lane and removes collisions", () => {
const result = reflowVerticalLayout(
[
{ id: "a", x: 0, y: 0, w: 1, h: 1 },
{ id: "b", x: 0, y: 3, w: 1, h: 1 },
{ id: "c", x: 1, y: 3, w: 1, h: 1 },
],
{ columnCount: 2 },
);
expect(result).toEqual([
{ id: "a", x: 0, y: 0, w: 1, h: 1 },
{ id: "b", x: 0, y: 1, w: 1, h: 1 },
{ id: "c", x: 1, y: 0, w: 1, h: 1 },
]);
expect(hasOverlap(result)).toBe(false);
});
test("shrinks a collapsed inline container and pulls neighbors up", () => {
const result = getCollapsedDisplayLayout(
[
{ id: "section", x: 0, y: 0, w: 2, h: 3 },
{ id: "below", x: 0, y: 3, w: 2, h: 1 },
{ id: "other-lane", x: 2, y: 4, w: 1, h: 1 },
],
{
columnCount: 3,
collapsedItemIds: new Set(["section"]),
},
);
expect(result).toEqual([
{ id: "section", x: 0, y: 0, w: 2, h: 1 },
{ id: "below", x: 0, y: 1, w: 2, h: 1 },
{ id: "other-lane", x: 2, y: 0, w: 1, h: 1 },
]);
});
test("pins a moved or expanded item and pushes collisions out of the way", () => {
const result = placeGridItem(
[
{ id: "section", x: 0, y: 0, w: 2, h: 1 },
{ id: "neighbor", x: 0, y: 1, w: 2, h: 1 },
{ id: "side", x: 2, y: 2, w: 1, h: 1 },
],
{ id: "section", x: 0, y: 0, w: 2, h: 3 },
3,
);
expect(result).toEqual([
{ id: "section", x: 0, y: 0, w: 2, h: 3 },
{ id: "neighbor", x: 0, y: 3, w: 2, h: 1 },
{ id: "side", x: 2, y: 0, w: 1, h: 1 },
]);
expect(hasOverlap(result)).toBe(false);
});
test("is deterministic regardless of input array order", () => {
const first = [
{ id: "b", x: 0, y: 2, w: 1, h: 1 },
{ id: "a", x: 0, y: 2, w: 1, h: 1 },
{ id: "c", x: 1, y: 8, w: 1, h: 1 },
];
const second = first.toReversed();
expect(byId(reflowVerticalLayout(first, { columnCount: 2 }))).toEqual(
byId(reflowVerticalLayout(second, { columnCount: 2 })),
);
});
test("provides row-major DOM order for the read-only renderer", () => {
const items = [
{ id: "last", x: 0, y: 2, w: 1, h: 1 },
{ id: "right", x: 1, y: 0, w: 1, h: 1 },
{ id: "left", x: 0, y: 0, w: 1, h: 1 },
];
expect(sortGridPlacementsForReadingOrder(items).map((item) => item.id)).toEqual(["left", "right", "last"]);
});
});
const hasOverlap = (placements: readonly GridPlacement[]) =>
placements.some((placement, index) =>
placements.slice(index + 1).some((otherPlacement) => doGridPlacementsOverlap(placement, otherPlacement)),
);
const byId = (placements: readonly GridPlacement[]) =>
Object.fromEntries(placements.map((placement) => [placement.id, placement]));