Skip to content

Commit 43747ea

Browse files
authored
Merge pull request #232 from codegouvfr/feature/add-consent-manager-placeholder
Feature / add consentManager Placeholder component
2 parents 10be541 + 0ddfff6 commit 43747ea

File tree

3 files changed

+118
-10
lines changed

3 files changed

+118
-10
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import React, { forwardRef, memo } from "react";
2+
import { fr } from "../fr";
3+
import { cx } from "../tools/cx";
4+
import { Equals, assert } from "tsafe";
5+
import { createComponentI18nApi } from "../i18n";
6+
import { symToStr } from "tsafe/symToStr";
7+
8+
export type PlaceholderProps = {
9+
className?: string;
10+
style?: React.CSSProperties;
11+
titleAs?: "span" | `h${1 | 2 | 3 | 4 | 5 | 6}`;
12+
classes?: Partial<Record<"root" | "title" | "description" | "button", string>>;
13+
title: string;
14+
description: string;
15+
onGranted: () => void;
16+
};
17+
18+
export const Placeholder = memo(
19+
forwardRef<HTMLDivElement, PlaceholderProps>((props, ref) => {
20+
const { t } = useTranslation();
21+
const {
22+
className,
23+
titleAs: HtmlTitleTag = "h4",
24+
classes = {},
25+
style,
26+
title,
27+
description,
28+
onGranted,
29+
...rest
30+
} = props;
31+
assert<Equals<keyof typeof rest, never>>();
32+
return (
33+
<div
34+
className={cx(fr.cx("fr-consent-placeholder"), classes.root, className)}
35+
ref={ref}
36+
style={style}
37+
{...rest}
38+
>
39+
<HtmlTitleTag className={cx(fr.cx("fr-h6", "fr-mb-2v"), classes.title)}>
40+
{title}
41+
</HtmlTitleTag>
42+
<p className={cx(fr.cx("fr-mb-6v"), classes.title)}>{description}</p>
43+
<button
44+
className={cx(fr.cx("fr-btn"), classes.button)}
45+
title={description}
46+
onClick={onGranted}
47+
>
48+
{t("enable message")}
49+
</button>
50+
</div>
51+
);
52+
})
53+
);
54+
55+
const { useTranslation, addPlaceholderTranslations } = createComponentI18nApi({
56+
"componentName": symToStr({ Placeholder }),
57+
"frMessages": {
58+
/* spell-checker: disable */
59+
"enable message": "Autoriser"
60+
/* spell-checker: enable */
61+
}
62+
});
63+
64+
addPlaceholderTranslations({
65+
"lang": "en",
66+
"messages": {
67+
"enable message": "Authorize"
68+
}
69+
});
70+
71+
addPlaceholderTranslations({
72+
"lang": "es",
73+
"messages": {
74+
/* spell-checker: disable */
75+
"enable message": "Permitir"
76+
/* spell-checker: enable */
77+
}
78+
});
79+
80+
export { addPlaceholderTranslations };

stories/ConsentManagement.stories.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { sectionName } from "./sectionName";
33
import { getStoryFactory } from "./getStory";
44
import { createConsentManagement } from "../dist/consentManagement";
55
import { localStorageKeyPrefix } from "../dist/consentManagement/createConsentManagement";
6+
import { Placeholder } from "../dist/consentManagement/Placeholder";
67
import { Footer } from "../dist/Footer";
78
import { Button } from "../dist/Button";
89
import { fr } from "../dist/fr";
@@ -263,7 +264,7 @@ const {
263264
});
264265

265266
function Story() {
266-
const { finalityConsent } = useConsent();
267+
const { finalityConsent, assumeConsent } = useConsent();
267268

268269
return (
269270
<>
@@ -272,6 +273,14 @@ function Story() {
272273
) : (
273274
<pre>{JSON.stringify({ finalityConsent }, null, 2)}</pre>
274275
)}
276+
{finalityConsent && finalityConsent.analytics === false && (
277+
<Placeholder
278+
title="Analytics are not enabled"
279+
description="We use cookies to measure the audience of our site and improve its content."
280+
onGranted={() => assumeConsent("analytics")}
281+
titleAs="span"
282+
/>
283+
)}
275284
<Button
276285
onClick={() => {
277286
Object.keys(localStorage)

test/types/consentManagement.ts renamed to test/types/consentManagement.tsx

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
/* eslint-disable @typescript-eslint/no-unused-vars */
2+
import React from "react";
23
import { assert, type Equals } from "tsafe/assert";
34
import type {
45
ExtractFinalityFromFinalityDescription,
56
FinalityConsent
67
} from "../../src/consentManagement/types";
8+
import { Placeholder } from "../../src/consentManagement/Placeholder";
79

810
{
911
type Input =
@@ -15,17 +17,17 @@ import type {
1517
| "advertising";
1618

1719
type ExpectedOutput = {
18-
analytics: boolean;
19-
personalization: boolean;
20-
advertising: boolean;
21-
statistics: {
22-
traffic: boolean;
23-
deviceType: boolean;
24-
browser: boolean;
20+
readonly analytics: boolean;
21+
readonly personalization: boolean;
22+
readonly advertising: boolean;
23+
readonly statistics: {
24+
readonly traffic: boolean;
25+
readonly deviceType: boolean;
26+
readonly browser: boolean;
2527
} & {
26-
isFullConsent: boolean;
28+
readonly isFullConsent: boolean;
2729
};
28-
isFullConsent: boolean;
30+
readonly isFullConsent: boolean;
2931
};
3032

3133
type ActualOutput = FinalityConsent<Input>;
@@ -74,3 +76,20 @@ import type {
7476

7577
assert<Equals<Got, Expected>>();
7678
}
79+
80+
{
81+
<Placeholder
82+
title="Instagram"
83+
description="We use cookies to display Instagram content."
84+
onGranted={() => console.log("clicked on enable button")}
85+
/>;
86+
}
87+
88+
{
89+
<Placeholder
90+
title="Instagram"
91+
description="We use cookies to display Instagram content."
92+
onGranted={() => console.log("clicked on enable button")}
93+
titleAs="h2"
94+
/>;
95+
}

0 commit comments

Comments
 (0)