From a846be930c6559c22500f10904fb423abfe2fd1a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?F=C3=A9lix=20Girault?=
Date: Tue, 2 Jun 2026 11:05:50 +0200
Subject: [PATCH] Made privacy policy link attributes configurable
---
.../components/ContextualNoticeContainer.tsx | 1 +
src/ui/components/Main.tsx | 6 ++++
src/ui/components/PrivacyPolicyLink.tsx | 33 +++++++++++++++++++
src/ui/components/types/Banner.ts | 3 +-
src/ui/components/types/ContextualNotice.ts | 3 +-
src/ui/components/types/Modal.ts | 7 +++-
src/ui/themes/dsfr/Banner.tsx | 11 +++++--
src/ui/themes/dsfr/Modal.tsx | 12 ++++---
src/ui/themes/standard/Banner.tsx | 10 +++---
src/ui/themes/standard/ContextualNotice.tsx | 13 +++++---
src/ui/themes/standard/Modal.tsx | 16 ++++-----
src/ui/types.ts | 2 ++
12 files changed, 90 insertions(+), 27 deletions(-)
create mode 100644 src/ui/components/PrivacyPolicyLink.tsx
diff --git a/src/ui/components/ContextualNoticeContainer.tsx b/src/ui/components/ContextualNoticeContainer.tsx
index b04a231f..1578e7f3 100644
--- a/src/ui/components/ContextualNoticeContainer.tsx
+++ b/src/ui/components/ContextualNoticeContainer.tsx
@@ -47,6 +47,7 @@ const ContextualNoticeContainer = ({
purpose={purpose}
data={data}
privacyPolicyUrl={config.privacyPolicyUrl}
+ privacyPolicyLinkAttributes={config.privacyPolicyLinkAttributes}
onAccept={() => {
manager.setConsent(purpose.id, true);
setIsBeingDisabled(true);
diff --git a/src/ui/components/Main.tsx b/src/ui/components/Main.tsx
index 92f67a70..bb7bce4b 100644
--- a/src/ui/components/Main.tsx
+++ b/src/ui/components/Main.tsx
@@ -52,6 +52,9 @@ const Main = ({apiRef}: MainProps) => {
needsUpdate={manager.needsUpdate()}
purposeTitles={config.purposes.map(({title}) => title)}
privacyPolicyUrl={config.privacyPolicyUrl}
+ privacyPolicyLinkAttributes={
+ config.privacyPolicyLinkAttributes
+ }
logo={config.logo}
onConfigure={openModal}
onAccept={() => {
@@ -74,6 +77,9 @@ const Main = ({apiRef}: MainProps) => {
isForced={config.forceModal && manager.isDirty()}
needsUpdate={manager.needsUpdate()}
privacyPolicyUrl={config.privacyPolicyUrl}
+ privacyPolicyLinkAttributes={
+ config.privacyPolicyLinkAttributes
+ }
onClose={closeModal}
onSave={commit}
>
diff --git a/src/ui/components/PrivacyPolicyLink.tsx b/src/ui/components/PrivacyPolicyLink.tsx
new file mode 100644
index 00000000..8a74af5a
--- /dev/null
+++ b/src/ui/components/PrivacyPolicyLink.tsx
@@ -0,0 +1,33 @@
+import {AnchorHTMLAttributes} from 'preact';
+import {useTranslations} from '../utils/hooks';
+
+interface PrivacyPolicyLinkProps extends AnchorHTMLAttributes {
+ label: string;
+ onExit?: () => void;
+}
+
+const PrivacyPolicyLink = ({
+ label,
+ onExit,
+ ...props
+}: PrivacyPolicyLinkProps) => {
+ const t = useTranslations();
+ const isBlank = props?.target === '_blank';
+ const title = isBlank ? `${label} (${t.misc.newWindowTitle})` : null;
+
+ return (
+ {
+ if (onExit && !isBlank) {
+ onExit();
+ }
+ }}
+ >
+ {label}
+
+ );
+};
+
+export default PrivacyPolicyLink;
diff --git a/src/ui/components/types/Banner.ts b/src/ui/components/types/Banner.ts
index 831b2ef3..ced91eae 100644
--- a/src/ui/components/types/Banner.ts
+++ b/src/ui/components/types/Banner.ts
@@ -1,4 +1,4 @@
-import {FunctionComponent} from 'preact';
+import {AnchorHTMLAttributes, FunctionComponent} from 'preact';
import {ImageDescriptor} from '../../types';
export interface BannerProps {
@@ -6,6 +6,7 @@ export interface BannerProps {
needsUpdate: boolean;
purposeTitles: string[];
privacyPolicyUrl: string;
+ privacyPolicyLinkAttributes?: AnchorHTMLAttributes;
logo?: ImageDescriptor;
onAccept: () => void;
onDecline: () => void;
diff --git a/src/ui/components/types/ContextualNotice.ts b/src/ui/components/types/ContextualNotice.ts
index 8cdfe0fe..6f06a72a 100644
--- a/src/ui/components/types/ContextualNotice.ts
+++ b/src/ui/components/types/ContextualNotice.ts
@@ -1,4 +1,4 @@
-import {FunctionComponent} from 'preact';
+import {AnchorHTMLAttributes, FunctionComponent} from 'preact';
import {Purpose} from '../../types';
export interface ContextualNoticeOptions extends Record {
@@ -9,6 +9,7 @@ export interface ContextualNoticeProps {
purpose: Purpose;
data: Data;
privacyPolicyUrl: string;
+ privacyPolicyLinkAttributes?: AnchorHTMLAttributes;
onAccept: () => void;
}
diff --git a/src/ui/components/types/Modal.ts b/src/ui/components/types/Modal.ts
index 5f1e72e5..20f83b0d 100644
--- a/src/ui/components/types/Modal.ts
+++ b/src/ui/components/types/Modal.ts
@@ -1,9 +1,14 @@
-import {ComponentChildren, FunctionComponent} from 'preact';
+import {
+ AnchorHTMLAttributes,
+ ComponentChildren,
+ FunctionComponent
+} from 'preact';
export interface ModalProps {
isForced: boolean;
needsUpdate: boolean;
privacyPolicyUrl: string;
+ privacyPolicyLinkAttributes?: AnchorHTMLAttributes;
onClose: () => void;
onSave: () => void;
children: ComponentChildren;
diff --git a/src/ui/themes/dsfr/Banner.tsx b/src/ui/themes/dsfr/Banner.tsx
index 3eb22fc4..2e0dc159 100644
--- a/src/ui/themes/dsfr/Banner.tsx
+++ b/src/ui/themes/dsfr/Banner.tsx
@@ -2,12 +2,14 @@ import {useRef} from 'preact/hooks';
import type {BannerComponent} from '../../components/types/Banner';
import {useNonObscuringElement, useTranslations} from '../../utils/hooks';
import {template} from '../../utils/template';
+import PrivacyPolicyLink from '../../components/PrivacyPolicyLink';
const Banner: BannerComponent = ({
needsUpdate,
isHidden,
purposeTitles,
privacyPolicyUrl,
+ privacyPolicyLinkAttributes,
onAccept,
onDecline,
onConfigure
@@ -28,9 +30,12 @@ const Banner: BannerComponent = ({
{purposeTitles.join(', ')}
),
privacyPolicy: (
-
- {t.banner.privacyPolicyLabel}
-
+
)
})}
diff --git a/src/ui/themes/dsfr/Modal.tsx b/src/ui/themes/dsfr/Modal.tsx
index a323cd24..a64e776e 100644
--- a/src/ui/themes/dsfr/Modal.tsx
+++ b/src/ui/themes/dsfr/Modal.tsx
@@ -3,11 +3,13 @@ import {template} from '../../utils/template';
import Dialog from '../../components/Dialog';
import PoweredByLink from '../../components/PoweredByLink';
import type {ModalComponent} from '../../components/types/Modal';
+import PrivacyPolicyLink from '../../components/PrivacyPolicyLink';
const Modal: ModalComponent = ({
isForced,
needsUpdate,
privacyPolicyUrl,
+ privacyPolicyLinkAttributes,
onClose,
onSave,
children
@@ -57,13 +59,13 @@ const Modal: ModalComponent = ({
{template(t.modal.description, {
privacyPolicy: (
-
- {t.modal.privacyPolicyLabel}
-
+ label={t.modal.privacyPolicyLabel}
+ onExit={onClose}
+ />
)
})}
diff --git a/src/ui/themes/standard/Banner.tsx b/src/ui/themes/standard/Banner.tsx
index 20979662..79224015 100644
--- a/src/ui/themes/standard/Banner.tsx
+++ b/src/ui/themes/standard/Banner.tsx
@@ -3,12 +3,14 @@ import {BannerComponent} from '../../components/types/Banner';
import {imageAttributes} from '../../utils/config';
import {useNonObscuringElement, useTranslations} from '../../utils/hooks';
import {template} from '../../utils/template';
+import PrivacyPolicyLink from '../../components/PrivacyPolicyLink';
const Banner: BannerComponent = ({
isHidden,
needsUpdate,
purposeTitles,
privacyPolicyUrl,
+ privacyPolicyLinkAttributes,
logo,
onAccept: onSaveRequest,
onDecline: onDeclineRequest,
@@ -57,13 +59,13 @@ const Banner: BannerComponent = ({
),
privacyPolicy: (
-
- {t.banner.privacyPolicyLabel}
-
+ label={t.banner.privacyPolicyLabel}
+ />
)
})}
diff --git a/src/ui/themes/standard/ContextualNotice.tsx b/src/ui/themes/standard/ContextualNotice.tsx
index 0a4ed336..e323be1b 100644
--- a/src/ui/themes/standard/ContextualNotice.tsx
+++ b/src/ui/themes/standard/ContextualNotice.tsx
@@ -4,12 +4,14 @@ import type {
ContextualNoticeOptions
} from '../../components/types/ContextualNotice';
import {template} from '../../utils/template';
+import PrivacyPolicyLink from '../../components/PrivacyPolicyLink';
const ContextualNotice: ContextualNoticeComponent = ({
purpose,
data,
onAccept,
- privacyPolicyUrl
+ privacyPolicyUrl,
+ privacyPolicyLinkAttributes
}) => {
const t = useTranslations();
const {titleLevel} = data;
@@ -18,9 +20,12 @@ const ContextualNotice: ContextualNoticeComponent = ({
const templateProps = {
purpose: purpose.title,
privacyPolicy: (
-
- {t.contextual.privacyPolicyLabel}
-
+
)
};
diff --git a/src/ui/themes/standard/Modal.tsx b/src/ui/themes/standard/Modal.tsx
index 2bdadb31..b9925c7c 100644
--- a/src/ui/themes/standard/Modal.tsx
+++ b/src/ui/themes/standard/Modal.tsx
@@ -4,17 +4,19 @@ import {template} from '../../utils/template';
import {useTranslations} from '../../utils/hooks';
import {ModalComponent} from '../../components/types/Modal';
import PoweredByLink from '../../components/PoweredByLink';
+import PrivacyPolicyLink from '../../components/PrivacyPolicyLink';
const Modal: ModalComponent = ({
isForced,
needsUpdate,
privacyPolicyUrl,
+ privacyPolicyLinkAttributes,
onClose,
onSave,
children
}) => {
const t = useTranslations();
-
+ console.log(privacyPolicyLinkAttributes);
return (