Adapt cookie banner UI and texts per regulation

The server now resolves regulation-specific translations
(opt-out notice for CCPA, simple notice when no regulation
applies) and remaps text keys before returning the config.
The client hides buttons whose text is empty, so the banner
layout adapts without client-side consent-mode logic.

Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
Émile Ré
2026-05-07 09:50:54 +04:00
parent 4598506076
commit b4bb8715ee
6 changed files with 107 additions and 17 deletions

View File

@@ -13,7 +13,7 @@
// PERFORMANCE OF THIS SOFTWARE.
import type { CookieBannerClient } from "../client";
import type { BannerConfig } from "../types";
import type { BannerConfig, Regulation } from "../types";
export type ProboState = "loading" | "banner" | "panel" | "hidden";
@@ -68,6 +68,8 @@ export interface ProboRootElement extends ProboElement {
readonly reopenWidget: string;
readonly consentDraft: ConsentDraft;
readonly gpcApplied: boolean;
readonly regulation: Regulation | null;
readonly consentMode: "OPT_IN" | "OPT_OUT" | null;
setState(state: ProboState): void;
updateDraft(category: string, value: boolean): void;
}

View File

@@ -13,7 +13,7 @@
// PERFORMANCE OF THIS SOFTWARE.
import { CookieBannerClient } from "../client";
import type { BannerConfig } from "../types";
import type { BannerConfig, Regulation } from "../types";
import { ProboElement } from "./base";
import type { ProboState, ProboRootElement, ConsentDraft } from "./base";
@@ -57,6 +57,16 @@ export class ProboCookieBannerRoot extends ProboElement implements ProboRootElem
return this._client?.gpcApplied ?? false;
}
get regulation(): Regulation | null {
return this._client?.regulation ?? null;
}
get consentMode(): "OPT_IN" | "OPT_OUT" | null {
const mode = this._config?.consent_mode;
if (mode === "OPT_IN" || mode === "OPT_OUT") return mode;
return null;
}
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {
if (name === "reopen-widget" && oldValue !== newValue) {
this.dispatchEvent(

View File

@@ -130,6 +130,7 @@ export class ProboThemedBanner extends HTMLElement {
const detail = (e as CustomEvent).detail;
const config = detail.config as BannerConfig;
this.applyTexts(config);
this.applyLayout(config);
if (!config.show_branding) {
this.shadow.querySelectorAll("[data-branding]").forEach(el => {
(el as HTMLElement).setAttribute("hidden", "");
@@ -224,6 +225,20 @@ export class ProboThemedBanner extends HTMLElement {
}
}
private applyLayout(config: BannerConfig): void {
const texts = config.texts ?? {};
const hideIfEmpty = (selector: string, textKey: string): void => {
if (texts[textKey]) return;
this.shadow.querySelectorAll(selector).forEach(el => {
(el as HTMLElement).hidden = true;
});
};
hideIfEmpty("probo-banner probo-reject-button", "button_reject_all");
hideIfEmpty("probo-banner probo-customize-button", "button_customize");
}
private esc(str: string): string {
return str.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
}