@spaced-out/ui-design-system
Version:
Sense UI components library
147 lines (129 loc) • 3.57 kB
Flow
// @flow strict
import * as React from 'react';
import classify from '../../utils/classify';
import {UnstyledButton} from '../Button';
import type {IconType} from '../Icon';
import {Icon} from '../Icon';
import {Truncate} from '../Truncate';
import css from './CollapsibleCard.module.css';
export const COLLAPSIBLE_CARD_SEMANTIC = Object.freeze({
neutral: 'neutral',
success: 'success',
information: 'information',
warning: 'warning',
danger: 'danger',
});
type ClassNames = $ReadOnly<{
wrapper?: string,
title?: string,
content?: string,
}>;
export type CollapsibleCardSemanticType = $Values<
typeof COLLAPSIBLE_CARD_SEMANTIC,
>;
export type CollapsibleCardProps = {
id?: string,
title: React.Node,
children: React.Node,
classNames?: ClassNames,
isOpen?: boolean,
headerIconName?: string,
headerIconType?: IconType,
semantic?: CollapsibleCardSemanticType,
onChange?: ?(
e: SyntheticEvent<HTMLElement>,
isOpen: boolean,
id?: string,
) => mixed,
};
export type CollapsibleCardActionProps = {
children: React.Node,
className?: string,
};
export const CollapsibleCardAction = ({
children,
className,
}: CollapsibleCardActionProps): React.Node => (
<div className={classify(css.actionContainer, className)}>{children}</div>
);
export type CollapsibleCardContentProps = {
children: React.Node,
className?: string,
};
export const CollapsibleCardContent = ({
children,
className,
}: CollapsibleCardActionProps): React.Node => (
<div className={classify(css.contentContainer, className)}>{children}</div>
);
export const CollapsibleCard: React$AbstractComponent<
CollapsibleCardProps,
HTMLDivElement,
> = React.forwardRef<CollapsibleCardProps, HTMLDivElement>(
(
{
id,
isOpen,
onChange,
classNames,
title,
children,
semantic = 'neutral',
headerIconName,
headerIconType = 'solid',
}: CollapsibleCardProps,
ref,
): React.Node => {
const [collapsibleCardIsOpen, setCollapsibleCardIsOpen] = React.useState(
Boolean(isOpen),
);
React.useEffect(() => {
setCollapsibleCardIsOpen(Boolean(isOpen));
}, [isOpen]);
return (
<div
data-testid="CollapsibleCard"
className={classify(css.wrapper, classNames?.wrapper)}
ref={ref}
>
<UnstyledButton
className={classify(
css.header,
{
[css.isOpen]: collapsibleCardIsOpen,
},
classNames?.title,
)}
onClick={(e) => {
onChange?.(e, !collapsibleCardIsOpen, id);
setCollapsibleCardIsOpen(!collapsibleCardIsOpen);
}}
>
<div className={css.headerContent}>
{!!headerIconName && (
<div className={classify(css.iconContainer, css[semantic])}>
<Icon
name={headerIconName}
type={headerIconType}
size="small"
className={css[semantic]}
/>
</div>
)}
<Truncate>{title}</Truncate>
</div>
{collapsibleCardIsOpen ? (
<Icon name="chevron-up" color="secondary" size="small" />
) : (
<Icon name="chevron-down" color="secondary" size="small" />
)}
</UnstyledButton>
{collapsibleCardIsOpen && (
<div className={classify(css.content, classNames?.content)}>
{children}
</div>
)}
</div>
);
},
);