UNPKG

@hackoregon/component-library

Version:

Official repo for Hack Oregon React component library

312 lines (304 loc) 9.07 kB
import React from "react"; import PropTypes from "prop-types"; import { VictoryAxis, VictoryBar, VictoryChart, VictoryLabel, VictoryPortal, VictoryTooltip, VictoryStack } from "victory"; import shortid from "shortid"; import SimpleLegend from "../SimpleLegend"; import ChartContainer from "../ChartContainer"; import civicFormat from "../utils/civicFormat"; import { chartEvents, categoricalColors, getDefaultDataSeriesLabels, transformDatato100 } from "../utils/chartHelpers"; import groupByKey from "../utils/groupByKey"; import CivicVictoryTheme from "../VictoryTheme/VictoryThemeIndex"; import DataChecker from "../utils/DataChecker"; const HorizontalBarChart = ({ data, sortOrder, dataValue, dataLabel, domain, title, loading, error, subtitle, xLabel, yLabel, dataValueFormatter, dataLabelFormatter, minimalist, stacked, hundredPercentData, dataSeriesKey, dataSeriesLabel, legendComponent }) => { const groupedDataIfStacked = () => { if (stacked) { if (hundredPercentData) { return transformDatato100( groupByKey(data, dataSeriesKey, dataLabel), dataLabel, dataValue ); } return groupByKey(data, dataSeriesKey, dataLabel); } return data; }; const groupedData = groupedDataIfStacked(); const barData = sortOrder && sortOrder.length ? data : data.map((d, index) => { return { ...d, defaultSort: index + 1 }; }); const sortOrderKey = sortOrder && sortOrder.length ? sortOrder : "defaultSort"; const padding = minimalist ? { left: 115, right: 50, bottom: 25, top: 40 } : { left: 115, right: 50, bottom: 50, top: 70 }; const barHeight = CivicVictoryTheme.civic.bar.style.data.width; const spaceHeight = CivicVictoryTheme.civic.bar.style.data.padding * 2; const additionalHeight = padding.bottom + padding.top; const minValue = Math.min(0, ...data.map(d => d[dataValue])); const bars = stacked ? groupedData.length : data.length; const spaces = bars - 1; const dataHeight = bars * barHeight + spaces * spaceHeight; const dataSeriesLabels = dataSeriesKey ? dataSeriesLabel || getDefaultDataSeriesLabels(data, dataSeriesKey) : null; const legendData = dataSeriesLabels && dataSeriesLabels.length ? dataSeriesLabels.map(series => ({ name: series.label })) : null; const NegativeAwareTickLabel = props => ( <VictoryLabel dx={props.scale.y(minValue) - 20} // eslint-disable-line {...props} textAnchor="end" /> ); return ( <ChartContainer title={title} subtitle={subtitle} loading={loading} error={error} > <DataChecker dataAccessors={{ dataValue }} data={data}> {legendData && (legendComponent ? ( legendComponent(legendData) ) : ( <SimpleLegend className="legend" legendData={legendData} /> ))} <VictoryChart height={dataHeight + additionalHeight} domain={domain} domainPadding={{ x: 11, y: 20 }} padding={padding} theme={CivicVictoryTheme.civic} > <VictoryAxis style={{ tickLabels: { fill: "none" }, ticks: { stroke: "none" }, grid: { stroke: "none" } }} title="Y Axis" /> {!minimalist && ( <VictoryAxis dependentAxis orientation="top" tickFormat={dataValueFormatter} title="X Axis" offsetY={padding.top} /> )} {!minimalist && ( <VictoryPortal> <VictoryLabel style={{ ...CivicVictoryTheme.civic.axisLabel.style }} text={yLabel} textAnchor="middle" title="Y Axis Label" verticalAnchor="end" x={50} y={65} /> </VictoryPortal> )} <VictoryPortal> <VictoryLabel style={{ ...CivicVictoryTheme.civic.axisLabel.style }} text={xLabel} textAnchor={minimalist ? "middle" : "end"} title="X Axis Label" verticalAnchor={minimalist ? "middle" : "end"} x={minimalist ? 325 : 600} y={minimalist ? 20 : 85} /> </VictoryPortal> {!stacked && ( <VictoryBar horizontal labelComponent={ <NegativeAwareTickLabel x={0} orientation="left" theme={CivicVictoryTheme.civic} /> } domainPadding={0} data={barData.map(d => ({ sortOrder: d[sortOrderKey], dataValue: d[dataValue], label: dataLabelFormatter(d[dataLabel]) }))} x="sortOrder" y="dataValue" events={chartEvents} animate /> )} {!stacked && ( <VictoryBar horizontal labelComponent={ <VictoryTooltip x={325} y={0} orientation="bottom" pointerLength={0} cornerRadius={0} theme={CivicVictoryTheme.civic} /> } domainPadding={0} data={barData.map(d => ({ sortOrder: d[sortOrderKey], dataValue: d[dataValue], label: `${dataLabelFormatter( d[dataLabel] )}: ${dataValueFormatter(d[dataValue])}` }))} style={{ data: { fill: "none" } }} title="Horizontal Bar Chart" x="sortOrder" y="dataValue" events={chartEvents} animate /> )} {stacked && ( <VictoryStack colorScale={categoricalColors(groupedData.length)}> {groupedData.map(arr => { return ( <VictoryBar title="Horizontal Bar Chart" domainPadding={0} data={arr.map(d => ({ dataKey: d[dataLabel], dataValue: d[dataValue], series: d[dataSeriesKey] }))} x="dataKey" y="dataValue" events={chartEvents} key={shortid.generate()} labels={arr.map( d => `${d[dataSeriesKey]}: ${d[dataValue]}` )} horizontal labelComponent={ <VictoryTooltip x={325} y={0} orientation="bottom" pointerLength={0} cornerRadius={0} theme={CivicVictoryTheme.civic} /> } animate /> ); })} </VictoryStack> )} {stacked && groupedData.map((arr, i) => { return ( <VictoryAxis style={{ tickFormat: arr[i][dataLabel], ticks: { stroke: "none" }, grid: { stroke: "none" } }} key={shortid.generate()} /> ); })} </VictoryChart> </DataChecker> </ChartContainer> ); }; HorizontalBarChart.propTypes = { data: PropTypes.arrayOf(PropTypes.shape({})), sortOrder: PropTypes.string, dataValue: PropTypes.string, dataLabel: PropTypes.string, domain: PropTypes.shape({ x: PropTypes.arrayOf(PropTypes.number), y: PropTypes.arrayOf(PropTypes.number) }), loading: PropTypes.bool, error: PropTypes.string, title: PropTypes.string, subtitle: PropTypes.string, xLabel: PropTypes.string, yLabel: PropTypes.string, dataValueFormatter: PropTypes.func, dataLabelFormatter: PropTypes.func, minimalist: PropTypes.bool, stacked: PropTypes.bool, hundredPercentData: PropTypes.bool, dataSeriesKey: PropTypes.string, dataSeriesLabel: PropTypes.shape({}), legendComponent: PropTypes.func }; HorizontalBarChart.defaultProps = { data: null, sortOrder: null, dataValue: "x", dataLabel: "y", domain: null, title: null, subtitle: null, xLabel: "X", yLabel: "Y", dataValueFormatter: civicFormat.numeric, dataLabelFormatter: civicFormat.unformatted, minimalist: false, stacked: false, hundredPercentData: false, dataSeriesKey: null, dataSeriesLabel: {}, legendComponent: null }; export default HorizontalBarChart;