Skip to content

Commit

Permalink
+ Tracking banner, without Matomo
Browse files Browse the repository at this point in the history
  • Loading branch information
emi420 committed Oct 10, 2024
1 parent 48d35ce commit 2bb285f
Show file tree
Hide file tree
Showing 9 changed files with 355 additions and 101 deletions.
4 changes: 4 additions & 0 deletions .storybook/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,8 @@

.docblock-typeset > div > div:last-child {
width: 75%;
}

a {
color: var(--hot-color-primary-700)
}
31 changes: 19 additions & 12 deletions .storybook/stories/tracking.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,6 @@ export const Tracking: StoryObj = {
type: "select",
},
},
domain: {
options: ["localhost", "anotherdomain"],
control: {
type: "select",
},
},
},
parameters: {
showAgreeToast: () => {
Expand All @@ -43,10 +37,10 @@ export const Tracking: StoryObj = {
}
},
addKeyLocalStorage: (siteId: number) => {
localStorage.setItem(`${siteId}-matomo-agree`, 'true');
localStorage.setItem(`${siteId}-tracking-agree`, 'true');
},
removeKeyLocalStorage: (siteId: number) => {
localStorage.removeItem(`${siteId}-matomo-agree`);
localStorage.removeItem(`${siteId}-tracking-agree`);
},
},
render: (args, { parameters }) => {
Expand All @@ -57,18 +51,31 @@ export const Tracking: StoryObj = {
<sl-button @click=${() => {
parameters.addKeyLocalStorage(args.siteId)
}}>Disable Banner</sl-button>
<br /><br />
<hot-tracking
id="matomo-banner"
site-id="${args.siteId}"
domain="${args.domain}"
id="tracking-banner"
site-id=${args.siteId}
agree-label="Yes, I accept"
not-agree-label="I DO NOT accept"
title=${"What info we collect about you?"}
@agree=${() => {
parameters.showAgreeToast()
}}
@disagree=${() => {
parameters.showDisagreeToast()
}}
></hot-tracking>
>
We use cookies and similar technologies to recognize and analyze your
visits, and measure traffic usage and activity. You can learn about how
we use the data about your visit or information you provide reading our
<a
href="https://www.hotosm.org/privacy"
target="_blank"
rel="noopener noreferrer"
>privacy policy</a
>. By clicking "I Agree", you consent to the use of cookies.
</hot-tracking>
<sl-alert id="agree-toast" variant="success" duration="3000" closable>
<sl-icon slot="icon" name="check2-circle"></sl-icon>
Expand Down
84 changes: 84 additions & 0 deletions .storybook/stories/trackingMatomo.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import type { Meta, StoryObj } from "@storybook/web-components";
import '@shoelace-style/shoelace/dist/components/alert/alert.js';
import { html } from "lit";

import "../../src/hotosm-ui";

const meta: Meta = {
title: "Tracking",
component: "hot-tracking",
};
export default meta;

export const TrackingMatomo: StoryObj = {
args: {
siteId: "1",
domain: "localhost",
},
argTypes: {
siteId: {
options: ["", "1", "2"],
control: {
type: "select",
},
},
domain: {
options: ["localhost", "anotherdomain"],
control: {
type: "select",
},
},
},
parameters: {
showAgreeToast: () => {
const agree = document.getElementById("agree-toast");
if (agree) {
agree.toast();
}
},
showDisagreeToast: () => {
const disagree = document.getElementById("disagree-toast");
if (disagree) {
disagree.toast();
}
},
addKeyLocalStorage: (siteId: number) => {
localStorage.setItem(`${siteId}-matomo-agree`, 'true');
},
removeKeyLocalStorage: (siteId: number) => {
localStorage.removeItem(`${siteId}-matomo-agree`);
},
},
render: (args, { parameters }) => {
return html`
<sl-button @click=${() => {
parameters.removeKeyLocalStorage(args.siteId)
}}>Re-Enable Banner</sl-button>
<sl-button @click=${() => {
parameters.addKeyLocalStorage(args.siteId)
}}>Disable Banner</sl-button>
<br /><br />
<hot-matomo-tracking
id="matomo-banner"
site-id="${args.siteId}"
domain="${args.domain}"
@agree=${() => {
parameters.showAgreeToast()
}}
@disagree=${() => {
parameters.showDisagreeToast()
}}
></hot-matomo-tracking>
<sl-alert id="agree-toast" variant="success" duration="3000" closable>
<sl-icon slot="icon" name="check2-circle"></sl-icon>
You clicked agree.
</sl-alert>
<sl-alert id="disagree-toast" variant="danger" duration="3000" closable>
<sl-icon slot="icon" name="exclamation-octagon"></sl-icon>
You clicked disagree.
</sl-alert>
`;
},
};
175 changes: 175 additions & 0 deletions src/components/matomoTracking/matomoTracking.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import "@shoelace-style/shoelace/dist/components/alert/alert.js";

import { LitElement, html } from "lit";
import { property } from "lit/decorators.js";
import styles from './matomoTracking.styles.js';
import type { CSSResultGroup } from 'lit';

import registerBundledIcons from "../icons.js";

registerBundledIcons();

declare global {
interface Window {
_paq: any[];
}
}

export class MatomoTracking extends LitElement {

static styles: CSSResultGroup = [styles];

name = "hot-matomo-tracking";

/** The Matomo site id for tracking. */
@property({ type: String, attribute: "site-id" })
accessor siteId: string = "";

/** The domains to apply tracking. */
@property({ type: String })
accessor domain: string = "";

/** Force display the banner. */
@property({ type: Boolean })
accessor force: boolean = false;

@property({ type: Boolean })
accessor isOpen: boolean = true;

protected render() {
return html`<sl-alert class="matomoTracking" variant="danger" ?open=${this.isOpen}>
<sl-icon
id="hot-red-text"
library="bundled"
slot="icon"
name="info-circle"
></sl-icon>
<p id="matomoTracking-header" class="matomoTracking--header">About the information we collect</p>
<p>
We use cookies and similar technologies to recognize and analyze your
visits, and measure traffic usage and activity. You can learn about how
we use the data about your visit or information you provide reading our
<a
class="matomoTracking--link"
href="https://www.hotosm.org/privacy"
target="_blank"
rel="noopener noreferrer"
>privacy policy</a
>. By clicking "I Agree", you consent to the use of cookies.
</p>
<sl-button
@click=${(e: MouseEvent) => {
this._setAgree(e);
}}
>I Agree</sl-button
>
<sl-button
@click=${(e: MouseEvent) => {
this._setDisagree(e);
}}
>I Do Not Agree</sl-button
>
</sl-alert>`;
}

private _setAgree(_e: MouseEvent) {
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
const _paq = (window._paq = window._paq || []);
if (_paq.length === 0) return;

_paq.push(["rememberConsentGiven"]);
this.isOpen = false;
localStorage.setItem(`${this.siteId}-matomo-agree`, "true");
this.dispatchEvent(new Event("agree", { bubbles: true, composed: true }));
}

private _setDisagree(_e: MouseEvent) {
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
const _paq = (window._paq = window._paq || []);
if (_paq.length === 0) return;

_paq.push(["forgetConsentGiven"]);
this.isOpen = false;
localStorage.setItem(`${this.siteId}-matomo-agree`, "false");
this.dispatchEvent(
new Event("disagree", { bubbles: true, composed: true })
);
}

connectedCallback() {
super.connectedCallback();

// Close and halt execution if wrong domain
if (!this.force && window.location.hostname !== this.domain) {
console.warn(
`Matomo init failed. ${window.location.hostname} does not match ${this.domain}.`
);
this.isOpen = false;
return;
}

const matomoTrackingId = this.siteId;

// Close and halt execution if siteId or domain not set
if (
!this.force &&
(matomoTrackingId.length === 0 || this.domain.length === 0)
) {
console.warn("Matomo init failed. No site id or domains provided.");
this.isOpen = false;
return;
}

// Close and halt execution if already disagreed
const consent = localStorage.getItem(`${this.siteId}-matomo-agree`);
if (consent === "false") {
this.isOpen = false;
return;
}

// Close prompt only if already agreed, continue
if (consent === "true") {
this.isOpen = false;
}

console.log(
`Setting Matomo tracking for site=${matomoTrackingId} domain=${this.domain}`
);

// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
const _paq = (window._paq = window._paq || []);

// tracker methods like "setCustomDimension" should be called before "trackPageView"
_paq.push(["requireConsent"]);
_paq.push(["setDomains", [this.domain]]);
_paq.push(["trackPageView"]);
_paq.push(["enableLinkTracking"]); // Tracks downloads
_paq.push(["trackVisibleContentImpressions"]); // Tracks content

(function () {
const u = "//matomo.hotosm.org/";
_paq.push(["setTrackerUrl", u + "matomo.php"]);
_paq.push(["setSiteId", matomoTrackingId]);

const d = document;
const g = d.createElement("script");
const s = d.getElementsByTagName("script")[0];

if (s?.parentNode != null) {
g.async = true;
g.src = u + "matomo.js";
s.parentNode.insertBefore(g, s);
} else {
console.warn("Script insertion failed. Parent node is null.");
}
})();
}
}

export default MatomoTracking;

// Define web component
customElements.define("hot-matomo-tracking", MatomoTracking);
23 changes: 23 additions & 0 deletions src/components/matomoTracking/matomoTracking.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { css } from 'lit';

export default css`
.matomoTracking {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
z-index: 1000;
width: 80vw;
text-align: center;
}
.matomoTracking--header {
font-weight: var(--hot-font-weight-bold);
font-size: var(--hot-font-size-large);
text-align: center;
}
.matomoTracking--link {
color: var(--hot-color-primary-700);
}
`
11 changes: 11 additions & 0 deletions src/components/matomoTracking/matomoTracking.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Tracking from './matomoTracking.component.js';

export * from './matomoTracking.component.js';
export default Tracking;

declare global {
interface HTMLElementTagNameMap {
'hot-tracking': Tracking;
}
}

Loading

0 comments on commit 2bb285f

Please sign in to comment.