From a5f5e3a53687076879615c17e9ae63988bd6f01f Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 11 Sep 2026 13:15:03 +0200 Subject: [PATCH] fix: one notepad on the quiz page, not two Per-question notes were already built and saved on blur. The global notes tab was still floating over the same screen beside them, so it was never clear which notepad a note was going into. The global note stays where it belongs, on the dashboard. The quiz test mock had no `put`, which is why nothing had ever covered the per-question note path; it does now. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01TqXevQJhxFrM7jJg82cgZN --- docs/TODO.md | 6 ++++-- frontend/src/pages/QuizPage.jsx | 7 +++++-- frontend/src/pages/QuizPage.test.jsx | 20 ++++++++++++++++++-- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/docs/TODO.md b/docs/TODO.md index ae62f41..133e061 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -217,8 +217,10 @@ Captured so nothing is lost while the article writing runs. ## Quiz runner -- [ ] **Per-question notes in study mode**, replacing the global notes tab that is - currently on the quiz page. +- [x] **Per-question notes** — done. The notes themselves were already built + (`question_notes`, saved on blur); what was missing was removing the + global notes tab that floated over the same screen, so it was never clear + which notepad you were writing in. The global note stays on the dashboard. - [ ] **Per-question feedback** to the educator. - [ ] **Tutorial mode** — first-run coach marks ("Step 2 of 6", Skip / Next). diff --git a/frontend/src/pages/QuizPage.jsx b/frontend/src/pages/QuizPage.jsx index c6f1e49..73e060c 100644 --- a/frontend/src/pages/QuizPage.jsx +++ b/frontend/src/pages/QuizPage.jsx @@ -9,7 +9,6 @@ import { useAuth } from '../context/AuthContext' import api from '../api/client' import useMediaQuery from '../hooks/useMediaQuery' import FigureStrip from '../components/FigureStrip' -import MyNote from '../components/MyNote' import QuizTools, { QuizDialog } from '../components/QuizTools' import './QuizPlayer.css' @@ -1112,7 +1111,11 @@ const timerStarted = timeLeft !== null return (
- + {/* The floating global-notes tab is gone. A note taken while sitting a + question is about that question, and there is a per-question note in + the toolbar below; a second, unrelated notepad floating over the same + screen only made it ambiguous which one you were writing in. The + global note still lives on the dashboard. */} {tool && setTool(null)} />} {showReview && setShowReview(false)}>

{answeredCount} of {totalCount} questions answered. Unanswered questions count as incorrect.

diff --git a/frontend/src/pages/QuizPage.test.jsx b/frontend/src/pages/QuizPage.test.jsx index 71d3367..87b2b6a 100644 --- a/frontend/src/pages/QuizPage.test.jsx +++ b/frontend/src/pages/QuizPage.test.jsx @@ -5,9 +5,8 @@ import { MemoryRouter, Route, Routes } from 'react-router-dom' import QuizPage from './QuizPage' import api from '../api/client' -vi.mock('../api/client', () => ({ default: { get: vi.fn(), post: vi.fn(), delete: vi.fn() } })) +vi.mock('../api/client', () => ({ default: { get: vi.fn(), post: vi.fn(), put: vi.fn(), delete: vi.fn() } })) vi.mock('../context/AuthContext', () => ({ useAuth: () => ({ user: { id: 1, name: 'Learner', role: 'user' } }) })) -vi.mock('../components/MyNote', () => ({ default: () => null })) vi.mock('../components/TeachChat', () => ({ default: ({ attemptId }) =>
Study tutor {attemptId}
})) const questions = [ @@ -249,6 +248,23 @@ describe('quiz player', () => { expect(screen.queryByText('Full first clinical question.')).not.toBeInTheDocument() }) + it('keeps notes with the question, not in a second notepad floating over it', async () => { + await begin() + const bar = await screen.findByRole('toolbar', { name: 'Question actions' }) + // The global note tab used to float over the player alongside this one, so + // it was never clear which notepad you were typing into. + expect(document.querySelector('.mynote-tab')).toBeNull() + + await userEvent.click(within(bar).getByRole('button', { name: /Add notes/ })) + const box = screen.getByRole('textbox', { name: /note/i }) + await userEvent.type(box, 'Ask about vaccination status') + expect(screen.getByText('Unsaved')).toBeInTheDocument() + // Saved on blur, so a note is not written on every keystroke. + await userEvent.click(screen.getByRole('button', { name: 'Save note' })) + await waitFor(() => expect(api.put).toHaveBeenCalledWith( + '/questions/detail/1/note', { content: 'Ask about vaccination status' })) + }) + it('shows per-option explanations in study feedback behind a toggle', async () => { const originalGet = api.get.getMockImplementation() api.get.mockImplementation(async (url, ...args) => {