Skip to content

COVE ‐ Wrapper Middleware

David Cummo edited this page Aug 28, 2024 · 1 revision
import React, { useEffect, useState, useCallback, Suspense } from 'react'
import 'whatwg-fetch'


import './styles.scss'

import cdcLogo from './cdc-hhs.svg'

const CdcMap = React.lazy(() => import('@cdc/map'));
const CdcChart = React.lazy(() => import('@cdc/chart'));
const CdcEditor = React.lazy(() => import('@cdc/editor'));
const CdcDashboard = React.lazy(() => import('@cdc/dashboard'));
const CdcDataBite = React.lazy(() => import('@cdc/data-bite'));
const CdcWaffleChart = React.lazy(() => import('@cdc/waffle-chart'));
const CdcMarkupInclude = React.lazy(() => import('@cdc/markup-include'));

const Loading = ({viewport = "lg"}) => {
  return (
		<section className="loading">
			<div className={`la-ball-beat la-dark ${viewport}`}>
				<div />
				<div />
				<div />
			</div>
		</section>
	)
}

const Wrapper = ({configURL, language, config: configObj, isEditor, hostname, sharePath}) => {
    const [ config, setConfig ] = useState(configObj)

    const [ type, setType ] = useState(null)

    const metricsCall = useCallback((type, url) => {
        const s = window.s || {}

        if(true === s.hasOwnProperty('tl')) {
            let newObj = {...s}


            newObj.pageURL = window.location.href
            newObj.linkTrackVars = "pageURL";
            newObj.linkURL = url // URL We are navigating to

            s.tl( true, type, null, newObj )
        }
    })

    const iframeCheck = () => {
        try {
            return window.self !== window.top;
        } catch (e) {
            return true;
        }
    }

    const navigationHandler = useCallback((urlString = '') => {
        // Abort if value is blank
        if(0 === urlString.length) {
            throw Error("Blank string passed as URL. Navigation aborted.");
        }

        // Make sure this isn't loading through an iFrame.
        const inIframe = iframeCheck();

        // Determine if link is a relative hash link
        const isHashLink = urlString.startsWith('#');

        // Smooth scrolling for hash links on the same page as the map
        if(true === isHashLink && false === inIframe) {
            let hashName = urlString.substr(1);
            let scrollSection = window.document.querySelector(`*[id="${hashName}"]`) || window.document.querySelector(`*[name="${hashName}"]`)

            if(scrollSection) {
                scrollSection.scrollIntoView({
                    behavior: 'smooth'
                })

                return true;
            } else {
                throw Error("Internal hash link detected but unable to find element on page. Navigation aborted.");
            }
        }

        // Metrics Call
        const extension = urlString.substring( urlString.lastIndexOf( '.' ) + 1 )

        const s = window.s || {}

        let metricsParam = 'e';

        if ( s.hasOwnProperty('linkDownloadFileTypes') && s.linkDownloadFileTypes.includes(extension) ) {
            metricsParam = 'd'; // Different parameter for downloads
        }

        let urlObj;

        // If we're not loading through iframe (ex: widget loader)
        if(false === inIframe) {
            // Insert proper base for relative URLs
            const parentUrlObj = new URL(window.location.href);

            // Only insert a dynamic base if this is on a CDC.gov page, regardless of environment.
            // This prevents security concerns where a party could embed a CDC visualization on their own site and have the relative URLs go to their own content making it look like its endorsed by the CDC.
            let urlBase = parentUrlObj.host.endsWith('cdc.gov') ? parentUrlObj.origin : 'https://www.cdc.gov/';

            urlObj = new URL(urlString, urlBase);
        } else {
            urlObj = new URL(urlString);
        }

        // Set the string to the newly constructed string.
        urlString = urlObj.toString();

        // Don't make a metrics call if it's a link to cdc.gov and does not have a download extension (ex. pdf) or if we're inside the editor.
        if( false === ( 'e' === metricsParam && urlString.includes('cdc.gov') ) && false === isEditor ) {
            metricsCall(metricsParam, urlString);
        }

        // Open constructed link in new tab/window
        window.open(urlString, '_blank');
    })

    useEffect(() => {
        if(null === configURL) {
            console.warn('No configuration URL detected.');
            return;
        }

        const grabConfigObj = async () => {

            try {
                const response = await fetch(configURL);

                const data = await response.json();

                let tempConfigObj = {language, ...data}

                setConfig(tempConfigObj);
                setLoading(false);

            } catch (err) {
                new Error(err)
            }
        };
        grabConfigObj();
    }, [configURL]);

    useEffect(() => {
        if(config && config.hasOwnProperty('type')) {
            setType(config.type)
        }
    }, [config])

    // WCMS Admin
    if(isEditor && config) {
        // This either passes an existing config or starts with a blank editor
        return (
            <Suspense fallback={<Loading />}>
                <CdcEditor config={config} hostname={hostname} sharepath={sharePath} />
            </Suspense>)
    }

    // Standalone mode when you run `npm run start` just so it isn't blank
    if(!config && !configURL) {
        return (<Suspense fallback={<Loading />}>
			<CdcEditor hostname={hostname} sharepath={sharePath} />
               </Suspense>)
    }

    switch (type) {
        case 'map':
            return (
                <Suspense fallback={<Loading />}>
                    <CdcMap config={config} hostname={hostname} navigationHandler={navigationHandler} logo={cdcLogo} />
                </Suspense>
            )
        case 'chart':
            return (
                <Suspense fallback={<Loading />}>
                    <CdcChart config={config} hostname={hostname} />
                </Suspense>
            )
        case 'dashboard':
            return (
                <Suspense fallback={<Loading />}>
                    <CdcDashboard config={config} hostname={hostname} />
                </Suspense>
            )
        case 'data-bite':
            return (
                <Suspense fallback={<Loading />}>
                    <CdcDataBite config={config} hostname={hostname} />
                </Suspense>
            )
		case 'waffle-chart':
			return (
				<Suspense fallback={<Loading />}>
					<CdcWaffleChart config={config} hostname={hostname} />
				</Suspense>
			)
		case 'markup-include':
			return (
				<Suspense fallback={<Loading />}>
					<CdcMarkupInclude config={config} hostname={hostname} />
				</Suspense>
			)
        default:
            return <Loading />
    }
}

export default Wrapper
Clone this wiki locally