Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add cookie consent v3 #825

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
"stylis": "^4.3.1",
"unified": "^10.1.1",
"util": "^0.12.5",
"vanilla-cookieconsent": "3.0.1",
ipeke94 marked this conversation as resolved.
Show resolved Hide resolved
"webpack": "^5.84.1",
"what-input": "^5.2.12"
},
Expand Down
54 changes: 54 additions & 0 deletions src/@types/cookieconsent.d.ts
ipeke94 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
declare module 'vanilla-cookieconsent' {
interface CookieValue {
categories: {
necessary: boolean;
analytics: boolean;
};
}

interface CookieConsentCallbackParam {
cookie: CookieValue;
}

interface CookieConsentConfig {
categories: {
necessary: {
enabled: boolean;
readOnly: boolean;
};
analytics: {
enabled: boolean;
};
};
language: {
default: string;
translations: {
[key: string]: {
consentModal: {
title: string;
description: string;
acceptAllBtn: string;
acceptNecessaryBtn: string;
showPreferencesBtn: string;
};
preferencesModal: {
title: string;
acceptAllBtn: string;
acceptNecessaryBtn: string;
savePreferencesBtn: string;
closeIconLabel: string;
sections: Array<{
title: string;
description: string;
linkedCategory?: string;
}>;
};
};
};
};
onFirstConsent?: (param: CookieConsentCallbackParam) => void;
onConsentChange?: (param: CookieConsentCallbackParam) => void;
}

export function run(config: CookieConsentConfig): void;
}
92 changes: 92 additions & 0 deletions src/components/CookieConsent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import React, { useEffect, useState } from 'react';
import 'vanilla-cookieconsent/dist/cookieconsent.css';
import * as CookieConsent from 'vanilla-cookieconsent';
import LeadinfoScript from '../components/layout/leadinfo-script';

const CookieConsentComponentV3 = () => {
const [analyticsEnabled, setAnalyticsEnabled] = useState(false);

useEffect(() => {
CookieConsent.run({
categories: {
necessary: {
enabled: true,
readOnly: true,
},
ipeke94 marked this conversation as resolved.
Show resolved Hide resolved
analytics: {
enabled: false,
},
},
language: {
default: 'en',
translations: {
en: {
consentModal: {
title: 'We use cookies',
description:
'This website uses cookies to ensure the best user experience.',
acceptAllBtn: 'Accept all',
acceptNecessaryBtn: 'Reject all',
showPreferencesBtn: 'Manage preferences',
},
preferencesModal: {
title: 'Manage Cookie Preferences',
acceptAllBtn: 'Accept all',
acceptNecessaryBtn: 'Accept necessary only',
savePreferencesBtn: 'Save preferences',
closeIconLabel: 'Close',
sections: [
{
title: 'Strictly Necessary Cookies',
description:
'These cookies are essential for website functionality and cannot be disabled.',
linkedCategory: 'necessary',
},
{
title: 'Analytics Cookies',
description:
'We use analytics cookies to analyze website usage and improve our services.',
linkedCategory: 'analytics',
},
],
},
ipeke94 marked this conversation as resolved.
Show resolved Hide resolved
},
},
},
onFirstConsent: (param) => {
handleConsent(param.cookie.categories);
},
onConsentChange: (param) => {
handleConsent(param.cookie.categories);
},
});
}, []);

const handleConsent = (categories) => {
const isAnalyticsAccepted = categories.includes('analytics');

if (isAnalyticsAccepted) {
setAnalyticsEnabled(true);
setLeadinfoCookies();
} else {
setAnalyticsEnabled(false);
clearLeadinfoCookies();
}
};

const setLeadinfoCookies = () => {
const twoYears = 63072000;
const sessionValue = new Date().toISOString();
document.cookie = `_li_id=some_value; max-age=${twoYears}; path=/`;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The article explains the nature of the cookies involved. I don't think that we're supposed to set those cookies.
Instead you have to pull in the matching script through the scripts management of the cookie consent js I think ?

https://cookieconsent.orestbida.com/advanced/manage-scripts.html

I'm not sure how to distinguish the two different modes (with cookies and without). Maybe there are two different scripts possible or so?

--> If that's not a given use case to allow both scenarios (cookie and without cookies) then let's enable cookies in the backend and integrate the tracker only when the consent was given. Maybe we find a solution afterwards.

document.cookie = `_li_ses=${sessionValue}; max-age=0; path=/`;
};

const clearLeadinfoCookies = () => {
document.cookie = '_li_id=; max-age=0; path=/';
document.cookie = '_li_ses=; max-age=0; path=/';
};

return <LeadinfoScript enable={analyticsEnabled} />;
};

export default CookieConsentComponentV3;
34 changes: 22 additions & 12 deletions src/components/layout/leadinfo-script.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,30 @@
import { useEffect } from 'react';
import { useEffect, useState } from 'react';

let leadinfoInitialized = false;

const LeadinfoScript = ({ enable }: { enable: boolean }) => {
const [script, setScript] = useState<HTMLScriptElement | null>(null);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as said I think it's easier to use the script manager instead of manually bootstrapping it

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello,

Thank you for the feedback and your time🙏. I tried to block/manage script tags according to the document you sent. You can see some FAQs in my comment below. Accordingly, when we do this, different cookies are set when analytics data is accepted by Leadinfo. The cookies are listed below possibly related to li_id and li_ses cookies in terms of tracking user sessions and identifying visitors. (like Google Analytics(_ga cookies) or HubSpot (__hstc, hubspotutk) or _hssrc session cookie)

Screenshot 2024-10-16 at 10 43 00 PM

Do you think we need to add detailed descriptions of these cookies under analytics or the relevant section?


const LeadinfoScript = () => {
useEffect(() => {
const script = document.createElement('script');
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;
l[i].q=l[i].q||[];o=e.createElement(a);f=e.getElementsByTagName(a)[0];o.async=1;o.src=d;f.parentNode.insertBefore(o,f);}
}(window,document,'script','https://cdn.leadinfo.eu/ping.js','leadinfo','LI-66D184CC97CFD'));
if (enable && !leadinfoInitialized) {
const leadinfoScript = document.createElement('script');
leadinfoScript.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;
l[i].q=l[i].q||[];o=e.createElement(a);f=e.getElementsByTagName(a)[0];o.async=1;o.src=d;f.parentNode.insertBefore(o,f);}
}(window,document,'script','https://cdn.leadinfo.eu/ping.js','leadinfo','LI-66D184CC97CFD'));
`;
document.head.appendChild(script);
document.head.appendChild(leadinfoScript);
setScript(leadinfoScript);
leadinfoInitialized = true;
}

return () => {
if (!enable && leadinfoInitialized && script) {
document.head.removeChild(script);
};
}, []);
setScript(null);
leadinfoInitialized = false;
}
}, [enable, script]);

return null;
};
Expand Down
4 changes: 2 additions & 2 deletions src/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
} from '../types';
import { IGatsbyImageData } from 'gatsby-plugin-image';
import { StructuredOrganizationData } from '../components/pages/landingpage/structured-organization-data';
import LeadinfoScript from '../components/layout/leadinfo-script'; // Import the new component
import CookieConsentComponentV3 from '../components/CookieConsent';

export interface OfficeImage {
relativePath: string;
Expand Down Expand Up @@ -72,8 +72,8 @@ export const Head = ({ data, location }: PageProps<IndexPageQueryProps>) => {
rssLink
locales={data.locales}
/>
<CookieConsentComponentV3 />
<StructuredOrganizationData />
<LeadinfoScript />
</>
);
};
Expand Down
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -15148,6 +15148,11 @@ value-or-promise@^1.0.12:
resolved "https://registry.yarnpkg.com/value-or-promise/-/value-or-promise-1.0.12.tgz#0e5abfeec70148c78460a849f6b003ea7986f15c"
integrity sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q==

[email protected]:
version "3.0.1"
resolved "https://registry.yarnpkg.com/vanilla-cookieconsent/-/vanilla-cookieconsent-3.0.1.tgz#059d1b2c712476ae4172d4ec7fa9d553a16be12d"
integrity sha512-gqc4x7O9t1I4xWr7x6/jtQWPr4PZK26SmeA0iyTv1WyoECfAGnu5JEOExmMEP+5Fz66AT9OiCBO3GII4wDQHLw==

vary@^1, vary@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
Expand Down