Add Stacked Bar Graph component (Closes #3) #37
|
@ -0,0 +1,35 @@
|
||||||
|
.container {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.barStack:hover {
|
||||||
|
filter: drop-shadow(0 0 calc(4rem / 16) var(--label));
|
||||||
|
}
|
||||||
|
|
||||||
|
.legend {
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
font-size: calc(16rem / 16);
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolTip {
|
||||||
|
font-family: "Inconsolata", monospace;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
position: absolute;
|
||||||
|
background-color: var(--label);
|
||||||
|
color: var(--primary-background);
|
||||||
|
pointer-events: none;
|
||||||
|
border-radius: calc(10rem / 16);
|
||||||
|
padding: calc(10rem / 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolTip p {
|
||||||
|
margin: 0 calc(5rem / 16);
|
||||||
|
font-size: calc(16rem / 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
.key {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
|
@ -0,0 +1,455 @@
|
||||||
|
import { AxisLeft, AxisBottom } from "@visx/axis";
|
||||||
|
import { localPoint } from "@visx/event";
|
||||||
|
import { GridRows, GridColumns } from "@visx/grid";
|
||||||
|
import { Group } from "@visx/group";
|
||||||
|
import { LegendOrdinal } from "@visx/legend";
|
||||||
|
import { Point } from "@visx/point";
|
||||||
|
import { scaleBand, scaleLinear, scaleOrdinal } from "@visx/scale";
|
||||||
|
import { BarStack, BarStackHorizontal, Line } from "@visx/shape";
|
||||||
|
import { SeriesPoint } from "@visx/shape/lib/types";
|
||||||
|
import { withTooltip, Tooltip } from "@visx/tooltip";
|
||||||
|
import { WithTooltipProvidedProps } from "@visx/tooltip/lib/enhancers/withTooltip";
|
||||||
|
import React from "react";
|
||||||
|
import { Color } from "utils/Color";
|
||||||
|
|
||||||
|
import styles from "./StackedBarGraph.module.css";
|
||||||
|
|
||||||
|
interface StackedBarData {
|
||||||
|
category: string;
|
||||||
|
|||||||
|
[key: string]: number | string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type TooltipData = {
|
||||||
|
bar: SeriesPoint<StackedBarData>;
|
||||||
|
key: string;
|
||||||
|
index: number;
|
||||||
|
height: number;
|
||||||
|
width: number;
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
color: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type StackedBarProps = {
|
||||||
|
data: StackedBarData[];
|
||||||
|
/** Width of the entire graph, in pixels, greater than 10. */
|
||||||
|
width: number;
|
||||||
|
/** Height of the entire graph, in pixels. */
|
||||||
|
height: number;
|
||||||
|
/** Names of the groups appearing in the legend */
|
||||||
|
keys: string[];
|
||||||
|
/** 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 };
|
||||||
|
/** 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. */
|
||||||
|
strokeDashArray?: string;
|
||||||
|
/** Padding between each bar in the stacked bar graph, from 0 to 1 */
|
||||||
|
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;
|
||||||
|
|
||||||
|
export const StackedBarGraphVertical = withTooltip<
|
||||||
|
StackedBarProps,
|
||||||
|
TooltipData
|
||||||
|
>(
|
||||||
|
({
|
||||||
|
data,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
keys,
|
||||||
|
colorRange,
|
||||||
|
margin,
|
||||||
|
scalePadding = 0.3,
|
||||||
|
numTicksValueAxis = 6,
|
||||||
|
axisLeftOffset = 40,
|
||||||
|
axisBottomOffset = 40,
|
||||||
|
strokeWidth = 2.5,
|
||||||
|
strokeDashArray = "10,4",
|
||||||
|
legendLeftOffset = 40,
|
||||||
|
legendTopOffset = 40,
|
||||||
|
itemMargin = "15px 0 0 0",
|
||||||
|
categoryAxisLeftFactor = 1,
|
||||||
|
tooltipOpen,
|
||||||
|
tooltipLeft,
|
||||||
|
tooltipTop,
|
||||||
|
tooltipData,
|
||||||
|
hideTooltip,
|
||||||
|
showTooltip,
|
||||||
|
}: StackedBarProps & WithTooltipProvidedProps<TooltipData>) => {
|
||||||
|
const yTotals = data.reduce((allTotals, currCategory) => {
|
||||||
|
const yTotal = keys.reduce((categoryTotal, k) => {
|
||||||
|
categoryTotal += currCategory[k] as number;
|
||||||
|
return categoryTotal;
|
||||||
|
}, 0);
|
||||||
|
allTotals.push(yTotal);
|
||||||
|
return allTotals;
|
||||||
|
}, [] as number[]);
|
||||||
|
|
||||||
|
const TICK_LABEL_FONT_WEIGHT = 800;
|
||||||
|
|
||||||
|
// accessors
|
||||||
|
const getCategory = (d: StackedBarData) => d.category;
|
||||||
|
|
||||||
|
// scales
|
||||||
|
const categoryScale = scaleBand<string>({
|
||||||
|
domain: data.map(getCategory),
|
||||||
|
padding: scalePadding,
|
||||||
|
});
|
||||||
|
const valueScale = scaleLinear<number>({
|
||||||
|
domain: [0, Math.max(...yTotals)],
|
||||||
|
nice: true,
|
||||||
|
});
|
||||||
|
const colorScale = scaleOrdinal<string, string>({
|
||||||
|
domain: keys,
|
||||||
|
range: colorRange,
|
||||||
|
});
|
||||||
|
|
||||||
|
// bounds
|
||||||
|
const xMax = width;
|
||||||
|
const yMax = height - margin.top - axisBottomOffset;
|
||||||
|
|
||||||
|
categoryScale.rangeRound([0, xMax - axisLeftOffset]);
|
||||||
|
valueScale.range([yMax, 0]);
|
||||||
|
|
||||||
|
return width < 10 ? null : (
|
||||||
|
<div className={styles.container}>
|
||||||
|
<svg width={width} height={height}>
|
||||||
|
<Group top={margin.top} left={margin.left}>
|
||||||
|
<GridRows
|
||||||
|
scale={valueScale}
|
||||||
|
width={xMax}
|
||||||
|
height={yMax}
|
||||||
|
left={axisLeftOffset}
|
||||||
|
numTicks={numTicksValueAxis}
|
||||||
|
stroke={Color.tertiaryBackground}
|
||||||
|
strokeWidth={strokeWidth}
|
||||||
|
strokeDasharray={strokeDashArray}
|
||||||
|
/>
|
||||||
|
<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 eventSvgCoords = localPoint(event);
|
||||||
|
const left = bar.x + bar.width / 2;
|
||||||
|
showTooltip({
|
||||||
|
tooltipData: bar,
|
||||||
|
tooltipTop: eventSvgCoords?.y,
|
||||||
|
tooltipLeft: left,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</BarStack>
|
||||||
|
</Group>
|
||||||
|
<Line
|
||||||
|
fill={Color.tertiaryBackground}
|
||||||
|
to={new Point({ x: axisLeftOffset, y: 0 })}
|
||||||
|
from={new Point({ x: axisLeftOffset, 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
|
||||||
|
labelProps={{
|
||||||
|
fontSize: `${10 / 16}rem`,
|
||||||
|
}}
|
||||||
|
tickLabelProps={() => {
|
||||||
|
return {
|
||||||
|
fill: Color.label,
|
||||||
|
fontWeight: TICK_LABEL_FONT_WEIGHT,
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Group>
|
||||||
|
</svg>
|
||||||
|
<div
|
||||||
|
className={styles.legend}
|
||||||
|
style={{ left: width + legendLeftOffset, top: legendTopOffset }}
|
||||||
|
>
|
||||||
|
<LegendOrdinal
|
||||||
|
scale={colorScale}
|
||||||
|
direction="column"
|
||||||
|
itemMargin={itemMargin}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{tooltipOpen && tooltipData ? (
|
||||||
|
<Tooltip
|
||||||
|
className={styles.toolTip}
|
||||||
|
top={tooltipTop}
|
||||||
|
left={tooltipLeft}
|
||||||
|
unstyled
|
||||||
|
>
|
||||||
|
<p className={styles.key}>{tooltipData.key}</p>
|
||||||
|
<p>{tooltipData.bar.data[tooltipData.key]}</p>
|
||||||
|
<p>{getCategory(tooltipData.bar.data)}</p>
|
||||||
|
</Tooltip>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const StackedBarGraphHorizontal = withTooltip<
|
||||||
|
StackedBarProps,
|
||||||
|
TooltipData
|
||||||
|
>(
|
||||||
|
({
|
||||||
|
data,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
keys,
|
||||||
|
colorRange,
|
||||||
|
margin,
|
||||||
|
scalePadding = 0.3,
|
||||||
|
numTicksValueAxis = 6,
|
||||||
|
axisLeftOffset = 40,
|
||||||
|
axisBottomOffset = 40,
|
||||||
|
strokeWidth = 2.5,
|
||||||
|
strokeDashArray = "10,4",
|
||||||
|
legendLeftOffset = 40,
|
||||||
|
legendTopOffset = 40,
|
||||||
|
itemMargin = "15px 0 0 0",
|
||||||
|
categoryAxisLeftFactor = 1,
|
||||||
|
tooltipOpen,
|
||||||
|
tooltipLeft,
|
||||||
|
tooltipTop,
|
||||||
|
tooltipData,
|
||||||
|
hideTooltip,
|
||||||
|
showTooltip,
|
||||||
|
}: StackedBarProps & WithTooltipProvidedProps<TooltipData>) => {
|
||||||
|
const yTotals = data.reduce((allTotals, currCategory) => {
|
||||||
|
const yTotal = keys.reduce((categoryTotal, k) => {
|
||||||
|
categoryTotal += currCategory[k] as number;
|
||||||
|
return categoryTotal;
|
||||||
|
}, 0);
|
||||||
|
allTotals.push(yTotal);
|
||||||
|
return allTotals;
|
||||||
|
}, [] as number[]);
|
||||||
|
|
||||||
|
const TICK_LABEL_FONT_WEIGHT = 800;
|
||||||
|
|
||||||
|
// accessors
|
||||||
|
const getCategory = (d: StackedBarData) => d.category;
|
||||||
|
|
||||||
|
// scales
|
||||||
|
const valueScale = scaleLinear<number>({
|
||||||
|
domain: [0, Math.max(...yTotals)],
|
||||||
|
nice: true,
|
||||||
|
});
|
||||||
|
const categoryScale = scaleBand<string>({
|
||||||
|
domain: data.map(getCategory),
|
||||||
|
padding: scalePadding,
|
||||||
|
});
|
||||||
|
const colorScale = scaleOrdinal<string, string>({
|
||||||
|
domain: keys,
|
||||||
|
range: colorRange,
|
||||||
|
});
|
||||||
|
|
||||||
|
// bounds
|
||||||
|
const xMax = width;
|
||||||
|
const yMax = height - margin.top - axisBottomOffset;
|
||||||
|
|
||||||
|
categoryScale.rangeRound([yMax, 0]);
|
||||||
a258wang
commented
Please change the range to Please change the range to `[0, yMax]` - it makes it so the order of the categories is the same top-to-bottom when the graph has horizontal bars vs. left-to-right when the graph has vertical bars.
|
|||||||
|
valueScale.range([0, xMax - axisLeftOffset]);
|
||||||
|
|
||||||
|
return width < 10 ? null : (
|
||||||
|
<div className={styles.container}>
|
||||||
|
<svg width={width} height={height}>
|
||||||
|
<Group top={margin.top} left={margin.left}>
|
||||||
|
<GridRows
|
||||||
|
scale={categoryScale}
|
||||||
|
width={xMax}
|
||||||
|
height={yMax}
|
||||||
|
offset={categoryScale.bandwidth() / 2}
|
||||||
|
left={axisLeftOffset}
|
||||||
|
stroke={Color.tertiaryBackground}
|
||||||
|
strokeWidth={strokeWidth}
|
||||||
|
strokeDasharray={strokeDashArray}
|
||||||
|
/>
|
||||||
|
<GridColumns
|
||||||
|
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 eventSvgCoords = localPoint(event);
|
||||||
|
const left = bar.x + bar.width / 2;
|
||||||
|
showTooltip({
|
||||||
|
tooltipData: bar,
|
||||||
|
tooltipTop: eventSvgCoords?.y,
|
||||||
|
tooltipLeft: left,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</BarStackHorizontal>
|
||||||
|
</Group>
|
||||||
|
<AxisBottom
|
||||||
|
top={yMax}
|
||||||
|
scale={valueScale}
|
||||||
|
left={axisLeftOffset}
|
||||||
|
numTicks={numTicksValueAxis}
|
||||||
|
hideAxisLine
|
||||||
|
hideTicks
|
||||||
|
labelProps={{
|
||||||
|
fontSize: `${10 / 16}rem`,
|
||||||
|
}}
|
||||||
|
tickLabelProps={() => ({
|
||||||
|
fill: Color.label,
|
||||||
|
fontWeight: TICK_LABEL_FONT_WEIGHT,
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
<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,
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Group>
|
||||||
|
</svg>
|
||||||
|
<div
|
||||||
|
className={styles.legend}
|
||||||
|
style={{ left: width + legendLeftOffset, top: legendTopOffset }}
|
||||||
|
>
|
||||||
|
<LegendOrdinal
|
||||||
|
scale={colorScale}
|
||||||
|
direction="column"
|
||||||
|
itemMargin={itemMargin}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{tooltipOpen && tooltipData ? (
|
||||||
|
<Tooltip
|
||||||
|
className={styles.toolTip}
|
||||||
|
top={tooltipTop}
|
||||||
|
left={tooltipLeft}
|
||||||
|
unstyled
|
||||||
|
>
|
||||||
|
<p className={styles.key}>{tooltipData.key}</p>
|
||||||
|
<p>{tooltipData.bar.data[tooltipData.key]}</p>
|
||||||
|
<p>{getCategory(tooltipData.bar.data)}</p>
|
||||||
|
</Tooltip>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
|
@ -80,6 +80,45 @@ export const moreMockCategoricalData = [
|
||||||
{ key: "Dart", value: 2.21 },
|
{ key: "Dart", value: 2.21 },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const mockStackedBarGraphData = [
|
||||||
|
{
|
||||||
|
category: "1A",
|
||||||
|
"geese watchers": 60,
|
||||||
|
"geese breeders": 80,
|
||||||
|
"geese catchers": 90,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: "1B",
|
||||||
|
"geese watchers": 25,
|
||||||
|
"geese breeders": 37,
|
||||||
|
"geese catchers": 80,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: "2A",
|
||||||
|
"geese watchers": 40,
|
||||||
|
"geese breeders": 50,
|
||||||
|
"geese catchers": 70,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: "2B",
|
||||||
|
"geese watchers": 40,
|
||||||
|
"geese breeders": 80,
|
||||||
|
"geese catchers": 88,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: "3A",
|
||||||
|
"geese watchers": 15,
|
||||||
|
"geese breeders": 30,
|
||||||
|
"geese catchers": 45,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const mockStackedBarKeys = [
|
||||||
|
"geese watchers",
|
||||||
|
"geese breeders",
|
||||||
|
"geese catchers",
|
||||||
|
];
|
||||||
|
|
||||||
export const mockTimelineData = [
|
export const mockTimelineData = [
|
||||||
{
|
{
|
||||||
time: "Fall 2020",
|
time: "Fall 2020",
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
"@visx/event": "^2.6.0",
|
"@visx/event": "^2.6.0",
|
||||||
"@visx/grid": "^2.10.0",
|
"@visx/grid": "^2.10.0",
|
||||||
"@visx/group": "^2.10.0",
|
"@visx/group": "^2.10.0",
|
||||||
|
"@visx/legend": "^2.10.0",
|
||||||
"@visx/mock-data": "^2.1.2",
|
"@visx/mock-data": "^2.1.2",
|
||||||
"@visx/scale": "^2.2.2",
|
"@visx/scale": "^2.2.2",
|
||||||
"@visx/shape": "^2.10.0",
|
"@visx/shape": "^2.10.0",
|
||||||
|
@ -973,6 +974,21 @@
|
||||||
"react": "^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0"
|
"react": "^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@visx/legend": {
|
||||||
|
"version": "2.10.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@visx/legend/-/legend-2.10.0.tgz",
|
||||||
|
"integrity": "sha512-OI8BYE6QQI9eXAng/C7UzuVw7d0fwlzrth6RmrdhlyT1K+BA3WpExapV+pDfwxu/tkEik8Ps5cZRV6HjX1/Mww==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@visx/group": "2.10.0",
|
||||||
|
"@visx/scale": "2.2.2",
|
||||||
|
"classnames": "^2.3.1",
|
||||||
|
"prop-types": "^15.5.10"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.3.0-0 || ^17.0.0-0 || ^18.0.0-0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@visx/mock-data": {
|
"node_modules/@visx/mock-data": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@visx/mock-data/-/mock-data-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@visx/mock-data/-/mock-data-2.1.2.tgz",
|
||||||
|
@ -5086,6 +5102,18 @@
|
||||||
"prop-types": "^15.6.2"
|
"prop-types": "^15.6.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@visx/legend": {
|
||||||
|
"version": "2.10.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@visx/legend/-/legend-2.10.0.tgz",
|
||||||
|
"integrity": "sha512-OI8BYE6QQI9eXAng/C7UzuVw7d0fwlzrth6RmrdhlyT1K+BA3WpExapV+pDfwxu/tkEik8Ps5cZRV6HjX1/Mww==",
|
||||||
|
"requires": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@visx/group": "2.10.0",
|
||||||
|
"@visx/scale": "2.2.2",
|
||||||
|
"classnames": "^2.3.1",
|
||||||
|
"prop-types": "^15.5.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@visx/mock-data": {
|
"@visx/mock-data": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@visx/mock-data/-/mock-data-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@visx/mock-data/-/mock-data-2.1.2.tgz",
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
"@visx/event": "^2.6.0",
|
"@visx/event": "^2.6.0",
|
||||||
"@visx/grid": "^2.10.0",
|
"@visx/grid": "^2.10.0",
|
||||||
"@visx/group": "^2.10.0",
|
"@visx/group": "^2.10.0",
|
||||||
|
"@visx/legend": "^2.10.0",
|
||||||
"@visx/mock-data": "^2.1.2",
|
"@visx/mock-data": "^2.1.2",
|
||||||
"@visx/scale": "^2.2.2",
|
"@visx/scale": "^2.2.2",
|
||||||
"@visx/shape": "^2.10.0",
|
"@visx/shape": "^2.10.0",
|
||||||
|
|
|
@ -3,6 +3,8 @@ import { BoxPlot } from "components/Boxplot";
|
||||||
import {
|
import {
|
||||||
mockCategoricalData,
|
mockCategoricalData,
|
||||||
moreMockCategoricalData,
|
moreMockCategoricalData,
|
||||||
|
mockStackedBarKeys,
|
||||||
|
mockStackedBarGraphData,
|
||||||
mockBoxPlotData,
|
mockBoxPlotData,
|
||||||
mockQuoteData,
|
mockQuoteData,
|
||||||
mockQuoteDataLong,
|
mockQuoteDataLong,
|
||||||
|
@ -11,11 +13,16 @@ import {
|
||||||
} from "data/mocks";
|
} from "data/mocks";
|
||||||
import { sectionsData } from "data/routes";
|
import { sectionsData } from "data/routes";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { Color } from "utils/Color";
|
||||||
|
|
||||||
import { About } from "@/components/About";
|
import { About } from "@/components/About";
|
||||||
import { PieChart } from "@/components/PieChart";
|
import { PieChart } from "@/components/PieChart";
|
||||||
import { QuotationCarousel } from "@/components/QuotationCarousel";
|
import { QuotationCarousel } from "@/components/QuotationCarousel";
|
||||||
import { Sections } from "@/components/Sections";
|
import { Sections } from "@/components/Sections";
|
||||||
|
import {
|
||||||
|
StackedBarGraphVertical,
|
||||||
|
StackedBarGraphHorizontal,
|
||||||
|
} from "@/components/StackedBarGraph";
|
||||||
import { Timeline } from "@/components/Timeline";
|
import { Timeline } from "@/components/Timeline";
|
||||||
|
|
||||||
import { CenterWrapper } from "../components/CenterWrapper";
|
import { CenterWrapper } from "../components/CenterWrapper";
|
||||||
|
@ -26,7 +33,7 @@ import styles from "./playground.module.css";
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
<div className={styles.page}>
|
<div className={styles.page} suppressHydrationWarning>
|
||||||
<h1>Playground</h1>
|
<h1>Playground</h1>
|
||||||
<p>Show off your components here!</p>
|
<p>Show off your components here!</p>
|
||||||
|
|
||||||
|
@ -95,6 +102,48 @@ export default function Home() {
|
||||||
}))}
|
}))}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<h2>
|
||||||
|
<code>{"<StackedBarGraphVertical />"}</code>
|
||||||
|
</h2>
|
||||||
|
<StackedBarGraphVertical
|
||||||
|
width={600}
|
||||||
|
height={400}
|
||||||
|
keys={mockStackedBarKeys}
|
||||||
|
colorRange={[
|
||||||
|
Color.primaryAccent,
|
||||||
|
Color.secondaryAccentLight,
|
||||||
|
Color.primaryAccentLighter,
|
||||||
|
]}
|
||||||
|
data={mockStackedBarGraphData}
|
||||||
|
margin={{
|
||||||
|
top: 20,
|
||||||
|
left: 20,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<h2>
|
||||||
|
<code>{"<StackedBarGraphHorizontal />"}</code>
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
<code>{"<StackedBarGraphHorizontal />"}</code> takes the same props as{" "}
|
||||||
|
<code>{"<StackedBarGraphVertical />"}</code>.
|
||||||
|
</p>
|
||||||
|
<StackedBarGraphHorizontal
|
||||||
|
width={600}
|
||||||
|
height={400}
|
||||||
|
keys={mockStackedBarKeys}
|
||||||
|
colorRange={[
|
||||||
|
Color.primaryAccent,
|
||||||
|
Color.secondaryAccentLight,
|
||||||
|
Color.primaryAccentLighter,
|
||||||
|
]}
|
||||||
|
data={mockStackedBarGraphData}
|
||||||
|
margin={{
|
||||||
|
top: 20,
|
||||||
|
left: 20,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
<h2>
|
<h2>
|
||||||
<code>{"<Timeline />"}</code>
|
<code>{"<Timeline />"}</code>
|
||||||
</h2>
|
</h2>
|
||||||
|
|
Loading…
Reference in New Issue
Have we considered
?
This would allow for a key called "category", at the expense of more nesting.
Also is there a reason why the value can beEDIT: nvm, it's to make TS stop complainingnumber | string
and not justnumber
?https://stackoverflow.com/questions/54460029/typescript-inteface-where-id-must-be-number-and-other-properties-must-be-strin
I have considered the above, but it doesn't work since the format of the data doesn't match what is expected in the props
data
taken by the<BarStack>
component:It specifically needs
category
to be a key indata
and all the other keys to be directly nested inside ofdata
(cannot be nested inside ofvalues
).