wix-style-react
Version:
213 lines (186 loc) • 5.94 kB
JavaScript
import React from 'react';
import PropTypes from 'prop-types';
import { st, classes } from './Accordion.st.css';
import AccordionItem from './AccordionItem';
import { WixStyleReactContext } from '../WixStyleReactProvider/context';
import AccordionSectionItem from './AccordionSectionItem';
import { horizontalPaddings, skins } from './constants';
class Accordion extends React.Component {
static displayName = 'Accordion';
static propTypes = {
/** Applied as data-hook HTML attribute that can be used to create driver in testing */
dataHook: PropTypes.string,
/** allow multiple rows to be opened simultaneously */
multiple: PropTypes.bool,
/** Accordion skin color */
skin: PropTypes.oneOf(['standard', 'light', 'neutral']),
/** Hide Accordion shadow effect */
hideShadow: PropTypes.bool,
/** Change items size */
size: PropTypes.oneOf(['tiny', 'small', 'medium', 'large']),
/** Change horizontal padding */
horizontalPadding: PropTypes.oneOf(['tiny', 'small', 'medium', 'large']),
/** Change expand and collapse animation speed */
transitionSpeed: PropTypes.oneOf(['slow', 'medium', 'fast']),
/** Callback fired immediately after the animation is started */
onAnimationEnter: PropTypes.func,
/** Callback fired immediately after the animation is ended */
onAnimationExit: PropTypes.func,
/** accordion items nodes */
items: PropTypes.oneOfType([
PropTypes.arrayOf(
PropTypes.shape({
render: PropTypes.func,
}),
),
PropTypes.arrayOf(
PropTypes.shape({
title: PropTypes.node,
subtitle: PropTypes.node,
icon: PropTypes.node,
children: PropTypes.node,
expandLabel: PropTypes.node,
collapseLabel: PropTypes.node,
showLabel: PropTypes.oneOf(['hover', 'always']),
buttonType: PropTypes.oneOf(['textButton', 'button', 'node']),
disabled: PropTypes.bool,
onToggle: PropTypes.func,
onMouseEnter: PropTypes.func,
onMouseleave: PropTypes.func,
open: PropTypes.bool,
initiallyOpen: PropTypes.bool,
}),
),
]),
};
static defaultProps = {
items: [],
multiple: false,
skin: skins.standard,
horizontalPadding: horizontalPaddings.large,
hideShadow: false,
transitionSpeed: 'medium',
};
_findOpenIndexes = (items, initial) =>
items
.map((item, index) =>
(initial && item.initiallyOpen) || item.open ? index : null,
)
.filter(index => index !== null);
constructor(props) {
super(props);
this.state = {
openIndexes: this._findOpenIndexes(this.props.items, true),
};
}
_compareOpenItems = (currentItems, prevItems) => {
if (prevItems.length !== currentItems.length) {
return false;
}
for (let i = 0; i < prevItems.length; i++) {
if (prevItems[i].open !== currentItems[i].open) {
return false;
}
}
return true;
};
componentDidUpdate = prevProps => {
if (!this._compareOpenItems(this.props.items, prevProps.items)) {
this.setState({
openIndexes: this._findOpenIndexes(this.props.items),
});
}
};
_toggle = index => () =>
this.setState(({ openIndexes }) => ({
openIndexes: openIndexes.includes(index)
? openIndexes.filter(i => i !== index)
: this.props.multiple
? [...openIndexes, index]
: [index],
}));
render() {
const { openIndexes } = this.state;
const {
dataHook,
items,
skin,
hideShadow,
size,
horizontalPadding,
transitionSpeed,
onAnimationEnter,
onAnimationExit,
} = this.props;
return (
<WixStyleReactContext.Consumer>
{({ reducedSpacingAndImprovedLayout }) => {
const defaultSize = reducedSpacingAndImprovedLayout
? 'small'
: 'large';
return (
<div
data-hook={dataHook}
className={classes.accordion}
data-transition-speed={transitionSpeed}
data-horizontal-padding={horizontalPadding}
data-skin={skin}
>
{items.map((item, index, allItems) => {
const uncontrolledProps = {
onToggle: this._toggle(index),
open: openIndexes.includes(index),
onAnimationEnter,
onAnimationExit,
};
const last = index === allItems.length - 1;
const internalProps = {
className: st(classes.item, { last }),
key: index,
skin,
hideShadow,
size: size || defaultSize,
horizontalPadding,
last,
transitionSpeed,
};
if (typeof item.render === 'function') {
return item.render(uncontrolledProps, internalProps);
} else {
return (
<AccordionItem
{...uncontrolledProps}
{...item}
{...internalProps}
/>
);
}
})}
</div>
);
}}
</WixStyleReactContext.Consumer>
);
}
}
export const accordionItemBuilder = item => {
return {
...item,
render: (uncontrolledProps, internalProps) => (
<AccordionItem {...uncontrolledProps} {...item} {...internalProps} />
),
};
};
export const accordionSectionItemBuilder = item => {
return {
...item,
render: (uncontrolledProps, internalProps) => (
<AccordionSectionItem
{...uncontrolledProps}
{...item}
{...internalProps}
/>
),
};
};
export default Accordion;