11"use client" ;
22
3- import React , { memo , forwardRef , useState , useEffect , useRef } from "react" ;
4- import type { ReactNode } from "react" ;
3+ import React , { memo , useState , useEffect , useRef } from "react" ;
4+ import type { ReactNode , ForwardedRef } from "react" ;
55import type { FrClassName } from "./fr/generatedFromCss/classNames" ;
66import { symToStr } from "tsafe/symToStr" ;
77import { fr } from "./fr" ;
@@ -16,6 +16,7 @@ export type AlertProps = {
1616 severity : AlertProps . Severity ;
1717 /** Default h3 */
1818 as ?: `h${2 | 3 | 4 | 5 | 6 } `;
19+ ref ?: ForwardedRef < HTMLDivElement > ;
1920 classes ?: Partial < Record < "root" | "title" | "description" | "close" , string > > ;
2021} & ( AlertProps . DefaultSize | AlertProps . Small ) &
2122 ( AlertProps . NonClosable | AlertProps . Closable ) ;
@@ -67,105 +68,103 @@ export namespace AlertProps {
6768}
6869
6970/** @see <https://react-dsfr-components.etalab.studio/?path=/docs/components-alert> */
70- export const Alert = memo (
71- forwardRef < HTMLDivElement , AlertProps > ( ( props , ref ) => {
72- const {
73- className,
74- severity,
75- as : HtmlTitleTag = "h3" ,
76- classes = { } ,
77- small : isSmall ,
78- title,
79- description,
80- closable : isClosable = false ,
81- isClosed : props_isClosed ,
82- onClose,
83- ...rest
84- } = props ;
85-
86- assert < Equals < keyof typeof rest , never > > ( ) ;
87-
88- const [ isClosed , setIsClosed ] = useState ( props_isClosed ?? false ) ;
89-
90- const [ buttonElement , setButtonElement ] = useState < HTMLButtonElement | null > ( null ) ;
91-
92- const refShouldButtonGetFocus = useRef ( false ) ;
93- const refShouldSetRole = useRef ( false ) ;
94-
95- useEffect ( ( ) => {
96- if ( props_isClosed === undefined ) {
97- return ;
98- }
99- setIsClosed ( isClosed => {
100- if ( isClosed && ! props_isClosed ) {
101- refShouldButtonGetFocus . current = true ;
102- refShouldSetRole . current = true ;
103- }
104-
105- return props_isClosed ;
106- } ) ;
107- } , [ props_isClosed ] ) ;
108-
109- useEffect ( ( ) => {
110- if ( ! refShouldButtonGetFocus . current ) {
111- return ;
112- }
113-
114- if ( buttonElement === null ) {
115- //NOTE: This should not be reachable
116- return ;
71+ export const Alert = memo ( ( props : AlertProps ) => {
72+ const {
73+ className,
74+ severity,
75+ as : HtmlTitleTag = "h3" ,
76+ classes = { } ,
77+ ref,
78+ small : isSmall ,
79+ title,
80+ description,
81+ closable : isClosable = false ,
82+ isClosed : props_isClosed ,
83+ onClose,
84+ ...rest
85+ } = props ;
86+
87+ assert < Equals < keyof typeof rest , never > > ( ) ;
88+
89+ const [ isClosed , setIsClosed ] = useState ( props_isClosed ?? false ) ;
90+
91+ const [ buttonElement , setButtonElement ] = useState < HTMLButtonElement | null > ( null ) ;
92+
93+ const refShouldButtonGetFocus = useRef ( false ) ;
94+ const refShouldSetRole = useRef ( false ) ;
95+
96+ useEffect ( ( ) => {
97+ if ( props_isClosed === undefined ) {
98+ return ;
99+ }
100+ setIsClosed ( isClosed => {
101+ if ( isClosed && ! props_isClosed ) {
102+ refShouldButtonGetFocus . current = true ;
103+ refShouldSetRole . current = true ;
117104 }
118105
119- refShouldButtonGetFocus . current = false ;
120- buttonElement . focus ( ) ;
121- } , [ buttonElement ] ) ;
122-
123- const onCloseButtonClick = useConstCallback ( ( ) => {
124- if ( props_isClosed === undefined ) {
125- //Uncontrolled
126- setIsClosed ( true ) ;
127- onClose ?.( ) ;
128- } else {
129- //Controlled
130- onClose ( ) ;
131- }
106+ return props_isClosed ;
132107 } ) ;
108+ } , [ props_isClosed ] ) ;
109+
110+ useEffect ( ( ) => {
111+ if ( ! refShouldButtonGetFocus . current ) {
112+ return ;
113+ }
133114
134- const { t } = useTranslation ( ) ;
115+ if ( buttonElement === null ) {
116+ //NOTE: This should not be reachable
117+ return ;
118+ }
135119
136- if ( isClosed ) {
137- return null ;
120+ refShouldButtonGetFocus . current = false ;
121+ buttonElement . focus ( ) ;
122+ } , [ buttonElement ] ) ;
123+
124+ const onCloseButtonClick = useConstCallback ( ( ) => {
125+ if ( props_isClosed === undefined ) {
126+ //Uncontrolled
127+ setIsClosed ( true ) ;
128+ onClose ?.( ) ;
129+ } else {
130+ //Controlled
131+ onClose ( ) ;
138132 }
133+ } ) ;
134+
135+ const { t } = useTranslation ( ) ;
139136
140- return (
141- < div
142- className = { cx (
143- fr . cx ( "fr-alert" , `fr-alert--${ severity } ` , { "fr-alert--sm" : isSmall } ) ,
144- classes . root ,
145- className
146- ) }
147- { ...( refShouldSetRole . current && { "role" : "alert" } ) }
148- ref = { ref }
149- { ...rest }
150- >
151- < HtmlTitleTag className = { cx ( fr . cx ( "fr-alert__title" ) , classes . title ) } >
152- { title }
153- </ HtmlTitleTag >
154- < p className = { classes . description } > { description } </ p >
155- { /* TODO: Use our button once we have one */ }
156- { isClosable && (
157- < button
158- ref = { setButtonElement }
159- className = { cx ( fr . cx ( "fr-link--close" , "fr-link" ) , classes . close ) }
160- onClick = { onCloseButtonClick }
161- >
162- { t ( "hide message" ) }
163- </ button >
164- ) }
165- </ div >
166- ) ;
167- } )
168- ) ;
137+ if ( isClosed ) {
138+ return null ;
139+ }
140+
141+ return (
142+ < div
143+ className = { cx (
144+ fr . cx ( "fr-alert" , `fr-alert--${ severity } ` , { "fr-alert--sm" : isSmall } ) ,
145+ classes . root ,
146+ className
147+ ) }
148+ { ...( refShouldSetRole . current && { "role" : "alert" } ) }
149+ ref = { ref }
150+ >
151+ < HtmlTitleTag className = { cx ( fr . cx ( "fr-alert__title" ) , classes . title ) } >
152+ { title }
153+ </ HtmlTitleTag >
154+ < p className = { classes . description } > { description } </ p >
155+ { /* TODO: Use our button once we have one */ }
156+ { isClosable && (
157+ < button
158+ ref = { setButtonElement }
159+ className = { cx ( fr . cx ( "fr-link--close" , "fr-link" ) , classes . close ) }
160+ onClick = { onCloseButtonClick }
161+ >
162+ { t ( "hide message" ) }
163+ </ button >
164+ ) }
165+ </ div >
166+ ) ;
167+ } ) ;
169168
170169Alert . displayName = symToStr ( { Alert } ) ;
171170
0 commit comments