import { i18n } from "@lingui/core";
import { I18nProvider } from "@lingui/react";
import { act, cleanup, fireEvent, render, screen, waitFor } from "@testing-library/react";
import * as React from "react";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import {
SortableContentSettingsSection,
SortableContentSettingsSections,
} from "../../src/components/SortableContentSettingsSections";
const STORAGE_KEY = "emdash:content-settings-layout:v1:user-1:posts";
function TestSections() {
return (
Publish
SEO
);
}
function renderSections() {
return render(
,
);
}
async function pressKey(target: Document | HTMLElement, key: string, code: string) {
await act(async () => {
fireEvent.keyDown(target, { key, code });
// dnd-kit schedules keyboard sensor listeners and state updates on the next task.
await new Promise((resolve) => setTimeout(resolve, 0));
});
}
describe("SortableContentSettingsSections", () => {
beforeEach(() => {
i18n.load("en", {});
i18n.activate("en");
window.localStorage.clear();
});
afterEach(cleanup);
it("restores a saved section order and exposes accessible drag handles", () => {
window.localStorage.setItem(
STORAGE_KEY,
JSON.stringify({ version: 1, order: ["seo", "publish"] }),
);
const { container } = renderSections();
const visibleSections = [...container.querySelectorAll("section")];
expect(visibleSections.map((section) => section.textContent)).toEqual(["SEO", "Publish"]);
expect(screen.getByRole("button", { name: "Drag to reorder SEO" })).toBeTruthy();
expect(screen.getByRole("button", { name: "Drag to reorder Publish" })).toBeTruthy();
});
it("falls back to the default order when browser state is malformed", () => {
window.localStorage.setItem(STORAGE_KEY, "not-json");
const { container } = renderSections();
const visibleSections = [...container.querySelectorAll("section")];
expect(visibleSections.map((section) => section.textContent)).toEqual(["Publish", "SEO"]);
});
it("keeps sortable rows full-width and exposes disclosure spacing", () => {
const { container } = render(
Outline
,
);
const section = container.querySelector("section");
const handle = screen.getByRole("button", { name: "Drag to reorder Outline" });
expect(section?.style.inlineSize).toBe("100%");
expect(section?.dataset.disclosure).toBe("true");
expect(handle.classList.contains("end-3")).toBe(true);
expect(handle.classList.contains("end-10")).toBe(false);
});
it("collapses every section to its heading while keyboard sorting is active", async () => {
const { container } = renderSections();
const handle = screen.getByRole("button", { name: "Drag to reorder Publish" });
handle.focus();
await pressKey(handle, " ", "Space");
const sections = [...container.querySelectorAll("section")];
expect(sections.every((section) => section.dataset.sorting === "true")).toBe(true);
expect(screen.getByTestId("publish-section").parentElement).toBe(sections[0]);
expect(screen.getByTestId("seo-section").parentElement).toBe(sections[1]);
for (const section of sections) {
const heading = section.querySelector("[data-sortable-heading]");
const sectionHandle = section.querySelector("[data-sortable-handle]");
expect(heading?.style.minHeight).toBe("48px");
expect(section.className).toContain(
"[&>*:not([data-sortable-heading]):not([data-sortable-handle])]:hidden",
);
expect(sectionHandle?.classList.contains("top-1/2")).toBe(true);
expect(sectionHandle?.classList.contains("-translate-y-1/2")).toBe(true);
}
await pressKey(document, "Escape", "Escape");
await waitFor(() => {
expect(sections.every((section) => section.dataset.sorting === "false")).toBe(true);
});
expect(container.querySelector("[data-sortable-heading]")).toBeNull();
expect(sections[0]?.className).not.toContain(
"[&>*:not([data-sortable-heading]):not([data-sortable-handle])]:hidden",
);
expect(screen.getByTestId("publish-section").parentElement).toBe(sections[0]);
expect(screen.getByTestId("seo-section").parentElement).toBe(sections[1]);
});
it("persists a keyboard reorder and restores expanded section content", async () => {
const { container } = renderSections();
const handle = screen.getByRole("button", { name: "Drag to reorder Publish" });
handle.focus();
await pressKey(handle, " ", "Space");
await pressKey(document, "ArrowDown", "ArrowDown");
await pressKey(document, " ", "Space");
await waitFor(() => {
const sections = [...container.querySelectorAll("section")];
expect(sections.map((section) => section.textContent)).toEqual(["SEO", "Publish"]);
expect(sections.every((section) => section.dataset.sorting === "false")).toBe(true);
});
const saved = JSON.parse(window.localStorage.getItem(STORAGE_KEY) ?? "null") as {
order: string[];
};
expect(saved.order.indexOf("seo")).toBeLessThan(saved.order.indexOf("publish"));
expect(screen.getByTestId("publish-section").parentElement?.tagName).toBe("SECTION");
expect(screen.getByTestId("seo-section").parentElement?.tagName).toBe("SECTION");
});
it("reports keyboard sorting start and cancellation to surrounding controls", async () => {
const onSortingChange = vi.fn();
const { container } = render(
Publish
,
);
const handle = screen.getByRole("button", { name: "Drag to reorder Publish" });
handle.focus();
await pressKey(handle, " ", "Space");
expect(onSortingChange).toHaveBeenLastCalledWith(true);
expect(container.querySelector("section")?.dataset.sorting).toBe("true");
await pressKey(document, "Escape", "Escape");
await waitFor(() => expect(onSortingChange).toHaveBeenLastCalledWith(false));
expect(container.querySelector("section")?.dataset.sorting).toBe("false");
});
});