feat: unify layout slot architecture - make header/footer configurable positions, footer as array, add defaultPosition fallback, add tests, update docs

This commit is contained in:
saberzero1
2026-07-27 17:32:40 +02:00
parent e37b4888f3
commit a121fd2e96
20 changed files with 1180 additions and 57 deletions
@@ -0,0 +1,86 @@
import test, { describe } from "node:test"
import assert from "node:assert"
import { resolveLayout } from "./dispatcher"
import { QuartzPageTypePluginInstance } from "../types"
import { QuartzComponent } from "../../components/types"
const StubA: QuartzComponent = (() => null) as unknown as QuartzComponent
const StubB: QuartzComponent = (() => null) as unknown as QuartzComponent
const StubHead: QuartzComponent = (() => null) as unknown as QuartzComponent
function makePageType(
overrides: Partial<QuartzPageTypePluginInstance> = {},
): QuartzPageTypePluginInstance {
return {
name: "test-page-type",
layout: "content",
match: () => true,
body: () => (() => null) as unknown as QuartzComponent,
...overrides,
} as QuartzPageTypePluginInstance
}
describe("resolveLayout", () => {
test("footer defaults to [] when sharedDefaults omits footer", () => {
const result = resolveLayout(makePageType(), { head: StubHead }, {})
assert.deepStrictEqual(result.footer, [])
})
test("header defaults to [] when sharedDefaults omits header", () => {
const result = resolveLayout(makePageType(), { head: StubHead }, {})
assert.deepStrictEqual(result.header, [])
})
test("footer from sharedDefaults is used when no override", () => {
const result = resolveLayout(makePageType(), { head: StubHead, footer: [StubA] }, {})
assert.deepStrictEqual(result.footer, [StubA])
})
test("byPageType override replaces footer", () => {
const result = resolveLayout(
makePageType(),
{ head: StubHead, footer: [StubA] },
{ content: { footer: [StubB] } },
)
assert.deepStrictEqual(result.footer, [StubB])
})
test("byPageType override clears footer with []", () => {
const result = resolveLayout(
makePageType(),
{ head: StubHead, footer: [StubA] },
{ content: { footer: [] } },
)
assert.deepStrictEqual(result.footer, [])
})
test("byPageType override clears header with []", () => {
const result = resolveLayout(
makePageType(),
{ head: StubHead, header: [StubA] },
{ content: { header: [] } },
)
assert.deepStrictEqual(result.header, [])
})
})
describe("resolveLayout frame resolution", () => {
test("config override frame wins over page type frame", () => {
const result = resolveLayout(
makePageType({ frame: "minimal" }),
{ head: StubHead },
{ content: { frame: "full-width" } },
)
assert.strictEqual(result.frame, "full-width")
})
test("page type frame wins when no config override", () => {
const result = resolveLayout(makePageType({ frame: "minimal" }), { head: StubHead }, {})
assert.strictEqual(result.frame, "minimal")
})
test("defaults to 'default' when no frame specified", () => {
const result = resolveLayout(makePageType(), { head: StubHead }, {})
assert.strictEqual(result.frame, "default")
})
})
+4 -3
View File
@@ -15,7 +15,8 @@ function getPageTypes(ctx: BuildCtx): QuartzPageTypePluginInstance[] {
return (ctx.cfg.plugins.pageTypes ?? []) as unknown as QuartzPageTypePluginInstance[]
}
function resolveLayout(
/** @internal Exported for testing only. */
export function resolveLayout(
pageType: QuartzPageTypePluginInstance,
sharedDefaults: Partial<FullPageLayout>,
byPageType: Record<string, Partial<FullPageLayout>>,
@@ -31,7 +32,7 @@ function resolveLayout(
afterBody: overrides.afterBody ?? sharedDefaults.afterBody ?? [],
left: overrides.left ?? sharedDefaults.left ?? [],
right: overrides.right ?? sharedDefaults.right ?? [],
footer: overrides.footer ?? sharedDefaults.footer!,
footer: overrides.footer ?? sharedDefaults.footer ?? [],
frame,
}
}
@@ -52,7 +53,7 @@ function collectComponents(
...layout.afterBody,
...layout.left,
...layout.right,
layout.footer,
...layout.footer,
]
for (const c of all) {
if (c) seen.add(c)