UNPKG

wix-style-react

Version:
213 lines (186 loc) • 5.94 kB
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;