@@ -22,7 +22,7 @@ type DataAttribute = Record<`data-${string}`, string | boolean | null | undefine
2222
2323export type TagProps = TagProps . Common &
2424 ( TagProps . WithIcon | TagProps . WithoutIcon ) &
25- ( TagProps . AsAnchor | TagProps . AsButton | TagProps . AsParagraph ) ;
25+ ( TagProps . AsAnchor | TagProps . AsButton | TagProps . AsParagraph | TagProps . AsSpan ) ;
2626export namespace TagProps {
2727 export type Common = {
2828 id ?: string ;
@@ -32,6 +32,7 @@ export namespace TagProps {
3232 style ?: CSSProperties ;
3333 title ?: string ;
3434 children : ReactNode ;
35+ as ?: "p" | "span" | "button" | "a" ;
3536 } ;
3637
3738 export type WithIcon = {
@@ -44,18 +45,20 @@ export namespace TagProps {
4445 } ;
4546
4647 export type AsAnchor = {
48+ as ?: "a" ;
4749 linkProps : RegisteredLinkProps ;
4850 onClick ?: never ;
4951 nativeButtonProps ?: never ;
50- /** @deprecated Tag is now <p> by default. Use `nativeParagraphProps` instead. */
52+ /** @deprecated Tag is now ` <p>` by default. Use `nativeParagraphProps` instead. */
5153 nativeSpanProps ?: never ;
5254 nativeParagraphProps ?: never ;
5355 dismissible ?: never ;
5456 pressed ?: never ;
5557 } ;
5658 export type AsButton = {
59+ as ?: "button" ;
5760 linkProps ?: never ;
58- /** @deprecated Tag is now <p> by default. Use `nativeParagraphProps` instead. */
61+ /** @deprecated Tag is now ` <p>` by default. Use `nativeParagraphProps` instead. */
5962 nativeSpanProps ?: never ;
6063 nativeParagraphProps ?: never ;
6164 /** Default: false */
@@ -65,104 +68,118 @@ export namespace TagProps {
6568 nativeButtonProps ?: ComponentProps < "button" > & DataAttribute ;
6669 } ;
6770 export type AsParagraph = {
71+ as ?: "p" ;
6872 linkProps ?: never ;
6973 onClick ?: never ;
7074 dismissible ?: never ;
7175 pressed ?: never ;
7276 nativeButtonProps ?: never ;
73- /** @deprecated Tag is now <p> by default. Use `nativeParagraphProps` instead. */
77+ /** @deprecated Tag is now ` <p>` by default. Use `nativeParagraphProps` instead. */
7478 nativeSpanProps ?: ComponentProps < "span" > & DataAttribute ;
7579 nativeParagraphProps ?: ComponentProps < "p" > & DataAttribute ;
7680 } ;
77-
78- /** @deprecated Tag is now <p> by default. Use `AsParagraph` instead. */
79- export type AsSpan = AsParagraph ;
81+ export type AsSpan = {
82+ as : "span" ;
83+ linkProps ?: never ;
84+ onClick ?: never ;
85+ dismissible ?: never ;
86+ pressed ?: never ;
87+ nativeButtonProps ?: never ;
88+ nativeSpanProps ?: ComponentProps < "span" > & DataAttribute ;
89+ nativeParagraphProps ?: never ;
90+ } ;
8091}
8192
8293/** @see <https://components.react-dsfr.codegouv.studio/?path=/docs/components-tag> */
8394export const Tag = memo (
84- forwardRef < HTMLButtonElement | HTMLAnchorElement | HTMLParagraphElement , TagProps > (
85- ( props , ref ) => {
86- const {
87- id : id_props ,
88- className : prop_className ,
89- children,
90- title,
91- iconId,
92- small = false ,
93- pressed,
94- dismissible = false ,
95- linkProps,
96- nativeButtonProps,
97- nativeParagraphProps,
98- nativeSpanProps,
99- style,
100- onClick,
101- ...rest
102- } = props ;
95+ forwardRef <
96+ HTMLButtonElement | HTMLAnchorElement | HTMLParagraphElement | HTMLSpanElement ,
97+ TagProps
98+ > ( ( props , ref ) => {
99+ const {
100+ id : id_props ,
101+ className : prop_className ,
102+ children,
103+ title,
104+ iconId,
105+ small = false ,
106+ pressed,
107+ dismissible = false ,
108+ linkProps,
109+ nativeButtonProps,
110+ nativeParagraphProps,
111+ nativeSpanProps,
112+ style,
113+ onClick,
114+ as : AsTag = "p" ,
115+ ...rest
116+ } = props ;
103117
104- assert < Equals < keyof typeof rest , never > > ( ) ;
118+ assert < Equals < keyof typeof rest , never > > ( ) ;
105119
106- const id = useAnalyticsId ( {
107- "defaultIdPrefix" : "fr-tag" ,
108- "explicitlyProvidedId" : id_props
109- } ) ;
120+ const id = useAnalyticsId ( {
121+ "defaultIdPrefix" : "fr-tag" ,
122+ "explicitlyProvidedId" : id_props
123+ } ) ;
110124
111- const { Link } = getLink ( ) ;
125+ const { Link } = getLink ( ) ;
112126
113- const className = cx (
114- fr . cx (
115- "fr-tag" ,
116- small && `fr-tag--sm` ,
117- iconId ,
118- iconId && "fr-tag--icon-left" , // actually, it's always left but we need it in order to have the icon rendering
119- dismissible && "fr-tag--dismiss"
120- ) ,
121- linkProps !== undefined && linkProps . className ,
122- prop_className
123- ) ;
127+ const className = cx (
128+ fr . cx (
129+ "fr-tag" ,
130+ small && `fr-tag--sm` ,
131+ iconId ,
132+ iconId && "fr-tag--icon-left" , // actually, it's always left but we need it in order to have the icon rendering
133+ dismissible && "fr-tag--dismiss"
134+ ) ,
135+ linkProps !== undefined && linkProps . className ,
136+ prop_className
137+ ) ;
124138
125- const nativeParagraphOrSpanProps = nativeParagraphProps ?? nativeSpanProps ;
139+ // to support old usage
140+ const nativeParagraphOrSpanProps = nativeParagraphProps ?? nativeSpanProps ;
126141
127- return (
128- < >
129- { linkProps !== undefined && (
130- < Link
131- { ...linkProps }
132- id = { id_props ?? linkProps . id ?? id }
133- title = { title ?? linkProps . title }
134- className = { cx ( linkProps ?. className , className ) }
135- style = { {
136- ...linkProps ?. style ,
137- ...style
138- } }
139- ref = { ref as React . ForwardedRef < HTMLAnchorElement > }
140- { ...rest }
141- >
142- { children }
143- </ Link >
144- ) }
145- { nativeButtonProps !== undefined && (
146- < button
147- { ...nativeButtonProps }
148- id = { id_props ?? nativeButtonProps . id ?? id }
149- className = { cx ( nativeButtonProps ?. className , className ) }
150- style = { {
151- ...nativeButtonProps ?. style ,
152- ...style
153- } }
154- title = { title ?? nativeButtonProps ?. title }
155- onClick = { onClick ?? nativeButtonProps ?. onClick }
156- disabled = { nativeButtonProps ?. disabled }
157- ref = { ref as React . ForwardedRef < HTMLButtonElement > }
158- aria-pressed = { pressed }
159- { ...rest }
160- >
161- { children }
162- </ button >
163- ) }
164- { linkProps === undefined && nativeButtonProps === undefined && (
165- < p
142+ return (
143+ < >
144+ { linkProps !== undefined && (
145+ < Link
146+ { ...linkProps }
147+ id = { id_props ?? linkProps . id ?? id }
148+ title = { title ?? linkProps . title }
149+ className = { cx ( linkProps ?. className , className ) }
150+ style = { {
151+ ...linkProps ?. style ,
152+ ...style
153+ } }
154+ ref = { ref as React . ForwardedRef < HTMLAnchorElement > }
155+ { ...rest }
156+ >
157+ { children }
158+ </ Link >
159+ ) }
160+ { nativeButtonProps !== undefined && (
161+ < button
162+ { ...nativeButtonProps }
163+ id = { id_props ?? nativeButtonProps . id ?? id }
164+ className = { cx ( nativeButtonProps ?. className , className ) }
165+ style = { {
166+ ...nativeButtonProps ?. style ,
167+ ...style
168+ } }
169+ title = { title ?? nativeButtonProps ?. title }
170+ onClick = { onClick ?? nativeButtonProps ?. onClick }
171+ disabled = { nativeButtonProps ?. disabled }
172+ ref = { ref as React . ForwardedRef < HTMLButtonElement > }
173+ aria-pressed = { pressed }
174+ { ...rest }
175+ >
176+ { children }
177+ </ button >
178+ ) }
179+ { linkProps === undefined &&
180+ nativeButtonProps === undefined &&
181+ ( AsTag === "span" || AsTag === "p" ) && (
182+ < AsTag
166183 { ...nativeParagraphOrSpanProps }
167184 id = { id_props ?? nativeParagraphOrSpanProps ?. id ?? id }
168185 className = { cx ( nativeParagraphOrSpanProps ?. className , className ) }
@@ -175,12 +192,11 @@ export const Tag = memo(
175192 { ...rest }
176193 >
177194 { children }
178- </ p >
195+ </ AsTag >
179196 ) }
180- </ >
181- ) ;
182- }
183- )
197+ </ >
198+ ) ;
199+ } )
184200) as MemoExoticComponent <
185201 ForwardRefExoticComponent <
186202 TagProps . Common &
@@ -189,6 +205,7 @@ export const Tag = memo(
189205 | ( TagProps . AsAnchor & RefAttributes < HTMLAnchorElement > )
190206 | ( TagProps . AsButton & RefAttributes < HTMLButtonElement > )
191207 | ( TagProps . AsParagraph & RefAttributes < HTMLParagraphElement > )
208+ | ( TagProps . AsSpan & RefAttributes < HTMLParagraphElement > )
192209 )
193210 >
194211> ;
0 commit comments