Skip to content

Commit

Permalink
[feat-779] added generic bar chart component (#849)
Browse files Browse the repository at this point in the history
* [feat-779] added generic bar chart component

* [feat-779] safety check for accessing array

* [feat-779] const y-axis offset

* [feat-779] drop-shadow em instead of mm

* [feat-779] const min bar height for showing label
  • Loading branch information
chenmu10 authored Jan 29, 2022
1 parent 91da59f commit 8d20b06
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 0 deletions.
10 changes: 10 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"@types/classnames": "^2.3.1",
"@types/leaflet.heat": "^0.2.0",
"@types/query-string": "^6.3.0",
"@types/tinycolor2": "^1.4.3",
"axios": "^0.21.2",
"classnames": "^2.3.1",
"env-cmd": "^10.1.0",
Expand All @@ -33,6 +34,7 @@
"react-scripts": "4.0.3",
"react-share": "^4.4.0",
"recharts": "^2.0.9",
"tinycolor2": "^1.4.2",
"typescript": "^4.3.5"
},
"scripts": {
Expand Down
119 changes: 119 additions & 0 deletions src/components/molecules/GenericBarChart.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import React, { FC } from 'react';
import { ResponsiveContainer, BarChart, LabelList, XAxis, Bar, Tooltip, Legend } from 'recharts';
import { roseColor, honeyColor, yellowColor, blackColor, whiteColor } from 'style';
import { Typography } from 'components/atoms';
import tinycolor from 'tinycolor2';

const colors = [yellowColor, honeyColor, roseColor];
const Y_AXIS_OFFSET = 20;
const MIN_BAR_HEIGHT = 20;

type BarDataMap = {
[key: string]: number | string;
};

type CustomizedLabelProps = {
x?: number;
y?: number;
value?: number;
height?: number;
width?: number;
isPercentage?: boolean;
isStacked?: boolean;
};

interface IBarChartBaseProps {
data: Array<BarDataMap>;
isPercentage: boolean;
textLabel?: string;
}

interface ISingleBarChartProps extends IBarChartBaseProps {}

interface IMultiBarChartProps extends IBarChartBaseProps {
isStacked: boolean;
}

const CustomizedLabel = (props: CustomizedLabelProps) => {
const { x = 0, y = 0, value = 0, height = 0, width = 0, isPercentage, isStacked } = props;
const calculatedValue = isPercentage ? value + '%' : value;
return (
<g>
<text fill={isStacked ? blackColor : whiteColor} textAnchor="middle" x={x + width / 2} y={y + Y_AXIS_OFFSET}>
{height < MIN_BAR_HEIGHT ? null : calculatedValue}
</text>
</g>
);
};

const BarChartContainer: FC<IBarChartBaseProps> = ({ data, textLabel, children }) => {
return (
<>
{textLabel && <Typography.Body3>{textLabel}</Typography.Body3>}
<ResponsiveContainer>
<BarChart data={data} margin={{ bottom: 20 }}>
<XAxis
angle={-7}
interval={0}
dataKey={'xType'}
tickLine={false}
axisLine={false}
style={{ fill: blackColor }}
/>
<Tooltip />
<Legend verticalAlign="top" align="right" iconType="circle" height={35} />
{children}
</BarChart>
</ResponsiveContainer>
</>
);
};

const SingleBarChart: FC<ISingleBarChartProps> = ({ data, isPercentage, textLabel }) => {
const yLabels = data ? Object.keys(data[0]) : [];
yLabels.splice(0, 1);

const barStyle = {
filter: `drop-shadow(0.2em 0.2em 0 ${tinycolor(roseColor).darken().toString()})`,
};
return (
<BarChartContainer data={data} isPercentage={isPercentage} textLabel={textLabel}>
<Bar fill={roseColor} dataKey={yLabels[0]} style={barStyle} isAnimationActive={false}>
<LabelList content={<CustomizedLabel isPercentage={isPercentage} />} dataKey={yLabels[0]} />
</Bar>
</BarChartContainer>
);
};

const MultiBarChart: FC<IMultiBarChartProps> = ({ data, isPercentage, isStacked, textLabel }) => {
const yLabels = data ? Object.keys(data[0]) : [];
yLabels.splice(0, 1);
const maxBarsNum = yLabels.length;

return (
<BarChartContainer data={data} isPercentage={isPercentage} textLabel={textLabel}>
{Array.from({ length: maxBarsNum }, (_, i) => {
const barStyle = {
filter: `drop-shadow(0.2em ${isStacked ? '0' : '0.2em'} 0 ${tinycolor(colors[i]).darken().toString()})`,
};

return (
<Bar
stackId={isStacked ? 'stack_1' : undefined}
fill={colors[i]}
dataKey={yLabels[i]}
style={barStyle}
isAnimationActive={false}
>
<LabelList
content={<CustomizedLabel isPercentage={isPercentage} isStacked={isStacked} />}
dataKey={yLabels[i]}
/>
</Bar>
);
})}
</BarChartContainer>
);
};

export { SingleBarChart, MultiBarChart };

0 comments on commit 8d20b06

Please sign in to comment.