diff --git a/src/assets/js/application.js b/src/assets/js/application.js index a207f019..547bf2a8 100644 --- a/src/assets/js/application.js +++ b/src/assets/js/application.js @@ -3,8 +3,14 @@ as it will be loaded into the base nunjucks template. */ +import CookieBanner from './components/cookie-banner.js' import initiateJsHiddenChecks from './js-hidden.js' +const initCookieBanner = () => { + return new CookieBanner(window.document) +} + window.addEventListener('load', () => { initiateJsHiddenChecks() + initCookieBanner() }) diff --git a/src/assets/js/components/cookie-banner.js b/src/assets/js/components/cookie-banner.js new file mode 100644 index 00000000..3cb14171 --- /dev/null +++ b/src/assets/js/components/cookie-banner.js @@ -0,0 +1,95 @@ +const cookieDefaults = { + cookieNames: { + preferenceCookie: 'cookies_preferences_set', + policyCookie: 'cookies_policy' + }, + cookieExpiryDays: 365, + cookiePath: '/', + defaultPolicyValue: { essential: true, settings: true, usage: true, campaigns: true } +} + +export default class CookieBanner { + constructor (document) { + this.document = document + this.banner = this.document.querySelector('.js-app-c-cookie-banner') + if (!this.banner) { + console.warn('Cookie banner element not found') + return + } + + this.form = this.banner.querySelector('.js-app-c-cookie-banner__form') + this.confirmationMessage = this.banner.querySelector('.js-app-c-cookie-banner__confirmation') + this.confirmationDecision = this.banner.querySelector('.js-app-c-cookie-banner__confirmation-decision') + this.acceptButton = this.banner.querySelector('.js-app-c-cookie-banner__accept') + this.rejectButton = this.banner.querySelector('.js-app-c-cookie-banner__reject') + this.hideButton = this.banner.querySelector('.js-app-c-cookie-banner__hide') + + this.init() + } + + init () { + if (this.getCookie(cookieDefaults.cookieNames.preferenceCookie) !== null) { + this.hideCookieBanner() + } + + this.acceptButton.addEventListener('click', this.accept.bind(this)) + this.rejectButton.addEventListener('click', this.reject.bind(this)) + this.hideButton.addEventListener('click', this.hideCookieBanner.bind(this)) + } + + accept () { + this.userCookiePolicyDecision(true) + } + + reject () { + this.userCookiePolicyDecision(false) + } + + userCookiePolicyDecision (userAcceptedCookiePolicy = true) { + this.setCookie( + cookieDefaults.cookieNames.preferenceCookie, + userAcceptedCookiePolicy, + cookieDefaults.cookieExpiryDays) + + this.setCookie( + cookieDefaults.cookieNames.policyCookie, + userAcceptedCookiePolicy ? cookieDefaults.defaultPolicyValue : null, + userAcceptedCookiePolicy ? cookieDefaults.cookieExpiryDays : 0 + ) + + this.showConfirmationMessage(userAcceptedCookiePolicy) + } + + setCookie (name, value, days) { + const expires = new Date() + expires.setTime(expires.getTime() + (days * 24 * 60 * 60 * 1000)) + this.document.cookie = `${name}=${JSON.stringify(value)};expires=${expires.toUTCString()};path=${cookieDefaults.cookiePath}` + } + + getCookie (name) { + const cookie = this.document.cookie + .split('; ') + .find(row => row.startsWith(name)) + + return cookie ? JSON.parse(cookie.split('=')[1]) : null + } + + showConfirmationMessage (userAcceptedCookiePolicy = true) { + this.form.classList.add('app-c-cookie-banner__form--hidden') + this.form.setAttribute('aria-hidden', 'true') + this.acceptButton.removeEventListener('click', this.accept.bind(this)) + this.rejectButton.removeEventListener('click', this.reject.bind(this)) + + if (!userAcceptedCookiePolicy) this.confirmationDecision.textContent = 'rejected' + this.confirmationMessage.classList.remove('app-c-cookie-banner__confirmation--hidden') + this.confirmationMessage.setAttribute('aria-hidden', 'false') + this.confirmationMessage.setAttribute('role', 'status') // Announce status to screen readers + } + + hideCookieBanner () { + if (this.banner) { + this.banner.style.display = 'none' + this.banner.setAttribute('aria-hidden', 'true') + } + } +} diff --git a/src/assets/scss/components/_cookie-banner.scss b/src/assets/scss/components/_cookie-banner.scss new file mode 100644 index 00000000..5294d8a2 --- /dev/null +++ b/src/assets/scss/components/_cookie-banner.scss @@ -0,0 +1,7 @@ +.app-c-cookie-banner__form--hidden { + display: none; +} + +.app-c-cookie-banner__confirmation--hidden { + display: none; +} diff --git a/src/assets/scss/index.scss b/src/assets/scss/index.scss index 6f63f5f6..c2c42f90 100644 --- a/src/assets/scss/index.scss +++ b/src/assets/scss/index.scss @@ -5,7 +5,7 @@ $govuk-global-styles: true; @import "src/assets/scss/_scrollable-container.scss"; @import "./step-by-step-nav.scss"; @import "./components/dataset-navigation"; - +@import "./components/cookie-banner"; .app-inset-text---error { border-left: 5px solid govuk-colour('red'); diff --git a/src/routes/cookies.js b/src/routes/cookies.js index 21139986..7ea00e3b 100644 --- a/src/routes/cookies.js +++ b/src/routes/cookies.js @@ -4,8 +4,23 @@ import nunjucks from 'nunjucks' const router = express.Router() router.get('/', (req, res) => { - const cookiesPage = nunjucks.render('cookies.html', {}) + const cookiesPage = nunjucks.render('cookies.html', { + cookiePreferences: req.cookies.cookies_preferences_set, + showCookieUpdatedMessage: req.cookies.cookies_preferences_set_updated + }) res.send(cookiesPage) }) +router.post('/update-preference', (req, res) => { + const defaultCookieExpiry = 1000 * 60 * 60 * 24 * 365 // 1 year + + res.cookie('cookies_preferences_set', req.body.accept_cookies, { maxAge: defaultCookieExpiry }) + res.cookie('cookies_preferences_set_updated', true, { maxAge: 1000 }) + res.cookie('cookies_policy', JSON.stringify({ essential: true, settings: true, usage: true, campaigns: true }), { + maxAge: req.body.accept_cookies === 'true' ? defaultCookieExpiry : 0 + }) + + res.redirect('/cookies') +}) + export default router diff --git a/src/views/components/cookie-banner.html b/src/views/components/cookie-banner.html new file mode 100644 index 00000000..f6275503 --- /dev/null +++ b/src/views/components/cookie-banner.html @@ -0,0 +1,64 @@ +{% from "govuk/components/cookie-banner/macro.njk" import govukCookieBanner %} + +{% if serviceType %} + {% set serviceName = serviceType | getFullServiceName %} +{% endif %} +{% set cookiePagePath = '/cookies' %} +{% set cookieHeader = "Cookies on " + serviceName %} + +{% set html %} +
We use some essential cookies to make this service work.
+We’d also like to use analytics cookies so we can understand how you use the service and make improvements.
+{% endset %} + +{% set acceptHtml %} +You’ve change your cookie settings at any time.
+{% endset %} + + additional cookies. You can diff --git a/src/views/cookies.html b/src/views/cookies.html index b626585d..100f9ded 100644 --- a/src/views/cookies.html +++ b/src/views/cookies.html @@ -1,6 +1,10 @@ +{% from "govuk/components/radios/macro.njk" import govukRadios %} +{% from "govuk/components/button/macro.njk" import govukButton %} +{% from "govuk/components/notification-banner/macro.njk" import govukNotificationBanner %} + {% extends "layouts/main.html" %} -{% set pageName = 'Accessibility statement for Submit and update planning and housing data for England' %} +{% set pageName = 'Cookie notice for Submit and update planning and housing data for England' %} {% set markdownContent %} @@ -54,7 +58,6 @@ - ### Smartlook cookies (optional) We use Smartlook to measure how you use the Check planning and housing data for England service. This helps us to improve the service experience and make sure it’s meeting your needs. No personal data will be stored in the cookies. @@ -67,7 +70,7 @@ * how long you spend on each part * what you click on the service -The cookies do not store any information you add to the service. +The cookies do not store any information you add to the service.