Fix GroupBarGraph component
continuous-integration/drone/push Build is passing Details

This commit is contained in:
e26chiu 2022-12-13 22:21:00 -05:00
parent 6ba124ee15
commit 26b7662b9e
2 changed files with 51 additions and 88 deletions

View File

@ -1,5 +1,6 @@
.wrapper { .wrapper {
display: flex; display: flex;
flex-direction: column;
align-items: center; align-items: center;
width: min-content; width: min-content;
} }
@ -35,4 +36,5 @@
.legend { .legend {
display: flex; display: flex;
margin: calc(16rem / 16); margin: calc(16rem / 16);
justify-content: center;
} }

View File

@ -49,7 +49,18 @@ interface GroupedBarGraphProps {
valueAxisLabelSize?: number; valueAxisLabelSize?: number;
/** Controls the distance between the value axis label and the value axis. */ /** Controls the distance between the value axis label and the value axis. */
valueAxisLabelOffset?: number; valueAxisLabelOffset?: number;
legendProps?: LegendProps; /** Margin for each item in the legend */
itemMargin?: string;
/** Minimum width of the graph. */
minWidth?: number;
/** Breakpoint width of graph where alernating labels are displayed. Only for Vertical graphs */
widthAlternatingLabel?: number;
/** Space added to the bottom of the graph to show overflowing labels. Only for Vertical graphs */
alternatingLabelSpace?: number;
/** Default position of labels in x-axis, in px. */
defaultLabelDy?: string;
/** Position of lower labels in x-axis, in px. Only for Vertical graphs */
lowerLabelDy?: string;
} }
// Best format for props // Best format for props
@ -66,38 +77,21 @@ interface BarGroupData {
[key: string]: string | number; [key: string]: string | number;
} }
interface LegendProps {
/** Position of the legend, relative to the graph. */
position?: "top" | "right";
/** Font size of the labels in the legend, in pixels. Default is 16px. */
itemLabelSize?: number;
/** Gap between items in the legend, in pixels. */
itemGap?: number;
/** Distance between the legend and other adjacent elements, in pixels. */
margin?: {
top?: number;
bottom?: number;
left?: number;
right?: number;
};
}
// BAR_PADDING must be in the range [0, 1) // BAR_PADDING must be in the range [0, 1)
const BAR_PADDING = 0.2; const BAR_PADDING = 0.2;
const BAR_TEXT_PADDING = 12; const BAR_TEXT_PADDING = 12;
const DEFAULT_LABEL_SIZE = 16; const DEFAULT_LABEL_SIZE = 16;
const DEFAULT_LEGEND_GAP = 16;
export function GroupedBarGraphVertical(props: GroupedBarGraphProps) { export function GroupedBarGraphVertical(props: GroupedBarGraphProps) {
const { const {
data: propsData, data: propsData,
barColors, barColors,
barHoverColorsMap, barHoverColorsMap,
width,
height, height,
margin, margin,
className, className,
minWidth = 500,
categoryTickLabelSize = DEFAULT_LABEL_SIZE, categoryTickLabelSize = DEFAULT_LABEL_SIZE,
valueTickLabelSize = DEFAULT_LABEL_SIZE, valueTickLabelSize = DEFAULT_LABEL_SIZE,
hoverLabelSize, hoverLabelSize,
@ -107,15 +101,17 @@ export function GroupedBarGraphVertical(props: GroupedBarGraphProps) {
valueAxisLabel, valueAxisLabel,
valueAxisLabelSize = DEFAULT_LABEL_SIZE, valueAxisLabelSize = DEFAULT_LABEL_SIZE,
valueAxisLabelOffset = 0, valueAxisLabelOffset = 0,
legendProps, itemMargin = "0 0 0 15px",
widthAlternatingLabel = 600,
alternatingLabelSpace = 80,
defaultLabelDy = `0px`,
lowerLabelDy = `30px`,
} = props; } = props;
const width = props.width < minWidth ? minWidth : props.width; // Ensuring graph's width >= minWidth
const { const alternatingLabel = width <= widthAlternatingLabel;
position: legendPosition = "right", const final_margin_bottom = alternatingLabel
itemLabelSize: legendLabelSize = DEFAULT_LABEL_SIZE, ? margin.bottom + alternatingLabelSpace
itemGap: legendItemGap = DEFAULT_LEGEND_GAP, : margin.bottom;
margin: legendMargin = {},
} = legendProps ?? {};
const data: BarGroupData[] = propsData.map((datum: GroupedBarGraphData) => { const data: BarGroupData[] = propsData.map((datum: GroupedBarGraphData) => {
return { category: datum.category, ...datum.values }; return { category: datum.category, ...datum.values };
@ -139,7 +135,7 @@ export function GroupedBarGraphVertical(props: GroupedBarGraphProps) {
.flat(); .flat();
const categoryMax = width - margin.left - margin.right; const categoryMax = width - margin.left - margin.right;
const valueMax = height - margin.top - margin.bottom; const valueMax = height - margin.top - final_margin_bottom;
const getCategory = (d: BarGroupData) => d.category; const getCategory = (d: BarGroupData) => d.category;
@ -168,10 +164,15 @@ export function GroupedBarGraphVertical(props: GroupedBarGraphProps) {
return ( return (
<div <div
className={className ? `${className} ${styles.wrapper}` : styles.wrapper} className={className ? `${className} ${styles.wrapper}` : styles.wrapper}
style={{
flexDirection: legendPosition === "right" ? "row" : "column-reverse",
}}
> >
<div className={styles.legend}>
<LegendOrdinal
scale={colorScale}
direction="row"
itemMargin={itemMargin}
labelAlign="center"
/>
</div>
<svg width={width} height={height}> <svg width={width} height={height}>
<defs> <defs>
{Object.keys(barHoverColorsMap).map((color: string) => { {Object.keys(barHoverColorsMap).map((color: string) => {
@ -256,11 +257,13 @@ export function GroupedBarGraphVertical(props: GroupedBarGraphProps) {
left={margin.left} left={margin.left}
hideAxisLine hideAxisLine
hideTicks hideTicks
tickLabelProps={() => { tickLabelProps={(value, index) => {
const alternatingDy =
index % 2 == 0 ? defaultLabelDy : lowerLabelDy;
return { return {
...bottomTickLabelProps(), ...bottomTickLabelProps(),
className: styles.tickLabel, className: styles.tickLabel,
dy: "-0.25rem", dy: alternatingLabel ? alternatingDy : defaultLabelDy,
fontSize: `${categoryTickLabelSize / 16}rem`, fontSize: `${categoryTickLabelSize / 16}rem`,
width: categoryScale.bandwidth(), width: categoryScale.bandwidth(),
verticalAnchor: "start", verticalAnchor: "start",
@ -297,27 +300,6 @@ export function GroupedBarGraphVertical(props: GroupedBarGraphProps) {
}} }}
/> />
</svg> </svg>
<LegendOrdinal
className={styles.legend}
style={{
marginTop: legendMargin.top,
marginRight: legendMargin.right,
marginBottom: legendMargin.bottom,
marginLeft: legendMargin.left,
fontSize: legendLabelSize,
}}
scale={colorScale}
direction={legendPosition === "right" ? "column" : "row"}
itemMargin={
legendPosition === "right"
? `calc(${legendItemGap / 2}rem / 16) 0 calc(${
legendItemGap / 2
}rem / 16) 0`
: `0 calc(${legendItemGap / 2}rem / 16) 0 calc(${
legendItemGap / 2
}rem / 16)`
}
/>
</div> </div>
); );
} }
@ -327,10 +309,10 @@ export function GroupedBarGraphHorizontal(props: GroupedBarGraphProps) {
data: propsData, data: propsData,
barColors, barColors,
barHoverColorsMap, barHoverColorsMap,
width,
height, height,
margin, margin,
className, className,
minWidth = 600,
categoryTickLabelSize = DEFAULT_LABEL_SIZE, categoryTickLabelSize = DEFAULT_LABEL_SIZE,
valueTickLabelSize = DEFAULT_LABEL_SIZE, valueTickLabelSize = DEFAULT_LABEL_SIZE,
hoverLabelSize, hoverLabelSize,
@ -340,15 +322,10 @@ export function GroupedBarGraphHorizontal(props: GroupedBarGraphProps) {
valueAxisLabel, valueAxisLabel,
valueAxisLabelSize = DEFAULT_LABEL_SIZE, valueAxisLabelSize = DEFAULT_LABEL_SIZE,
valueAxisLabelOffset = 0, valueAxisLabelOffset = 0,
legendProps, itemMargin = "0 0 0 15px",
defaultLabelDy = "0",
} = props; } = props;
const width = props.width < minWidth ? minWidth : props.width; // Ensuring graph's width >= minWidth
const {
position: legendPosition = "top",
itemLabelSize: legendLabelSize = DEFAULT_LABEL_SIZE,
itemGap: legendItemGap = DEFAULT_LEGEND_GAP,
margin: legendMargin = {},
} = legendProps ?? {};
const data: BarGroupData[] = propsData.map((datum: GroupedBarGraphData) => { const data: BarGroupData[] = propsData.map((datum: GroupedBarGraphData) => {
return { category: datum.category, ...datum.values }; return { category: datum.category, ...datum.values };
@ -401,10 +378,15 @@ export function GroupedBarGraphHorizontal(props: GroupedBarGraphProps) {
return ( return (
<div <div
className={className ? `${className} ${styles.wrapper}` : styles.wrapper} className={className ? `${className} ${styles.wrapper}` : styles.wrapper}
style={{
flexDirection: legendPosition === "right" ? "row" : "column-reverse",
}}
> >
<div className={styles.legend}>
<LegendOrdinal
scale={colorScale}
direction="row"
itemMargin={itemMargin}
labelAlign="center"
/>
</div>
<svg width={width} height={height}> <svg width={width} height={height}>
<defs> <defs>
{Object.keys(barHoverColorsMap).map((color: string) => { {Object.keys(barHoverColorsMap).map((color: string) => {
@ -495,7 +477,7 @@ export function GroupedBarGraphHorizontal(props: GroupedBarGraphProps) {
...leftTickLabelProps(), ...leftTickLabelProps(),
className: styles.tickLabel, className: styles.tickLabel,
dx: "-0.5rem", dx: "-0.5rem",
dy: "0.25rem", dy: defaultLabelDy,
fontSize: `${valueTickLabelSize / 16}rem`, fontSize: `${valueTickLabelSize / 16}rem`,
height: categoryScale.bandwidth(), height: categoryScale.bandwidth(),
}; };
@ -531,27 +513,6 @@ export function GroupedBarGraphHorizontal(props: GroupedBarGraphProps) {
}} }}
/> />
</svg> </svg>
<LegendOrdinal
className={styles.legend}
style={{
marginTop: legendMargin.top,
marginRight: legendMargin.right,
marginBottom: legendMargin.bottom,
marginLeft: legendMargin.left,
fontSize: legendLabelSize,
}}
scale={colorScale}
direction={legendPosition === "right" ? "column" : "row"}
itemMargin={
legendPosition === "right"
? `calc(${legendItemGap / 2}rem / 16) 0 calc(${
legendItemGap / 2
}rem / 16) 0`
: `0 calc(${legendItemGap / 2}rem / 16) 0 calc(${
legendItemGap / 2
}rem / 16)`
}
/>
</div> </div>
); );
} }