wix-style-react
Version:
wix-style-react
173 lines (153 loc) • 5.25 kB
JavaScript
import React from 'react';
import PropTypes from 'prop-types';
import Card from '../Card';
import WixComponent from '../BaseComponents/WixComponent';
import styles from './StatsWidget.scss';
import Heading from '../Heading';
import SortByArrowUp from '../new-icons/system/SortByArrowUp';
import SortByArrowDown from '../new-icons/system/SortByArrowDown';
import ButtonWithOptions from '../ButtonWithOptions';
import Badge from '../Badge';
import DropdownBase from '../DropdownBase';
import TextButton from '../TextButton';
import ChevronDown from '../new-icons/ChevronDown';
import deprecationLog from '../utils/deprecationLog';
export const deprecationMessage = `Usage of <StatsWidget.Filter/> with <ButtonWithOptions/> is deprecated, use the newer <StatsWidget.FilterButton/> instead.`;
function renderTrend(percent, invertPercentColor) {
const badgeProps = {
icon: null,
skin: null,
dataHook: 'percent-value',
type: 'transparent',
};
//TODO - the data-class is just a hack in order not to break the testkit function that exposes it
if (percent > 0) {
badgeProps.prefixIcon = (
<SortByArrowUp data-hook="percent-icon" data-class="isPositive" />
);
badgeProps.skin = invertPercentColor ? 'danger' : 'success';
} else if (percent < 0) {
badgeProps.prefixIcon = (
<SortByArrowDown data-hook="percent-icon" data-class="isNegative" />
);
badgeProps.skin = invertPercentColor ? 'success' : 'danger';
} else {
badgeProps.prefixIcon = null;
badgeProps.skin = 'neutral';
}
return <Badge {...badgeProps}>{Math.abs(percent)}%</Badge>;
}
/**
* Component for app widget in Business Manager
*/
class StatsWidget extends WixComponent {
static propTypes = {
/** Widget title */
title: PropTypes.string.isRequired,
/** Statistics to display:
*
* `invertPercentColor`: Change color of `percent` prop marking negative percent as `success` and positive as `danger` */
statistics: PropTypes.arrayOf(
PropTypes.shape({
title: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
.isRequired,
subtitle: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
.isRequired,
percent: PropTypes.number,
invertPercentColor: PropTypes.bool,
}),
),
/**
* Filters for statistics (will be shown in right top corner). Accepts an array of
* `<StatsWidget.FilterButton>` which accepts all `<DropdownBase/>` props.
*/
children: (props, propName) => {
if (!props[propName]) {
return;
}
const childrenArray = [].concat(props[propName]);
if (childrenArray.some(child => child.type === StatsWidget.Filter)) {
deprecationLog(deprecationMessage);
}
if (childrenArray.length > 3) {
return new Error(
`Invalid Prop children, maximum amount of filters are 3`,
);
}
// TODO: when deprecating <StatsWidget.Filter/>, remove it from the validation
if (
childrenArray.some(
child =>
child.type !== StatsWidget.Filter &&
child.type !== StatsWidget.FilterButton,
)
) {
return new Error(
`StatsWidget: Invalid Prop children, only <StatsWidget.FilterButton/> is allowed`,
);
}
},
emptyState: PropTypes.node,
};
_renderColumn(statistics, index) {
return (
<div
className={styles.statsColumn}
key={index}
data-hook="statistics-item"
>
<Heading dataHook="statistics-item-title" appearance="H1">
{statistics.title}
</Heading>
<Heading dataHook="statistics-item-subtitle" appearance="H5">
{statistics.subtitle}
</Heading>
{typeof statistics.percent === 'number' &&
renderTrend(statistics.percent, statistics.invertPercentColor)}
</div>
);
}
_renderFilters(filters) {
return <div className={styles.filtersWrapper}>{filters}</div>;
}
render() {
const { title, statistics, children, emptyState } = this.props;
return (
<Card>
<Card.Header
dataHook="stats-widget-title"
title={title}
suffix={this._renderFilters(children)}
/>
<Card.Content>
{statistics ? (
<div
className={styles.statsColumnWrapper}
data-hook="stats-widget-content-wrapper"
>
{statistics.map((stat, index) => this._renderColumn(stat, index))}
</div>
) : (
<div data-hook="stats-widget-empty-state">{emptyState}</div>
)}
</Card.Content>
</Card>
);
}
}
const FilterButton = props => (
<DropdownBase minWidth={160} {...props}>
{({ toggle, selectedOption = {} }) => {
return (
<TextButton suffixIcon={<ChevronDown />} onClick={toggle} skin="dark">
{selectedOption.value || 'Choose a filter'}
</TextButton>
);
}}
</DropdownBase>
);
FilterButton.displayName = 'StatsWidget.FilterButton';
StatsWidget.Filter = ButtonWithOptions;
StatsWidget.FilterButton = FilterButton;
StatsWidget.displayName = 'StatsWidget';
export default StatsWidget;