@@ -9,6 +9,8 @@ import type { FrIconClassName, RiIconClassName } from "./fr/generatedFromCss/cla
99import Button , { ButtonProps } from "./Button" ;
1010import { capitalize } from "tsafe/capitalize" ;
1111import { uncapitalize } from "tsafe/uncapitalize" ;
12+ import { typeGuard } from "tsafe/typeGuard" ;
13+ import { overwriteReadonlyProp } from "tsafe/lab/overwriteReadonlyProp" ;
1214
1315export type ModalProps = {
1416 className ?: string ;
@@ -32,7 +34,12 @@ export namespace ModalProps {
3234 doClosesModal ?: boolean ;
3335 } ;
3436
35- export type ModalButtonProps = Pick < ButtonProps , "onClick" | "nativeButtonProps" > ;
37+ export type ModalButtonProps = {
38+ "nativeButtonProps" : {
39+ "aria-controls" : string ;
40+ "data-fr-opened" : boolean ;
41+ } ;
42+ } ;
3643}
3744
3845const Modal = memo (
@@ -190,38 +197,28 @@ addModalTranslations({
190197
191198export { addModalTranslations } ;
192199
193- function createOpenModalButtonProps ( params : {
194- modalId : string ;
195- isOpenedByDefault : boolean ;
196- } ) : ModalProps . ModalButtonProps {
197- const { modalId, isOpenedByDefault } = params ;
198-
199- return {
200- //For RSC we don't want to pass an empty function.
201- "onClick" : undefined as any as ( ) => void ,
202- "nativeButtonProps" : {
203- "aria-controls" : modalId ,
204- "data-fr-opened" : isOpenedByDefault
205- }
206- } ;
207- }
208-
209200let counter = 0 ;
210201
211202/** @see <https://react-dsfr-components.etalab.studio/?path=/docs/components-modal> */
212203export function createModal < Name extends string > ( params : {
213204 name : Name ;
214205 isOpenedByDefault : boolean ;
215206} ) : Record < `${Uncapitalize < Name > } ModalButtonProps`, ModalProps . ModalButtonProps > &
216- Record < `${Capitalize < Name > } Modal`, ( props : ModalProps ) => JSX . Element > {
207+ Record < `${Capitalize < Name > } Modal`, ( props : ModalProps ) => JSX . Element > &
208+ Record < `close${Capitalize < Name > } Modal`, ( ) => void > &
209+ Record < `open${Capitalize < Name > } Modal`, ( ) => void > {
217210 const { name, isOpenedByDefault } = params ;
218211
219212 const modalId = `${ uncapitalize ( name ) } -modal-${ counter ++ } ` ;
220213
221- const openModalButtonProps = createOpenModalButtonProps ( {
222- modalId,
223- isOpenedByDefault
224- } ) ;
214+ const openModalButtonProps : ModalProps . ModalButtonProps = {
215+ "nativeButtonProps" : {
216+ "aria-controls" : modalId ,
217+ "data-fr-opened" : isOpenedByDefault
218+ }
219+ } ;
220+
221+ const hiddenControlButtonId = `${ modalId } -hidden-control-button` ;
225222
226223 function InternalModal ( props : ModalProps ) {
227224 return (
@@ -238,10 +235,41 @@ export function createModal<Name extends string>(params: {
238235
239236 InternalModal . displayName = `${ capitalize ( name ) } Modal` ;
240237
241- Object . defineProperty ( InternalModal , "name" , { "value" : InternalModal . displayName } ) ;
238+ overwriteReadonlyProp ( InternalModal as any , "name" , InternalModal . displayName ) ;
239+
240+ function openModal ( ) {
241+ const hiddenControlButton = document . getElementById ( hiddenControlButtonId ) ;
242+
243+ assert ( hiddenControlButton !== null , "Modal isn't mounted" ) ;
244+
245+ hiddenControlButton . click ( ) ;
246+ }
247+
248+ overwriteReadonlyProp ( openModal as any , "name" , `open${ capitalize ( name ) } Modal` ) ;
249+
250+ function closeModal ( ) {
251+ const modalElement = document . getElementById ( modalId ) ;
252+
253+ assert ( modalElement !== null , "Modal isn't mounted" ) ;
254+
255+ const closeButtonElement = modalElement . querySelector ( `.${ fr . cx ( "fr-btn--close" ) } ` ) ;
256+
257+ assert ( closeButtonElement !== null ) ;
258+
259+ assert (
260+ typeGuard < HTMLButtonElement > ( closeButtonElement , "click" in closeButtonElement ) ,
261+ "Close button isn't a button"
262+ ) ;
263+
264+ closeButtonElement . click ( ) ;
265+ }
266+
267+ overwriteReadonlyProp ( closeModal as any , "name" , `close${ capitalize ( name ) } Modal` ) ;
242268
243269 return {
244270 [ InternalModal . displayName ] : InternalModal ,
245- [ `${ uncapitalize ( name ) } ModalButtonProps` ] : openModalButtonProps
271+ [ `${ uncapitalize ( name ) } ModalButtonProps` ] : openModalButtonProps ,
272+ [ openModal . name ] : openModal ,
273+ [ closeModal . name ] : closeModal
246274 } as any ;
247275}
0 commit comments