From c78406fc80b22ba887f44745f0bf48c64d41834a Mon Sep 17 00:00:00 2001
From: Chai Landau <112015853+chailandau@users.noreply.github.com>
Date: Thu, 30 Nov 2023 13:57:06 -0500
Subject: [PATCH] feat: Cookie banner / consent (#100)
* feat: add CookieBanner component
* chore: add js-cookie package
* feat: add darkBlue shadow mixin
* feat: add cookie consent banner
* refactor: put GA in separate component, only use if cookies accepted
* refactor: setNoScroll affects html el instead of body
* feat: add focus trap
---
app/layout.tsx | 23 +---
package.json | 2 +
.../CookieBanner/CookieBanner.module.scss | 67 ++++++++++
src/components/CookieBanner/CookieBanner.tsx | 124 ++++++++++++++++++
src/components/CookieBanner/GoogleScript.tsx | 43 ++++++
src/components/CookieBanner/index.ts | 3 +
src/store/createCookieBannerSlice.ts | 15 +++
src/store/useStore.ts | 9 +-
src/styles/_variables.scss | 3 +-
src/utils/setNoScroll.ts | 4 +-
yarn.lock | 10 ++
11 files changed, 279 insertions(+), 24 deletions(-)
create mode 100644 src/components/CookieBanner/CookieBanner.module.scss
create mode 100644 src/components/CookieBanner/CookieBanner.tsx
create mode 100644 src/components/CookieBanner/GoogleScript.tsx
create mode 100644 src/components/CookieBanner/index.ts
create mode 100644 src/store/createCookieBannerSlice.ts
diff --git a/app/layout.tsx b/app/layout.tsx
index 41765ad..e255534 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -1,12 +1,12 @@
import { Metadata } from 'next';
-import Script from 'next/script';
import { FOOTER_QUERY, HEADER_QUERY } from '@/api/graphqlQueries';
+import CookieBanner from '@/components/CookieBanner';
+import GoogleScript from '@/components/CookieBanner/GoogleScript';
import Footer from '@/components/Footer';
import Header from '@/components/Header/Header';
import BackToTop from '@/molecules/BackToTop';
import { getData } from '@/utils/getData';
-
import '@/assets/fonts/fonts.css';
import '@/styles/main.scss';
@@ -24,24 +24,9 @@ export default async function RootLayout({
return (
-
-
+
+
{
+ const { showCookieBanner, setShowCookieBanner } = useStore();
+
+ const prefersReducedMotion = useReducedMotion() || false;
+
+ useEffect(() => {
+ const consentCookie = cookie.get('cookieConsent');
+
+ if (!consentCookie) {
+ setShowCookieBanner(true);
+ }
+ }, []);
+ useEffect(() => {
+ setNoScroll(showCookieBanner);
+ }, [showCookieBanner]);
+
+ const handleAccept = () => {
+ setShowCookieBanner(false);
+ cookie.set('cookieConsent', 'accepted', { expires: 365 });
+ };
+
+ const handleReject = () => {
+ setShowCookieBanner(false);
+ cookie.set('cookieConsent', 'rejected', { expires: 365 });
+ };
+
+ if (!showCookieBanner) {
+ return null;
+ }
+
+ return (
+
+ {showCookieBanner && (
+
+
+
+
+
+ Long Island Laser Tag uses cookies to
+ improve your browsing experience.
+
+
+ Read our
+
+ Privacy Policy
+
+ to learn more.
+
+
+
+
+
+ Accept
+
+
+ Reject
+
+
+
+
+
+ )}
+
+
+ );
+};
+
+export default CookieBanner;
diff --git a/src/components/CookieBanner/GoogleScript.tsx b/src/components/CookieBanner/GoogleScript.tsx
new file mode 100644
index 0000000..4bf0564
--- /dev/null
+++ b/src/components/CookieBanner/GoogleScript.tsx
@@ -0,0 +1,43 @@
+'use client';
+
+import cookie from 'js-cookie';
+import Script from 'next/script';
+import { useEffect, useState } from 'react';
+
+import useStore from '@/store/useStore';
+
+const GoogleScript = () => {
+ const [cookiesAccepted, setCookiesAccepted] = useState(false);
+
+ const { showCookieBanner } = useStore();
+
+ useEffect(() => {
+ setCookiesAccepted(cookie.get('cookieConsent') === 'accepted');
+ }, [showCookieBanner]);
+
+ return (
+ cookiesAccepted && (
+ <>
+
+
+ >
+ )
+ );
+};
+
+export default GoogleScript;
diff --git a/src/components/CookieBanner/index.ts b/src/components/CookieBanner/index.ts
new file mode 100644
index 0000000..670981b
--- /dev/null
+++ b/src/components/CookieBanner/index.ts
@@ -0,0 +1,3 @@
+import CookieBanner from './CookieBanner';
+
+export default CookieBanner;
diff --git a/src/store/createCookieBannerSlice.ts b/src/store/createCookieBannerSlice.ts
new file mode 100644
index 0000000..992799f
--- /dev/null
+++ b/src/store/createCookieBannerSlice.ts
@@ -0,0 +1,15 @@
+import { ZuSlice } from 'declarations';
+
+export interface CookieBannerSlice {
+ showCookieBanner: boolean;
+ setShowCookieBanner: (arg: CookieBannerSlice['showCookieBanner']) => void;
+}
+
+const createCookieBannerSlice: ZuSlice = (
+ set: (arg: () => Partial) => void
+) => ({
+ showCookieBanner: false,
+ setShowCookieBanner: (arg) => set(() => ({ showCookieBanner: arg }))
+});
+
+export default createCookieBannerSlice;
diff --git a/src/store/useStore.ts b/src/store/useStore.ts
index da7619e..266cf91 100644
--- a/src/store/useStore.ts
+++ b/src/store/useStore.ts
@@ -1,11 +1,16 @@
import { create } from 'zustand';
+import createCookieBannerSlice, {
+ CookieBannerSlice
+} from './createCookieBannerSlice';
+
import createMenuSlice, { MenuSlice } from '@/store/createMenuSlice';
-type StoreState = MenuSlice;
+type StoreState = MenuSlice & CookieBannerSlice;
const useStore = create()((...args) => ({
- ...createMenuSlice(...args)
+ ...createMenuSlice(...args),
+ ...createCookieBannerSlice(...args)
}));
export default useStore;
diff --git a/src/styles/_variables.scss b/src/styles/_variables.scss
index 96c98bf..d2f26ea 100644
--- a/src/styles/_variables.scss
+++ b/src/styles/_variables.scss
@@ -24,7 +24,8 @@ $colors: (
$shadows: (
lightFuchsia: 0px 0px 20px rgba(map-get($colors, neon, fuchsia), 0.15),
fuchsia: 0px 0px 20px rgba(map-get($colors, neon, fuchsia), 0.3),
- purple: 0px 0px 20px 5px rgba(map-get($colors, dark, purple), 0.2)
+ purple: 0px 0px 20px 5px rgba(map-get($colors, dark, purple), 0.2),
+ darkBlue: 0px 20px 20px 18px rgba(map-get($colors, dark, blue), 0.8)
);
$gradients: (
diff --git a/src/utils/setNoScroll.ts b/src/utils/setNoScroll.ts
index 15046e7..1d02ef2 100644
--- a/src/utils/setNoScroll.ts
+++ b/src/utils/setNoScroll.ts
@@ -8,8 +8,8 @@ export const setNoScroll = (condition: boolean) => {
return;
}
if (condition) {
- document.body.style.overflowY = 'hidden';
+ document.documentElement.style.overflowY = 'hidden';
} else {
- document.body.style.overflowY = 'unset';
+ document.documentElement.style.overflowY = '';
}
};
diff --git a/yarn.lock b/yarn.lock
index 19928c0..f430538 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4863,6 +4863,11 @@
expect "^29.0.0"
pretty-format "^29.0.0"
+"@types/js-cookie@^3.0.6":
+ version "3.0.6"
+ resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-3.0.6.tgz#a04ca19e877687bd449f5ad37d33b104b71fdf95"
+ integrity sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==
+
"@types/js-yaml@^4.0.0":
version "4.0.5"
resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.5.tgz#738dd390a6ecc5442f35e7f03fa1431353f7e138"
@@ -10536,6 +10541,11 @@ jpeg-js@^0.4.0, jpeg-js@^0.4.2:
resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.4.4.tgz#a9f1c6f1f9f0fa80cdb3484ed9635054d28936aa"
integrity sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==
+js-cookie@^3.0.5:
+ version "3.0.5"
+ resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-3.0.5.tgz#0b7e2fd0c01552c58ba86e0841f94dc2557dcdbc"
+ integrity sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==
+
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"