214 lines
9.1 KiB
TypeScript
214 lines
9.1 KiB
TypeScript
import { describe, expect, test } from "vitest";
|
|
|
|
import { CategorySectionMockBuilder } from "../items/actions/test/mocks/category-section-mock";
|
|
import { BoardMockBuilder } from "../items/actions/test/mocks/board-mock";
|
|
import { DynamicSectionMockBuilder } from "../items/actions/test/mocks/dynamic-section-mock";
|
|
import { EmptySectionMockBuilder } from "../items/actions/test/mocks/empty-section-mock";
|
|
import { ItemMockBuilder } from "../items/actions/test/mocks/item-mock";
|
|
import {
|
|
createMobileBoardElements,
|
|
createMobileBoardItems,
|
|
getMobileRootSection,
|
|
getMobileSectionAnchorId,
|
|
} from "./mobile-layout";
|
|
|
|
describe("createMobileBoardItems", () => {
|
|
test("selects the first root section in visual order", () => {
|
|
const board = new BoardMockBuilder().build();
|
|
board.sections.push(
|
|
new EmptySectionMockBuilder({ id: "stored-first", xOffset: 0, yOffset: 1 }).build(),
|
|
new EmptySectionMockBuilder({ id: "visually-first", xOffset: 1, yOffset: 0 }).build(),
|
|
new EmptySectionMockBuilder({ id: "visually-second", xOffset: 2, yOffset: 0 }).build(),
|
|
);
|
|
|
|
expect(getMobileRootSection(board).id).toBe("visually-first");
|
|
});
|
|
|
|
test("flattens categories and dynamic sections in visual order", () => {
|
|
const board = new BoardMockBuilder().build();
|
|
const desktopLayoutId = board.layouts.at(0)?.id;
|
|
if (!desktopLayoutId) throw new Error("Expected a desktop layout");
|
|
const rootSection = new EmptySectionMockBuilder({ id: "root", yOffset: 0 }).build();
|
|
const category = new CategorySectionMockBuilder({ id: "category", yOffset: 1, collapsed: true }).build();
|
|
const dynamicSection = new DynamicSectionMockBuilder({ id: "dynamic" })
|
|
.addLayout({ layoutId: desktopLayoutId, parentSectionId: rootSection.id, xOffset: 0, yOffset: 1 })
|
|
.build();
|
|
const nestedDynamicSection = new DynamicSectionMockBuilder({ id: "nested-dynamic" })
|
|
.addLayout({ layoutId: desktopLayoutId, parentSectionId: dynamicSection.id, xOffset: 0, yOffset: 1 })
|
|
.build();
|
|
|
|
board.sections.push(rootSection, category, dynamicSection, nestedDynamicSection);
|
|
board.items.push(
|
|
new ItemMockBuilder({ id: "second" })
|
|
.addLayout({ layoutId: desktopLayoutId, sectionId: rootSection.id, xOffset: 1, yOffset: 0 })
|
|
.build(),
|
|
new ItemMockBuilder({ id: "first" })
|
|
.addLayout({
|
|
layoutId: desktopLayoutId,
|
|
sectionId: rootSection.id,
|
|
xOffset: 0,
|
|
yOffset: 0,
|
|
width: 8,
|
|
height: 6,
|
|
})
|
|
.build(),
|
|
new ItemMockBuilder({ id: "inside-dynamic" })
|
|
.addLayout({ layoutId: desktopLayoutId, sectionId: dynamicSection.id })
|
|
.build(),
|
|
new ItemMockBuilder({ id: "inside-nested-dynamic" })
|
|
.addLayout({ layoutId: desktopLayoutId, sectionId: nestedDynamicSection.id })
|
|
.build(),
|
|
new ItemMockBuilder({ id: "inside-collapsed-category" })
|
|
.addLayout({ layoutId: desktopLayoutId, sectionId: category.id })
|
|
.build(),
|
|
);
|
|
|
|
const result = createMobileBoardItems(board, desktopLayoutId);
|
|
|
|
expect(result.map((item) => item.id)).toEqual([
|
|
"first",
|
|
"second",
|
|
"inside-dynamic",
|
|
"inside-nested-dynamic",
|
|
"inside-collapsed-category",
|
|
]);
|
|
expect(result[0]).toMatchObject({ width: 2, height: 3, xOffset: 0, yOffset: 0 });
|
|
});
|
|
|
|
test("uses the desktop layout and keeps orphaned items", () => {
|
|
const board = new BoardMockBuilder().build();
|
|
const legacyLayoutId = board.layouts.at(0)?.id;
|
|
if (!legacyLayoutId) throw new Error("Expected a legacy layout");
|
|
const desktopLayoutId = "desktop";
|
|
board.layouts.push({ id: desktopLayoutId, name: "Large", columnCount: 16, breakpoint: 1200 });
|
|
board.sections.push(new EmptySectionMockBuilder({ id: "root" }).build());
|
|
board.items.push(
|
|
new ItemMockBuilder({ id: "first" })
|
|
.addLayout({ layoutId: legacyLayoutId, sectionId: "root", yOffset: 2 })
|
|
.addLayout({ layoutId: desktopLayoutId, sectionId: "missing-section", yOffset: 0, width: 0, height: 0 })
|
|
.build(),
|
|
new ItemMockBuilder({ id: "second" })
|
|
.addLayout({ layoutId: legacyLayoutId, sectionId: "root", yOffset: 0 })
|
|
.addLayout({ layoutId: desktopLayoutId, sectionId: "root", yOffset: 1 })
|
|
.build(),
|
|
);
|
|
|
|
const result = createMobileBoardItems(board, desktopLayoutId);
|
|
|
|
expect(result.map((item) => item.id)).toEqual(["second", "first"]);
|
|
expect(result[1]).toMatchObject({ width: 1, height: 1 });
|
|
});
|
|
|
|
test("emits named category and dynamic section headings in visual order", () => {
|
|
const board = new BoardMockBuilder().build();
|
|
const desktopLayoutId = board.layouts.at(0)?.id;
|
|
if (!desktopLayoutId) throw new Error("Expected a desktop layout");
|
|
|
|
const category = new CategorySectionMockBuilder({ id: "category", name: " Services ", yOffset: 0 }).build();
|
|
const dynamicSection = new DynamicSectionMockBuilder({
|
|
id: "dynamic",
|
|
options: { title: "Monitoring", borderColor: "", customCssClasses: [] },
|
|
})
|
|
.addLayout({ layoutId: desktopLayoutId, parentSectionId: category.id, xOffset: 1, yOffset: 0 })
|
|
.build();
|
|
const nestedDynamicSection = new DynamicSectionMockBuilder({
|
|
id: "nested-dynamic",
|
|
options: { title: "Details", borderColor: "", customCssClasses: [] },
|
|
})
|
|
.addLayout({ layoutId: desktopLayoutId, parentSectionId: dynamicSection.id, xOffset: 0, yOffset: 1 })
|
|
.build();
|
|
|
|
board.sections.push(category, dynamicSection, nestedDynamicSection);
|
|
board.items.push(
|
|
new ItemMockBuilder({ id: "before-dynamic" })
|
|
.addLayout({ layoutId: desktopLayoutId, sectionId: category.id, xOffset: 0, yOffset: 0 })
|
|
.build(),
|
|
new ItemMockBuilder({ id: "inside-dynamic" })
|
|
.addLayout({ layoutId: desktopLayoutId, sectionId: dynamicSection.id, xOffset: 0, yOffset: 0 })
|
|
.build(),
|
|
new ItemMockBuilder({ id: "inside-nested" })
|
|
.addLayout({ layoutId: desktopLayoutId, sectionId: nestedDynamicSection.id })
|
|
.build(),
|
|
);
|
|
|
|
const result = createMobileBoardElements(board, desktopLayoutId);
|
|
|
|
expect(
|
|
result.map((element) => (element.type === "sectionHeading" ? `heading:${element.title}` : `item:${element.id}`)),
|
|
).toEqual([
|
|
"heading:Services",
|
|
"item:before-dynamic",
|
|
"heading:Monitoring",
|
|
"item:inside-dynamic",
|
|
"heading:Details",
|
|
"item:inside-nested",
|
|
]);
|
|
expect(result[0]).toMatchObject({
|
|
type: "sectionHeading",
|
|
sectionId: category.id,
|
|
anchorId: getMobileSectionAnchorId(category.id),
|
|
headingLevel: 2,
|
|
});
|
|
expect(result[2]).toMatchObject({ type: "sectionHeading", headingLevel: 3 });
|
|
expect(result[4]).toMatchObject({ type: "sectionHeading", headingLevel: 4 });
|
|
});
|
|
|
|
test("omits headings for empty and unnamed sections while preserving their items", () => {
|
|
const board = new BoardMockBuilder().build();
|
|
const desktopLayoutId = board.layouts.at(0)?.id;
|
|
if (!desktopLayoutId) throw new Error("Expected a desktop layout");
|
|
|
|
const emptyNamedCategory = new CategorySectionMockBuilder({
|
|
id: "empty-category",
|
|
name: "Empty",
|
|
yOffset: 0,
|
|
}).build();
|
|
const unnamedCategory = new CategorySectionMockBuilder({ id: "unnamed-category", name: " ", yOffset: 1 }).build();
|
|
const unnamedDynamicSection = new DynamicSectionMockBuilder({ id: "unnamed-dynamic" })
|
|
.addLayout({ layoutId: desktopLayoutId, parentSectionId: unnamedCategory.id })
|
|
.build();
|
|
|
|
board.sections.push(emptyNamedCategory, unnamedCategory, unnamedDynamicSection);
|
|
board.items.push(
|
|
new ItemMockBuilder({ id: "inside-unnamed" })
|
|
.addLayout({ layoutId: desktopLayoutId, sectionId: unnamedDynamicSection.id })
|
|
.build(),
|
|
new ItemMockBuilder({ id: "orphan" })
|
|
.addLayout({ layoutId: desktopLayoutId, sectionId: "missing-section" })
|
|
.build(),
|
|
);
|
|
|
|
const result = createMobileBoardElements(board, desktopLayoutId);
|
|
|
|
expect(result.map((element) => element.type)).toEqual(["item", "item"]);
|
|
expect(result.map((element) => element.id)).toEqual(["inside-unnamed", "orphan"]);
|
|
});
|
|
|
|
test("does not skip heading levels when unnamed sections are flattened", () => {
|
|
const board = new BoardMockBuilder().build();
|
|
const desktopLayoutId = board.layouts.at(0)?.id;
|
|
if (!desktopLayoutId) throw new Error("Expected a desktop layout");
|
|
|
|
const unnamedCategory = new CategorySectionMockBuilder({ id: "category", name: " " }).build();
|
|
const namedDynamicSection = new DynamicSectionMockBuilder({
|
|
id: "dynamic",
|
|
options: { title: "Services", borderColor: "", customCssClasses: [] },
|
|
})
|
|
.addLayout({ layoutId: desktopLayoutId, parentSectionId: unnamedCategory.id })
|
|
.build();
|
|
|
|
board.sections.push(unnamedCategory, namedDynamicSection);
|
|
board.items.push(
|
|
new ItemMockBuilder({ id: "item" })
|
|
.addLayout({ layoutId: desktopLayoutId, sectionId: namedDynamicSection.id })
|
|
.build(),
|
|
);
|
|
|
|
expect(createMobileBoardElements(board, desktopLayoutId)[0]).toMatchObject({
|
|
type: "sectionHeading",
|
|
title: "Services",
|
|
headingLevel: 2,
|
|
});
|
|
});
|
|
});
|