Skip to content

Commit

Permalink
Add cookies banner. Add GA scripts only on user consent (#640)
Browse files Browse the repository at this point in the history
  • Loading branch information
Bluesmile82 authored Jun 27, 2023
1 parent 6ee03cf commit fa738aa
Show file tree
Hide file tree
Showing 13 changed files with 232 additions and 13 deletions.
11 changes: 2 additions & 9 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,9 @@
<!-- ivipresto Adobe font -->
<link href="https://use.typekit.net/yql8sej.css" rel="stylesheet">

<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=%REACT_APP_GA_MEASUREMENT_ID%"></script>
<!-- Transifex -->
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/@transifex/native/dist/browser.native.min.js"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', "%REACT_APP_GA_MEASUREMENT_ID%");
gtag('set', 'hostname', 'map.half-earthproject.org');
</script>

<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Expand Down
3 changes: 3 additions & 0 deletions src/app.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { tx, PseudoTranslationPolicy } from '@transifex/native';

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

import ThirdParty from 'containers/third-party';

import { useMobile } from 'constants/responsive';

import styles from './app-styles.module.scss';
Expand Down Expand Up @@ -88,6 +90,7 @@ function App(props) {
<QueryClientProvider client={queryClient}>
<div className={styles.app}>
<AppLayout {...props} />
<ThirdParty />
</div>
</QueryClientProvider>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@ import { motion } from 'framer-motion';

import styles from './styles.module.scss';

interface ButtonProps {
type: 'rectangular' | 'rectangular-primary' | 'icon' | 'icon-square';
Icon?: React.FC<React.SVGProps<SVGSVGElement>>;
label?: string;
active?: boolean;
className?: string;
handleClick: () => void;
tooltipText?: string;
onboardingOverlay?: any;
reference?: any;
}

function Component({
type,
Icon,
Expand All @@ -15,7 +27,7 @@ function Component({
tooltipText,
onboardingOverlay,
reference,
}) {
}: ButtonProps) {
return (
<motion.button
{...onboardingOverlay}
Expand All @@ -34,4 +46,14 @@ function Component({
);
}

Component.defaultProps = {
Icon: undefined,
label: undefined,
reference: undefined,
onboardingOverlay: undefined,
tooltipText: undefined,
className: undefined,
active: undefined,
};

export default Component;
3 changes: 0 additions & 3 deletions src/components/button/index.js

This file was deleted.

1 change: 1 addition & 0 deletions src/components/button/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './component';
76 changes: 76 additions & 0 deletions src/components/cookies/component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import React from 'react';

import { useT } from '@transifex/react';

import { AnimatePresence, motion } from 'framer-motion';

import Button from 'components/button';

import styles from './styles.module.scss';

import type { CookiesProps } from './types';

function Cookies({ open, onAccept, onReject }: CookiesProps) {
const t = useT();
return (
<AnimatePresence>
{open && (
<div>
<motion.div
initial={{
opacity: 0,
y: '100%',
}}
animate={{
opacity: 1,
y: '0%',
transition: {
delay: 0.5,
},
}}
exit={{
opacity: 0,
y: '100%',
transition: {
delay: 0,
},
}}
className={styles.cookies}
>
<div className={styles.bannerContainer}>
<p>
This website uses cookies to ensure you get the best experience
on our website. Read our{' '}
<a
href="https://eowilsonfoundation.org/privacy-policy/"
target="_blank"
rel="noreferrer"
className={styles.link}
>
privacy policy
</a>{' '}
to know more.
</p>
<div className={styles.actions}>
<Button
type="rectangular"
handleClick={onReject}
label={t('Deny')}
className={styles.button}
/>
<Button
type="rectangular-primary"
handleClick={onAccept}
label={t('Accept')}
className={styles.button}
/>
</div>
</div>
</motion.div>
</div>
)}
</AnimatePresence>
);
}

export default Cookies;
2 changes: 2 additions & 0 deletions src/components/cookies/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default } from './component';
export type { CookiesProps } from './types';
49 changes: 49 additions & 0 deletions src/components/cookies/styles.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
@import 'styles/settings';
@import 'styles/typography-extends';

.cookies {
position: fixed;
bottom: 0;
left: 0;
z-index: $over-modal;
width: 100%;
transform: translateY(100%);
overflow: hidden;
background-color: rgba($navy-he, 0.8);
padding: 6px;
padding-left: 2rem;
outline: none;

@extend %annotation;
color: $white;
}

.bannerContainer {
display: flex;
flex-direction: column;
}

@media #{$desktop} {
.bannerContainer {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
}
}

.link {
color: $white;
font-weight: $font-weight-bold;
text-decoration: underline;
}

.actions {
display: flex;
justify-content: flex-end;
gap: 1rem;
}

.button {
min-width: 150px;
}
14 changes: 14 additions & 0 deletions src/components/cookies/types.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export interface CookiesProps {
/**
* Whether the modal is opened
*/
open: boolean;
/**
* Callback executed when the cookies are accepted
*/
onAccept: () => void;
/**
* Callback executed when the cookies are rejected
*/
onReject: () => void;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useMemo, useState } from 'react';

import { t as tNative } from '@transifex/native';
import { useT, useLocale } from '@transifex/react';

import { roundGlobalRange } from 'utils/data-formatting-utils';
Expand Down Expand Up @@ -126,6 +127,7 @@ function Component(props) {
tabIndex={0}
className={`${styles.previousSpeciesImageWrapper} ${styles.speciesImageWrapper}`}
onClick={handlePreviousSpeciesSelection}
aria-label={tNative('Go to previous species')}
style={{
backgroundImage: `url(${previousImage})`,
}}
Expand All @@ -151,6 +153,7 @@ function Component(props) {
tabIndex={0}
className={`${styles.nextSpeciesImageWrapper} ${styles.speciesImageWrapper}`}
onClick={handleNextSpeciesSelection}
aria-label={tNative('Go to next species')}
style={{
backgroundImage: `url(${nextImage})`,
}}
Expand Down
57 changes: 57 additions & 0 deletions src/containers/third-party/component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React, { useEffect, useState } from 'react';

import Cookies from 'components/cookies';

const { REACT_APP_GA_MEASUREMENT_ID } = process.env;
const consentCookieName = 'HE_COOKIES_CONSENT';
function ThirdParty() {
const [isOpenCookies, setOpen] = useState(false);
const [scriptAdded, setScriptAdded] = useState(false);
const consentCookie = localStorage.getItem(consentCookieName);

useEffect(() => {
if (consentCookie === 'true' && !scriptAdded) {
const script1 = document.createElement('script');
script1.src = `https://www.googletagmanager.com/gtag/js?id=${REACT_APP_GA_MEASUREMENT_ID}`;
script1.async = true;

const script2 = document.createElement('script');
script2.innerHTML = `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${REACT_APP_GA_MEASUREMENT_ID}');
`;
script2.async = true;

document.body.appendChild(script1);
document.body.appendChild(script2);

setScriptAdded(true);
}
}, [consentCookie, scriptAdded]);

const setConsentCookie = (cookie: string) =>
localStorage.setItem(consentCookieName, cookie);

const handleCookieClick = (c: boolean) => {
setConsentCookie(String(c));
setOpen(false);
};

useEffect(() => {
if (!consentCookie) {
setOpen(true);
}
}, [consentCookie]);

return (
<Cookies
open={isOpenCookies}
onAccept={() => handleCookieClick(true)}
onReject={() => handleCookieClick(false)}
/>
);
}

export default ThirdParty;
1 change: 1 addition & 0 deletions src/containers/third-party/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './component';
1 change: 1 addition & 0 deletions src/store/store-middleware/analytics/analytics-actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export const trackEvent = createThunkAction(
'trackEvent',
({ category, action, label }) =>
() =>
gtag &&
gtag('event', action, {
event_category: category,
event_label: label,
Expand Down

0 comments on commit fa738aa

Please sign in to comment.