resolve conf
continuous-integration/drone/push Build is failing Details

This commit is contained in:
Rebecca-Chou 2022-09-06 13:55:40 -04:00
commit ebd713afd2
6 changed files with 2223 additions and 2 deletions

View File

@ -0,0 +1,38 @@
.boxplot {
fill: var(--primary-accent-light);
}
.boxplot:hover {
fill: var(--primary-accent);
filter: drop-shadow(0 0 calc(4rem / 16) var(--primary-accent));
}
.tooltip {
font-family: "Inconsolata", monospace;
top: 0;
left: 0;
position: absolute;
background-color: var(--label);
color: var(--primary-background);
pointer-events: none;
padding: calc(10rem / 16);
border-radius: calc(10rem / 16);
}
.tooltip .category {
margin: calc(10rem / 16) 0 0 0;
font-size: calc(16rem / 16);
font-weight: 700;
}
.tooltip .toolTipData {
margin-top: calc(5rem / 16);
margin-bottom: calc(10rem / 16);
font-size: calc(16rem / 16);
}
.tooltip .toolTipData p {
margin: 0;
padding: 0;
font-size: calc(16rem / 16);
}

361
components/Boxplot.tsx Normal file
View File

@ -0,0 +1,361 @@
import { AxisLeft, AxisBottom } from "@visx/axis";
import { GridRows, GridColumns } from "@visx/grid";
import { Group } from "@visx/group";
import { Stats } from "@visx/mock-data/lib/generators/genStats";
import { Point } from "@visx/point";
import { scaleBand, scaleLinear } from "@visx/scale";
import { Line } from "@visx/shape";
import { BoxPlot as VisxBoxPlot } from "@visx/stats";
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 "./Boxplot.module.css";
const DEFAULT_LABEL_SIZE = 16;
const TICK_LABEL_FONT_WEIGHT = 800;
interface BoxPlotData {
category: string;
min: number;
median: number;
max: number;
firstQuartile: number;
thirdQuartile: number;
outliers?: number[];
}
type TooltipData = Omit<BoxPlotData, "outliers">;
export type StatsPlotProps = {
data: BoxPlotData[];
/** Width of the entire graph, in pixels, greater than 10. */
width: number;
/** Height of the entire graph, in pixels. */
height: number;
/** Distance between the edge of the graph and the area where the bars are drawn, in pixels. */
margin: {
top: number;
left: 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;
/** Number of ticks for the value (y-)axis */
numTicksLeftAxis?: number;
/** Distance between the boxplot and the top of the grid, in px. */
plotTopOffset?: number;
/** Distance between the left axis labels and the start of the lines of the graph, in px. */
valueAxisLeftOffset?: number;
/** Distance between the top and the first label of the y axis, in px. */
valueAxisLabelTopOffset?: number;
/** Distance between the left and the labels of the y axis, in px. */
valueAxisLabelLeftOffset?: number;
/** Distance between the left and the start of the first label of the x axis, in px. */
categoryAxisLabelLeftOffset?: number;
/** Distance between the top and the column lines of the grid of the graph, in px. */
gridColumnTopOffset?: number;
/** Distance between the top of the point in the boxplot and the start of the tooltip box, in px. */
toolTipTopOffset?: number;
/** Distance between the left of the point in the boxplot and the start of the tooltip box, in px. */
toolTipLeftOffset?: number;
/** Font size of the category (x-)axis labels */
categoryAxisLabelSize?: number;
/** Font size of the value (y-)axis labels */
valueAxisLabelSize?: number;
/** Font size of the text in the tool tip box */
toolTipFontSize?: number;
/** Factor multiplied with the compressed width to determine the box width, in px. */
boxPlotWidthFactor?: number;
/** Factor multiplied with the compressed width to determine the distance between boxes, in px. */
boxPlotLeftOffset?: number;
};
export const BoxPlot = withTooltip<StatsPlotProps, TooltipData>(
({
width,
height,
data,
margin,
tooltipOpen,
tooltipLeft,
tooltipTop,
tooltipData,
showTooltip,
hideTooltip,
strokeWidth = 2.5,
strokeDashArray = "10,4",
numTicksLeftAxis = 6,
plotTopOffset = 10,
valueAxisLeftOffset = 40,
gridColumnTopOffset = -20,
valueAxisLabelTopOffset = 5,
valueAxisLabelLeftOffset = 10,
categoryAxisLabelLeftOffset = 30,
toolTipTopOffset = 20,
toolTipLeftOffset = 5,
categoryAxisLabelSize = DEFAULT_LABEL_SIZE,
valueAxisLabelSize = DEFAULT_LABEL_SIZE,
boxPlotWidthFactor = 0.4,
boxPlotLeftOffset = 0.3,
}: StatsPlotProps & WithTooltipProvidedProps<TooltipData>) => {
// bounds
const xMax = width;
const yMax = height - 120;
// formatting data
const plotData: Stats[] = data.map((d) => {
return {
boxPlot: {
...d,
x: d.category,
outliers: [],
},
binData: [],
};
});
// accessors
const getX = (d: Stats) => d.boxPlot.x;
const getMin = (d: Stats) => d.boxPlot.min;
const getMax = (d: Stats) => d.boxPlot.max;
const getMedian = (d: Stats) => d.boxPlot.median;
const getFirstQuartile = (d: Stats) => d.boxPlot.firstQuartile;
const getThirdQuartile = (d: Stats) => d.boxPlot.thirdQuartile;
// scales
const xScale = scaleBand<string>({
range: [18, xMax - 80], // scaling is needed due to the left offset
round: true,
domain: plotData.map(getX),
padding: 0.3,
});
const values = plotData.reduce((allValues, { boxPlot }) => {
allValues.push(boxPlot.min, boxPlot.max);
return allValues;
}, [] as number[]);
const minYValue = Math.min(...values);
const maxYValue = Math.max(...values);
const yScale = scaleLinear<number>({
range: [yMax, 0],
round: true,
domain: [minYValue, maxYValue],
});
const constrainedWidth = Math.min(200, xScale.bandwidth());
return width < 10 ? null : (
<div>
<svg width={width} height={height}>
<Group top={margin.top} left={margin.left}>
<GridRows
top={plotTopOffset}
left={valueAxisLeftOffset}
scale={yScale}
width={xMax}
numTicks={numTicksLeftAxis}
stroke={Color.tertiaryBackground}
strokeWidth={strokeWidth}
strokeDasharray={strokeDashArray}
/>
<GridColumns
scale={xScale}
height={yMax + plotTopOffset - gridColumnTopOffset}
top={gridColumnTopOffset}
left={valueAxisLeftOffset}
stroke={Color.tertiaryBackground}
strokeWidth={strokeWidth}
strokeDasharray={strokeDashArray}
/>
<Line
fill={Color.tertiaryBackground}
to={new Point({ x: valueAxisLeftOffset, y: gridColumnTopOffset })}
from={
new Point({ x: valueAxisLeftOffset, y: yMax + plotTopOffset })
}
stroke={Color.tertiaryBackground}
strokeWidth={strokeWidth}
strokeDasharray={strokeDashArray}
/>
<Line
fill={Color.tertiaryBackground}
to={
new Point({
x: xMax - margin.left - strokeWidth,
y: gridColumnTopOffset,
})
}
from={
new Point({
x: xMax - margin.left - strokeWidth,
y: yMax + plotTopOffset,
})
}
stroke={Color.tertiaryBackground}
strokeWidth={strokeWidth}
strokeDasharray={strokeDashArray}
/>
<AxisBottom
top={yMax + plotTopOffset - gridColumnTopOffset}
left={categoryAxisLabelLeftOffset}
scale={xScale}
hideAxisLine
hideTicks
labelProps={{
fontSize: `${categoryAxisLabelSize / 16}rem`,
}}
tickLabelProps={() => {
return {
fill: Color.label,
fontWeight: TICK_LABEL_FONT_WEIGHT,
};
}}
/>
<AxisLeft
scale={yScale}
top={plotTopOffset + valueAxisLabelTopOffset}
left={valueAxisLabelLeftOffset}
numTicks={numTicksLeftAxis}
hideAxisLine
labelProps={{
fontSize: `${valueAxisLabelSize / 16}rem`,
}}
tickLabelProps={() => {
return {
fill: Color.label,
fontWeight: TICK_LABEL_FONT_WEIGHT,
};
}}
/>
<Group top={plotTopOffset}>
{plotData.map((d: Stats, i) => (
<Group key={i}>
<VisxBoxPlot
className={styles.boxplot}
min={getMin(d)}
max={getMax(d)}
left={
xScale(getX(d))! +
boxPlotLeftOffset * constrainedWidth +
valueAxisLeftOffset
}
firstQuartile={getFirstQuartile(d)}
thirdQuartile={getThirdQuartile(d)}
median={getMedian(d)}
boxWidth={constrainedWidth * boxPlotWidthFactor}
rx={0}
ry={0}
stroke={Color.label}
strokeWidth={strokeWidth}
valueScale={yScale}
minProps={{
onMouseOver: () => {
showTooltip({
tooltipTop:
(yScale(getMin(d)) ?? 0) + toolTipTopOffset,
tooltipLeft:
xScale(getX(d))! +
constrainedWidth +
toolTipLeftOffset,
tooltipData: {
...d.boxPlot,
category: getX(d),
},
});
},
onMouseLeave: () => {
hideTooltip();
},
}}
maxProps={{
onMouseOver: () => {
showTooltip({
tooltipTop:
(yScale(getMax(d)) ?? 0) + toolTipTopOffset,
tooltipLeft:
xScale(getX(d))! +
constrainedWidth +
toolTipLeftOffset,
tooltipData: {
...d.boxPlot,
category: getX(d),
},
});
},
onMouseLeave: () => {
hideTooltip();
},
}}
boxProps={{
onMouseOver: () => {
showTooltip({
tooltipTop:
(yScale(getMedian(d)) ?? 0) + toolTipTopOffset,
tooltipLeft:
xScale(getX(d))! +
constrainedWidth +
toolTipLeftOffset,
tooltipData: {
...d.boxPlot,
category: getX(d),
},
});
},
strokeWidth: 0,
onMouseLeave: () => {
hideTooltip();
},
}}
medianProps={{
style: {
stroke: Color.label,
},
onMouseOver: () => {
showTooltip({
tooltipTop:
(yScale(getMedian(d)) ?? 0) + toolTipTopOffset,
tooltipLeft:
xScale(getX(d))! +
constrainedWidth +
toolTipLeftOffset,
tooltipData: {
...d.boxPlot,
category: getX(d),
},
});
},
onMouseLeave: () => {
hideTooltip();
},
}}
/>
</Group>
))}
</Group>
</Group>
</svg>
{tooltipOpen && tooltipData && (
<Tooltip
top={tooltipTop}
left={tooltipLeft}
className={styles.tooltip}
unstyled
>
<p className={styles.category}>{tooltipData.category}</p>
<div className={styles.toolTipData}>
<p>max: {tooltipData.max}</p>
<p>third quartile: {tooltipData.thirdQuartile}</p>
<p>median: {tooltipData.median}</p>
<p>first quartile: {tooltipData.firstQuartile}</p>
<p>min: {tooltipData.min}</p>
</div>
</Tooltip>
)}
</div>
);
}
);

View File

@ -78,6 +78,37 @@ export const mockLineData = {
}, },
], ],
}; };
export const mockBoxPlotData = [
{
category: "1A",
min: 20,
firstQuartile: 25,
median: 30,
thirdQuartile: 80,
max: 100,
outliers: [],
},
{
category: "1B",
min: 0,
firstQuartile: 20,
median: 30,
thirdQuartile: 50,
max: 100,
outliers: [],
},
{
category: "2A",
min: 25,
firstQuartile: 35,
median: 50,
thirdQuartile: 90,
max: 100,
outliers: [],
},
];
export const mockQuoteData = [ export const mockQuoteData = [
"The quick brown fox jumps over the lazy dog.", "The quick brown fox jumps over the lazy dog.",
"Sphinx of black quartz, judge my vow!", "Sphinx of black quartz, judge my vow!",

1775
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -21,6 +21,7 @@
"@visx/group": "^2.10.0", "@visx/group": "^2.10.0",
"@visx/scale": "^2.2.2", "@visx/scale": "^2.2.2",
"@visx/shape": "^2.10.0", "@visx/shape": "^2.10.0",
"@visx/stats": "^2.10.0",
"@visx/text": "^2.10.0", "@visx/text": "^2.10.0",
"@visx/tooltip": "^2.10.0", "@visx/tooltip": "^2.10.0",
"@visx/wordcloud": "^2.10.0", "@visx/wordcloud": "^2.10.0",

View File

@ -1,7 +1,9 @@
import { BarGraphHorizontal, BarGraphVertical } from "components/BarGraph"; import { BarGraphHorizontal, BarGraphVertical } from "components/BarGraph";
import { BoxPlot } from "components/Boxplot";
import { import {
mockCategoricalData, mockCategoricalData,
moreMockCategoricalData, moreMockCategoricalData,
mockBoxPlotData,
mockLineData, mockLineData,
mockQuoteData, mockQuoteData,
mockQuoteDataLong, mockQuoteDataLong,
@ -32,7 +34,7 @@ export default function Home() {
width={800} width={800}
height={500} height={500}
margin={{ margin={{
top: 20, top: 25,
bottom: 40, bottom: 40,
left: 150, left: 150,
right: 20, right: 20,
@ -82,7 +84,20 @@ export default function Home() {
left: 40, left: 40,
right: 20, right: 20,
}} }}
></LineGraph> />
<h2>
<code>{"<BoxPlot />"}</code>
</h2>
<BoxPlot
width={600}
height={400}
data={mockBoxPlotData}
margin={{
top: 20,
left: 20,
}}
/>
<h2> <h2>
<code>{"<QuotationCarousel />"}</code> <code>{"<QuotationCarousel />"}</code>