@@ -45,27 +45,163 @@ export interface AnswerProps {
45
45
contentRenderer ?: JSX . Element ;
46
46
show_all_feedback ?: boolean ;
47
47
tableFeedbackEnabled ?: boolean ;
48
+ feedbackId ?: string ;
48
49
}
49
50
50
- export const Answer = ( props : AnswerProps ) => {
51
+ type AnswerAnswerProps = Pick <
52
+ AnswerBodyProps ,
53
+ 'answer' |
54
+ 'contentRenderer' |
55
+ 'show_all_feedback' |
56
+ 'tableFeedbackEnabled' |
57
+ 'isCorrect' |
58
+ 'isIncorrect'
59
+ > ;
60
+
61
+ const AnswerAnswer = ( props : AnswerAnswerProps ) => {
62
+ const {
63
+ answer : { content_html, feedback_html } ,
64
+ contentRenderer,
65
+ show_all_feedback,
66
+ tableFeedbackEnabled,
67
+ isCorrect,
68
+ isIncorrect,
69
+ } = props ;
70
+ return (
71
+ < div className = "answer-answer" >
72
+ < AnswerIndicator isCorrect = { isCorrect } isIncorrect = { isIncorrect } />
73
+ < Content className = "answer-content" component = { contentRenderer } html = { content_html } />
74
+ { show_all_feedback && feedback_html && ! tableFeedbackEnabled &&
75
+ < SimpleFeedback key = "question-mc-feedback" contentRenderer = { contentRenderer } >
76
+ { feedback_html }
77
+ </ SimpleFeedback > }
78
+ </ div >
79
+ )
80
+ }
81
+
82
+ interface AnswerBodyProps extends AnswerProps {
83
+ isCorrect ?: boolean ;
84
+ isSelected ?: boolean ;
85
+ isIncorrect ?: boolean ;
86
+ }
87
+
88
+ const TeacherReview = ( props : AnswerBodyProps ) => {
89
+ const {
90
+ answer,
91
+ answered_count,
92
+ isCorrect,
93
+ contentRenderer,
94
+ iter,
95
+ show_all_feedback,
96
+ tableFeedbackEnabled,
97
+ } = props ;
98
+ const percent = answer . selected_count && answered_count
99
+ ? Math . round ( ( answer . selected_count / answered_count ) * 100 )
100
+ : 0 ;
101
+ return (
102
+ < div className = "review-wrapper" >
103
+ < div className = { cn ( 'review-count' , { 'green' : isCorrect , 'red' : ! isCorrect } ) } >
104
+ < span
105
+ className = "selected-count"
106
+ data-percent = { `${ percent } ` }
107
+ >
108
+ { answer . selected_count }
109
+ </ span >
110
+ < span className = { cn ( 'letter' , { 'green' : isCorrect , 'red' : ! isCorrect } ) } >
111
+ { ALPHABET [ iter ] }
112
+ </ span >
113
+ </ div >
114
+ < AnswerAnswer
115
+ answer = { answer }
116
+ contentRenderer = { contentRenderer }
117
+ show_all_feedback = { show_all_feedback }
118
+ tableFeedbackEnabled = { tableFeedbackEnabled } />
119
+ </ div >
120
+ ) ;
121
+ }
122
+
123
+ const AnswerChoice = ( props : AnswerBodyProps ) => {
51
124
const {
52
125
type,
53
126
iter,
54
127
answer,
55
128
disabled,
56
129
onKeyPress,
57
130
qid,
58
- answerId,
59
- correctAnswerId,
60
- incorrectAnswerId,
61
- hasCorrectAnswer,
62
- answered_count,
63
131
contentRenderer,
132
+ correctIncorrectIcon,
133
+ feedbackId,
134
+ isSelected,
135
+ isCorrect,
136
+ isIncorrect,
137
+ hasCorrectAnswer,
64
138
show_all_feedback,
65
139
tableFeedbackEnabled,
66
140
} = props ;
141
+ const ariaLabel = `${ isSelected ? 'Selected ' : '' } Choice ${ ALPHABET [ iter ] } :` ;
142
+ let onChangeAnswer : AnswerProps [ 'onChangeAnswer' ] ;
143
+
144
+ const onChange = ( ) => onChangeAnswer && onChangeAnswer ( answer ) ;
145
+
146
+ if ( ! hasCorrectAnswer
147
+ && ( type !== 'teacher-review' )
148
+ && ( type !== 'teacher-preview' )
149
+ && ( type !== 'student-mpp' ) ) {
150
+ ( { onChangeAnswer } = props ) ;
151
+ }
67
152
68
- let body , feedback , selectedCount ;
153
+ return < >
154
+ { type === 'teacher-preview' &&
155
+ < div className = "correct-incorrect" >
156
+ { isCorrect && correctIncorrectIcon }
157
+ </ div > }
158
+ < input
159
+ type = "radio"
160
+ className = "answer-input-box"
161
+ checked = { isSelected }
162
+ id = { `${ qid } -option-${ iter } ` }
163
+ name = { `${ qid } -options` }
164
+ onChange = { onChange }
165
+ disabled = { disabled || ! onChangeAnswer }
166
+ aria-details = { feedbackId }
167
+ />
168
+ < label
169
+ onKeyPress = { onKeyPress }
170
+ htmlFor = { `${ qid } -option-${ iter } ` }
171
+ className = "answer-label" >
172
+ < span
173
+ className = "answer-letter-wrapper"
174
+ aria-label = { ariaLabel }
175
+ data-answer-choice = { ALPHABET [ iter ] }
176
+ data-test-id = { `answer-choice-${ ALPHABET [ iter ] } ` }
177
+ >
178
+ </ span >
179
+ < AnswerAnswer
180
+ answer = { answer }
181
+ contentRenderer = { contentRenderer }
182
+ show_all_feedback = { show_all_feedback }
183
+ tableFeedbackEnabled = { tableFeedbackEnabled }
184
+ isCorrect = { isCorrect }
185
+ isIncorrect = { isIncorrect } />
186
+ </ label >
187
+ </ >
188
+ }
189
+
190
+ const AnswerBody = ( props : AnswerBodyProps ) => {
191
+ return props . type === 'teacher-review'
192
+ ? < TeacherReview { ...props } />
193
+ : < AnswerChoice { ...props } />
194
+ }
195
+
196
+ export const Answer = ( props : AnswerProps ) => {
197
+ const {
198
+ type,
199
+ answer,
200
+ disabled,
201
+ answerId,
202
+ correctAnswerId,
203
+ incorrectAnswerId,
204
+ } = props ;
69
205
70
206
const isChecked = isAnswerChecked ( answer , answerId ) ;
71
207
const isCorrect = isAnswerCorrect ( answer , correctAnswerId ) ;
@@ -78,124 +214,22 @@ export const Answer = (props: AnswerProps) => {
78
214
// incorrectAnswerId will be empty.
79
215
const isPreviousResponse = answerId === undefined && ( ! incorrectAnswerId && isCorrect || isIncorrect ) ;
80
216
217
+ const isSelected = isChecked || isPreviousResponse ;
81
218
const classes = cn ( 'answers-answer' , {
82
219
'disabled' : disabled ,
83
- 'answer-selected' : isChecked || isPreviousResponse ,
220
+ 'answer-selected' : isSelected ,
84
221
'answer-correct' : isCorrect && type !== 'student-mpp' ,
85
222
'answer-incorrect' : incorrectAnswerId && isAnswerIncorrect ( answer , incorrectAnswerId ) ,
86
223
} ) ;
87
224
88
- const correctIncorrectIcon = (
89
- < div className = "correct-incorrect" >
90
- { isCorrect && props . correctIncorrectIcon }
91
- </ div >
92
- ) ;
93
-
94
- let ariaLabel = `${ isChecked ? 'Selected ' : '' } Choice ${ ALPHABET [ iter ] } ` ;
95
- // somewhat misleading - this means that there is a correct answer,
96
- // not necessarily that this answer is correct
97
- if ( hasCorrectAnswer ) {
98
- ariaLabel += `(${ isCorrect ? 'Correct' : 'Incorrect' } Answer)` ;
99
- }
100
- ariaLabel += ':' ;
101
-
102
- let onChangeAnswer : AnswerProps [ 'onChangeAnswer' ] , radioBox ;
103
-
104
- const onChange = ( ) => onChangeAnswer && onChangeAnswer ( answer ) ;
105
-
106
- if ( ! hasCorrectAnswer
107
- && ( type !== 'teacher-review' )
108
- && ( type !== 'teacher-preview' )
109
- && ( type !== 'student-mpp' ) ) {
110
- ( { onChangeAnswer } = props ) ;
111
- }
112
-
113
- if ( onChangeAnswer ) {
114
- radioBox = (
115
- < input
116
- type = "radio"
117
- className = "answer-input-box"
118
- checked = { isChecked }
119
- id = { `${ qid } -option-${ iter } ` }
120
- name = { `${ qid } -options` }
121
- onChange = { onChange }
122
- disabled = { disabled }
123
- />
124
- ) ;
125
- }
126
-
127
- if ( show_all_feedback && answer . feedback_html && ! tableFeedbackEnabled ) {
128
- feedback = (
129
- < SimpleFeedback key = "question-mc-feedback" contentRenderer = { contentRenderer } >
130
- { answer . feedback_html }
131
- </ SimpleFeedback >
132
- ) ;
133
- }
134
-
135
- if ( type === 'teacher-review' ) {
136
- let percent = 0 ;
137
- if ( answer . selected_count && answered_count ) {
138
- percent = Math . round ( ( answer . selected_count / answered_count ) * 100 ) ;
139
- }
140
- selectedCount = (
141
- < span
142
- className = "selected-count"
143
- data-percent = { `${ percent } ` }
144
- >
145
- { answer . selected_count }
146
- </ span >
147
- ) ;
148
-
149
- body = (
150
- < div className = "review-wrapper" >
151
- < div className = { cn ( 'review-count' , { 'green' : isCorrect , 'red' : ! isCorrect } ) } >
152
- { selectedCount }
153
- < span className = { cn ( 'letter' , { 'green' : isCorrect , 'red' : ! isCorrect } ) } >
154
- { ALPHABET [ iter ] }
155
- </ span >
156
- </ div >
157
-
158
- < div className = "answer-answer" >
159
- < Content className = "answer-content" component = { contentRenderer } html = { answer . content_html } />
160
- { feedback }
161
- </ div >
162
- </ div >
163
- ) ;
164
- } else {
165
- body = (
166
- < >
167
- { type === 'teacher-preview' && correctIncorrectIcon }
168
- { selectedCount }
169
- { radioBox }
170
- < label
171
- onKeyPress = { onKeyPress }
172
- htmlFor = { `${ qid } -option-${ iter } ` }
173
- className = "answer-label" >
174
- < span className = "answer-letter-wrapper" >
175
- < button
176
- onClick = { onChange }
177
- aria-label = { ariaLabel }
178
- className = "answer-letter"
179
- disabled = { disabled || isIncorrect }
180
- data-test-id = { `answer-choice-${ ALPHABET [ iter ] } ` }
181
- >
182
- { ALPHABET [ iter ] }
183
- </ button >
184
- </ span >
185
- < div className = "answer-answer" >
186
- < AnswerIndicator isCorrect = { isCorrect } isIncorrect = { isIncorrect } />
187
- < Content className = "answer-content" component = { contentRenderer } html = { answer . content_html } />
188
- { feedback }
189
- </ div >
190
- </ label >
191
- </ >
192
- ) ;
193
- }
194
-
195
225
return (
196
226
< div className = "openstax-answer" >
197
227
< section className = { classes } >
198
- { body }
228
+ < AnswerBody
229
+ { ...props }
230
+ isCorrect = { isCorrect }
231
+ isSelected = { isSelected }
232
+ isIncorrect = { isIncorrect } />
199
233
</ section >
200
234
</ div >
201
235
) ;
0 commit comments