mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-08-24 15:22:20 +10:00
fix: render cover letter exports without resume chrome
This commit is contained in:
@@ -20,6 +20,7 @@ import {
|
||||
WebsiteContactItem,
|
||||
} from "../shared/contact-item";
|
||||
import { TemplateProvider } from "../shared/context";
|
||||
import { shouldShowResumeHeader } from "../shared/cover-letter";
|
||||
import { filterSections } from "../shared/filtering";
|
||||
import { getTemplateMetrics } from "../shared/metrics";
|
||||
import { getTemplatePageMinHeightStyle, getTemplatePageSize } from "../shared/page-size";
|
||||
@@ -64,7 +65,7 @@ export const AzurillPage = ({ page, pageIndex }: TemplatePageProps) => {
|
||||
const metrics = getTemplateMetrics(metadata.page);
|
||||
const pageSize = getTemplatePageSize(metadata.page.format);
|
||||
const pageMinHeightStyle = getTemplatePageMinHeightStyle(metadata.page.format);
|
||||
const showHeader = pageIndex === 0;
|
||||
const showHeader = shouldShowResumeHeader(data, pageIndex);
|
||||
const sidebarSections = filterSections(page.sidebar, data);
|
||||
const mainSections = filterSections(page.main, data);
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
WebsiteContactItem,
|
||||
} from "../shared/contact-item";
|
||||
import { TemplateProvider } from "../shared/context";
|
||||
import { shouldShowResumeHeader } from "../shared/cover-letter";
|
||||
import { filterSections } from "../shared/filtering";
|
||||
import { getTemplateMetrics } from "../shared/metrics";
|
||||
import { getTemplatePageMinHeightStyle, getTemplatePageSize } from "../shared/page-size";
|
||||
@@ -76,7 +77,7 @@ export const BronzorPage = ({ page, pageIndex }: TemplatePageProps) => {
|
||||
const metrics = getTemplateMetrics(metadata.page);
|
||||
const pageSize = getTemplatePageSize(metadata.page.format);
|
||||
const pageMinHeightStyle = getTemplatePageMinHeightStyle(metadata.page.format);
|
||||
const showHeader = pageIndex === 0;
|
||||
const showHeader = shouldShowResumeHeader(data, pageIndex);
|
||||
const sidebarSections = filterSections(page.sidebar, data);
|
||||
const mainSections = filterSections(page.main, data);
|
||||
const sections = getBronzorSections({ mainSections, sidebarSections, fullWidth: page.fullWidth });
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
WebsiteContactItem,
|
||||
} from "../shared/contact-item";
|
||||
import { TemplateProvider } from "../shared/context";
|
||||
import { shouldShowResumeHeader } from "../shared/cover-letter";
|
||||
import { filterSections } from "../shared/filtering";
|
||||
import { getTemplateMetrics } from "../shared/metrics";
|
||||
import { getTemplatePageMinHeightStyle, getTemplatePageSize } from "../shared/page-size";
|
||||
@@ -54,7 +55,7 @@ export const ChikoritaPage = ({ page, pageIndex }: TemplatePageProps) => {
|
||||
const pageSize = getTemplatePageSize(metadata.page.format);
|
||||
const pageMinHeightStyle = getTemplatePageMinHeightStyle(metadata.page.format);
|
||||
const hasPicture = hasTemplatePicture(picture);
|
||||
const showHeader = pageIndex === 0;
|
||||
const showHeader = shouldShowResumeHeader(data, pageIndex);
|
||||
const sidebarSections = filterSections(page.sidebar, data);
|
||||
const mainSections = filterSections(page.main, data);
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { basename } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
const pageFiles = [
|
||||
"azurill/AzurillPage.tsx",
|
||||
"bronzor/BronzorPage.tsx",
|
||||
"chikorita/ChikoritaPage.tsx",
|
||||
"ditgar/DitgarPage.tsx",
|
||||
"ditto/DittoPage.tsx",
|
||||
"gengar/GengarPage.tsx",
|
||||
"glalie/GlaliePage.tsx",
|
||||
"kakuna/KakunaPage.tsx",
|
||||
"lapras/LaprasPage.tsx",
|
||||
"leafish/LeafishPage.tsx",
|
||||
"meowth/MeowthPage.tsx",
|
||||
"onyx/OnyxPage.tsx",
|
||||
"pikachu/PikachuPage.tsx",
|
||||
"rhyhorn/RhyhornPage.tsx",
|
||||
"scizor/ScizorPage.tsx",
|
||||
];
|
||||
|
||||
const readTemplate = (file: string) => {
|
||||
const path = fileURLToPath(new URL(file, import.meta.url));
|
||||
return readFileSync(path, "utf8");
|
||||
};
|
||||
|
||||
describe("cover letter PDF layout", () => {
|
||||
it.each(pageFiles)("%s suppresses the resume header for cover-letter-only documents", (file) => {
|
||||
const source = readTemplate(file);
|
||||
|
||||
expect(source, basename(file)).toContain('from "../shared/cover-letter"');
|
||||
expect(source, basename(file)).toContain("shouldShowResumeHeader(data, pageIndex)");
|
||||
expect(source, basename(file)).not.toContain("const showHeader = pageIndex === 0;");
|
||||
});
|
||||
});
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
WebsiteContactItem,
|
||||
} from "../shared/contact-item";
|
||||
import { TemplateProvider } from "../shared/context";
|
||||
import { shouldShowResumeHeader } from "../shared/cover-letter";
|
||||
import { getFeaturedSummaryLayout } from "../shared/featured-summary";
|
||||
import { filterSections } from "../shared/filtering";
|
||||
import { getTemplateMetrics } from "../shared/metrics";
|
||||
@@ -64,7 +65,7 @@ export const DitgarPage = ({ page, pageIndex }: TemplatePageProps) => {
|
||||
const metrics = getTemplateMetrics(metadata.page);
|
||||
const pageSize = getTemplatePageSize(metadata.page.format);
|
||||
const pageMinHeightStyle = getTemplatePageMinHeightStyle(metadata.page.format);
|
||||
const showHeader = pageIndex === 0;
|
||||
const showHeader = shouldShowResumeHeader(data, pageIndex);
|
||||
const showSidebar = !page.fullWidth || showHeader;
|
||||
const sidebarSections = filterSections(page.sidebar, data);
|
||||
const mainSections = filterSections(page.main, data);
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
WebsiteContactItem,
|
||||
} from "../shared/contact-item";
|
||||
import { TemplateProvider } from "../shared/context";
|
||||
import { shouldShowResumeHeader } from "../shared/cover-letter";
|
||||
import { filterSections } from "../shared/filtering";
|
||||
import { getTemplateMetrics } from "../shared/metrics";
|
||||
import { getTemplatePageMinHeightStyle, getTemplatePageSize } from "../shared/page-size";
|
||||
@@ -59,7 +60,7 @@ export const DittoPage = ({ page, pageIndex }: TemplatePageProps) => {
|
||||
const pageSize = getTemplatePageSize(metadata.page.format);
|
||||
const pageMinHeightStyle = getTemplatePageMinHeightStyle(metadata.page.format);
|
||||
const hasPicture = hasTemplatePicture(picture);
|
||||
const showHeader = pageIndex === 0;
|
||||
const showHeader = shouldShowResumeHeader(data, pageIndex);
|
||||
const sidebarSections = filterSections(page.sidebar, data);
|
||||
const mainSections = filterSections(page.main, data);
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
WebsiteContactItem,
|
||||
} from "../shared/contact-item";
|
||||
import { TemplateProvider } from "../shared/context";
|
||||
import { shouldShowResumeHeader } from "../shared/cover-letter";
|
||||
import { getFeaturedSummaryLayout } from "../shared/featured-summary";
|
||||
import { filterSections } from "../shared/filtering";
|
||||
import { getTemplateMetrics } from "../shared/metrics";
|
||||
@@ -59,7 +60,7 @@ export const GengarPage = ({ page, pageIndex }: TemplatePageProps) => {
|
||||
const metrics = getTemplateMetrics(metadata.page);
|
||||
const pageSize = getTemplatePageSize(metadata.page.format);
|
||||
const pageMinHeightStyle = getTemplatePageMinHeightStyle(metadata.page.format);
|
||||
const showHeader = pageIndex === 0;
|
||||
const showHeader = shouldShowResumeHeader(data, pageIndex);
|
||||
const showSidebar = !page.fullWidth || showHeader;
|
||||
const sidebarSections = filterSections(page.sidebar, data);
|
||||
const mainSections = filterSections(page.main, data);
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
WebsiteContactItem,
|
||||
} from "../shared/contact-item";
|
||||
import { TemplateProvider } from "../shared/context";
|
||||
import { shouldShowResumeHeader } from "../shared/cover-letter";
|
||||
import { filterSections } from "../shared/filtering";
|
||||
import { getTemplateMetrics } from "../shared/metrics";
|
||||
import { getTemplatePageMinHeightStyle, getTemplatePageSize } from "../shared/page-size";
|
||||
@@ -61,7 +62,7 @@ export const GlaliePage = ({ page, pageIndex }: TemplatePageProps) => {
|
||||
const metrics = getTemplateMetrics(metadata.page);
|
||||
const pageSize = getTemplatePageSize(metadata.page.format);
|
||||
const pageMinHeightStyle = getTemplatePageMinHeightStyle(metadata.page.format);
|
||||
const showHeader = pageIndex === 0;
|
||||
const showHeader = shouldShowResumeHeader(data, pageIndex);
|
||||
const showSidebar = !page.fullWidth || showHeader;
|
||||
const mainSections = filterSections(page.main, data);
|
||||
const sidebarSections = filterSections(page.sidebar, data);
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
WebsiteContactItem,
|
||||
} from "../shared/contact-item";
|
||||
import { TemplateProvider } from "../shared/context";
|
||||
import { shouldShowResumeHeader } from "../shared/cover-letter";
|
||||
import { filterSections } from "../shared/filtering";
|
||||
import { getTemplateMetrics } from "../shared/metrics";
|
||||
import { getTemplatePageMinHeightStyle, getTemplatePageSize } from "../shared/page-size";
|
||||
@@ -52,7 +53,7 @@ export const KakunaPage = ({ page, pageIndex }: TemplatePageProps) => {
|
||||
const metrics = getTemplateMetrics(metadata.page);
|
||||
const pageSize = getTemplatePageSize(metadata.page.format);
|
||||
const pageMinHeightStyle = getTemplatePageMinHeightStyle(metadata.page.format);
|
||||
const showHeader = pageIndex === 0;
|
||||
const showHeader = shouldShowResumeHeader(data, pageIndex);
|
||||
const mainSections = filterSections(page.main, data);
|
||||
const sidebarSections = filterSections(page.sidebar, data);
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
WebsiteContactItem,
|
||||
} from "../shared/contact-item";
|
||||
import { TemplateProvider } from "../shared/context";
|
||||
import { shouldShowResumeHeader } from "../shared/cover-letter";
|
||||
import { filterSections } from "../shared/filtering";
|
||||
import { getTemplateMetrics } from "../shared/metrics";
|
||||
import { getTemplatePageMinHeightStyle, getTemplatePageSize } from "../shared/page-size";
|
||||
@@ -51,7 +52,7 @@ export const LaprasPage = ({ page, pageIndex }: TemplatePageProps) => {
|
||||
const metrics = getTemplateMetrics(metadata.page);
|
||||
const pageSize = getTemplatePageSize(metadata.page.format);
|
||||
const pageMinHeightStyle = getTemplatePageMinHeightStyle(metadata.page.format);
|
||||
const showHeader = pageIndex === 0;
|
||||
const showHeader = shouldShowResumeHeader(data, pageIndex);
|
||||
const mainSections = filterSections(page.main, data);
|
||||
const sidebarSections = filterSections(page.sidebar, data);
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
WebsiteContactItem,
|
||||
} from "../shared/contact-item";
|
||||
import { TemplateProvider } from "../shared/context";
|
||||
import { shouldShowResumeHeader } from "../shared/cover-letter";
|
||||
import { filterSections } from "../shared/filtering";
|
||||
import { getTemplateMetrics } from "../shared/metrics";
|
||||
import { getTemplatePageMinHeightStyle, getTemplatePageSize } from "../shared/page-size";
|
||||
@@ -57,7 +58,7 @@ export const LeafishPage = ({ page, pageIndex }: TemplatePageProps) => {
|
||||
const metrics = getTemplateMetrics(metadata.page);
|
||||
const pageSize = getTemplatePageSize(metadata.page.format);
|
||||
const pageMinHeightStyle = getTemplatePageMinHeightStyle(metadata.page.format);
|
||||
const showHeader = pageIndex === 0;
|
||||
const showHeader = shouldShowResumeHeader(data, pageIndex);
|
||||
const mainSections = filterSections(page.main, data).filter((section) => section !== "summary");
|
||||
const sidebarSections = filterSections(page.sidebar, data).filter((section) => section !== "summary");
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
WebsiteContactItem,
|
||||
} from "../shared/contact-item";
|
||||
import { TemplateProvider } from "../shared/context";
|
||||
import { shouldShowResumeHeader } from "../shared/cover-letter";
|
||||
import { filterSections } from "../shared/filtering";
|
||||
import { getTemplateMetrics } from "../shared/metrics";
|
||||
import { getTemplatePageMinHeightStyle, getTemplatePageSize } from "../shared/page-size";
|
||||
@@ -56,7 +57,7 @@ export const MeowthPage = ({ page, pageIndex }: TemplatePageProps) => {
|
||||
const metrics = getTemplateMetrics(metadata.page);
|
||||
const pageSize = getTemplatePageSize(metadata.page.format);
|
||||
const pageMinHeightStyle = getTemplatePageMinHeightStyle(metadata.page.format);
|
||||
const showHeader = pageIndex === 0;
|
||||
const showHeader = shouldShowResumeHeader(data, pageIndex);
|
||||
const mainSections = filterSections(page.main, data);
|
||||
const sidebarSections = filterSections(page.sidebar, data);
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
WebsiteContactItem,
|
||||
} from "../shared/contact-item";
|
||||
import { TemplateProvider } from "../shared/context";
|
||||
import { shouldShowResumeHeader } from "../shared/cover-letter";
|
||||
import { filterSections } from "../shared/filtering";
|
||||
import { getTemplateMetrics } from "../shared/metrics";
|
||||
import { getTemplatePageMinHeightStyle, getTemplatePageSize } from "../shared/page-size";
|
||||
@@ -51,7 +52,7 @@ export const OnyxPage = ({ page, pageIndex }: TemplatePageProps) => {
|
||||
const metrics = getTemplateMetrics(metadata.page);
|
||||
const pageSize = getTemplatePageSize(metadata.page.format);
|
||||
const pageMinHeightStyle = getTemplatePageMinHeightStyle(metadata.page.format);
|
||||
const showHeader = pageIndex === 0;
|
||||
const showHeader = shouldShowResumeHeader(data, pageIndex);
|
||||
const mainSections = filterSections(page.main, data);
|
||||
const sidebarSections = filterSections(page.sidebar, data);
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
WebsiteContactItem,
|
||||
} from "../shared/contact-item";
|
||||
import { TemplateProvider } from "../shared/context";
|
||||
import { shouldShowResumeHeader } from "../shared/cover-letter";
|
||||
import { filterSections } from "../shared/filtering";
|
||||
import { getTemplateMetrics } from "../shared/metrics";
|
||||
import { getTemplatePageMinHeightStyle, getTemplatePageSize } from "../shared/page-size";
|
||||
@@ -61,7 +62,7 @@ export const PikachuPage = ({ page, pageIndex }: TemplatePageProps) => {
|
||||
const metrics = getTemplateMetrics(metadata.page);
|
||||
const pageSize = getTemplatePageSize(metadata.page.format);
|
||||
const pageMinHeightStyle = getTemplatePageMinHeightStyle(metadata.page.format);
|
||||
const showHeader = pageIndex === 0;
|
||||
const showHeader = shouldShowResumeHeader(data, pageIndex);
|
||||
const showSidebar = !page.fullWidth;
|
||||
const hasPicture = hasTemplatePicture(picture);
|
||||
const mainSections = filterSections(page.main, data);
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
WebsiteContactItem,
|
||||
} from "../shared/contact-item";
|
||||
import { TemplateProvider } from "../shared/context";
|
||||
import { shouldShowResumeHeader } from "../shared/cover-letter";
|
||||
import { filterSections } from "../shared/filtering";
|
||||
import { getTemplateMetrics } from "../shared/metrics";
|
||||
import { getTemplatePageMinHeightStyle, getTemplatePageSize } from "../shared/page-size";
|
||||
@@ -54,7 +55,7 @@ export const RhyhornPage = ({ page, pageIndex }: TemplatePageProps) => {
|
||||
const metrics = getTemplateMetrics(metadata.page);
|
||||
const pageSize = getTemplatePageSize(metadata.page.format);
|
||||
const pageMinHeightStyle = getTemplatePageMinHeightStyle(metadata.page.format);
|
||||
const showHeader = pageIndex === 0;
|
||||
const showHeader = shouldShowResumeHeader(data, pageIndex);
|
||||
const mainSections = filterSections(page.main, data);
|
||||
const sidebarSections = filterSections(page.sidebar, data);
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
WebsiteContactItem,
|
||||
} from "../shared/contact-item";
|
||||
import { TemplateProvider } from "../shared/context";
|
||||
import { shouldShowResumeHeader } from "../shared/cover-letter";
|
||||
import { filterSections } from "../shared/filtering";
|
||||
import { getTemplateMetrics } from "../shared/metrics";
|
||||
import { getTemplatePageMinHeightStyle, getTemplatePageSize } from "../shared/page-size";
|
||||
@@ -52,7 +53,7 @@ export const ScizorPage = ({ page, pageIndex }: TemplatePageProps) => {
|
||||
const metrics = getTemplateMetrics(metadata.page);
|
||||
const pageSize = getTemplatePageSize(metadata.page.format);
|
||||
const pageMinHeightStyle = getTemplatePageMinHeightStyle(metadata.page.format);
|
||||
const showHeader = pageIndex === 0;
|
||||
const showHeader = shouldShowResumeHeader(data, pageIndex);
|
||||
const mainSections = filterSections(page.main, data);
|
||||
const sidebarSections = page.fullWidth ? [] : filterSections(page.sidebar, data);
|
||||
const sections = [...mainSections, ...sidebarSections];
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { sampleResumeData } from "@reactive-resume/schema/resume/sample";
|
||||
import { shouldShowResumeHeader } from "./cover-letter";
|
||||
|
||||
const createCoverLetterOnlyData = () => {
|
||||
const data = structuredClone(sampleResumeData);
|
||||
const coverLetter = data.customSections.find((section) => section.type === "cover-letter");
|
||||
|
||||
if (!coverLetter) throw new Error("sample resume must include a cover letter");
|
||||
|
||||
return {
|
||||
...data,
|
||||
customSections: [coverLetter],
|
||||
metadata: {
|
||||
...data.metadata,
|
||||
layout: {
|
||||
...data.metadata.layout,
|
||||
pages: [{ fullWidth: true, main: [coverLetter.id], sidebar: [] }],
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
describe("shouldShowResumeHeader", () => {
|
||||
it("hides the header when every visible layout section is a cover letter", () => {
|
||||
expect(shouldShowResumeHeader(createCoverLetterOnlyData(), 0)).toBe(false);
|
||||
});
|
||||
|
||||
it("keeps the first-page header for normal resume documents", () => {
|
||||
expect(shouldShowResumeHeader(sampleResumeData, 0)).toBe(true);
|
||||
expect(shouldShowResumeHeader(sampleResumeData, 1)).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
import type { ResumeData } from "@reactive-resume/schema/resume/data";
|
||||
import { filterSections } from "./filtering";
|
||||
|
||||
const isCoverLetterSection = (data: ResumeData, sectionId: string) =>
|
||||
data.customSections.some((section) => section.id === sectionId && section.type === "cover-letter");
|
||||
|
||||
const isCoverLetterOnlyDocument = (data: ResumeData) => {
|
||||
const visibleSections = data.metadata.layout.pages.flatMap((page) =>
|
||||
filterSections([...page.main, ...page.sidebar], data),
|
||||
);
|
||||
|
||||
return visibleSections.length > 0 && visibleSections.every((sectionId) => isCoverLetterSection(data, sectionId));
|
||||
};
|
||||
|
||||
export const shouldShowResumeHeader = (data: ResumeData, pageIndex: number) =>
|
||||
pageIndex === 0 && !isCoverLetterOnlyDocument(data);
|
||||
Reference in New Issue
Block a user