Skip to content

Commit

Permalink
fix: support dynamic changes to cookie consent properties (#6707)
Browse files Browse the repository at this point in the history
  • Loading branch information
tomivirkki authored Oct 27, 2023
1 parent 38f66a6 commit 7b0c2bb
Show file tree
Hide file tree
Showing 2 changed files with 186 additions and 44 deletions.
90 changes: 57 additions & 33 deletions packages/cookie-consent/src/vaadin-cookie-consent.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,10 @@ class CookieConsent extends ElementMixin(PolymerElement) {
};
}

static get observers() {
return ['__initialize(_showLearnMore, message, dismiss, learnMore, learnMoreLink, cookieName, position)'];
}

/** @private */
_showLink(learnMoreLink) {
return !!learnMoreLink;
Expand All @@ -155,6 +159,38 @@ class CookieConsent extends ElementMixin(PolymerElement) {
this._css.innerText = '.cc-window{opacity:1;transition:opacity 1s ease}.cc-window.cc-invisible{opacity:0}.cc-animate.cc-revoke{transition:transform 1s ease}.cc-animate.cc-revoke.cc-top{transform:translateY(-2em)}.cc-animate.cc-revoke.cc-bottom{transform:translateY(2em)}.cc-animate.cc-revoke.cc-active.cc-bottom,.cc-animate.cc-revoke.cc-active.cc-top,.cc-revoke:hover{transform:translateY(0)}.cc-grower{max-height:0;overflow:hidden;transition:max-height 1s}.cc-link,.cc-revoke:hover{text-decoration:underline}.cc-revoke,.cc-window{position:fixed;overflow:hidden;box-sizing:border-box;font-family:Helvetica,Calibri,Arial,sans-serif;font-size:16px;line-height:1.5em;display:-ms-flexbox;display:flex;-ms-flex-wrap:nowrap;flex-wrap:nowrap;z-index:9999}.cc-window.cc-static{position:static}.cc-window.cc-floating{padding:2em;max-width:24em;-ms-flex-direction:column;flex-direction:column}.cc-window.cc-banner{padding:1em 1.8em;width:100%;-ms-flex-direction:row;flex-direction:row}.cc-revoke{padding:.5em}.cc-header{font-size:18px;font-weight:700}.cc-btn,.cc-close,.cc-link,.cc-revoke{cursor:pointer}.cc-link{opacity:.8;display:inline-block;padding:.2em}.cc-link:hover{opacity:1}.cc-link:active,.cc-link:visited{color:initial}.cc-btn{display:block;padding:.4em .8em;font-size:.9em;font-weight:700;border-width:2px;border-style:solid;text-align:center;white-space:nowrap}.cc-banner .cc-btn:last-child{min-width:140px}.cc-highlight .cc-btn:first-child{background-color:transparent;border-color:transparent}.cc-highlight .cc-btn:first-child:focus,.cc-highlight .cc-btn:first-child:hover{background-color:transparent;text-decoration:underline}.cc-close{display:block;position:absolute;top:.5em;right:.5em;font-size:1.6em;opacity:.9;line-height:.75}.cc-close:focus,.cc-close:hover{opacity:1}.cc-revoke.cc-top{top:0;left:3em;border-bottom-left-radius:.5em;border-bottom-right-radius:.5em}.cc-revoke.cc-bottom{bottom:0;left:3em;border-top-left-radius:.5em;border-top-right-radius:.5em}.cc-revoke.cc-left{left:3em;right:unset}.cc-revoke.cc-right{right:3em;left:unset}.cc-top{top:1em}.cc-left{left:1em}.cc-right{right:1em}.cc-bottom{bottom:1em}.cc-floating>.cc-link{margin-bottom:1em}.cc-floating .cc-message{display:block;margin-bottom:1em}.cc-window.cc-floating .cc-compliance{-ms-flex:1 0 auto;flex:1 0 auto}.cc-window.cc-banner{-ms-flex-align:center;align-items:center}.cc-banner.cc-top{left:0;right:0;top:0}.cc-banner.cc-bottom{left:0;right:0;bottom:0}.cc-banner .cc-message{-ms-flex:1;flex:1}.cc-compliance{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-line-pack:justify;align-content:space-between}.cc-compliance>.cc-btn{-ms-flex:1;flex:1}.cc-btn+.cc-btn{margin-left:.5em}@media print{.cc-revoke,.cc-window{display:none}}@media screen and (max-width:900px){.cc-btn{white-space:normal}}@media screen and (max-width:414px) and (orientation:portrait),screen and (max-width:736px) and (orientation:landscape){.cc-window.cc-top{top:0}.cc-window.cc-bottom{bottom:0}.cc-window.cc-banner,.cc-window.cc-left,.cc-window.cc-right{left:0;right:0}.cc-window.cc-banner{-ms-flex-direction:column;flex-direction:column}.cc-window.cc-banner .cc-compliance{-ms-flex:1;flex:1}.cc-window.cc-floating{max-width:none}.cc-window .cc-message{margin-bottom:1em}.cc-window.cc-banner{-ms-flex-align:unset;align-items:unset}}.cc-floating.cc-theme-classic{padding:1.2em;border-radius:5px}.cc-floating.cc-type-info.cc-theme-classic .cc-compliance{text-align:center;display:inline;-ms-flex:none;flex:none}.cc-theme-classic .cc-btn{border-radius:5px}.cc-theme-classic .cc-btn:last-child{min-width:140px}.cc-floating.cc-type-info.cc-theme-classic .cc-btn{display:inline-block}.cc-theme-edgeless.cc-window{padding:0}.cc-floating.cc-theme-edgeless .cc-message{margin:2em 2em 1.5em}.cc-banner.cc-theme-edgeless .cc-btn{margin:0;padding:.8em 1.8em;height:100%}.cc-banner.cc-theme-edgeless .cc-message{margin-left:1em}.cc-floating.cc-theme-edgeless .cc-btn+.cc-btn{margin-left:0}'; // eslint-disable-line max-len
document.head.appendChild(this._css);

this.__initialize(
this._showLearnMore,
this.message,
this.dismiss,
this.learnMore,
this.learnMoreLink,
this.cookieName,
this.position,
);
}

/** @protected */
disconnectedCallback() {
super.disconnectedCallback();
this.__closePopup();
if (this._css.isConnected) {
this._css.remove();
}
}

/** @private */
__closePopup() {
const popup = this._getPopup();
if (popup) {
popup.parentNode.removeChild(popup);
}
}

/** @private */
__initialize(_showLearnMore, message, dismiss, learnMore, learnMoreLink, cookieName, position) {
this.__closePopup();

window.cookieconsent.initialise({
palette: {
popup: {
Expand All @@ -168,52 +204,40 @@ class CookieConsent extends ElementMixin(PolymerElement) {
hover: 'rgba(22, 118, 243, 1)',
},
},
showLink: this._showLearnMore,
showLink: _showLearnMore,
content: {
message: this.message,
dismiss: this.dismiss,
link: this.learnMore,
href: this.learnMoreLink,
message,
dismiss,
link: learnMore,
href: learnMoreLink,
},
cookie: {
name: this.cookieName,
name: cookieName,
},
position: this.position,
position,
elements: {
messagelink: `<span id="cookieconsent:desc" class="cc-message">${this.message} <a tabindex="0" class="cc-link" href="${this.learnMoreLink}" target="_blank" rel="noopener noreferrer nofollow">${this.learnMore}</a></span>`,
dismiss: `<a tabindex="0" class="cc-btn cc-dismiss">${this.dismiss}</a>`,
messagelink: `<span id="cookieconsent:desc" class="cc-message">${message} <a tabindex="0" class="cc-link" href="${learnMoreLink}" target="_blank" rel="noopener noreferrer nofollow">${learnMore}</a></span>`,
dismiss: `<a tabindex="0" class="cc-btn cc-dismiss">${dismiss}</a>`,
},
});

const popup = this._getPopup();
if (popup) {
// NVDA announces a popup appearance only if the role is alert
popup.setAttribute('role', 'alert');
}

// In order to make an `<a>` element act as a button, setting
// `role="button"` is not enough: https://developer.mozilla.org/en-US
// /docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_button_role
const dismissButton = popup.querySelector('a.cc-btn');
dismissButton.addEventListener('keydown', (event) => {
const SPACE = 32;
const ENTER = 13;
const key = event.keyCode || event.which;
if (key === SPACE || key === ENTER) {
dismissButton.click();
}
});
}

/** @protected */
disconnectedCallback() {
super.disconnectedCallback();
const popup = this._getPopup();
if (popup) {
popup.parentNode.removeChild(popup);
}
if (document.head.contains(this._css)) {
document.head.removeChild(this._css);
// In order to make an `<a>` element act as a button, setting
// `role="button"` is not enough: https://developer.mozilla.org/en-US
// /docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_button_role
const dismissButton = popup.querySelector('a.cc-btn');
dismissButton.addEventListener('keydown', (event) => {
const SPACE = 32;
const ENTER = 13;
const key = event.keyCode || event.which;
if (key === SPACE || key === ENTER) {
dismissButton.click();
}
});
}
}

Expand Down
140 changes: 129 additions & 11 deletions packages/cookie-consent/test/cookie-consent.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,62 @@ describe('vaadin-cookie-consent', () => {
expect(link.textContent).to.be.equal('Learn more');
expect(link.href).to.be.equal('https://cookiesandyou.com/');
});

it('should have alert role', () => {
const popup = document.querySelector('[aria-label="cookieconsent"]');
expect(popup.getAttribute('role')).to.equal('alert');
});

it('should close window on detach', async () => {
expect(ccWindow.isConnected).to.be.true;
consent.remove();
await aTimeout(50);
expect(ccWindow.isConnected).to.be.false;
});

it('should remove styles on detach', async () => {
expect(consent._css.isConnected).to.be.true;
consent.remove();
await aTimeout(50);
expect(consent._css.isConnected).to.be.false;
});

it('should only have one active window after reattach', async () => {
const parent = consent.parentNode;
consent.remove();
parent.appendChild(consent);

await aTimeout(50);

expect(document.querySelectorAll('.cc-window').length).to.be.equal(1);
});
});

describe('custom texts', () => {
let consent, ccWindow;
describe('custom properties', () => {
let consent;

function getOverlay() {
return document.querySelector('.cc-window');
}

function getOverlayContent() {
const ccWindow = getOverlay();
const message = ccWindow.querySelector('.cc-message');
const dismiss = ccWindow.querySelector('.cc-dismiss');
const link = ccWindow.querySelector('.cc-link');

return {
message,
dismiss,
link,
};
}

async function waitUntilOpened() {
// By default the cookie consent dialog has a 20 ms delay after it
// is initialized and before it starts the fade-in animation.
await aTimeout(50);
}

beforeEach(async () => {
consent = fixtureSync(`
Expand All @@ -108,23 +160,89 @@ describe('vaadin-cookie-consent', () => {
// Force cookie consent to appear.
consent._show();

// By default the cookie consent dialog has a 20 ms delay after it
// is initialized and before it starts the fade-in animation.
await aTimeout(50);

ccWindow = document.querySelector('.cc-window');
await waitUntilOpened();
});

it('should display default text', () => {
const message = ccWindow.querySelector('.cc-message');
const dismiss = ccWindow.querySelector('.cc-dismiss');
const link = ccWindow.querySelector('.cc-link');
it('should display static custom text', () => {
const { message, dismiss, link } = getOverlayContent();

expect(message.textContent).to.be.equal('custom-message custom-learn-more');
expect(dismiss.textContent).to.be.equal('custom-dismiss');
expect(link.textContent).to.be.equal('custom-learn-more');
expect(link.href).to.be.equal('https://example.com/');
});

it('should display dynamically changed text', async () => {
consent.message = 'custom-message2';
consent.dismiss = 'custom-dismiss2';
consent.learnMore = 'custom-learn-more2';
consent.learnMoreLink = 'https://example2.com/';

await waitUntilOpened();

const { message, dismiss, link } = getOverlayContent();

expect(message.textContent).to.be.equal('custom-message2 custom-learn-more2');
expect(dismiss.textContent).to.be.equal('custom-dismiss2');
expect(link.textContent).to.be.equal('custom-learn-more2');
expect(link.href).to.be.equal('https://example2.com/');
});

it('should change position', async () => {
expect(getOverlay().getBoundingClientRect().top).to.be.equal(0);

consent.position = 'bottom';

await waitUntilOpened();

expect(getOverlay().getBoundingClientRect().top).not.to.be.equal(0);
expect(getOverlay().getBoundingClientRect().bottom).to.be.closeTo(window.innerHeight, 1);
});

it('should change cookie name', async () => {
// Change cookie name and dismiss
consent.cookieName = 'custom-cookie-name';
await waitUntilOpened();
getOverlay().querySelector('.cc-dismiss').click();

// Change cookie name
consent.cookieName = 'custom-cookie-name2';
await waitUntilOpened();
// The constent should show
expect(getOverlay().offsetHeight).to.be.greaterThan(0);

// Change the cookie name back to the original one
consent.cookieName = 'custom-cookie-name';
await waitUntilOpened();
// The constent should not show
expect(getOverlay().offsetHeight).to.be.equal(0);
});

it('should only have one active window after change', async () => {
consent.message = 'custom-message2';
consent.dismiss = 'custom-dismiss2';

await waitUntilOpened();

expect(document.querySelectorAll('.cc-window').length).to.be.equal(1);
});

it('should have alert role after change', () => {
consent.message = 'custom-message2';

const popup = document.querySelector('[aria-label="cookieconsent"]');
expect(popup.getAttribute('role')).to.equal('alert');
});

it('should close on Space key after change', async () => {
consent.cookieName = 'custom-cookie-name-foo';
await waitUntilOpened();

const event = keyboardEventFor('keydown', 32, [], ' ');
getOverlay().querySelector('.cc-dismiss').dispatchEvent(event);
await waitUntilOpened();
expect(getOverlay().offsetHeight).to.be.equal(0);
});
});

describe('accessibility', () => {
Expand Down

0 comments on commit 7b0c2bb

Please sign in to comment.