UNPKG

wix-style-react

Version:
182 lines 8.04 kB
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