Fix StackedBar Graph nitpicks (Closes #51) #133

Merged
e26chiu merged 9 commits from fix-stackedbar-graph into main 2023-01-03 13:51:47 -05:00
5 changed files with 141 additions and 182 deletions

View File

@ -42,17 +42,9 @@ export type StackedBarProps = {
/** Colours for each key */
colorRange: string[];
/** Distance between the edge of the graph and the area where the bars are drawn, in pixels. */
margin: { top: number; left: number };
margin: { top: number; left: number; right: number; bottom: number };
/** Number of ticks for the value axis */
numTicksValueAxis?: number;
/** Distance between the left axis labels and the start of the lines of the graph, in px. */
axisLeftOffset?: number;
/** Distance between the bottom axis and the bottom of the container of the graph, in px. */
axisBottomOffset?: number;
/** Distance between the right side of the graph and the legend, in px. */
legendLeftOffset?: number;
/** Distance between the top of the graph and the legend, in px. */
legendTopOffset?: number;
/** Width of the lines in the graph, in px. */
strokeWidth?: number;
/** Length of the dashes and the gaps in the graph, in px. */
@ -61,9 +53,6 @@ export type StackedBarProps = {
scalePadding?: number;
/** Margin for each item in the legend */
itemMargin?: string;
/** Factor multiplied with an offset to center the labels of the category-axis depending on the width/height of the graph.
* >1 for width/height <600 and <1 for width/height >600 (vertical=width/horizontal=height) */
categoryAxisLeftFactor?: number;
};
let tooltipTimeout: number;
@ -81,14 +70,9 @@ export const StackedBarGraphVertical = withTooltip<
margin,
scalePadding = 0.3,
numTicksValueAxis = 6,
axisLeftOffset = 40,
axisBottomOffset = 40,
strokeWidth = 2.5,
strokeDashArray = "10,4",
legendLeftOffset = 40,
legendTopOffset = 40,
itemMargin = "0 0 0 15px",
categoryAxisLeftFactor = 1,
tooltipOpen,
tooltipLeft,
tooltipTop,
@ -125,18 +109,15 @@ export const StackedBarGraphVertical = withTooltip<
});
// bounds
const xMax = width;
const yMax = height - margin.top - axisBottomOffset;
const xMax = width - margin.left - margin.right;
const yMax = height - margin.top - margin.bottom;
categoryScale.rangeRound([0, xMax - axisLeftOffset]);
categoryScale.rangeRound([0, xMax]);
valueScale.range([yMax, 0]);
return width < 10 ? null : (
<div className={styles.container}>
<div
className={styles.legend}
style={{ left: width + legendLeftOffset, top: legendTopOffset }}
>
<div className={styles.legend}>
<LegendOrdinal
scale={colorScale}
direction="row"
@ -151,7 +132,6 @@ export const StackedBarGraphVertical = withTooltip<
scale={valueScale}
width={xMax}
height={yMax}
left={axisLeftOffset}
numTicks={numTicksValueAxis}
stroke={Color.tertiaryBackground}
strokeWidth={strokeWidth}
@ -160,93 +140,88 @@ export const StackedBarGraphVertical = withTooltip<
<GridColumns
scale={categoryScale}
height={yMax}
left={axisLeftOffset}
offset={categoryScale.bandwidth() / 2}
stroke={Color.tertiaryBackground}
strokeWidth={strokeWidth}
strokeDasharray={strokeDashArray}
/>
<Group left={axisLeftOffset}>
<BarStack<StackedBarData, string>
data={data}
keys={keys}
x={getCategory}
xScale={categoryScale}
yScale={valueScale}
color={colorScale}
>
{(barStacks) =>
barStacks.map((barStack) =>
barStack.bars.map((bar) => (
<rect
className={styles.barStack}
key={`bar-stack-${barStack.index}-${bar.index}`}
x={bar.x}
y={bar.y}
height={bar.height}
width={bar.width / 2}
fill={bar.color}
onMouseLeave={() => {
tooltipTimeout = window.setTimeout(() => {
hideTooltip();
}, 300);
}}
onMouseMove={(event) => {
if (tooltipTimeout) clearTimeout(tooltipTimeout);
const tooltipPos = getTooltipPosition(event);
showTooltip({
tooltipData: bar,
tooltipLeft: tooltipPos.x,
tooltipTop: tooltipPos.y,
});
}}
/>
))
)
}
</BarStack>
</Group>
<BarStack<StackedBarData, string>
data={data}
keys={keys}
x={getCategory}
xScale={categoryScale}
yScale={valueScale}
color={colorScale}
>
{(barStacks) =>
barStacks.map((barStack) =>
barStack.bars.map((bar) => (
<rect
className={styles.barStack}
key={`bar-stack-${barStack.index}-${bar.index}`}
x={bar.x}
y={bar.y}
height={bar.height}
width={bar.width / 2}
fill={bar.color}
onMouseLeave={() => {
tooltipTimeout = window.setTimeout(() => {
hideTooltip();
}, 300);
}}
onMouseMove={(event) => {
if (tooltipTimeout) clearTimeout(tooltipTimeout);
const tooltipPos = getTooltipPosition(event);
showTooltip({
tooltipData: bar,
tooltipLeft: tooltipPos.x,
tooltipTop: tooltipPos.y,
});
}}
/>
))
)
}
</BarStack>
<Line
fill={Color.tertiaryBackground}
to={new Point({ x: axisLeftOffset, y: 0 })}
from={new Point({ x: axisLeftOffset, y: yMax })}
to={new Point({ x: 0, y: 0 })}
from={new Point({ x: 0, y: yMax })}
stroke={Color.tertiaryBackground}
strokeWidth={strokeWidth}
strokeDasharray={strokeDashArray}
/>
<AxisBottom
top={yMax}
scale={categoryScale}
left={
((categoryScale.bandwidth() * 100) / width) *
categoryAxisLeftFactor
}
hideTicks
hideAxisLine
labelProps={{
fontSize: `${10 / 16}rem`,
}}
tickLabelProps={() => ({
fill: Color.label,
fontWeight: TICK_LABEL_FONT_WEIGHT,
})}
/>
<AxisLeft
scale={valueScale}
top={5}
numTicks={numTicksValueAxis}
hideAxisLine
hideTicks
labelProps={{
fontSize: `${10 / 16}rem`,
}}
tickLabelProps={() => {
return {
fill: Color.label,
fontWeight: TICK_LABEL_FONT_WEIGHT,
};
}}
tickLabelProps={() => ({
fill: Color.label,
fontWeight: TICK_LABEL_FONT_WEIGHT,
textAnchor: "end",
})}
/>
</Group>
<AxisBottom
top={yMax + margin.top}
scale={categoryScale}
left={margin.left - categoryScale.bandwidth() / 4}
hideTicks
hideAxisLine
labelProps={{
fontSize: `${10 / 16}rem`,
}}
tickLabelProps={() => ({
fill: Color.label,
fontWeight: TICK_LABEL_FONT_WEIGHT,
textAnchor: "middle",
})}
/>
</svg>
{tooltipOpen && tooltipData ? (
@ -277,14 +252,9 @@ export const StackedBarGraphHorizontal = withTooltip<
margin,
scalePadding = 0.3,
numTicksValueAxis = 6,
axisLeftOffset = 40,
axisBottomOffset = 40,
strokeWidth = 2.5,
strokeDashArray = "10,4",
legendLeftOffset = 40,
legendTopOffset = 40,
itemMargin = "0 0 0 15px",
categoryAxisLeftFactor = 1,
tooltipOpen,
tooltipLeft,
tooltipTop,
@ -321,18 +291,15 @@ export const StackedBarGraphHorizontal = withTooltip<
});
// bounds
const xMax = width;
const yMax = height - margin.top - axisBottomOffset;
const xMax = width - margin.left - margin.right;
const yMax = height - margin.top - margin.bottom;
categoryScale.rangeRound([yMax, 0]);
valueScale.range([0, xMax - axisLeftOffset]);
valueScale.range([0, xMax]);
return width < 10 ? null : (
<div className={styles.container}>
<div
className={styles.legend}
style={{ left: width + legendLeftOffset, top: legendTopOffset }}
>
<div className={styles.legend}>
<LegendOrdinal
scale={colorScale}
direction="row"
@ -347,7 +314,6 @@ export const StackedBarGraphHorizontal = withTooltip<
width={xMax}
height={yMax}
offset={categoryScale.bandwidth() / 2}
left={axisLeftOffset}
stroke={Color.tertiaryBackground}
strokeWidth={strokeWidth}
strokeDasharray={strokeDashArray}
@ -356,55 +322,59 @@ export const StackedBarGraphHorizontal = withTooltip<
scale={valueScale}
height={yMax}
numTicks={numTicksValueAxis}
left={axisLeftOffset}
stroke={Color.tertiaryBackground}
strokeWidth={strokeWidth}
strokeDasharray={strokeDashArray}
/>
<Group left={axisLeftOffset}>
<BarStackHorizontal<StackedBarData, string>
data={data}
keys={keys}
y={getCategory}
xScale={valueScale}
yScale={categoryScale}
color={colorScale}
>
{(barStacks) =>
barStacks.map((barStack) =>
barStack.bars.map((bar) => (
<rect
className={styles.barStack}
key={`bar-stack-${barStack.index}-${bar.index}`}
x={bar.x}
y={bar.y}
height={bar.height / 2}
width={bar.width}
fill={bar.color}
onMouseLeave={() => {
tooltipTimeout = window.setTimeout(() => {
hideTooltip();
}, 300);
}}
onMouseMove={(event) => {
if (tooltipTimeout) clearTimeout(tooltipTimeout);
const tooltipPos = getTooltipPosition(event);
showTooltip({
tooltipData: bar,
tooltipLeft: tooltipPos.x,
tooltipTop: tooltipPos.y,
});
}}
/>
))
)
}
</BarStackHorizontal>
</Group>
<Line
fill={Color.tertiaryBackground}
to={new Point({ x: 0, y: 2 })}
from={new Point({ x: xMax, y: 2 })}
stroke={Color.tertiaryBackground}
strokeWidth={strokeWidth}
strokeDasharray={strokeDashArray}
/>
<BarStackHorizontal<StackedBarData, string>
data={data}
keys={keys}
y={getCategory}
xScale={valueScale}
yScale={categoryScale}
color={colorScale}
>
{(barStacks) =>
barStacks.map((barStack) =>
barStack.bars.map((bar) => (
<rect
className={styles.barStack}
key={`bar-stack-${barStack.index}-${bar.index}`}
x={bar.x}
y={bar.y}
height={bar.height / 2}
width={bar.width}
fill={bar.color}
onMouseLeave={() => {
tooltipTimeout = window.setTimeout(() => {
hideTooltip();
}, 300);
}}
onMouseMove={(event) => {
if (tooltipTimeout) clearTimeout(tooltipTimeout);
const tooltipPos = getTooltipPosition(event);
showTooltip({
tooltipData: bar,
tooltipLeft: tooltipPos.x,
tooltipTop: tooltipPos.y,
});
}}
/>
))
)
}
</BarStackHorizontal>
<AxisBottom
top={yMax}
scale={valueScale}
left={axisLeftOffset}
numTicks={numTicksValueAxis}
hideAxisLine
hideTicks
@ -414,25 +384,21 @@ export const StackedBarGraphHorizontal = withTooltip<
tickLabelProps={() => ({
fill: Color.label,
fontWeight: TICK_LABEL_FONT_WEIGHT,
textAnchor: "middle",
})}
/>
<AxisLeft
top={
-((categoryScale.bandwidth() * 100) / width) *
categoryAxisLeftFactor
}
scale={categoryScale}
hideAxisLine
hideTicks
labelProps={{
fontSize: `${10 / 16}rem`,
}}
tickLabelProps={() => {
return {
fill: Color.label,
fontWeight: TICK_LABEL_FONT_WEIGHT,
};
}}
tickLabelProps={() => ({
fill: Color.label,
fontWeight: TICK_LABEL_FONT_WEIGHT,
textAnchor: "middle",
})}
/>
</Group>
</svg>

View File

@ -1331,7 +1331,7 @@ export const A16 = [
"95-100": 0.059,
},
{
category: "Cumulative Average",
category: "CAV",
"0-30": 0,
"30-60": 0,
"60-70": 0.03774,

View File

@ -253,11 +253,7 @@ export default function Academics() {
Color.primaryAccentLighter,
]}
data={A12i}
axisLeftOffset={80}
margin={{
top: 20,
left: 20,
}}
margin={{ ...barGraphMargin, ...{ left: 90 } }}
/>
</ComponentWrapper>
@ -270,7 +266,7 @@ export default function Academics() {
<ComponentWrapper
heading="What were your averages for each term? Approximately, what is your cumulative average as of right now?"
bodyText="The majority of people did their best in 1A and their worst in 1B, 2A, or 2B, likely because most of the mandatory challenging CS+MATH courses lie around that period of time. Students started having better grades in 3A, correlating with the difficulty of the courses question seen previously."
bodyText="The majority of people did their best in 1A and their worst in 1B, 2A, or 2B, likely because most of the mandatory challenging CS+MATH courses lie around that period of time. Students started having better grades in 3A, correlating with the difficulty of the courses question seen previously. CAV stands for Cumulative Average."
align="center"
noBackground
>
@ -284,10 +280,7 @@ export default function Academics() {
Color.primaryAccentLighter,
]}
data={A16}
margin={{
top: 20,
left: 20,
}}
margin={barGraphMargin}
/>
</ComponentWrapper>

View File

@ -20,7 +20,12 @@ import {
import { pageRoutes } from "data/routes";
import React from "react";
import { Color } from "utils/Color";
import { DefaultProp, pieChartProps, wordCloudWidth } from "utils/defaultProps";
import {
barGraphMargin,
DefaultProp,
pieChartProps,
wordCloudWidth,
} from "utils/defaultProps";
import { useWindowDimensions } from "utils/getWindowDimensions";
import { useIsMobile } from "utils/isMobile";
@ -225,10 +230,7 @@ export default function CoopPage() {
Color.secondaryAccent,
]}
data={C7v}
margin={{
top: 20,
left: 20,
}}
margin={barGraphMargin}
/>
</div>
</ComponentWrapper>
@ -253,10 +255,7 @@ export default function CoopPage() {
Color.primaryText,
]}
data={C7vi}
margin={{
top: 20,
left: 20,
}}
margin={barGraphMargin}
/>
</div>
</ComponentWrapper>
@ -296,10 +295,7 @@ export default function CoopPage() {
keys={C7viiiKey}
colorRange={[Color.primaryAccent, Color.secondaryAccentLight]}
data={C7viii}
margin={{
top: 20,
left: 20,
}}
margin={barGraphMargin}
/>
</div>
</ComponentWrapper>

View File

@ -125,7 +125,9 @@ export default function Home() {
data={mockStackedBarGraphData}
margin={{
top: 20,
left: 20,
left: 28,
right: 20,
bottom: 20,
}}
/>
@ -147,8 +149,10 @@ export default function Home() {
]}
data={mockStackedBarGraphData}
margin={{
top: 20,
top: 60,
left: 20,
right: 20,
bottom: 40,
}}
/>