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
This commit is contained in:
NunoSempere 2022-05-20 13:53:21 -04:00
parent 7236e9662f
commit 020f0c0c5e
4 changed files with 156 additions and 79 deletions

View File

@ -36,8 +36,8 @@ const getVictoryGroup = ({
style={{ style={{
data: { data: {
// strokeOpacity: highlight ? 1 : 0.5, // strokeOpacity: highlight ? 1 : 0.5,
strokeOpacity: 0.6, strokeOpacity: highlight && !isBinary ? 0.8 : 0.6,
strokeWidth: 3, strokeWidth: highlight && !isBinary ? 4 : 3,
}, },
}} }}
/> />
@ -196,14 +196,14 @@ export const InnerChart: React.FC<Props> = ({
// note: this produces an annoying change of color effect // note: this produces an annoying change of color effect
/* /*
highlight === undefined highlight === undefined
? null ? null
: // render highlighted series on top of everything else : // render highlighted series on top of everything else
getVictoryGroup({ getVictoryGroup({
data: seriesList[highlight], data: seriesList[highlight],
i: highlight, i: highlight,
highlight: true, highlight: true,
}) })
*/ */
} }
</VictoryChart> </VictoryChart>
); );

View File

@ -116,14 +116,24 @@ export const QuestionCard: React.FC<Props> = ({
</div> </div>
{isBinary ? ( {isBinary ? (
<div className="flex justify-between"> <div className="flex justify-between">
<QuestionOptions question={question} /> <QuestionOptions
question={question}
maxNumOptions={5}
optionTextSize={"text-normal"}
onlyFirstEstimate={false}
/>
<div className={`hidden ${showTimeStamp ? "sm:block" : ""}`}> <div className={`hidden ${showTimeStamp ? "sm:block" : ""}`}>
<LastUpdated timestamp={lastUpdated} /> <LastUpdated timestamp={lastUpdated} />
</div> </div>
</div> </div>
) : ( ) : (
<div className="space-y-2"> <div className="space-y-2">
<QuestionOptions question={question} /> <QuestionOptions
question={question}
maxNumOptions={5}
optionTextSize={"text-sm"}
onlyFirstEstimate={false}
/>
<div className={`hidden ${showTimeStamp ? "sm:block" : ""} ml-6`}> <div className={`hidden ${showTimeStamp ? "sm:block" : ""} ml-6`}>
<LastUpdated timestamp={lastUpdated} /> <LastUpdated timestamp={lastUpdated} />
</div> </div>

View File

@ -1,4 +1,7 @@
import { FullQuestionOption, isFullQuestionOption } from "../../../common/types"; import {
FullQuestionOption,
isFullQuestionOption,
} from "../../../common/types";
import { QuestionFragment } from "../../fragments.generated"; import { QuestionFragment } from "../../fragments.generated";
import { isQuestionBinary } from "../../utils"; import { isQuestionBinary } from "../../utils";
import { formatProbability } from "../utils"; import { formatProbability } from "../utils";
@ -89,26 +92,38 @@ const chooseColor = (probability: number) => {
} }
}; };
const OptionRow: React.FC<{ option: FullQuestionOption }> = ({ option }) => { const OptionRow: React.FC<{
option: FullQuestionOption;
optionTextSize: string;
}> = ({ option, optionTextSize }) => {
return ( return (
<div className="flex items-center"> <div className="flex items-center">
<div <div
className={`${chooseColor( className={`${chooseColor(
option.probability option.probability
)} w-14 flex-none rounded-md py-0.5 text-sm text-center`} )} w-14 flex-none rounded-md py-0.5 ${
optionTextSize || "text-sm"
} text-center`}
> >
{formatProbability(option.probability)} {formatProbability(option.probability)}
</div> </div>
<div className="text-gray-700 pl-3 leading-snug text-sm"> <div
className={`text-gray-700 pl-3 leading-snug ${
optionTextSize || "text-sm"
}`}
>
{option.name} {option.name}
</div> </div>
</div> </div>
); );
}; };
export const QuestionOptions: React.FC<{ question: QuestionFragment }> = ({ export const QuestionOptions: React.FC<{
question, question: QuestionFragment;
}) => { maxNumOptions: number;
optionTextSize: string;
onlyFirstEstimate: boolean;
}> = ({ question, maxNumOptions, optionTextSize, onlyFirstEstimate }) => {
const isBinary = isQuestionBinary(question); const isBinary = isQuestionBinary(question);
if (isBinary) { if (isBinary) {
@ -124,30 +139,65 @@ export const QuestionOptions: React.FC<{ question: QuestionFragment }> = ({
<span <span
className={`${primaryForecastColor( className={`${primaryForecastColor(
yesOption.probability yesOption.probability
)} text-white w-16 rounded-md px-1.5 py-0.5 font-bold`} )} text-white w-16 rounded-md px-2 py-1 font-bold ${
optionTextSize || "text-normal"
}`}
> >
{formatProbability(yesOption.probability)} {formatProbability(yesOption.probability)}
</span> </span>
<span <span
className={`${textColor( className={`${textColor(yesOption.probability)} ${
yesOption.probability optionTextSize || "text-normal"
)} text-gray-500 inline-block`} } text-gray-500 inline-block`}
> >
{primaryEstimateAsText(yesOption.probability)} {primaryEstimateAsText(yesOption.probability)}
</span> </span>
</div> </div>
); );
} else if (onlyFirstEstimate) {
if (question.options.length > 0) {
const yesOption =
question.options.length > 0 ? question.options[0] : null;
if (!yesOption) {
return null; // shouldn't happen
}
if (!isFullQuestionOption(yesOption)) {
return null; // missing data
}
return (
<div className="space-x-2">
<span
className={`${primaryForecastColor(
yesOption.probability
)} text-white w-16 rounded-md px-2 py-1 font-bold ${
optionTextSize || "text-normal"
}`}
>
{formatProbability(yesOption.probability)}
</span>
<span
className={`${textColor(yesOption.probability)} ${
optionTextSize || "text-normal"
} text-gray-500 inline-block`}
>
{yesOption.name}
</span>
</div>
);
} else {
return null;
}
} else { } else {
const optionsSorted = question.options const optionsSorted = question.options
.filter(isFullQuestionOption) .filter(isFullQuestionOption)
.sort((a, b) => b.probability - a.probability); .sort((a, b) => b.probability - a.probability);
const optionsMax5 = optionsSorted.slice(0, 5); // display max 5 options. const optionsMaxN = optionsSorted.slice(0, maxNumOptions); // display max 5 options.
return ( return (
<div className="space-y-2"> <div className="space-y-2">
{optionsMax5.map((option, i) => ( {optionsMaxN.map((option, i) => (
<OptionRow option={option} key={i} /> <OptionRow option={option} key={i} optionTextSize={optionTextSize} />
))} ))}
</div> </div>
); );

View File

@ -12,6 +12,7 @@ import { CaptureQuestion } from "../components/CaptureQuestion";
import { HistoryChart } from "../components/HistoryChart"; import { HistoryChart } from "../components/HistoryChart";
import { IndicatorsTable } from "../components/IndicatorsTable"; import { IndicatorsTable } from "../components/IndicatorsTable";
import { Stars } from "../components/Stars"; import { Stars } from "../components/Stars";
import { QuestionOptions } from "../components/QuestionOptions";
import { QuestionPageDocument } from "../queries.generated"; import { QuestionPageDocument } from "../queries.generated";
interface Props { interface Props {
@ -49,62 +50,78 @@ const Section: React.FC<{ title: string }> = ({ title, children }) => (
const LargeQuestionCard: React.FC<{ const LargeQuestionCard: React.FC<{
question: QuestionWithHistoryFragment; question: QuestionWithHistoryFragment;
}> = ({ question }) => ( }> = ({ question }) => {
<Card highlightOnHover={false} large={true}> let probabilities = question.options;
<h1 className="sm:text-3xl text-xl"> let optionsOrderedByProbability = question.options.sort((a, b) =>
<a (a.probability || 0) > (b.probability || 0) ? -1 : 1
className="text-black no-underline hover:text-gray-700" );
href={question.url} let optionWithHighestProbability =
target="_blank" question.options.length > 0 ? [optionsOrderedByProbability[0]] : [];
>
{question.title}{" "}
</a>
</h1>
<div className="flex gap-2 mb-5"> return (
<a <Card highlightOnHover={false} large={true}>
className="text-black no-underline border-2 rounded-lg border-gray-400 rounded p-1 px-2 text-2xs hover:text-gray-600" <h1 className="sm:text-3xl text-xl">
href={question.url} <a
target="_blank" className="text-black no-underline hover:text-gray-700"
> href={question.url}
{question.platform.label}{" "} target="_blank"
<FaExternalLinkAlt className="text-gray-400 inline sm:text-md text-md mb-1" />
</a>
<Stars num={question.qualityIndicators.stars} />
</div>
<div className="mb-10">
{question.platform.id === "guesstimate" && question.visualization ? (
<a className="no-underline" href={question.url} target="_blank">
<img
className="rounded-sm"
src={question.visualization}
alt="Guesstimate Screenshot"
/>
</a>
) : (
<HistoryChart question={question} />
)}
</div>
<div className="mx-auto max-w-prose">
<Section title="Question description">
<ReactMarkdown
linkTarget="_blank"
className="font-normal text-gray-900"
> >
{question.description.replaceAll("---", "")} {question.title}{" "}
</ReactMarkdown> </a>
</Section> </h1>
<div className="mt-5">
<Section title="Indicators">
<IndicatorsTable question={question} />
</Section>
</div>
</div>
</Card>
);
<div className="flex gap-2 mb-5 mt-5">
<a
className="text-black no-underline border-2 rounded-lg border-gray-400 rounded p-1 px-2 text-normal hover:text-gray-600"
href={question.url}
target="_blank"
>
{question.platform.label}{" "}
<FaExternalLinkAlt className="text-gray-400 inline sm:text-md text-md mb-1" />
</a>
<Stars num={question.qualityIndicators.stars} />
<span className="border-2 border-white p-1 px-2 ">
<QuestionOptions
question={{ ...question }}
maxNumOptions={1}
optionTextSize={"text-normal"}
onlyFirstEstimate={true}
/>
</span>
</div>
<div className="mb-10">
{question.platform.id === "guesstimate" && question.visualization ? (
<a className="no-underline" href={question.url} target="_blank">
<img
className="rounded-sm"
src={question.visualization}
alt="Guesstimate Screenshot"
/>
</a>
) : (
<HistoryChart question={question} />
)}
</div>
<div className="mx-auto max-w-prose">
<Section title="Question description">
<ReactMarkdown
linkTarget="_blank"
className="font-normal text-gray-900"
>
{question.description.replaceAll("---", "")}
</ReactMarkdown>
</Section>
<div className="mt-5">
<Section title="Indicators">
<IndicatorsTable question={question} />
</Section>
</div>
</div>
</Card>
);
};
const QuestionScreen: React.FC<{ question: QuestionWithHistoryFragment }> = ({ const QuestionScreen: React.FC<{ question: QuestionWithHistoryFragment }> = ({
question, question,
}) => ( }) => (