Skip to content

Commit fb3b4e6

Browse files
committed
fix(academy): replace sr-only radio/checkbox inputs with buttons to prevent scroll-on-focus; restore layout min-h-screen
1 parent 9d13dd4 commit fb3b4e6

File tree

3 files changed

+34
-40
lines changed

3 files changed

+34
-40
lines changed

apps/sim/app/academy/(catalog)/layout.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ export default async function AcademyCatalogLayout({ children }: { children: Rea
77
const blogPosts = await getNavBlogPosts()
88

99
return (
10-
<div className='min-h-screen'>
10+
<>
1111
<Navbar blogPosts={blogPosts} />
1212
{children}
1313
<Footer hideCTA />
14-
</div>
14+
</>
1515
)
1616
}

apps/sim/app/academy/[courseSlug]/[lessonSlug]/components/lesson-quiz.tsx

Lines changed: 27 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ function scoreQuiz(questions: QuizQuestion[], answers: Answers, passingScore: nu
3838
return { score, passed: score >= passingScore, feedback }
3939
}
4040

41+
const optionBase =
42+
'w-full text-left rounded-[6px] border px-4 py-3 text-[14px] transition-colors disabled:cursor-default'
43+
4144
/**
4245
* Interactive quiz component with per-question feedback and retry support.
4346
* Scoring is performed entirely client-side.
@@ -46,11 +49,12 @@ export function LessonQuiz({ lessonId, quizConfig, onPass }: LessonQuizProps) {
4649
const [answers, setAnswers] = useState<Answers>({})
4750
const [result, setResult] = useState<QuizResult | null>(null)
4851

49-
const handleAnswer = (qi: number, value: number | number[] | boolean) => {
50-
setAnswers((prev) => ({ ...prev, [qi]: value }))
52+
const handleAnswer = (qi: number, value: number | boolean) => {
53+
if (!result) setAnswers((prev) => ({ ...prev, [qi]: value }))
5154
}
5255

5356
const handleMultiSelect = (qi: number, oi: number) => {
57+
if (result) return
5458
setAnswers((prev) => {
5559
const current = (prev[qi] as number[] | undefined) ?? []
5660
const next = current.includes(oi) ? current.filter((i) => i !== oi) : [...current, oi]
@@ -61,9 +65,9 @@ export function LessonQuiz({ lessonId, quizConfig, onPass }: LessonQuizProps) {
6165
const allAnswered = quizConfig.questions.every((_, i) => answers[i] !== undefined)
6266

6367
const handleSubmit = () => {
64-
const result = scoreQuiz(quizConfig.questions, answers, quizConfig.passingScore)
65-
setResult(result)
66-
if (result.passed) {
68+
const scored = scoreQuiz(quizConfig.questions, answers, quizConfig.passingScore)
69+
setResult(scored)
70+
if (scored.passed) {
6771
markLessonComplete(lessonId)
6872
onPass?.()
6973
}
@@ -89,10 +93,13 @@ export function LessonQuiz({ lessonId, quizConfig, onPass }: LessonQuizProps) {
8993
{q.type === 'multiple_choice' && (
9094
<div className='space-y-2'>
9195
{q.options.map((opt, oi) => (
92-
<label
96+
<button
9397
key={oi}
98+
type='button'
99+
onClick={() => handleAnswer(qi, oi)}
100+
disabled={Boolean(result)}
94101
className={cn(
95-
'flex cursor-pointer items-center gap-3 rounded-[6px] border px-4 py-3 text-[14px] transition-colors',
102+
optionBase,
96103
answers[qi] === oi
97104
? 'border-[#ECECEC]/40 bg-[#ECECEC]/5 text-[#ECECEC]'
98105
: 'border-[#2A2A2A] text-[#999] hover:border-[#3A3A3A] hover:bg-[#272727]',
@@ -105,16 +112,8 @@ export function LessonQuiz({ lessonId, quizConfig, onPass }: LessonQuizProps) {
105112
'border-[#f44336]/40 bg-[#f44336]/5 text-[#f44336]'
106113
)}
107114
>
108-
<input
109-
type='radio'
110-
name={`q-${qi}`}
111-
checked={answers[qi] === oi}
112-
onChange={() => handleAnswer(qi, oi)}
113-
disabled={Boolean(result)}
114-
className='sr-only'
115-
/>
116115
{opt}
117-
</label>
116+
</button>
118117
))}
119118
</div>
120119
)}
@@ -124,10 +123,13 @@ export function LessonQuiz({ lessonId, quizConfig, onPass }: LessonQuizProps) {
124123
{(['True', 'False'] as const).map((label) => {
125124
const val = label === 'True'
126125
return (
127-
<label
126+
<button
128127
key={label}
128+
type='button'
129+
onClick={() => handleAnswer(qi, val)}
130+
disabled={Boolean(result)}
129131
className={cn(
130-
'flex flex-1 cursor-pointer items-center justify-center rounded-[6px] border px-4 py-3 text-[14px] transition-colors',
132+
'flex-1 rounded-[6px] border px-4 py-3 text-[14px] transition-colors disabled:cursor-default',
131133
answers[qi] === val
132134
? 'border-[#ECECEC]/40 bg-[#ECECEC]/5 text-[#ECECEC]'
133135
: 'border-[#2A2A2A] text-[#999] hover:border-[#3A3A3A] hover:bg-[#272727]',
@@ -140,16 +142,8 @@ export function LessonQuiz({ lessonId, quizConfig, onPass }: LessonQuizProps) {
140142
'border-[#f44336]/40 bg-[#f44336]/5 text-[#f44336]'
141143
)}
142144
>
143-
<input
144-
type='radio'
145-
name={`q-${qi}`}
146-
checked={answers[qi] === val}
147-
onChange={() => handleAnswer(qi, val)}
148-
disabled={Boolean(result)}
149-
className='sr-only'
150-
/>
151145
{label}
152-
</label>
146+
</button>
153147
)
154148
})}
155149
</div>
@@ -160,10 +154,13 @@ export function LessonQuiz({ lessonId, quizConfig, onPass }: LessonQuizProps) {
160154
{q.options.map((opt, oi) => {
161155
const selected = ((answers[qi] as number[]) ?? []).includes(oi)
162156
return (
163-
<label
157+
<button
164158
key={oi}
159+
type='button'
160+
onClick={() => handleMultiSelect(qi, oi)}
161+
disabled={Boolean(result)}
165162
className={cn(
166-
'flex cursor-pointer items-center gap-3 rounded-[6px] border px-4 py-3 text-[14px] transition-colors',
163+
optionBase,
167164
selected
168165
? 'border-[#ECECEC]/40 bg-[#ECECEC]/5 text-[#ECECEC]'
169166
: 'border-[#2A2A2A] text-[#999] hover:border-[#3A3A3A] hover:bg-[#272727]',
@@ -176,15 +173,8 @@ export function LessonQuiz({ lessonId, quizConfig, onPass }: LessonQuizProps) {
176173
'border-[#f44336]/40 bg-[#f44336]/5 text-[#f44336]'
177174
)}
178175
>
179-
<input
180-
type='checkbox'
181-
checked={selected}
182-
onChange={() => handleMultiSelect(qi, oi)}
183-
disabled={Boolean(result)}
184-
className='h-3.5 w-3.5 accent-[#ECECEC]'
185-
/>
186176
{opt}
187-
</label>
177+
</button>
188178
)
189179
})}
190180
</div>

apps/sim/app/academy/layout.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,9 @@ export const metadata: Metadata = {
1717
}
1818

1919
export default function AcademyLayout({ children }: { children: React.ReactNode }) {
20-
return <div className='bg-[#1C1C1C] font-[430] font-season text-[#ECECEC]'>{children}</div>
20+
return (
21+
<div className='min-h-screen bg-[#1C1C1C] font-[430] font-season text-[#ECECEC]'>
22+
{children}
23+
</div>
24+
)
2125
}

0 commit comments

Comments
 (0)