1- import React , { forwardRef , memo , ReactNode , useId } from "react" ;
1+ import React , { forwardRef , memo , ReactNode , useId , useState } from "react" ;
22import { assert } from "tsafe" ;
33import type { Equals } from "tsafe" ;
44import { fr } from "./lib" ;
55import { cx } from "./lib/tools/cx" ;
66import "@gouvfr/dsfr/dist/component/accordion/accordion.css" ;
77import { symToStr } from "tsafe/symToStr" ;
8+ import { useConstCallback } from "./lib/tools/powerhooks/useConstCallback" ;
89
9- export type AccordionProps = AccordionProps . Controlled ;
10+ export type AccordionProps = AccordionProps . Controlled | AccordionProps . Uncontrolled ;
1011
1112//TODO Controlled mode (callback onClick, expended etc ...)
1213export namespace AccordionProps {
@@ -16,9 +17,18 @@ export namespace AccordionProps {
1617 label : ReactNode ;
1718 classes ?: Partial < Record < "root" | "accordion" | "title" | "collapse" , string > > ;
1819 content : NonNullable < ReactNode > ;
20+ defaultExpanded ?: boolean ;
1921 } ;
2022
21- export type Controlled = Common & { } ;
23+ export type Uncontrolled = Common & {
24+ expanded ?: undefined ;
25+ onChange ?: ( e : React . MouseEvent < HTMLButtonElement , MouseEvent > , expanded : boolean ) => void ;
26+ } ;
27+
28+ export type Controlled = Common & {
29+ expanded : boolean ;
30+ onChange : ( e : React . MouseEvent < HTMLButtonElement , MouseEvent > , expanded : boolean ) => void ;
31+ } ;
2232}
2333
2434/** @see <https://www.systeme-de-design.gouv.fr/elements-d-interface/composants/accordeon> */
@@ -30,20 +40,37 @@ export const Accordion = memo(
3040 label,
3141 classes = { } ,
3242 content,
43+ expanded : expandedProp ,
44+ defaultExpanded = false ,
45+ onChange,
3346 ...rest
3447 } = props ;
3548
3649 assert < Equals < keyof typeof rest , never > > ( ) ;
3750
3851 const id = useId ( ) ;
3952
53+ const [ expandedState , setExpandedState ] = useState ( defaultExpanded ) ;
54+
55+ const value = expandedProp ? expandedProp : expandedState ;
56+
57+ const handleChange = useConstCallback (
58+ ( event : React . MouseEvent < HTMLButtonElement , MouseEvent > ) => {
59+ setExpandedState ( ! value ) ;
60+ if ( onChange ) {
61+ onChange ( event , ! value ) ;
62+ }
63+ }
64+ ) ;
65+
4066 return (
4167 < section className = { cx ( fr . cx ( "fr-accordion" ) , className ) } ref = { ref } { ...rest } >
4268 < HtmlTitleTag className = { cx ( fr . cx ( "fr-accordion__title" ) , classes . title ) } >
4369 < button
4470 className = { fr . cx ( "fr-accordion__btn" ) }
45- aria-expanded = "true"
71+ aria-expanded = { value }
4672 aria-controls = { `accordion-${ id } ` }
73+ onClick = { handleChange }
4774 >
4875 { label }
4976 </ button >
0 commit comments