Skip to content

Commit c06eb0f

Browse files
committed
added dynamic scale detection and showing graph correspondingly
1 parent d08f58f commit c06eb0f

File tree

6 files changed

+162
-115
lines changed

6 files changed

+162
-115
lines changed

src/constants.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
export const DEFAULT_BAR_WIDTH = 20;
1+
export const DEFAULT_BAR_WIDTH = 50;
2+
3+
export const DEFAULT_PIXELS_PER_Y_UNIT = 30;

src/stories/Component.stories.tsx

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,30 +11,41 @@ export default {
1111
}
1212
} as ComponentMeta<typeof Component>;
1313

14-
const Template: ComponentStory<typeof Component> = (args) => <Component {...args} />;
15-
14+
const Template: ComponentStory<typeof Component> = (args) => (
15+
<div style={{ width: '100%', height: '100%', padding: '10% 20%' }}>
16+
<Component {...args} />
17+
</div>
18+
);
1619
export const WaterFallChart = Template.bind({});
1720
WaterFallChart.args = {
1821
transactions: [
1922
{
2023
label: 'Income',
21-
value: 300
24+
value: 10
2225
},
2326
{
2427
label: 'Expense1',
25-
value: -60
28+
value: 3
2629
},
2730
{
2831
label: 'Gain1',
29-
value: 200
32+
value: -2
3033
},
3134
{
3235
label: 'Expense2',
33-
value: -100
36+
value: -60
3437
},
3538
{
3639
label: 'Gain2',
37-
value: 200
40+
value: 40
41+
},
42+
{
43+
label: 'Expense3',
44+
value: -10
45+
},
46+
{
47+
label: 'Gain3',
48+
value: 80
3849
}
3950
]
4051
};

src/types/types.d.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export type IWaterfallGraphProps = {
22
transactions: Array<ITransaction>;
3+
barWidth?: number;
34
};
45

56
export type ITransaction = {
@@ -17,7 +18,15 @@ export type IChartElement = {
1718

1819
export type IUseWaterfallChartReturnType = {
1920
chartElements: Array<IChartElement>;
20-
yAxisUnitsPerPixel: number;
2121
yValueForZeroLine: number;
2222
yAxisPoints: Array<number>;
23+
yAxisScale: number;
24+
calculateBarWidth: ICalculateBarWidth;
2325
};
26+
27+
export type IGetIntervalAndYPointsReturnType = {
28+
yAxisScale: number;
29+
yAxisPoints: Array<number>;
30+
};
31+
32+
export type ICalculateBarWidth = (graphWidth: number) => number;

src/waterfall-chart/WaterFallChart.tsx

Lines changed: 43 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -3,94 +3,85 @@ import { IWaterfallGraphProps } from '../types/types';
33
import { useWaterfallChart } from './utils';
44
import styles from './styles.module.scss';
55
import '../index.css';
6-
import { DEFAULT_BAR_WIDTH } from '../constants';
6+
import { DEFAULT_BAR_WIDTH, DEFAULT_PIXELS_PER_Y_UNIT } from '../constants';
77

88
const WaterFallChart: FC<IWaterfallGraphProps> = (props) => {
9-
const { transactions } = props;
9+
const { transactions, barWidth } = props;
1010

1111
const wrapperRef = useRef<HTMLDivElement | null>(null);
1212
const [wrapperHeight, setWrapperHeight] = useState(0);
13+
const [barWidthVal, setBarWidthVal] = useState(barWidth ?? DEFAULT_BAR_WIDTH);
1314

14-
useEffect(() => {
15+
const { chartElements, yValueForZeroLine, yAxisPoints, yAxisScale, calculateBarWidth } = useWaterfallChart(
16+
transactions,
17+
wrapperHeight
18+
);
1519

16-
const calculateWrapperHeight = (): void => {
20+
useEffect(() => {
21+
const onWrapperDimensionsChange = (): void => {
1722
if (wrapperRef.current) {
18-
const height = wrapperRef?.current?.offsetHeight;
19-
setWrapperHeight(height);
23+
setWrapperHeight(wrapperRef?.current?.offsetHeight);
24+
if (!barWidth) setBarWidthVal(calculateBarWidth(wrapperRef?.current?.offsetWidth));
2025
}
2126
};
2227

23-
calculateWrapperHeight();
28+
onWrapperDimensionsChange();
2429

25-
window.addEventListener('resize', calculateWrapperHeight);
30+
window.addEventListener('resize', onWrapperDimensionsChange);
2631

27-
return () => {
28-
window.removeEventListener('resize', calculateWrapperHeight);
29-
};
30-
}, []);
31-
32-
const {
33-
chartElements,
34-
yAxisUnitsPerPixel,
35-
yValueForZeroLine,
36-
yAxisPoints
37-
} = useWaterfallChart(transactions, wrapperHeight);
32+
return () => window.removeEventListener('resize', onWrapperDimensionsChange);
33+
}, [barWidth, calculateBarWidth]);
3834

3935
return (
4036
<div ref={wrapperRef} className={styles.chartWrapper}>
4137
<svg className={styles.svgContainer}>
4238
{/* y-axis */}
43-
<line
44-
x1='0'
45-
y1='0'
46-
x2='0'
47-
y2='100%'
48-
className={styles.axisLines}
49-
/>
39+
<line x1='0' y1='0' x2='0' y2='100%' className={styles.axisLines} />
5040
{/* x-axis */}
51-
<line
52-
x1='0'
53-
y1='100%'
54-
x2='100%'
55-
y2='100%'
56-
className={styles.axisLines}
57-
/>
41+
<line x1='0' y1='100%' x2='100%' y2='100%' className={styles.axisLines} />
5842
{/* zero line if negative values present */}
59-
<line
60-
x1='0'
61-
y1={yValueForZeroLine}
62-
x2='100%'
63-
y2={yValueForZeroLine}
64-
className={`${styles.axisLines} ${yValueForZeroLine === wrapperHeight && styles.hideLine}`}
65-
/>
43+
{yAxisPoints?.map((yPoint, index) => (
44+
<line
45+
key={yPoint}
46+
x1='0'
47+
y1={wrapperHeight - index * DEFAULT_PIXELS_PER_Y_UNIT}
48+
x2='100%'
49+
y2={wrapperHeight - index * DEFAULT_PIXELS_PER_Y_UNIT}
50+
className={`${styles.axisLines}`}
51+
/>
52+
))}
6653
{chartElements?.map((chartElement, index) => (
6754
<>
6855
<rect
6956
key={chartElement?.name}
70-
width={DEFAULT_BAR_WIDTH}
71-
height={Math.abs(chartElement?.value / yAxisUnitsPerPixel)}
57+
width={barWidthVal}
58+
height={(Math.abs(chartElement?.value) / yAxisScale) * DEFAULT_PIXELS_PER_Y_UNIT}
7259
y={chartElement?.yVal}
73-
x={(2 * index + 1) * DEFAULT_BAR_WIDTH}
60+
x={(2 * index + 1) * barWidthVal}
7461
className={`${styles.graphBar} ${chartElement?.value >= 0 ? styles.positiveGraph : styles.negativeGraph}`}
7562
/>
7663
<line
7764
key={chartElement?.name}
78-
x1={(2 * index + 2) * DEFAULT_BAR_WIDTH}
79-
y1={wrapperHeight - chartElement?.cumulativeSum / yAxisUnitsPerPixel - (wrapperHeight - yValueForZeroLine)}
80-
x2={(2 * index + 3) * DEFAULT_BAR_WIDTH}
81-
y2={wrapperHeight - chartElement?.cumulativeSum / yAxisUnitsPerPixel - (wrapperHeight - yValueForZeroLine)}
65+
x1={(2 * index + 2) * barWidthVal}
66+
y1={
67+
wrapperHeight -
68+
(chartElement?.cumulativeSum / yAxisScale) * DEFAULT_PIXELS_PER_Y_UNIT -
69+
(wrapperHeight - yValueForZeroLine)
70+
}
71+
x2={(2 * index + 3) * barWidthVal}
72+
y2={
73+
wrapperHeight -
74+
(chartElement?.cumulativeSum / yAxisScale) * DEFAULT_PIXELS_PER_Y_UNIT -
75+
(wrapperHeight - yValueForZeroLine)
76+
}
8277
className={styles.bridgeLine}
8378
/>
8479
</>
8580
))}
8681
</svg>
8782
<div className={styles.yPoints}>
8883
{yAxisPoints?.map((yAxisPoint, index) => (
89-
<div
90-
key={yAxisPoint}
91-
className={styles.yPoint}
92-
style={{bottom: (index *30) - 8 }}
93-
>
84+
<div key={yAxisPoint} className={styles.yPoint} style={{ bottom: index * DEFAULT_PIXELS_PER_Y_UNIT - 8 }}>
9485
{yAxisPoint}
9586
</div>
9687
))}

src/waterfall-chart/styles.module.scss

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,12 @@
3333
transition: all 0.3s ease-in-out;
3434
}
3535
.positiveGraph {
36-
fill: #00B050;
37-
stroke: #00B050;
36+
fill: #00b050;
37+
stroke: #00b050;
3838
}
3939
.negativeGraph {
40-
fill: #FF0000;
41-
stroke: #FF0000;
40+
fill: #ff0000;
41+
stroke: #ff0000;
4242
}
4343
.bridgeLine {
4444
stroke: #545453;
@@ -48,11 +48,13 @@
4848
}
4949
.yPoints {
5050
position: relative;
51+
width: 0px;
5152
.yPoint {
5253
position: absolute;
53-
width: 20px;
54-
font-size: 15px;
55-
line-height: 18px;
54+
font-size: 11px;
55+
line-height: 14px;
5656
color: #545453;
57+
right: 0px;
58+
text-align: right;
5759
}
5860
}

0 commit comments

Comments
 (0)