diff --git a/docs/.vitepress/env.d.ts b/docs/.vitepress/env.d.ts index 790070d89..ebf0aff5f 100644 --- a/docs/.vitepress/env.d.ts +++ b/docs/.vitepress/env.d.ts @@ -1,2 +1,7 @@ declare const __EDIT_REPO__: string; declare const __EDIT_BRANCH__: string; + +interface Window { + dataLayer: any[]; + gtag: (...args: any[]) => void; +} diff --git a/docs/.vitepress/theme/Layout.vue b/docs/.vitepress/theme/Layout.vue index c79c8353a..664315f5b 100644 --- a/docs/.vitepress/theme/Layout.vue +++ b/docs/.vitepress/theme/Layout.vue @@ -2,6 +2,7 @@ import { useData } from 'vitepress' import DefaultTheme from 'vitepress/theme' import NotFound from './NotFound.vue' +import CookieConsent from './components/CookieConsent.vue' const { Layout } = DefaultTheme const { frontmatter } = useData() @@ -18,5 +19,8 @@ const { frontmatter } = useData() + + + diff --git a/docs/.vitepress/theme/components/CookieConsent.vue b/docs/.vitepress/theme/components/CookieConsent.vue new file mode 100644 index 000000000..f6a06ab70 --- /dev/null +++ b/docs/.vitepress/theme/components/CookieConsent.vue @@ -0,0 +1,209 @@ + + + + + + + + We use cookies and Google Analytics to understand site usage and improve your experience. + + + Decline + Accept + + + + + + + diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts index 7529d6753..bed8b86d3 100644 --- a/docs/.vitepress/theme/index.ts +++ b/docs/.vitepress/theme/index.ts @@ -1,10 +1,14 @@ +import { nextTick } from 'vue' import DefaultTheme from 'vitepress/theme' import Layout from './Layout.vue' import NotFound from './NotFound.vue' import ImageMap from './components/ImageMap.vue' import StubNotice from './components/StubNotice.vue' +import CookieConsent from './components/CookieConsent.vue' import './style.css' +const GA_MEASUREMENT_ID = 'G-LL4RX9KM6Q' + export default { extends: DefaultTheme, Layout: Layout, @@ -12,6 +16,7 @@ export default { app.component('ImageMap', ImageMap) app.component('NotFound', NotFound) app.component('StubNotice', StubNotice) + app.component('CookieConsent', CookieConsent) // Handle 404 redirection from public/404.html shim if (typeof window !== 'undefined') { @@ -25,6 +30,22 @@ export default { router.go(redirectedPath) } } + + // Track page views on route change in SPA mode + if (router) { + router.onAfterRouteChange = (to) => { + nextTick(() => { + if (typeof window.gtag === 'function') { + window.gtag('event', 'page_view', { + page_title: document.title, + page_location: window.location.href, + page_path: to, + send_to: GA_MEASUREMENT_ID, + }) + } + }) + } + } } } } diff --git a/package-lock.json b/package-lock.json index ba199fb3b..8c672528b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -804,53 +804,6 @@ "node": ">=20" } }, - "node_modules/@playwright/test/node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/@playwright/test/node_modules/playwright": { - "version": "1.61.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.61.1.tgz", - "integrity": "sha512-DWnY5o3YbLWK4GovuAVwpqL+1VwGNdUGrRr++8j8PtQQzvAVZUIMjKQ90fY689sEJZJBbZVw1rXaOKSTitkzPQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "playwright-core": "1.61.1" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "fsevents": "2.3.2" - } - }, - "node_modules/@playwright/test/node_modules/playwright-core": { - "version": "1.61.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.61.1.tgz", - "integrity": "sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "playwright-core": "cli.js" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/@rolldown/pluginutils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", diff --git a/tests/consent.spec.ts b/tests/consent.spec.ts new file mode 100644 index 000000000..0617c6a3c --- /dev/null +++ b/tests/consent.spec.ts @@ -0,0 +1,34 @@ +import { test, expect } from '@playwright/test' + +test.describe('Cookie Consent Banner', () => { + test('displays banner on desktop and allows accepting', async ({ page }) => { + await page.goto('http://localhost:3000/wiki/') + const banner = page.locator('.cookie-consent-banner') + await expect(banner).toBeVisible() + + const acceptBtn = page.locator('.cookie-btn-accept') + await expect(acceptBtn).toBeVisible() + await acceptBtn.click() + + await expect(banner).not.toBeVisible() + + const consent = await page.evaluate(() => localStorage.getItem('mume_cookie_consent')) + expect(consent).toBe('granted') + }) + + test('displays correctly on mobile viewport', async ({ page }) => { + await page.setViewportSize({ width: 375, height: 667 }) + await page.goto('http://localhost:3000/wiki/') + const banner = page.locator('.cookie-consent-banner') + await expect(banner).toBeVisible() + + const declineBtn = page.locator('.cookie-btn-decline') + await expect(declineBtn).toBeVisible() + await declineBtn.click() + + await expect(banner).not.toBeVisible() + + const consent = await page.evaluate(() => localStorage.getItem('mume_cookie_consent')) + expect(consent).toBe('denied') + }) +})
+ We use cookies and Google Analytics to understand site usage and improve your experience. +