From 6e925e03d98ec166cec1f5276fcd521b483e90ad Mon Sep 17 00:00:00 2001 From: Jamie Henson Date: Mon, 16 Dec 2024 13:58:26 +0000 Subject: [PATCH] fix: make Accordion state internally controlled to facilitate dynamic openIndexes defaults --- src/core/Accordion.tsx | 47 +++++++++++++++++++----- src/core/Accordion/Accordion.stories.tsx | 37 +++++++++++++++---- 2 files changed, 68 insertions(+), 16 deletions(-) diff --git a/src/core/Accordion.tsx b/src/core/Accordion.tsx index 06cf340c..3f2a7c1e 100644 --- a/src/core/Accordion.tsx +++ b/src/core/Accordion.tsx @@ -1,4 +1,10 @@ -import React, { ReactNode, useMemo, useState } from "react"; +import React, { + ReactNode, + useCallback, + useEffect, + useMemo, + useState, +} from "react"; import { AccordionContent, AccordionItem, @@ -55,6 +61,8 @@ export type AccordionProps = { options?: AccordionOptions; } & React.HTMLAttributes; +const rowKey = (index: number) => `accordion-item-${index}`; + const AccordionRow = ({ name, children, @@ -67,8 +75,8 @@ const AccordionRow = ({ openRowValues, }: AccordionRowProps) => { const { selectable, sticky } = options || {}; - const rowKey = `accordion-item-${index}`; - const isOpen = openRowValues.includes(rowKey); + const indexRowKey = rowKey(index); + const isOpen = openRowValues.includes(indexRowKey); const { text, @@ -84,7 +92,7 @@ const AccordionRow = ({ return ( ( - openIndexes, + useEffect(() => { + setOpenRowValues(openIndexes); + }, [openIndexes]); + + const [openRowValues, setOpenRowValues] = useState(openIndexes); + + const newOpenRowValues = useCallback( + (values: string[], key: string) => { + // With auto-close mode, we only deal with one open row at a time + if (options?.autoClose) { + return [key]; + } + + // Otherwise, toggle the presence of the clicked row in the open indexes array + return values.includes(key) + ? values.filter((value) => value !== key) + : [...values, key].sort(); + }, + [options?.autoClose], ); + const innerAccordion = data.map((item, index) => ( { + setOpenRowValues((prevValues) => + newOpenRowValues(prevValues, rowKey(index)), + ); item.onClick?.(index); }} openRowValues={openRowValues} @@ -188,15 +217,15 @@ const Accordion = ({ setOpenRowValues(values)} + value={openRowValues[0]} + onValueChange={(values) => setOpenRowValues([values])} > {innerAccordion} ) : ( setOpenRowValues(values)} > {innerAccordion} diff --git a/src/core/Accordion/Accordion.stories.tsx b/src/core/Accordion/Accordion.stories.tsx index d9a16e68..44c9fbfd 100644 --- a/src/core/Accordion/Accordion.stories.tsx +++ b/src/core/Accordion/Accordion.stories.tsx @@ -1,4 +1,4 @@ -import React, { Fragment } from "react"; +import React, { useState } from "react"; import Accordion, { AccordionProps } from "../Accordion"; import { IconName } from "../Icon/types"; import { accordionThemes } from "./types"; @@ -133,16 +133,39 @@ export const StickyHeaders = { }; export const WithDefaultOpenSections = { - render: () => - AccordionPresentation({ - data: data, - options: { defaultOpenIndexes: [1] }, - }), + render: () => { + const [openIndexes, setOpenIndexes] = useState([1]); + + const randomizeOpenIndexes = () => { + const randomIndexes = Array.from( + { length: Math.floor(Math.random() * 3) + 1 }, + () => Math.floor(Math.random() * 5), + ); + setOpenIndexes(randomIndexes); + }; + + return ( +
+ {AccordionPresentation({ + data: data, + options: { defaultOpenIndexes: openIndexes }, + })}{" "} + +
+ ); + }, parameters: { docs: { description: { story: - "The sections that correspond to the supplied indexes will be open by default. (e.g. `[1]` will open the second section). Set with `defaultOpenIndexes` on `options`.", + "The sections that correspond to the supplied indexes will be open by default. (e.g. `[1]` will open the second section). Set with `defaultOpenIndexes` on `options`. To demonstrate, use the button to randomize the open sections.", }, }, },