import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import type { BannerConfig, ButtonConfig } from '../types/api'; type DisplayMode = 'bottom_banner' | 'top_banner' | 'overlay' | 'corner_popup'; type CornerPosition = 'left' | 'right'; type Viewport = 'desktop' | 'mobile'; /* ── Default text values ─────────────────────────────────────────────── */ const DEFAULT_TITLE = 'We use cookies'; const DEFAULT_DESCRIPTION = 'We use cookies and similar technologies to enhance your browsing experience, ' + 'analyse site traffic, and personalise content. You can choose which categories to allow.'; const DEFAULT_ACCEPT_ALL = 'Accept all'; const DEFAULT_REJECT_ALL = 'Reject all'; const DEFAULT_MANAGE_PREFERENCES = 'Manage preferences'; const DEFAULT_SAVE_PREFERENCES = 'Save preferences'; interface Props { bannerConfig: BannerConfig; displayMode: DisplayMode; cornerPosition?: CornerPosition; viewport: Viewport; privacyPolicyUrl: string | null; siteUrl?: string | null; previewLocale?: string; } export default function BannerPreview({ bannerConfig, displayMode, cornerPosition = 'right', viewport, privacyPolicyUrl, siteUrl, previewLocale, }: Props) { const [iframeLoadFailed, setIframeLoadFailed] = useState(false); const [iframeLoaded, setIframeLoaded] = useState(false); const siteIframeRef = useRef(null); const bannerSrcdoc = useMemo( () => buildBannerOnlyHtml(bannerConfig, displayMode, cornerPosition, privacyPolicyUrl, previewLocale), [bannerConfig, displayMode, cornerPosition, privacyPolicyUrl, previewLocale], ); const fallbackSrcdoc = useMemo( () => buildPreviewHtml(bannerConfig, displayMode, cornerPosition, privacyPolicyUrl, previewLocale), [bannerConfig, displayMode, cornerPosition, privacyPolicyUrl, previewLocale], ); const fullSiteUrl = useMemo(() => { if (!siteUrl) return null; // Ensure the URL has a protocol if (siteUrl.startsWith('http://') || siteUrl.startsWith('https://')) return siteUrl; return `https://${siteUrl}`; }, [siteUrl]); // Reset state when the site URL changes useEffect(() => { setIframeLoadFailed(false); setIframeLoaded(false); }, [fullSiteUrl]); const handleSiteIframeLoad = useCallback(() => { // Check if the iframe actually loaded content by trying to access it // If X-Frame-Options or CSP blocks it, the iframe will be blank const iframe = siteIframeRef.current; if (!iframe) return; try { // Try to detect if the iframe loaded — accessing contentDocument will throw // for cross-origin frames, but that's fine (it means it loaded) // If the iframe is blank/error, some browsers fire load anyway const doc = iframe.contentDocument; if (doc && doc.body && doc.body.innerHTML === '') { // Empty body might mean it was blocked setIframeLoadFailed(true); } else { setIframeLoaded(true); } } catch { // Cross-origin — means the site loaded successfully setIframeLoaded(true); } }, []); const handleSiteIframeError = useCallback(() => { setIframeLoadFailed(true); }, []); const width = viewport === 'mobile' ? 375 : '100%'; const useLiveSite = fullSiteUrl && !iframeLoadFailed; return (
{useLiveSite ? ( <> {/* Live site iframe (background) */}