import type { AriaAttributes, HTMLAttributes, ReactElement } from 'react' import React, { cloneElement, createContext, forwardRef, useContext, } from 'react' interface AccordionContext { indices: Set onChange: (index: number) => void numberOfItems: number } const AccordionContext = createContext(undefined) export interface AccordionProps extends Omit, 'onChange'> { /** * ID to find this component in testing tools (e.g.: cypress, * testing-library, and jest). */ testId?: string /** * Indices that indicate which accordion items are opened. */ indices: Iterable /** * Function that is triggered when an accordion item is opened/closed. */ onChange: (index: number) => void /** * For accessibility purposes, define a string that labels the current accodion. */ 'aria-label'?: AriaAttributes['aria-label'] } const Accordion = forwardRef(function Accordion( { testId = 'fs-accordion', indices, onChange, children, 'aria-label': ariaLabel, ...otherProps }, ref ) { const childrenWithIndex = React.Children.map( children as ReactElement, (child, index) => { if (!child) return return cloneElement(child, { index: child.props.index ?? index }) } ) const context = { indices: new Set(indices), onChange, numberOfItems: childrenWithIndex.length, } return (
{childrenWithIndex}
) }) export function useAccordion() { const context = useContext(AccordionContext) if (context === undefined) { throw new Error( 'Do not use Accordion components outside the Accordion context.' ) } return context } export default Accordion