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 {
@@ -18,7 +19,23 @@ export namespace AccordionProps {
1819 content : NonNullable < ReactNode > ;
1920 } ;
2021
21- export type Controlled = Common & { } ;
22+ export type Uncontrolled = Common & {
23+ defaultExpanded ?: boolean ;
24+ expanded ?: undefined ;
25+ onExpandedChange ?: (
26+ expanded : boolean ,
27+ e : React . MouseEvent < HTMLButtonElement , MouseEvent >
28+ ) => void ;
29+ } ;
30+
31+ export type Controlled = Common & {
32+ defaultExpanded ?: undefined ;
33+ expanded : boolean ;
34+ onExpandedChange : (
35+ expanded : boolean ,
36+ e : React . MouseEvent < HTMLButtonElement , MouseEvent >
37+ ) => void ;
38+ } ;
2239}
2340
2441/** @see <https://www.systeme-de-design.gouv.fr/elements-d-interface/composants/accordeon> */
@@ -30,20 +47,37 @@ export const Accordion = memo(
3047 label,
3148 classes = { } ,
3249 content,
50+ expanded : expandedProp ,
51+ defaultExpanded = false ,
52+ onExpandedChange,
3353 ...rest
3454 } = props ;
3555
3656 assert < Equals < keyof typeof rest , never > > ( ) ;
3757
3858 const id = useId ( ) ;
3959
60+ const [ expandedState , setExpandedState ] = useState ( defaultExpanded ) ;
61+
62+ const value = expandedProp ? expandedProp : expandedState ;
63+
64+ const onExtendButtonClick = useConstCallback (
65+ ( event : React . MouseEvent < HTMLButtonElement , MouseEvent > ) => {
66+ setExpandedState ( ! value ) ;
67+ if ( onExpandedChange ) {
68+ onExpandedChange ( ! value , event ) ;
69+ }
70+ }
71+ ) ;
72+
4073 return (
4174 < section className = { cx ( fr . cx ( "fr-accordion" ) , className ) } ref = { ref } { ...rest } >
4275 < HtmlTitleTag className = { cx ( fr . cx ( "fr-accordion__title" ) , classes . title ) } >
4376 < button
4477 className = { fr . cx ( "fr-accordion__btn" ) }
45- aria-expanded = "true"
78+ aria-expanded = { value }
4679 aria-controls = { `accordion-${ id } ` }
80+ onClick = { onExtendButtonClick }
4781 >
4882 { label }
4983 </ button >
0 commit comments