From 30ecd21d4b55c5b43070e91857d93ab14b2ed870 Mon Sep 17 00:00:00 2001 From: Bissbert <43237892+Bissbert@users.noreply.github.com> Date: Wed, 15 Jul 2026 17:03:36 +0200 Subject: [PATCH] =?UTF-8?q?feat(ux):=20resolve=20design-critique=20finding?= =?UTF-8?q?s=20=E2=80=94=20quiz=20overhaul=20+=20a11y/reading-comfort=20fi?= =?UTF-8?q?xes;=20bump=20knowledge=20to=20v1.4.4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - fill-blank + matching question UIs with unrenderable-question Skip fallback (fixes practice-mode dead end) - curated question bank wired curated-first into /quiz - ConfidenceTap/RationalePanel/ScheduleBadge + SM-2 scheduling wired into practice mode - due-aware session selection with safe fallback - fresh-start fix (stale persisted sessions no longer resurrect) - mid-quiz Restart - category multi-select affordance - keyboard shortcuts (1-4/Enter, Esc for mobile menu) - header focus rings + aria-expanded - learn-article reading comfort (no hard line breaks, 70ch measure) - tools hub single h1 + measurement jump-nav - callout/nav side-stripe removal - contrast fixes - honest "Last updated" dates - 97 new tests (385 total) --- docs/authoring-questions.md | 24 ++ .../calculator/MeasurementTools.tsx | 26 ++ src/components/docs/Callout.astro | 10 +- src/components/layout/Header.astro | 25 +- src/components/learn/SectionRenderer.astro | 2 +- src/components/quiz/ConfirmDialog.tsx | 70 +++++ src/components/quiz/Exam.tsx | 234 +++++++------- src/components/quiz/ExamResults.tsx | 30 +- src/components/quiz/QuestionCard.test.tsx | 292 ++++++++++++++++++ src/components/quiz/QuestionCard.tsx | 211 +++++++++++-- src/components/quiz/Quiz.test.tsx | 287 +++++++++++++++++ src/components/quiz/Quiz.tsx | 216 ++++++++++++- src/components/quiz/QuizPage.tsx | 71 ++++- src/components/quiz/QuizSetup.test.tsx | 80 +++++ src/components/quiz/QuizSetup.tsx | 16 +- .../quiz/UnrenderableQuestionCard.tsx | 50 +++ src/components/quiz/index.ts | 1 + src/components/quiz/study/ConfidenceTap.tsx | 6 +- src/components/tools/ToolsHub.tsx | 9 +- src/components/ui/SearchInput.tsx | 2 +- src/hooks/useExam.test.ts | 135 ++++++++ src/hooks/useExam.ts | 43 ++- src/hooks/useQuiz.test.ts | 248 +++++++++++++++ src/hooks/useQuiz.ts | 50 ++- src/hooks/useQuizKeyboard.test.ts | 61 ++++ src/hooks/useQuizKeyboard.ts | 96 ++++++ src/lib/knowledge-version.ts | 2 +- src/lib/quiz/answer-normalize.test.ts | 37 +++ src/lib/quiz/answer-normalize.ts | 25 ++ src/lib/quiz/curated-questions.test.ts | 181 +++++++++++ src/lib/quiz/curated-questions.ts | 129 ++++++++ src/lib/quiz/index.ts | 19 ++ src/lib/quiz/question-generator.test.ts | 92 ++++++ src/lib/quiz/question-generator.ts | 14 +- src/lib/quiz/question-pool.test.ts | 177 +++++++++++ src/lib/quiz/question-pool.ts | 119 +++++++ src/lib/quiz/question-types.test.ts | 66 ++++ src/lib/quiz/question-types.ts | 25 ++ src/lib/quiz/question-validity.test.ts | 68 ++++ src/lib/quiz/question-validity.ts | 31 ++ src/lib/quiz/scoring.test.ts | 96 ++++++ src/lib/quiz/scoring.ts | 20 +- src/pages/index.astro | 6 +- src/pages/learn/[...slug].astro | 38 ++- src/pages/quiz/index.astro | 23 +- src/pages/tools/measurement.astro | 1 + src/styles/global.css | 2 +- 47 files changed, 3222 insertions(+), 244 deletions(-) create mode 100644 src/components/quiz/ConfirmDialog.tsx create mode 100644 src/components/quiz/QuestionCard.test.tsx create mode 100644 src/components/quiz/Quiz.test.tsx create mode 100644 src/components/quiz/QuizSetup.test.tsx create mode 100644 src/components/quiz/UnrenderableQuestionCard.tsx create mode 100644 src/hooks/useExam.test.ts create mode 100644 src/hooks/useQuiz.test.ts create mode 100644 src/hooks/useQuizKeyboard.test.ts create mode 100644 src/hooks/useQuizKeyboard.ts create mode 100644 src/lib/quiz/answer-normalize.test.ts create mode 100644 src/lib/quiz/answer-normalize.ts create mode 100644 src/lib/quiz/curated-questions.test.ts create mode 100644 src/lib/quiz/curated-questions.ts create mode 100644 src/lib/quiz/question-generator.test.ts create mode 100644 src/lib/quiz/question-pool.test.ts create mode 100644 src/lib/quiz/question-pool.ts create mode 100644 src/lib/quiz/question-types.test.ts create mode 100644 src/lib/quiz/question-validity.test.ts create mode 100644 src/lib/quiz/question-validity.ts create mode 100644 src/lib/quiz/scoring.test.ts diff --git a/docs/authoring-questions.md b/docs/authoring-questions.md index f8d3a7b..e774f0d 100644 --- a/docs/authoring-questions.md +++ b/docs/authoring-questions.md @@ -143,6 +143,30 @@ At least one other FGA-qualified reviewer should approve the rationale content b --- +## Fill-in-the-Blank Answer Matching + +`fill-blank` items are scored by comparing the student's typed input against every string in +`acceptedAnswers`. Matching is **normalized** on both sides before comparison: + +1. Leading/trailing whitespace is trimmed. +2. The string is Unicode-decomposed (NFKD) and combining diacritics are stripped (so `café` + and `cafe` are treated as equal). +3. The string is lowercased. +4. Runs of internal whitespace are collapsed to a single space. + +This means a student does not need to match case, accents, or exact spacing to be marked +correct. It does **not** perform numeric-word equivalence — `"10"` and `"ten"` are treated as +different answers. If a fill-blank item has a numeral form and a spelled-out form that should +both be accepted, list both explicitly in `acceptedAnswers`: + +```yaml +acceptedAnswers: + - "10" + - ten +``` + +--- + ## Quality Bar for Curated Items > This section restates V1-PLAN.md §10 with additional worked examples. diff --git a/src/components/calculator/MeasurementTools.tsx b/src/components/calculator/MeasurementTools.tsx index 273306a..9405be6 100644 --- a/src/components/calculator/MeasurementTools.tsx +++ b/src/components/calculator/MeasurementTools.tsx @@ -13,6 +13,17 @@ import { DensityEstimator } from './DensityEstimator'; import { HannemanRI } from './HannemanRI'; import { ToolSection } from '../ui/ToolSection'; +const JUMP_LINKS = [ + { id: 'sg', label: 'SG' }, + { id: 'ri-lookup', label: 'RI Lookup' }, + { id: 'birefringence', label: 'Birefringence' }, + { id: 'critical-angle', label: 'Critical Angle' }, + { id: 'dispersion', label: 'Dispersion' }, + { id: 'carat-estimate', label: 'Carat Estimator' }, + { id: 'density', label: 'Density' }, + { id: 'hanneman-ri', label: 'Hanneman RI' }, +]; + const ICON_PATHS = { scale: 'M3 6l3 1m0 0l-3 9a5.002 5.002 0 006.001 0M6 7l3 9M6 7l6-2m6 2l3-1m-3 1l-3 9a5.002 5.002 0 006.001 0M18 7l3 9m-3-9l-6-2m0-2v2m0 16V5m0 16H9m3 0h3', search: 'M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z', @@ -26,6 +37,21 @@ const ICON_PATHS = { export function MeasurementTools() { return (
{question.explanation}
-{question.explanation}
+- The correct answer is: {correctAnswer} + The correct answer is: {correctAnswerDisplay}
)} {question.explanation && ( @@ -154,6 +316,7 @@ export function QuestionCard({+ This saved session has a display issue — start fresh? +
+ ++ This will clear your current answers and start the quiz over from the first question. +
+756 questions across all FGA subject areas. Practise at your own pace, or sit a timed mock exam.
diff --git a/src/components/quiz/QuizSetup.test.tsx b/src/components/quiz/QuizSetup.test.tsx new file mode 100644 index 0000000..0c9bd1b --- /dev/null +++ b/src/components/quiz/QuizSetup.test.tsx @@ -0,0 +1,80 @@ +import { describe, it, expect, vi } from 'vitest'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { QuizSetup } from './QuizSetup'; +import { CATEGORIES } from '../../lib/quiz'; +import type { Category } from '../../lib/quiz'; + +function makeAvailableQuestions(overrides: Partial+ {selectedCategories.length} of {CATEGORIES.length} categories selected +
This question could not be displayed
++ It is missing data required for its question type. It has not been scored — skip + ahead to continue. +
+- Calculators for the bench: SG by hydrostatic weighing, RI lookup, birefringence, critical angle, carat estimation, and unit conversion. Also includes dichroscope, polariscope, and Chelsea-filter references. -
-