Collapsible accordion
Renders an accordion menu with multiple collapsible content elements.
- Define an
AccordionItem
component, that renders a<button>
. The button updates the component and notifies its parent via thehandleClick
callback. - Use the
isCollapsed
prop inAccordionItem
to determine its appearance and set itsclassName
. - Define an
Accordion
component. Use theuseState()
hook to initialize the value of thebindIndex
state variable todefaultIndex
. - Filter
children
to remove unnecessary nodes except forAccordionItem
by identifying the function's name. - Use
Array.prototype.map()
on the collected nodes to render the individual collapsible elements. - Define
changeItem
, which will be executed when clicking anAccordionItem
's<button>
. changeItem
executes the passed callback,onItemClick
, and updatesbindIndex
based on the clicked element.
.accordion-item.collapsed {
display: none;
}
.accordion-item.expanded {
display: block;
}
.accordion-button {
display: block;
width: 100%;
}
const AccordionItem = ({ label, isCollapsed, handleClick, children }) => {
return (
<>
<button className="accordion-button" onClick={handleClick}>
{label}
</button>
<div
className={`accordion-item ${isCollapsed ? 'collapsed' : 'expanded'}`}
aria-expanded={isCollapsed}
>
{children}
</div>
</>
);
};
const Accordion = ({ defaultIndex, onItemClick, children }) => {
const [bindIndex, setBindIndex] = React.useState(defaultIndex);
const changeItem = itemIndex => {
if (typeof onItemClick === 'function') onItemClick(itemIndex);
if (itemIndex !== bindIndex) setBindIndex(itemIndex);
};
const items = children.filter(item => item.type.name === 'AccordionItem');
return (
<>
{items.map(({ props }) => (
<AccordionItem
isCollapsed={bindIndex !== props.index}
label={props.label}
handleClick={() => changeItem(props.index)}
children={props.children}
/>
))}
</>
);
};