Skip to content

Commit

Permalink
Add cookie banner and cookie handling
Browse files Browse the repository at this point in the history
  • Loading branch information
ahosgood committed Sep 14, 2023
1 parent b6f9d5a commit c4d9ad7
Show file tree
Hide file tree
Showing 11 changed files with 491 additions and 19 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Small button option
- Tabs also respond to the up/down keys on keyboard navigation as well as left/right
- Allow other image sources in a card image using a `<picture>` element
- Cookie banner and cookie handling

### Changed

Expand Down
2 changes: 2 additions & 0 deletions src/nationalarchives/all.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Header } from "./components/header/header.mjs";
import { Picture } from "./components/picture/picture.mjs";
import { SensitiveImage } from "./components/sensitive-image/sensitive-image.mjs";
import { Tabs } from "./components/tabs/tabs.mjs";
import Cookies from "./lib/cookies.mjs";

const $body = document.documentElement;
$body.classList.add("tna-template--js-enabled");
Expand Down Expand Up @@ -88,6 +89,7 @@ const initAll = (options) => {

export {
initAll,
Cookies,
Breadcrumbs,
CookieBanner,
Gallery,
Expand Down
98 changes: 90 additions & 8 deletions src/nationalarchives/components/cookie-banner/cookie-banner.mjs
Original file line number Diff line number Diff line change
@@ -1,22 +1,104 @@
import Cookies from "../../lib/cookies.mjs";

export class CookieBanner {
constructor($module) {
this.$module = $module;
this.$accept = $module && $module.querySelector('[value="accept"]');
this.$reject = $module && $module.querySelector('[value="reject"]');
this.$acceptButton = $module && $module.querySelector('[value="accept"]');
this.$rejectButton = $module && $module.querySelector('[value="reject"]');
this.$prompt =
$module && $module.querySelector(".tna-cookie-banner__message--prompt");
this.$acceptedMessage =
$module && $module.querySelector(".tna-cookie-banner__message--accepted");
this.$rejectedMessage =
$module && $module.querySelector(".tna-cookie-banner__message--rejected");
this.$closeButtons = $module && $module.querySelectorAll('[value="close"]');
}

init() {
if (!this.$module || !this.$accept || !this.$reject) {
if (
!this.$module ||
!this.$acceptButton ||
!this.$rejectButton ||
!this.$prompt ||
!this.$acceptedMessage ||
!this.$rejectedMessage ||
!this.$closeButtons
) {
return;
}

const policies = this.$module.getAttribute("data-policies");
if (!policies) {
return;
}
this.cookies = new Cookies(
policies.split(",").map((policy) => policy.trim()),
);

this.loadScriptsOnAccept = this.$module.getAttribute("data-acceptscripts");

this.hideCookieBannerKey = this.$module.getAttribute("data-hidekey");
const cookiePreferencesSet = this.cookies.exists(this.hideCookieBannerKey);

if (!cookiePreferencesSet) {
this.$module.removeAttribute("hidden");

this.$acceptButton.addEventListener("click", () => this.accept());
this.$rejectButton.addEventListener("click", () => this.reject());

this.$closeButtons.forEach(($closeButton) => {
$closeButton.addEventListener("click", () => this.close());
});
}

// ==================== DEV ====================
// document.getElementById("reset").addEventListener("click", () => {
// this.cookies.delete(this.hideCookieBannerKey);
// this.cookies.delete("cookies_policy");
// window.scrollY = 0;
// window.location = window.location;
// });
// document
// .getElementById("accept-analytics-policy")
// .addEventListener("click", () => this.cookies.acceptPolicy("analytics"));
// document
// .getElementById("reject-analytics-policy")
// .addEventListener("click", () => this.cookies.rejectPolicy("analytics"));
// document
// .getElementById("accept-settings-policy")
// .addEventListener("click", () => this.cookies.acceptPolicy("settings"));
// document
// .getElementById("reject-settings-policy")
// .addEventListener("click", () => this.cookies.rejectPolicy("settings"));
// ==================== END ====================
}

this.$module.removeAttribute("hidden");
accept() {
this.$prompt.setAttribute("hidden", true);
this.complete();
this.$acceptedMessage.removeAttribute("hidden");
this.cookies.acceptAllPolicies();
if (this.loadScriptsOnAccept) {
this.loadScriptsOnAccept.split(",").forEach((script) => {
const $script = document.createElement("script");
$script.src = script;
document.head.appendChild($script);
});
}
}

this.$accept.addEventListener("click", () => this.accept());
this.$reject.addEventListener("click", () => this.reject());
reject() {
this.$prompt.setAttribute("hidden", true);
this.complete();
this.$rejectedMessage.removeAttribute("hidden");
this.cookies.rejectAllPolicies();
}

accept() {}
complete() {
this.cookies.set(this.hideCookieBannerKey, true);
}

reject() {}
close() {
this.$module.setAttribute("hidden", true);
}
}
20 changes: 20 additions & 0 deletions src/nationalarchives/components/cookie-banner/cookie-banner.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
@use "../../tools/colour";
@use "../../tools/spacing";
@use "../../tools/media";
@use "../../tools/typography";
@use "../../utilities";
@use "../button";
@use "../grid";
Expand All @@ -9,4 +11,22 @@

padding-top: 2rem;
padding-bottom: 2rem;

@include media.on-tiny {
padding-top: 1rem;
padding-bottom: 1rem;

@include typography.relative-font-size(16);
}

&__message {
&--prompt {
}

&--accepted {
}

&--rejected {
}
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import CookieBanner from "./template.njk";
import macroOptions from "./macro-options.json";
import { expect } from "@storybook/jest";
import { within, userEvent } from "@storybook/testing-library";
import Cookies from "../../lib/cookies.mjs";

const argTypes = {
url: { control: "text" },
policies: { control: "text" },
hideCookieBannerKey: { control: "text" },
loadScriptsOnAccept: { control: "text" },
classes: { control: "text" },
attributes: { control: "object" },
};
Expand All @@ -18,10 +24,20 @@ export default {
argTypes,
};

const Template = ({ url, classes, attributes }) =>
const Template = ({
url,
policies,
hideCookieBannerKey,
loadScriptsOnAccept,
classes,
attributes,
}) =>
CookieBanner({
params: {
url,
policies,
hideCookieBannerKey,
loadScriptsOnAccept,
classes,
attributes,
},
Expand All @@ -32,3 +48,117 @@ Standard.args = {
cookiesUrl: "#",
classes: "tna-cookie-banner--demo",
};

const deleteAllCookies = (cookies) => {
Object.keys(cookies.all).forEach((cookie) => cookies.delete(cookie));
};

export const Accept = Template.bind({});
Accept.args = {
cookiesUrl: "#",
classes: "tna-cookie-banner--demo",
};
Accept.play = async ({ canvasElement }) => {
const cookies = new Cookies();
deleteAllCookies(cookies);

await expect(cookies.isPolicyAccepted("analytics")).toEqual(false);
await expect(cookies.isPolicyAccepted("settings")).toEqual(false);
await expect(cookies.isPolicyAccepted("unknown")).toEqual(null);

const canvas = within(canvasElement);
const acceptButton = canvas.getByText("Accept cookies");
await expect(acceptButton).toBeVisible();
await userEvent.click(acceptButton);

await expect(cookies.isPolicyAccepted("analytics")).toEqual(true);
await expect(cookies.isPolicyAccepted("settings")).toEqual(true);
await expect(cookies.isPolicyAccepted("unknown")).toEqual(null);
await expect(acceptButton).not.toBeVisible();

// const closeButton = canvas.getByText("Close this message");
// await expect(closeButton).toBeVisible();
// await userEvent.click(closeButton);

// await expect(closeButton).not.toBeVisible();

deleteAllCookies(cookies);
};

export const Reject = Template.bind({});
Reject.args = {
cookiesUrl: "#",
classes: "tna-cookie-banner--demo",
};
Reject.play = async ({ canvasElement }) => {
const cookies = new Cookies();
deleteAllCookies(cookies);

await expect(cookies.isPolicyAccepted("analytics")).toEqual(false);
await expect(cookies.isPolicyAccepted("settings")).toEqual(false);
await expect(cookies.isPolicyAccepted("unknown")).toEqual(null);

const canvas = within(canvasElement);
const rejectButton = canvas.getByText("Reject cookies");
await expect(rejectButton).toBeVisible();
await userEvent.click(rejectButton);

await expect(cookies.isPolicyAccepted("analytics")).toEqual(false);
await expect(cookies.isPolicyAccepted("settings")).toEqual(false);
await expect(cookies.isPolicyAccepted("unknown")).toEqual(null);

deleteAllCookies(cookies);
};

export const CustomPolicies = Template.bind({});
CustomPolicies.args = {
cookiesUrl: "#",
policies: "custom",
classes: "tna-cookie-banner--demo",
};
CustomPolicies.play = async ({ args, canvasElement }) => {
const cookies = new Cookies(args.policies.split(","));
deleteAllCookies(cookies);

await expect(cookies.isPolicyAccepted("analytics")).toEqual(null);
await expect(cookies.isPolicyAccepted("settings")).toEqual(null);
await expect(cookies.isPolicyAccepted("custom")).toEqual(false);

const canvas = within(canvasElement);
const acceptButton = canvas.getByText("Accept cookies");
await userEvent.click(acceptButton);

await expect(cookies.isPolicyAccepted("analytics")).toEqual(null);
await expect(cookies.isPolicyAccepted("settings")).toEqual(null);
await expect(cookies.isPolicyAccepted("custom")).toEqual(true);

deleteAllCookies(cookies);
};

export const AddScriptsOnAccept = Template.bind({});
AddScriptsOnAccept.args = {
cookiesUrl: "#",
loadScriptsOnAccept: "my-analytics-script.js",
classes: "tna-cookie-banner--demo",
};
AddScriptsOnAccept.play = async ({ args, canvasElement }) => {
const cookies = new Cookies();
deleteAllCookies(cookies);

const noScript = document.querySelector(
`script[src="${args.loadScriptsOnAccept}"]`,
);
await expect(noScript).toEqual(null);

const canvas = within(canvasElement);
const acceptButton = canvas.getByText("Accept cookies");
await userEvent.click(acceptButton);

const script = document.querySelector(
`script[src="${args.loadScriptsOnAccept}"]`,
);
await expect(script).toBeTruthy();

deleteAllCookies(cookies);
script.remove();
};
18 changes: 18 additions & 0 deletions src/nationalarchives/components/cookie-banner/macro-options.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,24 @@
"required": true,
"description": ""
},
{
"name": "policies",
"type": "string",
"required": false,
"description": ""
},
{
"name": "hideCookieBannerKey",
"type": "string",
"required": false,
"description": ""
},
{
"name": "loadScriptsOnAccept",
"type": "string",
"required": false,
"description": ""
},
{
"name": "classes",
"type": "string",
Expand Down
Loading

0 comments on commit c4d9ad7

Please sign in to comment.