Skip to content

Commit d571d8b

Browse files
committed
controlled Accordion
1 parent 9c6c111 commit d571d8b

File tree

3 files changed

+48
-7
lines changed

3 files changed

+48
-7
lines changed

src/Accordion.tsx

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
import React, { forwardRef, memo, ReactNode, useId } from "react";
1+
import React, { forwardRef, memo, ReactNode, useId, useState } from "react";
22
import { assert } from "tsafe";
33
import type { Equals } from "tsafe";
44
import { fr } from "./lib";
55
import { cx } from "./lib/tools/cx";
66
import "@gouvfr/dsfr/dist/component/accordion/accordion.css";
77
import { 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 ...)
1213
export 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>

stories/Accordion.stories.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ _NOTE: In controlled mode there is no animation transition when expanding or col
1515
1616
\`\`\`tsx
1717
function ControlledAccordion() {
18-
18+
const [expanded,setExpanded] = useState(true)
1919
return (
20-
<Accordion label="Name of the Accordion" content="Content of the Accordion"/>
20+
<Accordion label="Name of the Accordion" content="Content of the Accordion" onChange={(e,value) => setExpanded(!value)} expanded={expanded}/>
2121
);
2222
2323
}
@@ -29,5 +29,6 @@ export default meta;
2929

3030
export const Default = getStory({
3131
"label": "Name of the Accordion",
32-
"content": "Content of the Accordion"
32+
"content": "Content of the Accordion",
33+
"defaultExpanded": false
3334
});
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import React, { useState } from "react"
2+
import { Accordion } from "@codegouvfr/react-dsfr/Accordion";
3+
4+
5+
export default function Test() {
6+
const [expanded, setExpanded] = useState<boolean>();
7+
return (
8+
<>
9+
<Accordion label="Accordion Uncontrolled" content="Content of the Uncontrolled Accordion" />
10+
<Accordion label="Accordion Controlled" content="Content of the controlled Accordion" defaultExpanded={true} expanded={expanded} onChange={(e, expanded) => { console.log(e); setExpanded(!expanded); }} />
11+
</>
12+
)
13+
}

0 commit comments

Comments
 (0)