wix-style-react
Version:
wix-style-react
182 lines • 8.04 kB
JavaScript
import React from 'react';
import PropTypes from 'prop-types';
import Tooltip from '../Tooltip';
import { TooltipCommonProps } from '../common/PropTypes/TooltipCommon';
import { st, classes } from './StackedBarChart.st.css';
import { stVars as colors } from '../Foundation/stylable/colors.st.css';
import { scaleLinear, scaleBand } from 'd3-scale';
import { select } from 'd3-selection';
import { axisRight } from 'd3-axis';
import { format } from 'd3-format';
import { dataHooks } from './constants';
import Text from '../Text';
import { WixStyleReactContext } from '../WixStyleReactProvider/context';
const defaultYAxisTickFormat = format(',');
const CONSTANTS = {
top: 30,
right: 30,
bottom: 30,
left: 100,
gap: 18,
barWidth: 48,
};
/** StackedBarChart */
class StackedBarChart extends React.PureComponent {
constructor(props) {
super(props);
this._getAmountOfBarsCanRender = data => {
const { width } = this.props;
const result = [];
const availableWidthForBars = width - this.margin.left;
const barWidth = CONSTANTS.barWidth + CONSTANTS.gap;
const availableBarsForRender = Math.floor(availableWidthForBars / barWidth);
for (let i = 0; i < availableBarsForRender; i++) {
if (!!data[i]) {
result.push(data[i]);
}
}
return result;
};
this._update = () => {
const { data, yAxisTickFormat, width } = this.props;
const { svg, x, y } = this.state;
// Data
const _data = this._getAmountOfBarsCanRender(data).map(d => ({
...d,
sum: d.values.reduce((a, b) => a + b, 0),
}));
const innerPadding = _data.length > 4 ? 0.8 : 0.75;
const scalesPadding = 0.5;
const scaleWidth = _data.length * (CONSTANTS.barWidth + CONSTANTS.gap) + CONSTANTS.gap;
// Scales
const _x = x
.domain(_data.map(d => d.label))
.range([0, scaleWidth])
.paddingInner(innerPadding)
.paddingOuter(scalesPadding)
.round(true);
const _y = y.domain([0, Math.max(..._data.map(d => d.sum))]);
const yAxis = axisRight(_y)
.tickSize(width)
.tickFormat(d => yAxisTickFormat(d, defaultYAxisTickFormat(d)))
.ticks(4);
svg
.select(`[data-hook="${dataHooks.yAxis}"]`)
.call(yAxis)
.selectAll('.tick text')
.attr('x', -6)
.attr('dy', 4);
this.setState({
data: _data,
x: _x,
y: _y,
yAxis,
width,
});
};
this.chart = React.createRef();
window.chart = this;
this.data = [];
this.colors = [colors.A1, colors.A6];
this.newBrandingColors = [colors.A1, colors.A2];
this.margin = Object.assign(CONSTANTS, props.margin);
this.height = props.height;
this.chartHeight = this.height - this.margin.top - this.margin.bottom;
this.state = {
data: [],
x: scaleBand().range([0, props.weight]),
y: scaleLinear().range([this.chartHeight, 0]),
width: props.weight,
};
}
componentDidMount() {
this.setState({
svg: select(this.chart.current),
}, this._update);
}
componentDidUpdate(prevProps) {
if (prevProps.data !== this.props.data) {
this.setState({
svg: select(this.chart.current),
}, this._update);
return true;
}
}
render() {
const { tooltipTemplate, tooltipCommonProps, className, dataHook } = this.props;
const { data, x, y, width } = this.state;
return (React.createElement(WixStyleReactContext.Consumer, null, ({ newColorsBranding }) => (React.createElement("div", { "data-hook": dataHook, className: st(classes.root, {}, className) },
React.createElement("svg", { ref: this.chart, width: width, height: this.height },
React.createElement("g", { "data-hook": dataHooks.yAxis, className: classes.yAxis, transform: `translate(${this.margin.left}, ${this.margin.top})` }),
React.createElement("g", { "data-hook": dataHooks.bars, transform: `translate(${this.margin.left}, ${this.margin.top})` }, data.map((d, itemIndex) => {
let stacked = this.chartHeight;
return (React.createElement("g", { key: itemIndex }, d.values.map((value, index) => {
const height = this.chartHeight - y(value);
stacked -= height;
return (React.createElement("rect", { key: index, fill: newColorsBranding
? this.newBrandingColors[index]
: this.colors[index], height: height, width: CONSTANTS.barWidth, x: x(d.label) - 25, y: stacked }));
})));
}))),
data &&
data.map((d, index) => (React.createElement("div", { key: index, className: classes.textPosition, style: {
left: x(d.label) + this.margin.left - 25,
top: this.height - 15,
} },
React.createElement(Text, { textAlign: "center", skin: "standard", light: true, secondary: true, weight: "normal", size: "small", ellipsis: true, "data-hook": dataHooks.xAxis }, d.label)))),
tooltipTemplate &&
data.map((d, index) => (React.createElement("div", { key: index, className: classes.tooltip, style: {
left: x(d.label) + this.margin.left - 25,
top: y(d.sum) + this.margin.top,
} },
React.createElement(Tooltip, { ...tooltipCommonProps, content: tooltipTemplate(d), dataHook: dataHooks.tooltip },
React.createElement("button", { className: classes.tooltipInner, style: { height: `${this.chartHeight - y(d.sum)}px` } })))))))));
}
}
StackedBarChart.displayName = 'StackedBarChart';
StackedBarChart.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,
/** Chart data */
data: PropTypes.arrayOf(PropTypes.shape({
label: PropTypes.node,
values: PropTypes.arrayOf(PropTypes.number),
})),
/** Tooltip template function */
tooltipTemplate: PropTypes.func,
/** Props that modify the Tooltip created from text bar charts
* @linkTypeTo components-overlays--tooltip
* @setTypeName TooltipCommonProps
*/
tooltipCommonProps: PropTypes.shape(TooltipCommonProps),
/** Chart height (px) */
height: PropTypes.number,
/** Chart width (px) */
width: PropTypes.number,
/** Margin (px) for each side of the Chart. For example, in order to render larger number of digits at the yAxis, increase the left margin prop. */
margin: PropTypes.shape({
right: PropTypes.number,
left: PropTypes.number,
bottom: PropTypes.number,
top: PropTypes.number,
}),
/**
* ##### Formats Y axis ticks
* * `param` {string} `rawValue` - a raw value e.g. 10000
* * `param` {string} `rawValue` - number formatted value, comma separated e.g. 10,000
* * `return` {string} - the value to be shown as Y axis tick
*/
yAxisTickFormat: PropTypes.func,
};
StackedBarChart.defaultProps = {
data: [],
width: 900,
height: 350,
margin: CONSTANTS,
tooltipCommonProps: {},
yAxisTickFormat: defaultYAxisTickFormat,
};
export default StackedBarChart;
//# sourceMappingURL=StackedBarChart.js.map