fix(cv): keep template thumbnails distinct
This commit is contained in:
@@ -1,12 +1,9 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import React from "react";
|
||||
import { Box } from "@mui/material";
|
||||
|
||||
import { CvTheme, cvBuilderApi } from "../cvBuilder";
|
||||
|
||||
const previewCache = new Map<string, string>();
|
||||
import { CvTheme } from "../cvBuilder";
|
||||
|
||||
export default function CvTemplateThumbnail({ theme, height = 150 }: { theme?: CvTheme; height?: number }) {
|
||||
const [previewHtml, setPreviewHtml] = useState(() => theme ? previewCache.get(theme.id) ?? "" : "");
|
||||
const accent = theme?.swatches?.[0] || "#3157d5";
|
||||
const sidebar = theme?.swatches?.[1] || "#eef1f4";
|
||||
const paper = theme?.swatches?.[2] || "#ffffff";
|
||||
@@ -15,34 +12,6 @@ export default function CvTemplateThumbnail({ theme, height = 150 }: { theme?: C
|
||||
const sidebarRight = layout === "sidebar-right";
|
||||
const lines = [92, 72, 84, 61, 88, 76, 95, 67];
|
||||
|
||||
useEffect(() => {
|
||||
if (!theme) return;
|
||||
const cached = previewCache.get(theme.id);
|
||||
if (cached) {
|
||||
setPreviewHtml(cached);
|
||||
return;
|
||||
}
|
||||
setPreviewHtml("");
|
||||
let active = true;
|
||||
void cvBuilderApi.themePreview(theme.id).then((render) => {
|
||||
previewCache.set(theme.id, render.html);
|
||||
if (active) setPreviewHtml(render.html);
|
||||
}).catch(() => undefined);
|
||||
return () => { active = false; };
|
||||
}, [theme]);
|
||||
|
||||
if (previewHtml) {
|
||||
const scale = height / (297 * 96 / 25.4);
|
||||
return (
|
||||
<Box aria-hidden sx={{ height, bgcolor: "#dfe4ea", border: "1px solid", borderColor: "divider", borderRadius: 1.5, overflow: "hidden", display: "flex", justifyContent: "center" }}>
|
||||
<Box sx={{ width: height * 210 / 297, height, bgcolor: "#fff", boxShadow: "0 5px 14px rgba(15,23,42,.15)", overflow: "hidden" }}>
|
||||
<iframe title={`${theme?.name ?? "CV"} template preview`} srcDoc={previewHtml} sandbox="allow-same-origin" tabIndex={-1}
|
||||
style={{ width: "210mm", height: "297mm", border: 0, display: "block", transform: `scale(${scale})`, transformOrigin: "top left", pointerEvents: "none" }} />
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
const body = <Box sx={{ p: "7%", minWidth: 0 }}>
|
||||
<Box sx={{ width: "48%", height: 5, borderRadius: 2, bgcolor: accent, mb: 1 }} />
|
||||
{lines.map((width, index) => <Box key={index} sx={{ width: `${width}%`, height: index === 4 ? 4 : 2.5, borderRadius: 2, bgcolor: index === 4 ? accent : "rgba(51,65,85,.24)", mt: index === 4 ? 1.25 : 0, mb: 0.7 }} />)}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import React from "react";
|
||||
import { render } from "@testing-library/react";
|
||||
|
||||
import CvTemplateThumbnail from "./components/CvTemplateThumbnail";
|
||||
import { CvTheme } from "./cvBuilder";
|
||||
|
||||
const theme = (id: string, layout: CvTheme["layout"], swatches: string[]): CvTheme => ({
|
||||
id,
|
||||
name: id,
|
||||
category: "Test",
|
||||
description: "",
|
||||
layout,
|
||||
accent: swatches[0],
|
||||
photoShape: "none",
|
||||
supportsIcons: false,
|
||||
atsFriendly: true,
|
||||
requiresPro: false,
|
||||
available: true,
|
||||
swatches,
|
||||
});
|
||||
|
||||
test("template thumbnails stay distinct and never replace themselves with sandboxed frames", () => {
|
||||
const minimal = render(<CvTemplateThumbnail theme={theme("minimal", "single", ["#111827", "#ffffff", "#ffffff"])} />);
|
||||
const minimalMarkup = minimal.container.innerHTML;
|
||||
minimal.unmount();
|
||||
|
||||
const nordic = render(<CvTemplateThumbnail theme={theme("nordic", "sidebar-right", ["#3b6ea5", "#eef3f8", "#ffffff"])} />);
|
||||
|
||||
expect(nordic.container.querySelector("iframe")).toBeNull();
|
||||
expect(nordic.container.innerHTML).not.toBe(minimalMarkup);
|
||||
});
|
||||
@@ -448,7 +448,7 @@ export default function CvBuilderEditor() {
|
||||
</Paper>
|
||||
|
||||
<Box sx={{ display: "grid", gridTemplateColumns: { xs: "minmax(0, 1fr)", md: "minmax(360px, 480px) minmax(0, 1fr)" }, gap: 1.5, alignItems: "start" }}>
|
||||
<Paper sx={{ display: compactEditor && mobilePane !== "edit" ? "none" : "block", p: 2, borderRadius: 3, position: { md: "sticky" }, top: 12, minWidth: 0, maxHeight: { md: "calc(100vh - 104px)" }, overflowY: { md: "auto" }, border: "1px solid", borderColor: "divider" }}>
|
||||
<Paper sx={{ display: compactEditor && mobilePane !== "edit" ? "none" : "block", p: 2, borderRadius: 3, position: { md: "sticky" }, top: 12, minWidth: 0, maxHeight: { md: "calc(100vh - 104px)" }, overflowY: { md: "auto" }, scrollbarWidth: "none", "&::-webkit-scrollbar": { display: "none" }, border: "1px solid", borderColor: "divider" }}>
|
||||
|
||||
<Tabs value={tab} onChange={(_, v) => { setTab(v); if (v === 4) loadVersions(); }} variant="scrollable" scrollButtons="auto" allowScrollButtonsMobile sx={{ mb: 1.5, minHeight: 38 }}>
|
||||
<Tab label={t("cvEditorTemplate")} />
|
||||
@@ -477,7 +477,7 @@ export default function CvBuilderEditor() {
|
||||
{previewOverflow && <Alert severity="warning" sx={{ mb: 1 }}>{t("cvEditorOverflowWarning")}</Alert>}
|
||||
{pages >= 3 && <Alert severity="info" sx={{ mb: 1 }}>{t("cvEditorLongDocument", { pages })}</Alert>}
|
||||
<iframe ref={iframeRef} title={t("cvEditorPreviewTitle")} srcDoc={previewDocumentHtml(html, pageMetrics.heightPx, pageMarginMm)} sandbox="allow-same-origin allow-scripts" onLoad={onIframeLoad} aria-hidden tabIndex={-1} style={{ position: "absolute", left: "-10000px", top: 0, width: `${pageMetrics.widthMm}mm`, height: `${pageMetrics.heightMm}mm`, visibility: "hidden", pointerEvents: "none" }} />
|
||||
<Box ref={scrollRef} sx={{ overflow: "auto", maxHeight: "86vh", pr: 0.5 }}>
|
||||
<Box ref={scrollRef} sx={{ overflow: "auto", maxHeight: "86vh", pr: 0.5, scrollbarWidth: "none", "&::-webkit-scrollbar": { display: "none" } }}>
|
||||
<Stack spacing={2} alignItems="center">
|
||||
{Array.from({ length: pages }).map((_, index) => (
|
||||
<Box key={index} data-cv-page={index + 1} sx={{ flex: "0 0 auto" }}>
|
||||
|
||||
Reference in New Issue
Block a user