metaforecast/src/web/questions/components/HistoryChart/InnerChart.tsx
NunoSempere 020f0c0c5e tweak: Further question page changes
- Add estimate of likeliest option to top
- Or of event happening for yes/no questions
- Restore selection highlight from legend
  - But avoid weird color errors
- Add a bunch of parameters to QuestionOptions
  - I could just have added a flag for isInQuestionPage,
  - Not sure if this is the best way to go about this

I'm not particularly planning to continue with changes in the upcoming
days
2022-05-20 13:53:21 -04:00

211 lines
5.6 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { differenceInDays, format } from "date-fns";
import {
VictoryAxis,
VictoryChart,
VictoryGroup,
VictoryStack,
VictoryLabel,
VictoryLine,
VictoryScatter,
VictoryArea,
VictoryTheme,
VictoryTooltip,
VictoryVoronoiContainer,
} from "victory";
import { chartColors, ChartData, ChartSeries, height, width } from "./utils";
let dateFormat = "MMM do y"; // "yyyy-MM-dd"
// can't be replaced with React component, VictoryChart requires VictoryGroup elements to be immediate children
const getVictoryGroup = ({
data,
i,
highlight,
isBinary,
}: {
data: ChartSeries;
i: number;
highlight?: boolean;
isBinary?: boolean;
}) => {
return (
<VictoryGroup color={chartColors[i] || "darkgray"} data={data} key={i}>
<VictoryLine
name={`line-${i}`}
style={{
data: {
// strokeOpacity: highlight ? 1 : 0.5,
strokeOpacity: highlight && !isBinary ? 0.8 : 0.6,
strokeWidth: highlight && !isBinary ? 4 : 3,
},
}}
/>
{isBinary ? (
<VictoryArea
standalone={false}
style={{
data: { fill: chartColors[i], fillOpacity: 0.1, strokeOpacity: 0 },
}}
data={data}
/>
) : null}
<VictoryScatter
name={`scatter-${i}`}
size={({ active }) => (active || highlight ? 0 : 0)} //(active || highlight ? 3.75 : 3)}
/>
</VictoryGroup>
);
};
export type Props = {
data: ChartData;
highlight: number | undefined;
};
export const InnerChart: React.FC<Props> = ({
data: { maxProbability, seriesList, minDate, maxDate },
highlight,
}) => {
const domainMax =
maxProbability < 0.5 ? Math.round(10 * (maxProbability + 0.05)) / 10 : 1;
const padding = {
top: 20,
bottom: 75,
left: 70,
right: 17,
};
const isBinary = seriesList.length == 1;
console.log(isBinary);
return (
<VictoryChart
domainPadding={{ x: 0 }}
padding={padding}
theme={VictoryTheme.material}
height={height}
width={width}
containerComponent={
<VictoryVoronoiContainer
labels={() => "Not shown"}
labelComponent={
<VictoryTooltip
constrainToVisibleArea
pointerLength={0}
dy={-12}
labelComponent={
<VictoryLabel
style={[
{
fontSize: 16,
fill: "black",
strokeWidth: 0.05,
},
{
fontSize: 16,
fill: "#777",
strokeWidth: 0.05,
},
]}
/>
}
text={({ datum }) =>
`${datum.name}: ${Math.round(datum.y * 100)}%\n${format(
datum.x,
dateFormat
)}`
}
style={{
fontSize: 17, // needs to be set here and not just in labelComponent for text size calculations
fontFamily:
'"Gill Sans", "Gill Sans MT", "Ser­avek", "Trebuchet MS", sans-serif',
// default font family from Victory, need to be specified explicitly for some reason, otherwise text size gets miscalculated
}}
flyoutStyle={{
stroke: "#999",
fill: "white",
}}
cornerRadius={4}
flyoutPadding={{ top: 4, bottom: 4, left: 16, right: 16 }}
/>
}
radius={50}
voronoiBlacklist={
[...Array(seriesList.length).keys()].map((i) => `line-${i}`)
// see: https://github.com/FormidableLabs/victory/issues/545
}
/>
}
scale={{
x: "time",
y: "linear",
}}
domain={{
x: [minDate, maxDate],
y: [0, domainMax],
}}
>
{
// Note: axis is not in fact unaligned. Fetchers are at ~12:00
// whereas the date is at the beginning of the day
// however, it still doesn't look very pretty.
}
<VictoryAxis
tickCount={Math.min(7, differenceInDays(maxDate, minDate) + 1)}
style={{
grid: { strokeWidth: 0.5 },
}}
tickLabelComponent={
<VictoryLabel
dx={-40}
dy={0}
angle={-30}
style={{ fontSize: 15, fill: "#777" }}
/>
}
scale={{ x: "time" }}
tickFormat={(t) => format(t, dateFormat)}
/>
<VictoryAxis
dependentAxis
style={{
grid: { stroke: "#D3D3D3", strokeWidth: 0.5 },
}}
tickLabelComponent={
<VictoryLabel dy={0} style={{ fontSize: 18, fill: "#777" }} />
}
// tickFormat specifies how ticks should be displayed
tickFormat={(x) => `${x * 100}%`}
/>
{[...Array(seriesList.length).keys()]
.reverse() // affects svg render order, we want to render largest datasets on top of others
//.filter((i) => i !== highlight)
.map((i) =>
getVictoryGroup({
data: seriesList[i],
i,
highlight: i == highlight, // false
isBinary: isBinary,
})
)}
{
// note: this produces an annoying change of color effect
/*
highlight === undefined
? null
: // render highlighted series on top of everything else
getVictoryGroup({
data: seriesList[highlight],
i: highlight,
highlight: true,
})
*/
}
</VictoryChart>
);
};