Skip to content

Commit

Permalink
feat: add cookie consent (#830)
Browse files Browse the repository at this point in the history
* feat: add cookie consent

* feat: move youtube consent into cookie consent using iframe manager

* chore: move translations into i18n

* feat: move leadinfo into cookie consent

* fix: import statement for css is now correct so prod build should work

* fix: don't expect imagemanager to be defined so the static build can work

* fix: reset im before reinitializing

* fix: contact link works now as expected

* fix: reload cookie banner on language change

* chore: add description of youtube cookies

* chore: style im like satalytes components

* chore: make buttons responsive

* chore: unpin cookie consent version

* fix: open german contact page when language is set to german
feat: open contact page in a new tab

* fix: open external links in new tab
fix: open contact page in the same tab

* chore: refactor use effect

* feat: improve mobile button style

* chore: updated lockfile

* fix: remove warnings that divs are in paragraphs by making everything a div

* fix: use correct css for buttons and text in iframemanager style

* chore: better text for a11y

* chore: better way to fix warning

* chore: update legal text for more clarity

* chore: import useEffect directly

* fix: add 24px margin on the bottom of the yt embed

* chore: simplify fix in custom components

Co-authored-by: Felix Hofmann <[email protected]>

* feat: better mobile styles

* fix: update YouTube embed button texts and improve styling

---------

Co-authored-by: noa.santo <[email protected]>
Co-authored-by: Felix Hofmann <[email protected]>
  • Loading branch information
3 people authored Nov 28, 2024
1 parent e1f984e commit b1e6063
Show file tree
Hide file tree
Showing 15 changed files with 316 additions and 155 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"dependencies": {
"@contentful/rich-text-html-renderer": "^16.0.5",
"@contentful/rich-text-plain-text-renderer": "^16.0.7",
"@orestbida/iframemanager": "^1.3.0",
"@reach/accordion": "^0.18.0",
"@reach/listbox": "^0.18.0",
"@reach/tabs": "^0.18.0",
Expand Down Expand Up @@ -95,6 +96,7 @@
"stylis": "^4.3.1",
"unified": "^10.1.1",
"util": "^0.12.5",
"vanilla-cookieconsent": "^3.0.1",
"webpack": "^5.84.1",
"what-input": "^5.2.12"
},
Expand Down
53 changes: 53 additions & 0 deletions src/assets/locales/de/cookie-consent-translations.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"consentModal": {
"title": "Ihre Datenschutzoptionen",
"description": "Wir verwenden Cookies, um Ihre Erfahrung zu verbessern, den Datenverkehr zu analysieren und personalisierte Inhalte bereitzustellen. Sie können entweder alle Cookies akzeptieren oder Ihre Präferenzen für verschiedene Kategorien verwalten.",
"acceptAllBtn": "Alle Cookies akzeptieren",
"acceptNecessaryBtn": "Nur notwendige Cookies akzeptieren",
"showPreferencesBtn": "Cookie-Präferenzen verwalten"
},
"preferencesModal": {
"title": "Verwalten Sie Ihre Cookie-Präferenzen",
"acceptAllBtn": "Alle Cookies akzeptieren",
"acceptNecessaryBtn": "Nur notwendige Cookies akzeptieren",
"savePreferencesBtn": "Präferenzen speichern",
"closeIconLabel": "Schließen",
"sections": [
{
"title": "Verwendung von Cookies",
"description": "Unsere Website verwendet Cookies, um wesentliche Funktionen sicherzustellen, Ihr Surferlebnis zu verbessern und personalisierte Inhalte bereitzustellen. Einige Cookies sind erforderlich, wie z. B. der, der zur Speicherung Ihrer Zustimmungspräferenzen verwendet wird, während andere uns helfen, unsere Dienste zu verbessern."
},
{
"title": "Leistungs- und Analyse-Cookies",
"description": "Leadinfo Cookies helfen uns zu analysieren, wie Besucher mit unserer Website interagieren, und ermöglichen es uns, die Leistung zu messen und zu verbessern. Sie sammeln Informationen in anonymer Form. YouTube kann Cookies setzen, um Videoaufrufe und Benutzerinteraktionen zu verfolgen.",
"linkedCategory": "analytics",
"cookieTable": {
"headers": {
"name": "Name",
"domain": "Dienst",
"description": "Beschreibung",
"expiration": "Ablauf"
},
"body": [
{
"name": "_li_id",
"domain": "Leadinfo.com",
"description": "Leadinfo platziert zwei Cookies, die Satellytes nur Einblicke in das Verhalten auf der Website geben. Diese Cookies werden nicht mit anderen Parteien geteilt. <a href=\"https://www.leadinfo.com/de/rechtliches/datenschutz/\" target=\"_blank\">Datenschutzerklärung von Leadinfo</a>",
"expiration": "_li_id wird für zwei Jahre gespeichert"
},
{
"name": "_li_ses",
"domain": "Leadinfo.com",
"description": "Leadinfo platziert zwei Cookies, die Satellytes nur Einblicke in das Verhalten auf der Website geben. Diese Cookies werden nicht mit anderen Parteien geteilt. <a href=\"https://www.leadinfo.com/de/rechtliches/datenschutz/\" target=\"_blank\">Datenschutzerklärung von Leadinfo</a>",
"expiration": "_li_ses wird nur für die aktuelle Sitzung verwendet"
}
]
}
},
{
"title": "Weitere Informationen",
"description": "Bei Fragen zu unserer Cookie-Richtlinie oder Ihren Auswahlmöglichkeiten wenden Sie sich bitte an <a class=\"cc-link\" href=\"/de/contact/\">uns</a>."
}
]
}
}
5 changes: 4 additions & 1 deletion src/assets/locales/de/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,5 +87,8 @@
"blog.copied": "Kopiert",
"blog.follow": "Blog abonnieren",
"blog.pagination": "Seite",
"image.attribution": "Foto von "
"image.attribution": "Foto von ",
"iframemanager.youtube.notice": "Dieser Inhalt wird von einer externen Quelle gehostet. Durch das Laden des Inhalts akzeptieren Sie die <a rel=\"noreferrer noopener\" href=\"https://www.youtube.com/t/terms\" target=\"_blank\">Nutzungsbedingungen</a> von youtube.com und stimmen einer möglichen Übertragung Ihrer Daten in Drittländer zu.",
"iframemanager.youtube.load-button": "Inhalt laden",
"iframemanager.youtube.load-all-button": "Inhalt immer laden"
}
53 changes: 53 additions & 0 deletions src/assets/locales/en/cookie-consent-translations.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"consentModal": {
"title": "Your Privacy Choices",
"description": "We use cookies to enhance your experience, analyze traffic, and deliver personalized content. You can choose to accept all cookies or manage your preferences for different categories.",
"acceptAllBtn": "Accept All Cookies",
"acceptNecessaryBtn": "Reject all",
"showPreferencesBtn": "Manage Cookie Preferences"
},
"preferencesModal": {
"title": "Manage Your Cookie Preferences",
"acceptAllBtn": "Accept All Cookies",
"acceptNecessaryBtn": "Reject all",
"savePreferencesBtn": "Save Preferences",
"closeIconLabel": "Close",
"sections": [
{
"title": "Cookie Usage",
"description": "Our website uses cookies to ensure essential functionality, enhance your browsing experience, and provide personalized content. Some cookies are required, such as the one used to store your consent preferences, while others help us improve our services."
},
{
"title": "Performance and Analytics Cookies",
"description": "Leadinfo cookies help us analyze how visitors interact with our website, allowing us to measure and improve performance. They collect information in an anonymous form. YouTube may set cookies to track video views and user interactions.",
"linkedCategory": "analytics",
"cookieTable": {
"headers": {
"name": "Name",
"domain": "Service",
"description": "Description",
"expiration": "Expiration"
},
"body": [
{
"name": "_li_id",
"domain": "Leadinfo.com",
"description": "Leadinfo places two cookies that only provides Satellytes insights into the behaviour on the website. These cookies will not be shared with other parties. <a href=\"https://www.leadinfo.com/en/privacy/\" target=\"_blank\">Privacy statement of Leadinfo</a>",
"expiration": "_li_id will be saved for two years"
},
{
"name": "_li_ses",
"domain": "Leadinfo.com",
"description": "Leadinfo places two cookies that only provides Satellytes insights into the behaviour on the website. These cookies will not be shared with other parties. <a href=\"https://www.leadinfo.com/en/privacy/\" target=\"_blank\">Privacy statement of Leadinfo</a>",
"expiration": "_li_ses is only used for the current session"
}
]
}
},
{
"title": "More information",
"description": "For any queries in relation to our policy on cookies and your choices, please <a class=\"cc-link\" href=\"/contact/\">contact us</a>."
}
]
}
}
5 changes: 4 additions & 1 deletion src/assets/locales/en/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,5 +87,8 @@
"blog.copied": "Copied",
"blog.follow": "Subscribe to blog",
"blog.pagination": "Page",
"image.attribution": "Photo by "
"image.attribution": "Photo by ",
"iframemanager.youtube.notice": "This content is hosted by a third party. By showing the external content you accept the <a rel=\"noreferrer noopener\" href=\"https://www.youtube.com/t/terms\" target=\"_blank\">terms and conditions</a> of youtube.com and agree to a possible transfer of your data to third countries.",
"iframemanager.youtube.load-button": "Load video",
"iframemanager.youtube.load-all-button": "Load & don't ask again"
}
89 changes: 89 additions & 0 deletions src/components/cookie-consent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import React, { useEffect } from 'react';
import '@orestbida/iframemanager/dist/iframemanager.css';
import '@orestbida/iframemanager/dist/iframemanager.js';
import 'vanilla-cookieconsent/dist/cookieconsent.css';
import * as CookieConsentLib from 'vanilla-cookieconsent';
import enCookieConsentTranslations from '../assets/locales/en/cookie-consent-translations.json';
import deCookieConsentTranslations from '../assets/locales/de/cookie-consent-translations.json';
import { useTranslation } from 'react-i18next';
import LeadinfoScript from './layout/leadinfo-script';

export const CookieConsent = () => {
const { t, i18n } = useTranslation();
// @ts-expect-error - iframemanager is defined at runtime
const im = typeof iframemanager !== 'undefined' ? iframemanager() : null;

// Code partially from https://cookieconsent.orestbida.com/advanced/iframemanager-setup.html
const iframeManagerOnChange = ({ changedServices, eventSource }) => {
if (eventSource.type === 'click') {
const servicesToAccept = [
...CookieConsentLib.getUserPreferences().acceptedServices['analytics'],
...changedServices,
];

CookieConsentLib.acceptService(servicesToAccept, 'analytics');
}
};

const setupIframeManager = () => {
im.reset();
im.run({
currLang: i18n.language,
onChange: iframeManagerOnChange,
services: {
youtube: {
embedUrl: 'https://www.youtube-nocookie.com/embed/{data-id}',
thumbnailUrl: 'https://i3.ytimg.com/vi/{data-id}/hqdefault.jpg',
iframe: {
allow:
'accelerometer; encrypted-media; gyroscope; picture-in-picture; fullscreen;',
},
languages: {
en: {
notice: t('iframemanager.youtube.notice'),
loadBtn: t('iframemanager.youtube.load-button'),
loadAllBtn: t('iframemanager.youtube.load-all-button'),
},
de: {
notice: t('iframemanager.youtube.notice'),
loadBtn: t('iframemanager.youtube.load-button'),
loadAllBtn: t('iframemanager.youtube.load-all-button'),
},
},
},
},
});
};

const setupCookieConsent = () => {
CookieConsentLib.reset();
CookieConsentLib.run({
categories: {
analytics: {
services: {
youtube: {
label: 'YouTube Embeds',
onAccept: () => im.acceptService('youtube'),
onReject: () => im.rejectService('youtube'),
},
},
},
},
language: {
default: i18n.language,
translations: {
en: enCookieConsentTranslations,
de: deCookieConsentTranslations,
},
},
}).then();
};

useEffect(() => {
if (!im) return;
setupIframeManager();
setupCookieConsent();
}, [i18n.language]);

return <LeadinfoScript />;
};
4 changes: 3 additions & 1 deletion src/components/layout/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ import { up } from '../support/breakpoint';
import { Breadcrumb, BreadcrumbEntry } from './breadcrumb/breadcrumb';
import { setPolarityBodyClass } from './set-polarity';
import { useAnchorTagScrolling } from './use-anchor-tag-scrolling';
import { CookieConsent } from '../cookie-consent';

/**
* this container is used to push the footer to the bottom
* if the page content is to short
* if the page content is too short
*/
const FullHeightContainer = styled.div`
display: flex;
Expand Down Expand Up @@ -123,6 +124,7 @@ export const Layout = ({

return (
<ThemeProvider theme={theme}>
<CookieConsent />
<GlobalStyle $lightTheme={isLight} />
<HeaderStickyContainer>
<Header
Expand Down
11 changes: 10 additions & 1 deletion src/components/layout/leadinfo-script.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import { useEffect } from 'react';
const LeadinfoScript = () => {
useEffect(() => {
const script = document.createElement('script');
script.setAttribute('type', 'text/plain');
script.setAttribute('data-category', 'analytics');
script.setAttribute('data-service', 'Leadinfo');

script.innerHTML = `
(function(l,e,a,d,i,n,f,o){if(!l[i]){l.GlobalLeadinfoNamespace=l.GlobalLeadinfoNamespace||[];
l.GlobalLeadinfoNamespace.push(i);l[i]=function(){(l[i].q=l[i].q||[]).push(arguments)};l[i].t=l[i].t||n;
Expand All @@ -12,7 +16,12 @@ const LeadinfoScript = () => {
document.head.appendChild(script);

return () => {
document.head.removeChild(script);
const existingScript = document.querySelector(
'script[data-service="Leadinfo"]',
);
if (existingScript) {
document.head.removeChild(existingScript);
}
};
}, []);

Expand Down
6 changes: 6 additions & 0 deletions src/components/legacy/markdown/custom-components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,12 @@ const customSatellytesComponents = {
);
},
p(props) {
const hasVideo = React.Children.toArray(props.children).some(
(child) => React.isValidElement(child) && child.props?.videoId,
);
if (hasVideo) {
return <>{props.children}</>;
}
return <Text>{props.children}</Text>;
},
em(props) {
Expand Down
Loading

0 comments on commit b1e6063

Please sign in to comment.