Skip to content

Commit e5c469a

Browse files
committed
Add default id to every component so analytics do not complains
1 parent 0d83ed9 commit e5c469a

38 files changed

+379
-92
lines changed

src/Accordion.tsx

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import React, {
44
forwardRef,
55
memo,
6-
useId,
76
useState,
87
type ReactNode,
98
type CSSProperties
@@ -14,12 +13,14 @@ import { fr } from "./fr";
1413
import { cx } from "./tools/cx";
1514
import { symToStr } from "tsafe/symToStr";
1615
import { useConstCallback } from "./tools/powerhooks/useConstCallback";
16+
import { useAnalyticsId } from "./tools/useAnalyticsId";
1717

1818
export type AccordionProps = AccordionProps.Controlled | AccordionProps.Uncontrolled;
1919

2020
export namespace AccordionProps {
2121
export type Common = {
2222
className?: string;
23+
id?: string;
2324
titleAs?: `h${2 | 3 | 4 | 5 | 6}`;
2425
label: ReactNode;
2526
classes?: Partial<Record<"root" | "accordion" | "title" | "collapse", string>>;
@@ -51,6 +52,7 @@ export const Accordion = memo(
5152
forwardRef<HTMLDivElement, AccordionProps>((props, ref) => {
5253
const {
5354
className,
55+
id: id_props,
5456
titleAs: HtmlTitleTag = "h3",
5557
label,
5658
classes = {},
@@ -64,7 +66,12 @@ export const Accordion = memo(
6466

6567
assert<Equals<keyof typeof rest, never>>();
6668

67-
const accordionId = `accordion-${useId()}`;
69+
const id = useAnalyticsId({
70+
"defaultIdPrefix": "fr-accordion",
71+
"explicitlyProvidedId": id_props
72+
});
73+
74+
const collapseElementId = `${id}-collapse`;
6875

6976
const [expandedState, setExpandedState] = useState(defaultExpanded);
7077

@@ -88,14 +95,14 @@ export const Accordion = memo(
8895
<button
8996
className={fr.cx("fr-accordion__btn")}
9097
aria-expanded={value}
91-
aria-controls={accordionId}
98+
aria-controls={collapseElementId}
9299
onClick={onExtendButtonClick}
93100
type="button"
94101
>
95102
{label}
96103
</button>
97104
</HtmlTitleTag>
98-
<div className={cx(fr.cx("fr-collapse"), classes.collapse)} id={accordionId}>
105+
<div className={cx(fr.cx("fr-collapse"), classes.collapse)} id={collapseElementId}>
99106
{children}
100107
</div>
101108
</section>

src/AgentConnectButton.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export type AgentConnectButtonProps =
1313
export namespace AgentConnectButtonProps {
1414
type Common = {
1515
className?: string;
16+
id?: string;
1617
style?: CSSProperties;
1718
};
1819
export type WithUrl = Common & {
@@ -28,7 +29,7 @@ export namespace AgentConnectButtonProps {
2829
/** @see <https://components.react-dsfr.fr/?path=/docs/components-franceconnectbutton> */
2930
export const AgentConnectButton = memo(
3031
forwardRef<HTMLDivElement, AgentConnectButtonProps>((props, ref) => {
31-
const { className, url: href, style, onClick, ...rest } = props;
32+
const { className, url: href, style, onClick, id: id_props, ...rest } = props;
3233

3334
assert<Equals<keyof typeof rest, never>>();
3435

@@ -37,7 +38,7 @@ export const AgentConnectButton = memo(
3738
const Inner = onClick !== undefined ? "button" : "a";
3839

3940
return (
40-
<div className={className} style={style} ref={ref}>
41+
<div id={id_props ?? "fr-agentconnect-button"} className={className} style={style} ref={ref}>
4142
<span className="agentconnect-button__preload-hover" />
4243
<Inner
4344
className="agentconnect-button__link"

src/Alert.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@ import { assert } from "tsafe/assert";
1717
import type { Equals } from "tsafe";
1818
import { useConstCallback } from "./tools/powerhooks/useConstCallback";
1919
import { createComponentI18nApi } from "./i18n";
20+
import { useAnalyticsId } from "./tools/useAnalyticsId";
2021

2122
export type AlertProps = {
2223
className?: string;
24+
id?: string;
2325
severity: AlertProps.Severity;
2426
/** Default h3 */
2527
as?: `h${2 | 3 | 4 | 5 | 6}`;
@@ -79,6 +81,7 @@ export const Alert = memo(
7981
forwardRef<HTMLDivElement, AlertProps>((props, ref) => {
8082
const {
8183
className,
84+
id: id_props,
8285
severity,
8386
as: HtmlTitleTag = "h3",
8487
classes = {},
@@ -94,6 +97,11 @@ export const Alert = memo(
9497

9598
assert<Equals<keyof typeof rest, never>>();
9699

100+
const id = useAnalyticsId({
101+
"explicitlyProvidedId": id_props,
102+
"defaultIdPrefix": "fr-alert"
103+
});
104+
97105
const [isClosed, setIsClosed] = useState(props_isClosed ?? false);
98106

99107
const [buttonElement, setButtonElement] = useState<HTMLButtonElement | null>(null);
@@ -148,6 +156,7 @@ export const Alert = memo(
148156

149157
return (
150158
<div
159+
id={id}
151160
className={cx(
152161
fr.cx("fr-alert", `fr-alert--${severity}`, { "fr-alert--sm": isSmall }),
153162
classes.root,

src/Badge.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ import type { Equals } from "tsafe";
55
import { fr } from "./fr";
66
import { cx } from "./tools/cx";
77
import type { AlertProps } from "./Alert";
8+
import { useAnalyticsId } from "./tools/useAnalyticsId";
89

910
export type BadgeProps = {
11+
id?: string;
1012
className?: string;
1113
style?: CSSProperties;
1214
severity?: AlertProps.Severity | "new";
@@ -19,6 +21,7 @@ export type BadgeProps = {
1921
export const Badge = memo(
2022
forwardRef<HTMLDivElement, BadgeProps>((props, ref) => {
2123
const {
24+
id: props_id,
2225
className,
2326
style,
2427
severity,
@@ -30,8 +33,14 @@ export const Badge = memo(
3033

3134
assert<Equals<keyof typeof rest, never>>();
3235

36+
const id= useAnalyticsId({
37+
"defaultIdPrefix": "fr-badge",
38+
"explicitlyProvidedId": props_id
39+
});
40+
3341
return (
3442
<p
43+
id={id}
3544
className={cx(
3645
fr.cx(
3746
"fr-badge",

src/Breadcrumb.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ import type { RegisteredLinkProps } from "./link";
77
import { createComponentI18nApi } from "./i18n";
88
import { fr } from "./fr";
99
import { cx } from "./tools/cx";
10+
import { useAnalyticsId } from "./tools/useAnalyticsId";
1011

1112
export type BreadcrumbProps = {
13+
id?: string;
1214
className?: string;
1315
homeLinkProps?: RegisteredLinkProps;
1416
segments: {
@@ -24,6 +26,7 @@ export type BreadcrumbProps = {
2426
export const Breadcrumb = memo(
2527
forwardRef<HTMLDivElement, BreadcrumbProps>((props, ref) => {
2628
const {
29+
id: props_id,
2730
className,
2831
homeLinkProps,
2932
segments,
@@ -35,13 +38,19 @@ export const Breadcrumb = memo(
3538

3639
assert<Equals<keyof typeof rest, never>>();
3740

41+
const id = useAnalyticsId({
42+
"defaultIdPrefix": "fr-breadcrumb",
43+
"explicitlyProvidedId": props_id
44+
});
45+
3846
const { t } = useTranslation();
3947

4048
const { Link } = getLink();
4149
const breadcrumbId = `breadcrumb-${useId()}`;
4250

4351
return (
4452
<nav
53+
id={id}
4554
ref={ref}
4655
role="navigation"
4756
className={cx(fr.cx("fr-breadcrumb"), classes.root, className)}

src/Button.tsx

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import React, {
55
type RefAttributes,
66
type MemoExoticComponent,
77
type ForwardRefExoticComponent,
8-
type CSSProperties
8+
type CSSProperties,
9+
type ComponentProps
910
} from "react";
1011
import { fr } from "./fr";
1112
import { cx } from "./tools/cx";
@@ -15,12 +16,14 @@ import type { RegisteredLinkProps } from "./link";
1516
import { assert } from "tsafe/assert";
1617
import type { Equals } from "tsafe";
1718
import { symToStr } from "tsafe/symToStr";
19+
import { useAnalyticsId } from "./tools/useAnalyticsId";
1820

1921
export type ButtonProps = ButtonProps.Common &
2022
(ButtonProps.IconOnly | ButtonProps.WithIcon | ButtonProps.WithoutIcon) &
2123
(ButtonProps.AsAnchor | ButtonProps.AsButton);
2224
export namespace ButtonProps {
2325
export type Common = {
26+
id?: string;
2427
className?: string;
2528
/** Default primary */
2629
priority?: "primary" | "secondary" | "tertiary" | "tertiary no outline";
@@ -64,21 +67,19 @@ export namespace ButtonProps {
6467
export type AsButton = {
6568
linkProps?: never;
6669
onClick?: React.MouseEventHandler<HTMLButtonElement>;
67-
nativeButtonProps?: React.DetailedHTMLProps<
68-
React.ButtonHTMLAttributes<HTMLButtonElement>,
69-
HTMLButtonElement
70-
> &
71-
Record<`data-${string}`, string | boolean | null | undefined>;
70+
nativeButtonProps?: ComponentProps<"button"> & Record<`data-${string}`, string | boolean | null | undefined>;
7271
disabled?: boolean;
7372
/** Default "button" */
7473
type?: "button" | "submit" | "reset";
7574
};
7675
}
7776

77+
7878
/** @see <https://components.react-dsfr.fr/?path=/docs/components-button> */
7979
export const Button = memo(
8080
forwardRef<HTMLButtonElement | HTMLAnchorElement, ButtonProps>((props, ref) => {
8181
const {
82+
id: props_id,
8283
className: prop_className,
8384
children,
8485
title,
@@ -97,6 +98,11 @@ export const Button = memo(
9798

9899
assert<Equals<keyof typeof rest, never>>();
99100

101+
const id = useAnalyticsId({
102+
"defaultIdPrefix": "fr-button",
103+
"explicitlyProvidedId": props_id
104+
});
105+
100106
const { Link } = getLink();
101107

102108
const className = cx(
@@ -126,6 +132,7 @@ export const Button = memo(
126132
return linkProps !== undefined ? (
127133
<Link
128134
{...linkProps}
135+
id={props_id ?? linkProps.id ?? id}
129136
title={title ?? linkProps.title}
130137
className={cx(linkProps?.className, className)}
131138
style={{
@@ -140,6 +147,7 @@ export const Button = memo(
140147
) : (
141148
<button
142149
{...nativeButtonProps}
150+
id={props_id ?? nativeButtonProps?.id ?? id}
143151
className={cx(nativeButtonProps?.className, className)}
144152
style={{
145153
...nativeButtonProps?.style,

src/ButtonsGroup.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ import { assert } from "tsafe/assert";
66
import type { Equals } from "tsafe";
77
import { fr } from "./fr";
88
import { cx } from "./tools/cx";
9+
import { useAnalyticsId } from "./tools/useAnalyticsId";
910

1011
export type ButtonsGroupProps = ButtonsGroupProps.AlwaysStacked | ButtonsGroupProps.Inline;
1112

1213
export namespace ButtonsGroupProps {
1314
export type Common = {
15+
id?: string;
1416
className?: string;
1517
buttonsSize?: ButtonProps["size"];
1618
/** Default: left */
@@ -49,6 +51,7 @@ export namespace ButtonsGroupProps {
4951
export const ButtonsGroup = memo(
5052
forwardRef<HTMLUListElement, ButtonsGroupProps>((props, ref) => {
5153
const {
54+
id: props_id,
5255
className,
5356
buttonsSize = "medium",
5457
buttonsIconPosition = "left",
@@ -63,6 +66,11 @@ export const ButtonsGroup = memo(
6366

6467
assert<Equals<keyof typeof rest, never>>();
6568

69+
const id = useAnalyticsId({
70+
"defaultIdPrefix": "fr-btns-group",
71+
"explicitlyProvidedId": props_id
72+
});
73+
6674
const buttonsGroupClassName = cx(
6775
fr.cx(
6876
"fr-btns-group",
@@ -97,7 +105,7 @@ export const ButtonsGroup = memo(
97105
);
98106

99107
return (
100-
<ul className={buttonsGroupClassName} style={style} ref={ref} {...rest}>
108+
<ul id={id} className={buttonsGroupClassName} style={style} ref={ref} {...rest}>
101109
{buttons.map((buttonProps, i) => (
102110
<li key={i}>
103111
<Button {...buttonProps} />

src/CallOut.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@ import type { ButtonProps } from "./Button";
1111
import { Button } from "./Button";
1212
import { cx } from "./tools/cx";
1313
import { fr } from "./fr";
14+
import { useAnalyticsId } from "./tools/useAnalyticsId";
1415

1516
export type CallOutProps = {
1617
className?: string;
18+
id?: string;
1719
iconId?: FrIconClassName | RiIconClassName;
1820
title?: ReactNode;
1921
buttonProps?: ButtonProps;
@@ -35,6 +37,7 @@ export namespace CallOutProps {
3537
export const CallOut = memo(
3638
forwardRef<HTMLDivElement, CallOutProps>((props, ref) => {
3739
const {
40+
id: props_id,
3841
className,
3942
iconId,
4043
title,
@@ -48,8 +51,14 @@ export const CallOut = memo(
4851

4952
assert<Equals<keyof typeof rest, never>>();
5053

54+
const id = useAnalyticsId({
55+
"defaultIdPrefix": "fr-callout",
56+
"explicitlyProvidedId": props_id
57+
});
58+
5159
return (
5260
<div
61+
id={id}
5362
className={cx(
5463
fr.cx(
5564
"fr-callout",

src/Card.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ import { fr } from "./fr";
88
import type { RegisteredLinkProps } from "./link";
99
import { getLink } from "./link";
1010
import { cx } from "./tools/cx";
11+
import { useAnalyticsId } from "./tools/useAnalyticsId";
1112

1213
//https://main--ds-gouv.netlify.app/example/component/card/
1314
export type CardProps = {
15+
id?: string;
1416
className?: string;
1517
title: ReactNode;
1618
desc?: ReactNode;
@@ -76,6 +78,7 @@ export namespace CardProps {
7678
export const Card = memo(
7779
forwardRef<HTMLDivElement, CardProps>((props, ref) => {
7880
const {
81+
id: props_id,
7982
className,
8083
title,
8184
linkProps,
@@ -103,10 +106,16 @@ export const Card = memo(
103106

104107
assert<Equals<keyof typeof rest, never>>();
105108

109+
const id = useAnalyticsId({
110+
"defaultIdPrefix": "fr-card",
111+
"explicitlyProvidedId": props_id
112+
});
113+
106114
const { Link } = getLink();
107115

108116
return (
109117
<div
118+
id={id}
110119
className={cx(
111120
fr.cx(
112121
"fr-card",

0 commit comments

Comments
 (0)