Show tracker type in cookie banner trackers list
The themed banner and headless cookie list exposed only the tracker name, description, and duration, so trackers that share a name but differ in technology were indistinguishable to visitors. Add the tracker type to the headless CookieItem model and render it as a labeled "Type: <value>" detail under the tracker name, with an explicit aria-label for assistive technology. Tracker type names are Web platform API names (Cookie, Local storage, IndexedDB, ...), so they are kept canonical while only the surrounding label is localized. Signed-off-by: Émile Ré <emile@probo.com>
This commit is contained in:
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
import type { CookieItem } from "../types";
|
import type { CookieItem } from "../types";
|
||||||
import { humanizeDuration } from "../cookie-utils";
|
import { humanizeDuration } from "../cookie-utils";
|
||||||
import { getCookieDetailLabels } from "../i18n";
|
import { getCookieDetailLabels, getTrackerTypeLabel, interpolate } from "../i18n";
|
||||||
import { ProboElement } from "./base";
|
import { ProboElement } from "./base";
|
||||||
import type { ProboCategory } from "./category";
|
import type { ProboCategory } from "./category";
|
||||||
import type { ProboCookieBannerRoot } from "./cookie-banner-root";
|
import type { ProboCookieBannerRoot } from "./cookie-banner-root";
|
||||||
@@ -55,19 +55,28 @@ export class ProboCookieList extends ProboElement {
|
|||||||
? humanizeDuration(cookie.max_age_seconds, lang)
|
? humanizeDuration(cookie.max_age_seconds, lang)
|
||||||
: humanizeDuration(0, lang);
|
: humanizeDuration(0, lang);
|
||||||
|
|
||||||
|
const type = getTrackerTypeLabel(cookie.tracker_type);
|
||||||
|
|
||||||
const wrapper = document.createElement("probo-cookie");
|
const wrapper = document.createElement("probo-cookie");
|
||||||
wrapper.setAttribute("name", cookie.name);
|
wrapper.setAttribute("name", cookie.name);
|
||||||
const clone = this.template.content.cloneNode(true) as DocumentFragment;
|
const clone = this.template.content.cloneNode(true) as DocumentFragment;
|
||||||
this.fillSlots(clone, {
|
this.fillSlots(clone, {
|
||||||
name: cookie.name,
|
name: cookie.name,
|
||||||
|
type,
|
||||||
duration,
|
duration,
|
||||||
description: cookie.description,
|
description: cookie.description,
|
||||||
});
|
});
|
||||||
this.fillLabels(clone, labels, {
|
this.fillLabels(clone, labels, {
|
||||||
|
type,
|
||||||
description: cookie.description,
|
description: cookie.description,
|
||||||
duration,
|
duration,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const typeEl = clone.querySelector<HTMLElement>(".cookie-type");
|
||||||
|
if (typeEl && labels.label_type) {
|
||||||
|
typeEl.setAttribute("aria-label", interpolate(labels.label_type, { value: type }));
|
||||||
|
}
|
||||||
|
|
||||||
wrapper.appendChild(clone);
|
wrapper.appendChild(clone);
|
||||||
this.appendChild(wrapper);
|
this.appendChild(wrapper);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,16 +43,30 @@ export function interpolate(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const COOKIE_DETAIL_LABELS: Record<string, Record<string, string>> = {
|
const COOKIE_DETAIL_LABELS: Record<string, Record<string, string>> = {
|
||||||
en: { label_description: "Description: {{value}}", label_duration: "Duration: {{value}}" },
|
en: { label_type: "Type: {{value}}", label_description: "Description: {{value}}", label_duration: "Duration: {{value}}" },
|
||||||
fr: { label_description: "Description : {{value}}", label_duration: "Durée : {{value}}" },
|
fr: { label_type: "Type : {{value}}", label_description: "Description : {{value}}", label_duration: "Durée : {{value}}" },
|
||||||
de: { label_description: "Beschreibung: {{value}}", label_duration: "Dauer: {{value}}" },
|
de: { label_type: "Typ: {{value}}", label_description: "Beschreibung: {{value}}", label_duration: "Dauer: {{value}}" },
|
||||||
es: { label_description: "Descripción: {{value}}", label_duration: "Duración: {{value}}" },
|
es: { label_type: "Tipo: {{value}}", label_description: "Descripción: {{value}}", label_duration: "Duración: {{value}}" },
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getCookieDetailLabels(lang: string): Record<string, string> {
|
export function getCookieDetailLabels(lang: string): Record<string, string> {
|
||||||
return COOKIE_DETAIL_LABELS[lang] ?? COOKIE_DETAIL_LABELS.en;
|
return COOKIE_DETAIL_LABELS[lang] ?? COOKIE_DETAIL_LABELS.en;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tracker type names are Web platform API names (proper nouns), so they are
|
||||||
|
// not translated; only the surrounding "Type:" label is localized.
|
||||||
|
const TRACKER_TYPE_LABELS: Record<string, string> = {
|
||||||
|
COOKIE: "Cookie",
|
||||||
|
LOCAL_STORAGE: "Local storage",
|
||||||
|
SESSION_STORAGE: "Session storage",
|
||||||
|
INDEXED_DB: "IndexedDB",
|
||||||
|
CACHE_STORAGE: "Cache storage",
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getTrackerTypeLabel(type: string): string {
|
||||||
|
return TRACKER_TYPE_LABELS[type] ?? type;
|
||||||
|
}
|
||||||
|
|
||||||
const GPC_LABELS: Record<string, string> = {
|
const GPC_LABELS: Record<string, string> = {
|
||||||
en: "Opt-Out Preference Signal Honored",
|
en: "Opt-Out Preference Signal Honored",
|
||||||
fr: "Signal de préférence de désinscription respecté",
|
fr: "Signal de préférence de désinscription respecté",
|
||||||
|
|||||||
@@ -353,6 +353,10 @@ export const THEMED_STYLES = `
|
|||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cookie-type {
|
||||||
|
font-size: calc(var(--_font-size) - 3px);
|
||||||
|
}
|
||||||
|
|
||||||
.cookie-detail {
|
.cookie-detail {
|
||||||
color: var(--_text);
|
color: var(--_text);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
|||||||
@@ -99,6 +99,7 @@ export class ProboThemedBanner extends HTMLElement {
|
|||||||
<template>
|
<template>
|
||||||
<div class="cookie-item">
|
<div class="cookie-item">
|
||||||
<span class="cookie-name" data-slot="name"></span>
|
<span class="cookie-name" data-slot="name"></span>
|
||||||
|
<span class="cookie-detail cookie-type" data-label="label_type"><span data-slot="type"></span></span>
|
||||||
<span class="cookie-detail" data-label="label_description"><span data-slot="description"></span></span>
|
<span class="cookie-detail" data-label="label_description"><span data-slot="description"></span></span>
|
||||||
<span class="cookie-detail" data-label="label_duration"><span data-slot="duration"></span></span>
|
<span class="cookie-detail" data-label="label_duration"><span data-slot="duration"></span></span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -14,8 +14,16 @@
|
|||||||
|
|
||||||
import type { BannerTexts } from "./i18n";
|
import type { BannerTexts } from "./i18n";
|
||||||
|
|
||||||
|
export type TrackerType =
|
||||||
|
| "COOKIE"
|
||||||
|
| "LOCAL_STORAGE"
|
||||||
|
| "SESSION_STORAGE"
|
||||||
|
| "INDEXED_DB"
|
||||||
|
| "CACHE_STORAGE";
|
||||||
|
|
||||||
export interface CookieItem {
|
export interface CookieItem {
|
||||||
name: string;
|
name: string;
|
||||||
|
tracker_type: TrackerType;
|
||||||
max_age_seconds: number | null;
|
max_age_seconds: number | null;
|
||||||
description: string;
|
description: string;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user