add custom-grid and associated files + test - test needs plugin config to work with d3-path and jest
This commit is contained in:
48
component/bar.tsx
Normal file
48
component/bar.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import React from 'react';
|
||||
import { Path } from 'react-native-svg';
|
||||
|
||||
import { calculateBarProps, drawBarPath } from './custom-bar-utils';
|
||||
import { ScaleFunction } from './graph-types';
|
||||
|
||||
interface BarProps {
|
||||
scaleX: ScaleFunction;
|
||||
scaleY: ScaleFunction;
|
||||
data: { value: number };
|
||||
barNumber: number;
|
||||
index: number;
|
||||
fill: string;
|
||||
barWidth: number;
|
||||
gap: number;
|
||||
roundedRadius: number;
|
||||
}
|
||||
|
||||
export const Bar: React.FC<BarProps> = ({
|
||||
scaleX,
|
||||
scaleY,
|
||||
data,
|
||||
barNumber,
|
||||
index,
|
||||
fill,
|
||||
barWidth,
|
||||
gap,
|
||||
roundedRadius
|
||||
}) => {
|
||||
const { xOrigin, yOrigin, height } = calculateBarProps({
|
||||
scaleX,
|
||||
scaleY,
|
||||
index,
|
||||
data,
|
||||
barNumber,
|
||||
barWidth,
|
||||
gap
|
||||
});
|
||||
|
||||
return (
|
||||
<Path
|
||||
key={`bar-${barNumber}-${index}`}
|
||||
d={drawBarPath(xOrigin, yOrigin, barWidth, height, roundedRadius)}
|
||||
fill={fill}
|
||||
testID={`bar-${barNumber}-${index}`}
|
||||
/>
|
||||
);
|
||||
};
|
59
component/custom-bar-utils.ts
Normal file
59
component/custom-bar-utils.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { path as d3 } from 'd3-path';
|
||||
|
||||
import { ScaleFunction } from './graph-types';
|
||||
|
||||
type BarCalculationProps = {
|
||||
scaleX: ScaleFunction;
|
||||
scaleY: ScaleFunction;
|
||||
data: { value: number };
|
||||
barNumber: number;
|
||||
index: number;
|
||||
barWidth: number;
|
||||
gap: number;
|
||||
};
|
||||
|
||||
export function calculateBarProps({
|
||||
scaleX,
|
||||
scaleY,
|
||||
barWidth,
|
||||
data,
|
||||
index,
|
||||
barNumber,
|
||||
gap
|
||||
}: BarCalculationProps): { xOrigin: number; yOrigin: number; height: number } {
|
||||
const firstBar = barNumber === 0;
|
||||
const xOrigin = scaleX(index) + (firstBar ? 0 : barWidth * barNumber + gap * barNumber);
|
||||
const yOrigin = scaleY(data.value);
|
||||
const height = scaleY(0) - yOrigin;
|
||||
|
||||
return { xOrigin, yOrigin, height };
|
||||
}
|
||||
|
||||
export function drawBarPath(
|
||||
xOrigin: number,
|
||||
yOrigin: number,
|
||||
barWidth: number,
|
||||
height: number,
|
||||
roundedRadius: number
|
||||
): string {
|
||||
const path = d3();
|
||||
path.moveTo(xOrigin, yOrigin + height);
|
||||
path.lineTo(xOrigin, yOrigin + roundedRadius);
|
||||
path.arcTo(xOrigin, yOrigin, xOrigin + roundedRadius, yOrigin, roundedRadius);
|
||||
path.lineTo(xOrigin + barWidth - roundedRadius, yOrigin);
|
||||
path.arcTo(
|
||||
xOrigin + barWidth,
|
||||
yOrigin,
|
||||
xOrigin + barWidth,
|
||||
yOrigin + roundedRadius,
|
||||
roundedRadius
|
||||
);
|
||||
path.lineTo(xOrigin + barWidth, yOrigin + height);
|
||||
path.lineTo(xOrigin, yOrigin + height);
|
||||
path.closePath();
|
||||
|
||||
return path.toString();
|
||||
}
|
||||
|
||||
export const calculateBarWidth = (bandwidth: number, combinedDataLength: number, gap: number) =>
|
||||
(bandwidth - gap * (combinedDataLength - 1)) / combinedDataLength;
|
54
component/custom-bars.tsx
Normal file
54
component/custom-bars.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
|
||||
import { Svg } from 'react-native-svg';
|
||||
|
||||
import { Bar } from './bar';
|
||||
import { ScaleFunction } from './graph-types';
|
||||
import { calculateBarWidth } from './custom-bar-utils';
|
||||
|
||||
export interface CombinedData {
|
||||
data: { value: number }[];
|
||||
svg: { fill: string };
|
||||
}
|
||||
|
||||
interface CustomBarsProps {
|
||||
x: ScaleFunction;
|
||||
y: ScaleFunction;
|
||||
bandwidth: number;
|
||||
barColors: string[];
|
||||
rawData: unknown[]; // TODO: update this value when this data type is defined
|
||||
combinedData: CombinedData[];
|
||||
gap: number;
|
||||
roundedRadius: number;
|
||||
}
|
||||
|
||||
export const CustomBars: React.FC<Partial<CustomBarsProps>> = ({
|
||||
x: scaleX,
|
||||
y: scaleY,
|
||||
bandwidth,
|
||||
combinedData,
|
||||
rawData,
|
||||
barColors,
|
||||
gap = 2,
|
||||
roundedRadius = 4
|
||||
}) => {
|
||||
const barWidth = calculateBarWidth(bandwidth, combinedData.length, gap);
|
||||
|
||||
return rawData.map((_, index) => (
|
||||
<Svg key={`group-${index}`} testID={`svg-${index}`}>
|
||||
{combinedData.map((item, i) => (
|
||||
<Bar
|
||||
key={`bar-${i}-${index}`}
|
||||
scaleX={scaleX}
|
||||
scaleY={scaleY}
|
||||
data={item.data[index]}
|
||||
barNumber={i}
|
||||
index={index}
|
||||
fill={barColors[i]}
|
||||
barWidth={barWidth}
|
||||
gap={gap}
|
||||
roundedRadius={roundedRadius}
|
||||
/>
|
||||
))}
|
||||
</Svg>
|
||||
));
|
||||
};
|
@@ -1,12 +1,10 @@
|
||||
import React from 'react';
|
||||
import { G, Line } from 'react-native-svg';
|
||||
import { ScaleLinear } from 'd3-scale'
|
||||
import { colors } from '../styles';
|
||||
|
||||
type scaleFunction = ScaleLinear<number, number>;
|
||||
import { ScaleFunction } from './graph-types';
|
||||
|
||||
interface CustomGridProps {
|
||||
y: scaleFunction;
|
||||
y: ScaleFunction;
|
||||
ticks: Array<number>;
|
||||
}
|
||||
|
||||
|
3
component/graph-types.ts
Normal file
3
component/graph-types.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { ScaleLinear } from 'd3-scale'
|
||||
|
||||
export type ScaleFunction = ScaleLinear<number, number>;
|
Reference in New Issue
Block a user