Added dynamic site preview iframe

refs https://github.com/TryGhost/Team/issues/3354

- adds dynamically loading iframe with site preview based on preview data in Admin
This commit is contained in:
Rishabh 2023-06-09 17:39:46 +05:30
parent a4b24b1c7a
commit c26f77586a
2 changed files with 110 additions and 5 deletions

View File

@ -108,7 +108,17 @@ const DesignModal: React.FC = () => {
buttonsDisabled={saveState === 'saving'}
cancelLabel='Close'
okLabel='Save'
preview={<ThemePreview />}
preview={
<ThemePreview
settings={{
description,
accentColor,
icon,
logo,
coverImage
}}
/>
}
previewToolbarURLs={urlOptions}
sidebar={<Sidebar
brandSettings={{description, accentColor, icon, logo, coverImage}}

View File

@ -1,11 +1,106 @@
import React from 'react';
import React, {useEffect, useRef} from 'react';
import useSettingGroup from '../../../../hooks/useSettingGroup';
const ThemePreview: React.FC = () => {
const tempURL = window.location.origin;
type BrandSettings = {
description: string,
accentColor: string,
icon: string,
logo: string,
coverImage: string
}
interface ThemePreviewProps {
settings: BrandSettings
}
function getApiUrl({siteUrl, path = ''}: {
siteUrl: string;
path?: string;
}): string {
const url = new URL(siteUrl);
const subdir = url.pathname.endsWith('/') ? url.pathname : `${url.pathname}/`;
const fullPath = `${subdir}${path.replace(/^\//, '')}`;
return `${url.origin}${fullPath}`;
}
function getPreviewData({
description,
accentColor,
icon,
logo,
coverImage
}: BrandSettings): string {
const params = new URLSearchParams();
params.append('c', accentColor);
params.append('d', description);
params.append('icon', icon);
params.append('logo', logo);
params.append('cover', coverImage);
return params.toString();
}
const ThemePreview: React.FC<ThemePreviewProps> = ({settings}) => {
const iframeRef = useRef<HTMLIFrameElement>(null);
const {siteData} = useSettingGroup();
const apiEndpoint = getApiUrl({
siteUrl: siteData?.url || '',
path: ''
});
useEffect(() => {
if (!apiEndpoint) {
return;
}
// Fetch theme preview HTML
fetch(apiEndpoint, {
method: 'POST',
headers: {
'Content-Type': 'text/html;charset=utf-8',
'x-ghost-preview': getPreviewData({
...settings
}),
Accept: 'text/plain',
mode: 'cors',
credentials: 'include'
}
})
.then(response => response.text())
.then((data) => {
// inject extra CSS to disable navigation and prevent clicks
const injectedCss = `html { pointer-events: none; }`;
const domParser = new DOMParser();
const htmlDoc = domParser.parseFromString(data, 'text/html');
const stylesheet = htmlDoc.querySelector('style') as HTMLStyleElement;
const originalCSS = stylesheet.innerHTML;
stylesheet.innerHTML = `${originalCSS}\n\n${injectedCss}`;
// replace the iframe contents with the doctored preview html
const doctype = htmlDoc.doctype ? new XMLSerializer().serializeToString(htmlDoc.doctype) : '';
let finalDoc = doctype + htmlDoc.documentElement.outerHTML;
// Send the data to the iframe's window using postMessage
// Inject the received content into the iframe
const iframe = iframeRef.current;
if (iframe) {
iframe.contentDocument?.open();
iframe.contentDocument?.write(finalDoc);
iframe.contentDocument?.close();
}
})
.catch(() => {
// handle error in fetching data
});
}, [apiEndpoint, settings]);
return (
<>
<iframe height="100%" src={tempURL} title="Temporary Preivew" width="100%" />
<iframe
ref={iframeRef} height="100%" title="Site Preview"
width="100%"
></iframe>
</>
);
};