Skip to content

Commit

Permalink
Optional Header and links to tabs in texts (#15)
Browse files Browse the repository at this point in the history
* created a new disableHeader option in the settings
* connect disableHeader settings with header rendering
* updated readme
* created a new option in settings to render "Allow All" button on the details tab
* connect tabDetails.showButtonAllowAll with button rendering
* enable changing tabs by clicking on anchor links in text content areas
* smaller spacing in between paragraphs
* created styles for base links used in texts
* updated gtm script
* updated readme
* updated demo html file
* added allow and reject all functions to anchor links in text content
* removed duplications in tabContentDetails
* fixing high cognitive complexity
* moved button click to props
* refactor overhaul
* more refactoring
* separated sub-elements out of the main class
* fixed disabling header
* simplified cookie creation, updated demo
  • Loading branch information
danielsitek authored Jan 25, 2024
1 parent 2327107 commit 4f26934
Show file tree
Hide file tree
Showing 48 changed files with 1,015 additions and 692 deletions.
179 changes: 104 additions & 75 deletions README.md

Large diffs are not rendered by default.

32 changes: 30 additions & 2 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ <h1>ZOOT Cookie Consent</h1>
<hr>

<button type="button" onclick="window.CookieConsentModalOpen();">Open</button>
<button type="button" onclick="document.cookie = `CookieConsent=;Path=/;Expires=Thu, 01 Jan 1970 00:00:01 GMT;`;">Reset</button>
<button type="button" onclick="document.cookie = `CookieConsent=;Path=/;Expires=Thu, 01 Jan 1970 00:00:01 GMT;`; window.location.reload();">Reset</button>

<hr>

Expand Down Expand Up @@ -150,10 +150,38 @@ <h2>How to use with GTM</h2>

window.CookieConsentThemeDark = {};

window.CookieConsentTranslations = {};
window.CookieConsentTranslations = {
tabAgree: {
body: `
<p><strong>Tato webová stránka používá cookies</strong></p>
<p>Snažíme se náš web neustále vylepšovat. K tomu však potřebujeme data. Sbíráme je pomocí cookies, jež nám pomáhají analyzovat návštěvnost a následně přizpůsobovat obsah a reklamu. Pokud se sběrem cookies nesouhlasíte klikněte <a href="#reject-all">zde</a></p>.</p>
<p><a href="#tab-agree">Tab Souhlas</a>, <a href="#tab-details">Tab Detail</a>, <a href="#tab-about">Tab O Aplikaci</a>, <a href="#allow-all">Povolit všechny cookies</a>, <a href="#reject-all"><strong>Povolit vše</strong></a></p>
`,
},
tabAbout: {
body: `
<p>Cookies jsou malé textové soubory, které mohou být používány webovými stránkami, aby učinily uživatelský zážitek více efektivní.</p>
<p>Zákon uvádí, že můžeme ukládat cookies na vašem zařízení, pokud jsou nezbytně nutné pro provoz této stránky. Pro všechny ostatní typy cookies potřebujeme vaše povolení.</p>
<p>Tato stránka používá různé typy cookies. Některé cookies jsou umístěny službami třetích stran, které se objevují na našich stránkách.</p>
<p>Kdykoliv můžete změnit nebo zrušit svůj souhlas prostřednictvím Vyjádření o souborech cookie na našich webových stránkách.</p>
<p><a href="#tab-agree">Tab Souhlas</a>, <a href="#tab-details">Tab Detail</a>, <a href="#tab-about">Tab O Aplikaci</a>, <a href="#allow-all">Povolit všechny cookies</a>, <a href="#reject-all">Zamítnout cookies</a></p>
`,
},
buttonEdit: {
label: 'Přizpůsobit',
},
buttonConfirm: {
label: 'Uložit',
},
};

window.CookieConsentSettings = {
tabAgree: {
showButtonRejectAll: false,
},
enableDarkMode: true,
disableBadge: true,
disableHeader: true,
};
</script>

Expand Down
113 changes: 20 additions & 93 deletions src/components/consent-badge/consent-badge.ts
Original file line number Diff line number Diff line change
@@ -1,100 +1,27 @@
import {
BADGE_ELEMENT_NAME,
DIALOG_FADE_IN_DURATION,
DIALOG_FADE_OUT_DURATION,
EVENT_CLICK,
INLINE_STYLES_BADGE,
} from '../../config';
import { themeService } from '../../services/theme-service';
import { translationService } from '../../services/translation-service';
import { fadeIn, fadeOut } from '../../utils/animation';
import { createVElement } from '../../utils/elements';
import { dispatchEventBadgeClick, dispatchEventBadgeHide, dispatchEventBadgeShow } from '../../utils/events';
import { translationService } from '@/services/translation-service';
import { createVElement } from '@/utils/elements';

const i18n = translationService();

const svgIcon = `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 32 32" aria-hidden="true">
<path fill="currentColor" fill-rule="evenodd" d="M19.732 12.97a9.002 9.002 0 0 1-9.074-5.589A10.999 10.999 0 0 0 5 17c0 6.075 4.925 11 11 11s11-4.925 11-11c0-.357-.017-.709-.05-1.056a7.986 7.986 0 0 1-7.219-2.973Zm.957-2.175a7.002 7.002 0 0 1-8.662-6.176A13 13 0 0 0 3 17c0 7.179 5.82 12.999 13 12.999s13-5.82 13-13a13.126 13.126 0 0 0-.49-3.549A5.961 5.961 0 0 1 26 14a6 6 0 0 1-5.31-3.205Z" clip-rule="evenodd"/>
<path fill="currentColor" d="M14 21.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Zm4-4.5a1 1 0 1 1-2 0 1 1 0 0 1 2 0Zm5 1a1 1 0 1 1-2 0 1 1 0 0 1 2 0Zm3-8a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"/>
<path fill="currentColor" d="M13 16a2 2 0 1 1-4 0 2 2 0 0 1 4 0Zm8 6.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Zm-1-18a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0ZM28 6a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z" opacity=".5"/>
</svg>`;

let hideTimeout: ReturnType<typeof setTimeout>;

const i18n = translationService();

export class ConsentBadge extends HTMLElement {
private mainElement: HTMLElement;

private shadow: ShadowRoot;

constructor() {
super();

this.shadow = this.attachShadow({ mode: 'closed' });

this.mainElement = createVElement<HTMLElement>(
'div',
{ class: 'cb t' },
createVElement<HTMLButtonElement>(
'button',
{
class: 'cb__b',
type: 'button',
'aria-label': i18n.badge.label,
title: i18n.badge.label,
},
createVElement<HTMLElement>('span', { class: 'cb__i' }, svgIcon),
),
);

this.mainElement.addEventListener(EVENT_CLICK, () => {
dispatchEventBadgeClick();
this.hideBadge();
});
}

appendCode(): void {
this.shadow.appendChild(createVElement<HTMLStyleElement>('style', {}, INLINE_STYLES_BADGE));
this.shadow.appendChild(createVElement<HTMLStyleElement>('style', {}, themeService().themeTextContent));
this.shadow.appendChild(this.mainElement);
}

hideBadge(): void {
clearTimeout(hideTimeout);
if (document.querySelector(BADGE_ELEMENT_NAME) === null) {
return;
}

hideTimeout = setTimeout(async () => {
if (document.querySelector(BADGE_ELEMENT_NAME) === null) {
return;
}
await fadeOut(this.mainElement, DIALOG_FADE_OUT_DURATION);
const element = document.querySelector(BADGE_ELEMENT_NAME);
element?.remove();
}, 100);
}

/**
* Connected Lifecycle Callback
*
* @link <https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements#using_the_lifecycle_callbacks>
*/
async connectedCallback(): Promise<void> {
this.appendCode();

dispatchEventBadgeShow();

await fadeIn(this.mainElement, DIALOG_FADE_IN_DURATION);
}

/**
* Disconnected Lifecycle Callback.
*
* @link <https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements#using_the_lifecycle_callbacks>
*/
disconnectedCallback(): void {
dispatchEventBadgeHide();
}
}

customElements.define(BADGE_ELEMENT_NAME, ConsentBadge);
export const consentBadge = (): HTMLDivElement => {
return createVElement<HTMLDivElement>(
'div',
{ class: 'cb t' },
createVElement<HTMLButtonElement>(
'button',
{
class: 'cb__b',
type: 'button',
'aria-label': i18n.badge.label,
title: i18n.badge.label,
},
createVElement<HTMLElement>('span', { class: 'cb__i' }, svgIcon),
),
);
};
8 changes: 4 additions & 4 deletions src/components/consent-button-close/consent-button-close.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { DIALOG_ELEMENT_NAME, EVENT_CLICK, EVENT_KEYDOWN } from '../../config';
import { translationService } from '../../services/translation-service';
import { createVElement } from '../../utils/elements';
import { dispatchEventConsentClose } from '../../utils/events';
import { DIALOG_ELEMENT_NAME, EVENT_CLICK, EVENT_KEYDOWN } from '@/config';
import { translationService } from '@/services/translation-service';
import { createVElement } from '@/utils/elements';
import { dispatchEventConsentClose } from '@/utils/events';

interface ConsentModalElement extends HTMLElement {
closeModal: () => void;
Expand Down
42 changes: 32 additions & 10 deletions src/components/consent-button/consent-button.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,46 @@
import { componentClassList, ComponentProps } from '../../utils/component-helpers';
import { createElement } from '../../utils/elements';
import { EVENT_CLICK } from '@/config';
import { componentClassList, ComponentProps } from '@/utils/component-helpers';
import { createVElement } from '@/utils/elements';

interface ConsentButtonProps extends ComponentProps {
label: string;
variant?: 'd' | 'p';
onClick?: () => void;
}

export const BUTTON_DEFAULT = 'd';
export const BUTTON_PRIMARY = 'p';

export const consentButton = (props: ConsentButtonProps): HTMLButtonElement => {
const element = createElement('button', componentClassList(
[
'c-b',
props.variant ? `c-b--${props.variant}` : '',
],
props.modifier
)) as HTMLButtonElement;
const element = createVElement<HTMLButtonElement>(
'button',
{
class: componentClassList(
['c-b', props.variant ? `c-b--${props.variant}` : `c-b--${BUTTON_DEFAULT}`],
props.modifier,
).join(' '),
},
createVElement(
'span',
{
class: 'c-b__i',
},
props.label,
),
);

element.innerHTML = `<span class="c-b__i">${props.label}</span>`;
element.addEventListener(EVENT_CLICK, () => {
if (props.onClick !== undefined && typeof props.onClick === 'function') {
props.onClick();
}
});

return element;
};

export const consentButtonPrimary = (props: ConsentButtonProps): HTMLButtonElement => {
return consentButton({
...props,
variant: BUTTON_PRIMARY,
});
};
27 changes: 9 additions & 18 deletions src/components/consent-dialog-footer/consent-dialog-footer.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,16 @@
import { componentClassList, ComponentProps } from '../../utils/component-helpers';
import { createDivElement } from '../../utils/elements';
import { componentClassList, ComponentProps } from '@/utils/component-helpers';
import { createVElement } from '@/utils/elements';

interface ConsentTabProps extends ComponentProps {
buttons: Array<HTMLButtonElement | boolean>;
}

export const consentDialogFooter = (props: ConsentTabProps): HTMLDivElement => {
const element = createDivElement(componentClassList(
[
'c-d__f',
],
props.modifier
));

props.buttons.filter(e => e).forEach((buttonElement) => {
if (typeof buttonElement === 'boolean') {
return;
}

element.appendChild(buttonElement);
});

return element;
return createVElement<HTMLDivElement>(
'div',
{
class: componentClassList(['c-d__f'], props.modifier).join(' '),
},
...props.buttons,
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { consentDialogInner } from './consent-dialog-inner';

export const consentDialogInnerInstance = consentDialogInner();
5 changes: 5 additions & 0 deletions src/components/consent-dialog-inner/consent-dialog-inner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { createVElement } from '@/utils/elements';

export const consentDialogInner = (): HTMLDivElement => {
return createVElement('div', { class: 'c-d__i' });
};
3 changes: 3 additions & 0 deletions src/components/consent-dialog/consent-dialog-instances.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { consentDialog } from './consent-dialog';

export const consentDialogInstance = consentDialog();
Loading

0 comments on commit 4f26934

Please sign in to comment.