/** * Copyright (c) HashiCorp, Inc. * SPDX-License-Identifier: MPL-2.0 */ import { useState, useEffect, useCallback } from 'react' import { EventEmitter } from 'events' import classNames from 'classnames' import { isInUS } from '@hashicorp/platform-util/geo' import { loadPreferences, savePreferences } from './util/cookies' import ConsentBanner from './components/banner' import ConsentPreferences from './components/dialog' import SegmentScript from './scripts/segment' import CustomScripts from './scripts/custom' import s from './style.module.css' import { ConsentManagerCategory, ConsentManagerService, ConsentManagerPreferences, } from './types' interface ConsentManagerProps { additionalServices?: ConsentManagerService[] categories?: ConsentManagerCategory[] className?: string cookiePolicyLink?: string forceShow?: boolean privacyPolicyLink?: string segmentServices?: ConsentManagerService[] segmentWriteKey?: string showDialog?: boolean version?: number onManagePreferences?: () => void onAcceptAll?: () => void } const emitter = new EventEmitter() export function open() { emitter.emit('openDialog') } export function saveAndLoadAnalytics(preferences: ConsentManagerPreferences) { emitter.emit('saveAndLoadAnalytics', preferences) } export default function ConsentManager(props: ConsentManagerProps) { const [preferences, setPreferences] = useState(loadPreferences() ?? {}) const [showDialog, setShowDialog] = useState(props.showDialog ?? false) const [showBanner, setShowBanner] = useState(() => { // 1. If prop override is set, always show the consent bar. if (props.forceShow) return true }) const closeDialog = () => { document.body.classList.remove('g-noscroll') setShowDialog(false) setShowBanner(false) setPreferences(loadPreferences() ?? {}) } const hasEmptyPreferencesOrVersionMismatch = Object.keys(preferences).length === 0 || preferences.version !== props.version const saveAndLoadAnalytics = useCallback( (preferences) => { if (typeof preferences === 'undefined') { preferences = { loadAll: false, segment: false } } savePreferences(preferences, props.version) // If analytics have already been added to page, it's likely you're updating your preferences // We reload the page to re-initiate the script with the updated integrations if (window.analytics && window.analytics.initialized) { window.location.reload() return } closeDialog() }, [props.version] ) const openDialog = useCallback(() => { document.body.classList.add('g-noscroll') setShowBanner(false) setShowDialog(true) }, []) // Setup the event handler to open the dialog imperatively useEffect(() => { emitter.on('openDialog', openDialog) return () => { emitter.off('openDialog', openDialog) } }, [openDialog]) // Setup the event handler to save and load analytics imperatively useEffect(() => { emitter.on('saveAndLoadAnalytics', saveAndLoadAnalytics) return () => { emitter.off('saveAndLoadAnalytics', saveAndLoadAnalytics) } }, [saveAndLoadAnalytics]) // Show banner if there are no preferences or the version mismatches useEffect(() => { setShowBanner(hasEmptyPreferencesOrVersionMismatch) // eslint-disable-next-line react-hooks/exhaustive-deps -- We only want to run this check once }, []) useEffect(() => { if (hasEmptyPreferencesOrVersionMismatch && isInUS()) { saveAndLoadAnalytics({ loadAll: true }) setShowBanner(true) } // eslint-disable-next-line react-hooks/exhaustive-deps -- We only want to run this check once }, []) const escFunction = useCallback((event) => { if (showDialog && event.key === "Escape") { closeDialog() } }, [showDialog]) useEffect(() => { document.addEventListener("keydown", escFunction, false) return () => { document.removeEventListener("keydown", escFunction, false) } }, [escFunction]) const filteredAdditionalServices = props.additionalServices?.filter( (service) => service.shouldLoad === undefined || service.shouldLoad() ) return (
{/* Consent banner at the bottom */} {showBanner && ( { openDialog() if (props.onManagePreferences) { props.onManagePreferences() } }} onAccept={() => { saveAndLoadAnalytics({ loadAll: true }) if (props.onAcceptAll) { props.onAcceptAll() } }} /> )} {/* Consent manager preferences dialog */} {showDialog && ( )}
) }