@hackoregon/component-library
Version:
Official repo for Hack Oregon React component library
223 lines (209 loc) • 6.33 kB
JavaScript
import PropTypes from "prop-types";
import React from "react";
import shortid from "shortid";
import { groupBy } from "lodash";
import {
VictoryAxis,
VictoryChart,
VictoryLabel,
VictoryPortal,
VictoryScatter,
VictoryTooltip,
VictoryLine
} from "victory";
import { ThemeProvider } from "emotion-theming";
import { VictoryTheme } from "../_Themes/index";
import ChartContainer from "../ChartContainer";
import SimpleLegend from "../SimpleLegend";
import civicFormat from "../utils/civicFormat";
import DataChecker from "../utils/DataChecker";
import {
chartEvents,
getDefaultDomain,
getDefaultDataSeriesLabels,
getDefaultFillStyle,
getDefaultLineStyle
} from "../utils/chartHelpers";
const LineChart = ({
data,
dataKey,
dataKeyLabel,
dataValue,
dataValueLabel,
dataSeries,
dataSeriesLabel,
domain,
size,
style,
subtitle,
title,
xLabel,
yLabel,
xNumberFormatter,
yNumberFormatter,
legendComponent,
theme
}) => {
const chartDomain = domain || getDefaultDomain(data, dataKey, dataValue);
const dataSeriesLabels = dataSeries
? dataSeriesLabel || getDefaultDataSeriesLabels(data, dataSeries)
: null;
const scatterPlotStyle = style || getDefaultFillStyle(dataSeriesLabels);
const legendData =
dataSeriesLabels && dataSeriesLabels.length
? dataSeriesLabels.map(series => ({ name: series.label }))
: null;
const lineData = dataSeries ? groupBy(data, dataSeries) : { category: data };
const lines = lineData
? Object.keys(lineData).map((category, index) => (
<VictoryLine
key={shortid.generate()}
data={lineData[category].map(d => ({
dataKey: d[dataKey],
dataValue: d[dataValue],
series: d[dataSeries]
}))}
x="dataKey"
y="dataValue"
style={getDefaultLineStyle(index)}
standalone={false}
// TODO: This is a workaround for a Victory bug that results in incomplete
// line animations when the animate properties are derived from the VictoryChart
// wrapping component. Remove this direct animate after the bug is fixed.
// https://github.com/FormidableLabs/victory/issues/1282
animate
/>
))
: null;
return (
<ThemeProvider theme={theme}>
<ChartContainer title={title} subtitle={subtitle}>
<DataChecker dataAccessors={{ dataKey, dataValue }} data={data}>
{legendData &&
(legendComponent ? (
legendComponent(legendData)
) : (
<SimpleLegend className="legend" legendData={legendData} />
))}
<VictoryChart
domain={chartDomain}
padding={{ left: 75, right: 50, bottom: 50, top: 50 }}
theme={theme}
>
<VictoryAxis
style={{ grid: { stroke: "none" } }}
tickFormat={x => xNumberFormatter(x)}
title="X Axis"
/>
<VictoryAxis
dependentAxis
tickFormat={y => yNumberFormatter(y)}
title="Y Axis"
/>
<VictoryPortal>
<VictoryLabel
style={{ ...theme.axisLabel.style }}
text={yLabel}
textAnchor="middle"
title="Y Axis Label"
verticalAnchor="end"
x={75}
y={45}
/>
</VictoryPortal>
<VictoryPortal>
<VictoryLabel
style={{ ...theme.axisLabel.style }}
text={xLabel}
textAnchor="end"
title="X Axis Label"
verticalAnchor="end"
x={600}
y={295}
/>
</VictoryPortal>
{lines}
<VictoryScatter
// categories={{ x: categoryData }}
data={data.map(d => ({
dataKey: d[dataKey],
dataValue: d[dataValue],
label: `${dataKeyLabel || xLabel}: ${xNumberFormatter(
d[dataKey]
)} • ${dataValueLabel || yLabel}: ${yNumberFormatter(
d[dataValue]
)}`,
series: d[dataSeries],
size: size ? d[size.key] || size.value : 3
}))}
events={chartEvents}
labelComponent={
<VictoryTooltip
x={325}
y={0}
orientation="bottom"
pointerLength={0}
cornerRadius={0}
theme={theme}
/>
}
size={d => d.size}
style={scatterPlotStyle}
title="Scatter Plot"
x="dataKey"
y="dataValue"
/>
</VictoryChart>
</DataChecker>
</ChartContainer>
</ThemeProvider>
);
};
LineChart.propTypes = {
data: PropTypes.arrayOf(
PropTypes.shape({ x: PropTypes.number, y: PropTypes.number })
),
dataKey: PropTypes.string,
dataKeyLabel: PropTypes.string,
dataValue: PropTypes.string,
dataValueLabel: PropTypes.string,
dataSeries: PropTypes.string,
dataSeriesLabel: PropTypes.arrayOf(
PropTypes.shape({ category: PropTypes.string, label: PropTypes.string })
),
domain: PropTypes.shape({
x: PropTypes.arrayOf(PropTypes.number),
y: PropTypes.arrayOf(PropTypes.number)
}),
size: PropTypes.shape({ key: PropTypes.string, value: PropTypes.string }),
style: PropTypes.arrayOf(PropTypes.shape({})),
subtitle: PropTypes.string,
title: PropTypes.string,
xLabel: PropTypes.string,
yLabel: PropTypes.string,
xNumberFormatter: PropTypes.func,
yNumberFormatter: PropTypes.func,
legendComponent: PropTypes.func,
theme: PropTypes.shape({})
};
LineChart.defaultProps = {
data: null,
dataKey: "x",
dataKeyLabel: null,
dataValue: "y",
dataValueLabel: null,
dataSeries: null,
dataSeriesLabel: null,
domain: null,
size: null,
style: null,
subtitle: null,
title: null,
xLabel: "X",
yLabel: "Y",
xNumberFormatter: civicFormat.numeric,
yNumberFormatter: civicFormat.numeric,
legendComponent: null,
theme: VictoryTheme
};
export default LineChart;