Skip to content

Commit

Permalink
fix: make Accordion state internally controlled to facilitate dynamic…
Browse files Browse the repository at this point in the history
… openIndexes defaults
  • Loading branch information
jamiehenson committed Dec 16, 2024
1 parent 42ec294 commit 2e74991
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 16 deletions.
49 changes: 40 additions & 9 deletions src/core/Accordion.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import React, { ReactNode, useMemo, useState } from "react";
import React, {
ReactNode,
useCallback,
useEffect,
useMemo,
useState,
} from "react";
import {
AccordionContent,
AccordionItem,
Expand Down Expand Up @@ -55,6 +61,8 @@ export type AccordionProps = {
options?: AccordionOptions;
} & React.HTMLAttributes<HTMLDivElement>;

const rowKey = (index: number) => `accordion-item-${index}`;

const AccordionRow = ({
name,
children,
Expand All @@ -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,
Expand All @@ -84,7 +92,7 @@ const AccordionRow = ({

return (
<AccordionItem
value={rowKey}
value={indexRowKey}
className={cn({
[`${border}`]: border && !options?.hideBorders,
})}
Expand Down Expand Up @@ -161,9 +169,29 @@ const Accordion = ({
data.length,
]);

const [openRowValues, setOpenRowValues] = useState<string | string[]>(
openIndexes,
useEffect(() => {
setOpenRowValues(openIndexes);
}, [openIndexes]);

const [openRowValues, setOpenRowValues] = useState<string[]>(
() => 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) => (
<AccordionRow
key={item.name}
Expand All @@ -174,6 +202,9 @@ const Accordion = ({
options={options}
index={index}
onClick={() => {
setOpenRowValues((prevValues) =>
newOpenRowValues(prevValues, rowKey(index)),
);
item.onClick?.(index);
}}
openRowValues={openRowValues}
Expand All @@ -188,15 +219,15 @@ const Accordion = ({
<RadixAccordion
type="single"
collapsible
defaultValue={openIndexes[0]}
onValueChange={(values) => setOpenRowValues(values)}
value={openRowValues[0]}
onValueChange={(values) => setOpenRowValues([values])}
>
{innerAccordion}
</RadixAccordion>
) : (
<RadixAccordion
type="multiple"
defaultValue={openIndexes}
value={openRowValues}
onValueChange={(values) => setOpenRowValues(values)}
>
{innerAccordion}
Expand Down
37 changes: 30 additions & 7 deletions src/core/Accordion/Accordion.stories.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand Down Expand Up @@ -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 (
<div>
{AccordionPresentation({
data: data,
options: { defaultOpenIndexes: openIndexes },
})}{" "}
<button onClick={randomizeOpenIndexes} className="ui-button-primary-xs">
Randomize default open sections: (currently:{" "}
{Array.from(new Set(openIndexes))
.map((index) => index + 1)
.sort()
.join(", ")}
)
</button>
</div>
);
},
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.",
},
},
},
Expand Down

0 comments on commit 2e74991

Please sign in to comment.