diff --git a/components/StackedBarGraph.module.css b/components/StackedBarGraph.module.css index 5930cf6..68e5a2a 100644 --- a/components/StackedBarGraph.module.css +++ b/components/StackedBarGraph.module.css @@ -9,7 +9,7 @@ .legend { position: absolute; display: flex; - font-size: 16px; + font-size: calc(16rem / 16); top: 0; } @@ -27,7 +27,7 @@ } .toolTip p { - margin: 0 5px; + margin: 0 calc(5rem / 16); font-size: calc(16rem / 16); } diff --git a/components/StackedBarGraph.tsx b/components/StackedBarGraph.tsx index 57b17cd..c12fce1 100644 --- a/components/StackedBarGraph.tsx +++ b/components/StackedBarGraph.tsx @@ -7,7 +7,9 @@ import { Point } from "@visx/point"; import { scaleBand, scaleLinear, scaleOrdinal } from "@visx/scale"; import { BarStack, Line } from "@visx/shape"; import { SeriesPoint } from "@visx/shape/lib/types"; -import { useTooltip, useTooltipInPortal, defaultStyles } from "@visx/tooltip"; +//import { useTooltip, useTooltipInPortal, withTooltip } from "@visx/tooltip"; +import { withTooltip, Tooltip } from "@visx/tooltip"; +import { WithTooltipProvidedProps } from "@visx/tooltip/lib/enhancers/withTooltip"; import React from "react"; import { Color } from "utils/Color"; @@ -45,6 +47,8 @@ export type StackedBarProps = { numTicksLeftAxis?: number; /** Distance between the left axis labels and the start of the lines of the graph, in px. */ valueAxisLeftOffset?: number; + /** Distance between the bottom axis and the bottom of the container of the graph, in px. */ + categoryAxisBottomOffset?: 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. */ @@ -57,204 +61,201 @@ export type StackedBarProps = { scalePadding?: number; /** Margin for each item in the legend */ itemMargin?: string; + /** Factor multiplied to the left offset to center the labels in the x-axis. >1 for width <600 and <1 for width >600 */ + categoryAxisLeftFactor?: number; }; let tooltipTimeout: number; -export function StackedBarGraph({ - data, - width, - height, - keys, - colorRange, - margin, - scalePadding = 0.3, - numTicksLeftAxis = 6, - valueAxisLeftOffset = 40, - strokeWidth = 2.5, - strokeDashArray = "10,4", - legendLeftOffset = 40, - legendTopOffset = 40, - itemMargin = "15px 0 0 0", -}: StackedBarProps) { - 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 xScale = scaleBand({ - domain: data.map(getCategory), - padding: scalePadding, - }); - const yScale = scaleLinear({ - domain: [0, Math.max(...yTotals)], - nice: true, - }); - const colorScale = scaleOrdinal({ - domain: keys, - range: colorRange, - }); - const { +export const StackedBarGraph = withTooltip( + ({ + data, + width, + height, + keys, + colorRange, + margin, + scalePadding = 0.3, + numTicksLeftAxis = 6, + valueAxisLeftOffset = 40, + categoryAxisBottomOffset = 40, + strokeWidth = 2.5, + strokeDashArray = "10,4", + legendLeftOffset = 40, + legendTopOffset = 40, + itemMargin = "15px 0 0 0", + categoryAxisLeftFactor = 1, tooltipOpen, tooltipLeft, tooltipTop, tooltipData, hideTooltip, showTooltip, - } = useTooltip(); + }: StackedBarProps & WithTooltipProvidedProps) => { + 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 { containerRef, TooltipInPortal } = useTooltipInPortal({ - // TooltipInPortal is rendered in a separate child of and positioned - // with page coordinates which should be updated on scroll. - scroll: true, - }); + const TICK_LABEL_FONT_WEIGHT = 800; - if (width < 10) return null; - // bounds - const xMax = width; - const yMax = height - margin.top - 50; + // accessors + const getCategory = (d: StackedBarData) => d.category; - xScale.rangeRound([0, xMax - valueAxisLeftOffset]); - yScale.range([yMax, 0]); + // scales + const xScale = scaleBand({ + domain: data.map(getCategory), + padding: scalePadding, + }); + const yScale = scaleLinear({ + domain: [0, Math.max(...yTotals)], + nice: true, + }); + const colorScale = scaleOrdinal({ + domain: keys, + range: colorRange, + }); - return width < 10 ? null : ( -
- - - - - - - data={data} - keys={keys} - x={getCategory} - xScale={xScale} - yScale={yScale} - color={colorScale} - > - {(barStacks) => - barStacks.map((barStack) => - barStack.bars.map((bar) => ( - { - tooltipTimeout = window.setTimeout(() => { - hideTooltip(); - }, 300); - }} - onMouseMove={(event) => { - if (tooltipTimeout) clearTimeout(tooltipTimeout); - // TooltipInPortal expects coordinates to be relative to containerRef - // localPoint returns coordinates relative to the nearest SVG - const eventSvgCoords = localPoint(event); - const left = bar.x + bar.width / 2; - showTooltip({ - tooltipData: bar, - tooltipTop: eventSvgCoords?.y, - tooltipLeft: left, - }); - }} - /> - )) - ) + // bounds + const xMax = width; + const yMax = height - margin.top - categoryAxisBottomOffset; + + xScale.rangeRound([0, xMax - valueAxisLeftOffset]); + yScale.range([yMax, 0]); + + return width < 10 ? null : ( +
+ + + + + + + data={data} + keys={keys} + x={getCategory} + xScale={xScale} + yScale={yScale} + color={colorScale} + > + {(barStacks) => + barStacks.map((barStack) => + barStack.bars.map((bar) => ( + { + 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, + }); + }} + /> + )) + ) + } + + + + - - - ({ - fill: Color.label, - fontWeight: TICK_LABEL_FONT_WEIGHT, - })} - /> - { - return { + hideTicks + hideAxisLine + labelProps={{ + fontSize: `${10 / 16}rem`, + }} + tickLabelProps={() => ({ fill: Color.label, fontWeight: TICK_LABEL_FONT_WEIGHT, - }; - }} - /> - - -
- -
- - {tooltipOpen && tooltipData ? ( - + { + return { + fill: Color.label, + fontWeight: TICK_LABEL_FONT_WEIGHT, + }; + }} + /> + + +
-

{tooltipData.key}

-

{tooltipData.bar.data[tooltipData.key]}

-

{getCategory(tooltipData.bar.data)}

- - ) : null} -
- ); -} + +
+ + {tooltipOpen && tooltipData ? ( + +

{tooltipData.key}

+

{tooltipData.bar.data[tooltipData.key]}

+

{getCategory(tooltipData.bar.data)}

+
+ ) : null} +
+ ); + } +); diff --git a/data/mocks.ts b/data/mocks.ts index b6024d8..f068337 100644 --- a/data/mocks.ts +++ b/data/mocks.ts @@ -65,36 +65,6 @@ export const moreMockCategoricalData = [ { key: "Dart", value: 2.21 }, ]; -export const mockBoxPlotData = [ - { - x: "1A", - min: 20, - firstQuartile: 25, - median: 30, - thirdQuartile: 80, - max: 100, - outliers: [], - }, - { - x: "1B", - min: 0, - firstQuartile: 20, - median: 30, - thirdQuartile: 50, - max: 100, - outliers: [], - }, - { - x: "2A", - min: 25, - firstQuartile: 35, - median: 50, - thirdQuartile: 90, - max: 100, - outliers: [], - }, -]; - export const mockStackedBarGraphData = [ { category: "1A",