diff --git a/docs/cookie-banner.html b/docs/cookie-banner.html index cdb9faba..67a5d4b5 100644 --- a/docs/cookie-banner.html +++ b/docs/cookie-banner.html @@ -197,13 +197,20 @@
GDPR compliant cookie banner and consent form.
Renders a cookie banner and a consent form based on configuration settings, and conditionally invokes cookie-reliant functionality based on user consent.
-Optionally send anonymous predefined cookie banner and consent form interaction measurements to a specified Google Analytics using the Google Measurement API.
Cookie consent is based on categorising cookies and the functions that initialise them, describing them in a configuration object passed into the module at initialisition.
The cookie banner renders itself if no consent preferences are recorded in the browser.
The consent form renders into a DOMElement with a particular className configurable options (classNames.formContainer).
A page containing a cookie consent form should include a visually hidden live region (role=alert) with a particular className (classNames.formAnnouncement), default: ‘privacy-banner__form-announcement’.
+Optionally the banner also supports basic Google EU consent mode [https://developers.google.com/tag-platform/security/guides/consent?consentmode=basic], and can push user consent preferences to the dataLayer for Google libraries to use. All that is necessary to suport Google consent mode is to map Google consent categories to the cookie categories in the configuration.
+For example, to map the ad_storage, ad_user_data, and ad_personalisation to an ‘ads’ consent category defined in the banner config, add a euConsentTypes
object to the configuration like this:
euConsentTypes: {
+ ad_storage: 'test',
+ ad_user_data: 'test',
+ ad_personalization: 'test'
+}
+
Install the package
npm i -S @stormid/cookie-banner
@@ -248,38 +255,7 @@ {
- name: '.CookiePreferences', //name of the cookie set to record user consent
- path: '/', //path of the preferences cookie
- domain: window.location.hostname === 'localhost' ? '' : `.${removeSubdomain(window.location.hostname)}`, //domain of the preferences cookie, defaults to .<root-domain>
- secure: true, //preferences cookie secure
- samesite: 'lax', //preferences cookie samesite
- expiry: 365, //preferences cookie expiry in days
- types: {}, //types of cookie-dependent functionality
- necessary: [], //cookie-dependent functionality that will always execute, for convenience only
- policyURL: '/cookie-policy#preferences', //URL to cookie policy page (location of cookie consent form) rendered in the banner
- classNames: {
- banner: 'privacy-banner',
- acceptBtn: 'privacy-banner__accept',
- rejectBtn: 'privacy-banner__reject',
- submitBtn: 'privacy-banner__submit',
- field: 'privacy-banner__field',
- form: 'privacy-banner__form',
- fieldset: 'privacy-banner__fieldset',
- legend: 'privacy-banner__legend',
- formContainer: 'privacy-banner__form-container', //where the form is rendered
- formMessage: 'privacy-banner__form-msg',
- formAnnouncement: 'privacy-banner__form-announcement', //screen reader announcement
- title: 'privacy-banner__form-title',
- description: 'privacy-banner__form-description'
},
- hideBannerOnFormPage: false, //don't show the banner when the user is on the same page as a consent form
- savedMessage: 'Your settings have been saved.', //displayed after consent form update,
- trapTab: false, //trap the user's keyboard tab within the banner when open
bannerTemplate(model){
return `<section role="dialog" aria-live="polite" aria-label="Your privacy" class="${model.classNames.banner}">
<div class="privacy-content">
@@ -288,18 +264,19 @@ Options
<div class="privacy-banner__title">Cookies</div>
<p>We use cookies to improve your experience on our site and show you personalised advertising.</p>
<p>Find out more from our <a class="privacy-banner__link" rel="noopener noreferrer nofollow" href="/privacy-policy">privacy policy</a> and <a class="privacy-banner__link" rel="noopener noreferrer nofollow" href="${model.policyURL}">cookie policy</a>.</p>
- <button class="btn btn--primary ${model.classNames.acceptBtn}">Accept and close</button>
- <a class="privacy-banner__link" rel="noopener noreferrer nofollow" href="${model.policyURL}">Your options</a>
+ <button type="button" class="btn btn--primary ${model.classNames.acceptBtn}">Accept all</button>
+ <button type="button" class="btn btn--primary ${model.classNames.rejectBtn}">Reject all</button>
+ <a class="privacy-banner__link ${model.classNames.optionsBtn}" rel="noopener noreferrer nofollow" href="${model.policyURL}">Your options</a>
<!--googleon: all-->
</div>
</div>
</section>`;
},
messageTemplate(model){
- return `<div class="${model.settings.classNames.formMessage}" aria-role="alert">${model.settings.savedMessage}</div>`
+ return `<div class="${model.settings.classNames.formMessage}" aria-hidden="true">${model.settings.savedMessage}</div>`
},
formTemplate(model){
- return `<form id="preferences" class="${model.settings.classNames.form}" novalidate>
+ return `<form id="preferences" class="${model.settings.classNames.form}" novalidate>
${Object.keys(model.settings.types).map(type => `<fieldset class="${model.settings.classNames.fieldset}">
<legend class="${model.settings.classNames.legend}">
<span class="${model.settings.classNames.title}">${model.settings.types[type].title}</span>
@@ -313,8 +290,44 @@ Options
type="radio"
name="privacy-${type.split(' ')[0].replace(' ', '-')}"
value="1"
- ${model.consent[type] === 1 ? ` checked` : ''}>
- <span class="privacy-banner__label-text">I am OK with this</span>
+ ${model.consent[type] === 1 ? `checked` : ''}>
+ <span class="privacy-banner__label-text&qureturn `<form id="preferences" class="${model.settings.classNames.form}" novalidate>
+ ${Object.keys(model.settings.types).map(type => `<fieldset class="${model.settings.classNames.fieldset}">
+ <legend class="${model.settings.classNames.legend}">
+ <span class="${model.settings.classNames.title}">${model.settings.types[type].title}</span>
+ <span class="${model.settings.classNames.description}">${model.settings.types[type].description}</span>
+ </legend>
+ <div class="form-row">
+ <div class="relative">
+ <label class="privacy-banner__label">
+ <input
+ class="${model.settings.classNames.field}"
+ type="radio"
+ name="privacy-${type.split(' ')[0].replace(' ', '-')}"
+ value="1"
+ ${model.consent[type] === 1 ? `checked` : ''}>
+ <span class="privacy-banner__label-text">I am OK with this</span>
+ <span class="privacy-banner__label-description">${model.settings.types[type].labels.yes}</span>
+ </label>
+ </div>
+ </div>
+ <div class="form-row">
+ <div class="relative">
+ <label class="privacy-banner__label">
+ <input
+ class="${model.settings.classNames.field}"
+ type="radio"
+ name="privacy-${type.split(' ')[0].replace(' ', '-')}"
+ value="0"
+ ${model.consent[type] === 0 ? `checked` : ''}>
+ <span class="privacy-banner__label-text">No thank you</span>
+ <span class="privacy-banner__label-description">${model.settings.types[type].labels.no}</span>
+ </label>
+ </div>
+ </div>
+</fieldset>`).join('')}
+<button class="${model.settings.classNames.submitBtn}"${Object.keys(model.consent).length !== Object.keys(model.settings.types).length ? ` disabled` : ''}>Save my settings</button>
+</form>`;ot;>I am OK with this</span>
<span class="privacy-banner__label-description">${model.settings.types[type].labels.yes}</span>
</label>
</div>
@@ -327,16 +340,53 @@ Options
type="radio"
name="privacy-${type.split(' ')[0].replace(' ', '-')}"
value="0"
- ${model.consent[type] === 0 ? ` checked` : ''}>
+ ${model.consent[type] === 0 ? `checked` : ''}>
<span class="privacy-banner__label-text">No thank you</span>
<span class="privacy-banner__label-description">${model.settings.types[type].labels.no}</span>
</label>
</div>
</div>
</fieldset>`).join('')}
- <button class="${model.settings.classNames.submitBtn}"${Object.keys(model.consent).length === 0 ? ` disabled` : ''}>Save my settings</button>
+ <button class="${model.settings.classNames.submitBtn}"${Object.keys(model.consent).length !== Object.keys(model.settings.types).length ? ` disabled` : ''}>Save my settings</button>
</form>`;
}
+});
+
+{
+ name: '.CookiePreferences', //name of the cookie set to record user consent
+ path: '/', //path of the preferences cookie
+ domain: window.location.hostname === 'localhost' ? '' : `.${removeSubdomain(window.location.hostname)}`, //domain of the preferences cookie, defaults to .<root-domain>
+ secure: true, //preferences cookie secure
+ samesite: 'lax', //preferences cookie samesite
+ expiry: 365, //preferences cookie expiry in days
+ types: {}, //types of cookie-dependent functionality
+ euConsentTypes: {}, //map Google EU consent categories to types of cookie defined in 'types'
+ necessary: [], //cookie-dependent functionality that will always execute, for convenience only
+ policyURL: '/cookie-policy#preferences', //URL to cookie policy page (location of cookie consent form) rendered in the banner
+ classNames: {
+ banner: 'privacy-banner',
+ acceptBtn: 'privacy-banner__accept',
+ rejectBtn: 'privacy-banner__reject',
+ submitBtn: 'privacy-banner__submit',
+ field: 'privacy-banner__field',
+ form: 'privacy-banner__form',
+ fieldset: 'privacy-banner__fieldset',
+ legend: 'privacy-banner__legend',
+ formContainer: 'privacy-banner__form-container', //where the form is rendered
+ formMessage: 'privacy-banner__form-msg',
+ formAnnouncement: 'privacy-banner__form-announcement', //screen reader announcement
+ title: 'privacy-banner__form-title',
+ description: 'privacy-banner__form-description'
+ },
+ hideBannerOnFormPage: false, //don't show the banner when the user is on the same page as a consent form
+ savedMessage: 'Your settings have been saved.', //displayed after consent form update,
+ trapTab: false, //trap the user's keyboard tab within the banner when open
+ bannerTemplate: null, //a function which returns the HTML string for the banner. There is no default for this since if varies significantly from project to project. **This option is mandatory** - the banner will not initialise if this is not provided. See example configration above.
+ formTemplate: null, //a function which returns the HTML string for the preferences form. There is no default for this since it varies significantly from project to project. **This option is mandatory** - the banner will not initialise if this is not provided. See example configration above.
+ messageTemplate(model){
+ return `<div class="${model.settings.classNames.formMessage}" aria-hidden="true">${model.settings.savedMessage}</div>`
+ } //A function which returns an HTML string to create the confirmation message. Default HTML is provided as above.
}
tabs() returns an array of instances. Each instance exposes the interface
-{
- getState, a Function that returns the current state Object
-}
+Setting the active tab
+On page load the active tab will be set by (in order of precedence):
+1. The page hash. If the page hash in the address bar matches the ID of a panel, it will be activated on page load
+2. The data-active-index attribute. If the tabs node found to have a <pre>data-active-index</pre> attribute, that tab will be activated on page load. This is a zero-based index.
+3. The tab specified by the activeIndex in the settings. This is a zero-based index.
+4. The first tab in the set.
+
+## API
+
+tabs() returns an array of instances. Each instance exposes the interface
-Tests
-npm t
+{
+getState, a Function that returns the current state Object
+}
+
+## Tests
-License
-MIT
+npm t
+
+## License
+MIT