wix-style-react
Version:
136 lines (114 loc) • 3.98 kB
JavaScript
import React from 'react';
import PropTypes from 'prop-types';
import SelectableAccordionItem from './Item';
import { WixStyleReactContext } from '../WixStyleReactProvider/context';
import { st, classes } from './SelectableAccordion.st.css';
import { VERTICAL_PADDING } from './constants';
/** SelectableAccordion */
class SelectableAccordion extends React.PureComponent {
static propTypes = {
/** Applied as data-hook HTML attribute that can be used in the tests */
dataHook: PropTypes.string,
/** A css class to be applied to the component's root element */
className: PropTypes.string,
/** A type can be ether radio, checkbox, or toggle, which will effect the way an accordion item is selected */
type: PropTypes.oneOf(['radio', 'checkbox', 'toggle']),
/** An array of Accordion items:
* - `title`: A title of the item
* - `subtitle`: An optional second row of the header
* - `content`: A content of the item
* - `initiallyOpen`: Whether the item is initially open
* - `open`: Whether the item is open
* - `disabled`: Whether the item is disabled
* */
items: PropTypes.arrayOf(
PropTypes.shape({
title: PropTypes.node,
subtitle: PropTypes.node,
content: PropTypes.node,
initiallyOpen: PropTypes.bool,
open: PropTypes.bool,
disabled: PropTypes.bool,
}),
),
/** Extra space on top and bottom of selectable accordion item */
verticalPadding: PropTypes.oneOf(['medium', 'small', 'tiny']),
/** A callback which is invoked every time the selection is changed */
onSelectionChanged: PropTypes.func,
};
static defaultProps = {
type: 'radio',
items: [],
};
static displayName = 'SelectableAccordion';
state = {
openIndices: this._populateInitiallyOpenIndices(),
};
static getDerivedStateFromProps({ items }, state) {
const controlled = items.some(item => item.open);
return controlled
? {
openIndices: items
.map((item, index) => (item.open ? index : null))
.filter(index => index !== null),
}
: state;
}
_populateInitiallyOpenIndices() {
const { items } = this.props;
const controlled = items.some(item => item.open);
return items
.map((item, index) =>
item[controlled ? 'open' : 'initiallyOpen'] ? index : null,
)
.filter(index => index !== null);
}
_onItemChanged = (idx, open) => {
const { type, onSelectionChanged } = this.props;
let openIndices;
if (open) {
if (type === 'radio') {
openIndices = [idx];
} else {
openIndices = [...this.state.openIndices, idx];
}
} else {
openIndices = [...this.state.openIndices].filter(
openIndex => idx !== openIndex,
);
}
this.setState(
{ openIndices },
() => onSelectionChanged && onSelectionChanged(openIndices),
);
};
render() {
const { dataHook, className, items, verticalPadding, type } = this.props;
const { openIndices } = this.state;
return (
<WixStyleReactContext.Consumer>
{({ reducedSpacingAndImprovedLayout }) => {
const defaultVerticalPadding = reducedSpacingAndImprovedLayout
? VERTICAL_PADDING.small
: VERTICAL_PADDING.medium;
return (
<div className={st(classes.root, className)} data-hook={dataHook}>
{items.map((item, idx) => (
<SelectableAccordionItem
key={idx}
idx={idx}
type={type}
verticalPadding={verticalPadding || defaultVerticalPadding}
onChange={this._onItemChanged}
{...item}
open={openIndices.includes(idx)}
/>
))}
</div>
);
}}
</WixStyleReactContext.Consumer>
);
}
}
export default SelectableAccordion;