@spaced-out/ui-design-system
Version:
Sense UI components library
117 lines (102 loc) • 2.9 kB
Flow
// @flow strict
import * as React from 'react';
import classify from '../../utils/classify';
import {Button} from '../Button';
import {Toggle} from '../Toggle';
import css from './Accordion.module.css';
export type AccordionClassNames = $ReadOnly<{
wrapper?: string,
headerWrapper?: string,
header?: string,
content?: string,
}>;
export type AccordionPropsType = {
id: string,
header: React.Node,
children: React.Node,
classNames?: AccordionClassNames,
isOpen?: boolean,
disabled?: boolean,
showToggle?: boolean,
onChange?: ?(id?: string, isOpen: boolean) => mixed,
onToggle?: ?(id?: string, checked?: boolean) => mixed,
};
export const Accordion: React$AbstractComponent<
AccordionPropsType,
HTMLDivElement,
> = React.forwardRef<AccordionPropsType, HTMLDivElement>(
(
{
id,
header,
onChange,
onToggle,
disabled = false,
classNames,
isOpen = false,
showToggle = false,
children,
}: AccordionPropsType,
ref,
) => {
const [accordionIsOpen, setAccordionIsOpen] = React.useState(isOpen);
const [checked, setChecked] = React.useState<boolean>(false);
const handleToggle = () => {
setChecked(!checked);
onToggle?.(id, !checked);
};
const handleAccordionClick = () => {
if (!disabled) {
onChange?.(id, !accordionIsOpen);
setAccordionIsOpen((prev) => !prev);
}
};
React.useEffect(() => {
setAccordionIsOpen(isOpen);
}, [isOpen]);
return (
<div
ref={ref}
data-testid="Accordion"
className={classify(css.wrapper, classNames?.wrapper)}
>
<div
className={classify(css.headerWrapper, classNames?.headerWrapper)}
onClick={handleAccordionClick}
>
<div className={classify(css.header, classNames?.header)}>
{showToggle && (
<div
onClick={(e) => {
e.stopPropagation(); // prevent toggle click from triggering accordion toggle
}}
>
<Toggle
checked={checked}
onChange={handleToggle}
ariaLabel={'toggle'}
/>
</div>
)}
{header}
</div>
<Button
iconLeftName={accordionIsOpen ? 'chevron-up' : 'chevron-down'}
disabled={disabled}
size="small"
type="ghost"
className={css.icon}
ariaLabel={'Collapse button'}
// we are not defining onClick here as we are using onClick of the parent here.
/>
</div>
{accordionIsOpen && (
<div className={classify(css.content, classNames?.content)}>
{children}
</div>
)}
</div>
);
},
);
Accordion.displayName = 'Accordion';