fix: exam mode's rail is numbers, as a real paper's is

Study mode reveals each stem in the rail once you have reached it, which
is right there — it is how you find the one you want to go back to. Exam
mode inherited it, and it should not have: a paper's question-status
rail says which items are answered and which are marked, and no more.
Reading back the stems of what you have sat, or ahead to what is coming,
is not something the exam being rehearsed would allow.

So in exam mode no row shows text, including the one you are on, and the
numbers are squared off rather than circled to match. Covered by a test,
because this is an integrity rule rather than styling and would come
back the next time the two modes shared a component.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TqXevQJhxFrM7jJg82cgZN
This commit is contained in:
Daniel 2026-09-11 23:09:32 +02:00
parent f9acdf42fa
commit 4fc149b8cc
3 changed files with 38 additions and 2 deletions

View file

@ -1172,11 +1172,15 @@ const timerStarted = timeLeft !== null
const marked = favorites.includes(q.id)
// Only questions the learner has reached show their text. Previewing one
// they have not opened would give away the case before they read it.
const seen = seenIndexes.has(i)
//
// In exam mode none of them do. A real paper's status rail is a column of
// numbers, and reading the stems of the questions still to come is not
// something the exam being rehearsed would allow.
const seen = !isStudy ? false : seenIndexes.has(i)
const excerpt = seen ? questionStem(q).replace(/\s+/g, ' ').trim() : ''
return (
<button type="button"
className={`quiz-rail-item${isActive ? ' is-active' : ''}${isDone ? ' is-done' : ''}${seen ? '' : ' is-unseen'}`}
className={`quiz-rail-item${isActive ? ' is-active' : ''}${isDone ? ' is-done' : ''}${seen ? '' : ' is-unseen'}${isStudy ? '' : ' is-numbers'}`}
aria-current={isActive ? 'true' : undefined}
onClick={() => { safeNavigate(i); setNavOpen(false) }}>
<span className="quiz-rail-num">

View file

@ -101,6 +101,26 @@ describe('quiz player', () => {
expect(within(after[1]).getByText('Full second clinical question.')).toBeInTheDocument()
})
it('shows numbers and nothing else in exam mode', async () => {
await begin(false)
const items = document.querySelectorAll('.quiz-rail-item')
expect(items).toHaveLength(2)
// A real paper's status rail is a column of numbers. Reading the stems of
// the questions still to come is not something the exam being rehearsed
// would allow so not even the one you are on shows its text.
expect(within(items[0]).getByText('1')).toBeInTheDocument()
expect(within(items[0]).queryByText('Full first clinical question.')).not.toBeInTheDocument()
expect(items[0].className).toMatch(/is-numbers/)
await userEvent.click(items[1])
await findStem('Full second clinical question.')
const after = document.querySelectorAll('.quiz-rail-item')
// Reached, and still a number: this is not the study rail's reveal.
expect(within(after[1]).queryByText('Full second clinical question.')).not.toBeInTheDocument()
expect(after[1]).toHaveAttribute('aria-current', 'true')
})
it('marks answered questions in the rail', async () => {
await begin()
const before = document.querySelectorAll('.quiz-rail-item')

View file

@ -17,6 +17,18 @@
}
.quiz-rail-item:hover { background: #f4f6fb; }
.quiz-rail-item.is-unseen { padding: 7px 8px; }
/* Exam mode: a column of numbers and nothing else.
A real paper's status rail says which items you have answered and which you
marked, and no more than that the stems of the questions still to come are
not something the exam being rehearsed would show you. */
.quiz-rail-item.is-numbers { padding: 5px 8px; }
.quiz-rail-item.is-numbers .quiz-rail-num {
width: 22px; height: 22px; font-size: .72rem;
border-radius: 4px; background: none; color: #6f767f;
}
.quiz-rail-item.is-numbers.is-done .quiz-rail-num { background: none; color: #327b64; font-weight: 700; }
.quiz-rail-item.is-numbers.is-active .quiz-rail-num { background: #496fa5; color: #fff; }
.quiz-rail-item.is-active { background: #eaf0fa; border-left-color: #496fa5; color: #253038; }
.quiz-rail-num {
flex-shrink: 0; width: 26px; height: 26px; border-radius: 50%;