@pocketprep/ui-kit 3.6.0 → 3.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,169 @@
1
+ <!-- eslint-disable max-len -->
2
+ <script setup lang="ts">
3
+ import RadioButton from '../../Forms/RadioButton.vue'
4
+ import { dark as vDark } from '../../../directives'
5
+ import type { TChoiceKey } from '../question'
6
+ import { stripHtmlTags } from '../../../utils'
7
+
8
+ interface Props {
9
+ isDarkMode?: boolean
10
+ choices?: TChoiceKey[]
11
+ choicesText?: string[]
12
+ showAnswers?: boolean
13
+ disabled?: boolean
14
+ }
15
+
16
+ const props = withDefaults(defineProps<Props>(), {
17
+ isDarkMode: false,
18
+ choices: () => [],
19
+ choicesText: () => [],
20
+ showAnswers: false,
21
+ disabled: false,
22
+ })
23
+
24
+ const selectedChoice = defineModel<TChoiceKey | null>({ default: null })
25
+
26
+ const selectChoice = (choiceKey: TChoiceKey) => {
27
+ selectedChoice.value = choiceKey
28
+ }
29
+
30
+ const radioButtonColor = (choice: TChoiceKey) => {
31
+ if (props.showAnswers) {
32
+ if (choice === selectedChoice.value && selectedChoice.value?.startsWith('a')) {
33
+ return 'green'
34
+ }
35
+
36
+ if (choice === selectedChoice.value && selectedChoice.value?.startsWith('d')) {
37
+ return 'gray'
38
+ }
39
+
40
+ if (choice.startsWith('a')) {
41
+ return 'green'
42
+ }
43
+ }
44
+
45
+ return 'blue'
46
+ }
47
+
48
+ </script>
49
+
50
+ <template>
51
+ <ul
52
+ v-if="choices && choices.length"
53
+ class="uikit-mpmc-radio-group"
54
+ :class="{
55
+ 'uikit-mpmc-radio-group--show-answer': showAnswers
56
+ }"
57
+ v-dark="isDarkMode"
58
+ role="radiogroup"
59
+ >
60
+ <li
61
+ v-for="(choice, index) in choices"
62
+ class="uikit-mpmc-radio-group__option"
63
+ :class="{
64
+ 'uikit-mpmc-radio-group__option--show-answer': showAnswers
65
+ }"
66
+ v-dark="isDarkMode"
67
+ role="radio"
68
+ :tabindex="disabled || showAnswers ? -1 : 0"
69
+ :aria-checked="choice === selectedChoice"
70
+ :aria-disabled="disabled || showAnswers"
71
+ :aria-label="`
72
+ ${stripHtmlTags(choicesText[index])}. ${choice === selectedChoice ? 'Selected' : 'Not selected'}.`"
73
+ :key="choice"
74
+ @click="!disabled && !showAnswers && selectChoice(choice)"
75
+ @keydown.enter.stop="!disabled && !showAnswers && selectChoice(choice)"
76
+ @keydown.space.prevent.stop="!disabled && !showAnswers && selectChoice(choice)"
77
+ >
78
+ <RadioButton
79
+ class="uikit-mpmc-radio-group__radio-btn"
80
+ :selected="(choice === selectedChoice) || (showAnswers && choice.startsWith('a'))"
81
+ :disabled="disabled"
82
+ :isDarkMode="isDarkMode"
83
+ :color="radioButtonColor(choice)"
84
+ @click="!disabled && !showAnswers && selectChoice(choice)"
85
+ />
86
+ <div
87
+ v-if="choicesText && choicesText.length > 0"
88
+ v-dark="props.isDarkMode"
89
+ class="uikit-mpmc-radio-group__choice-text"
90
+ :class="{
91
+ 'uikit-mpmc-radio-group__choice-text--distractor':
92
+ showAnswers && choice?.startsWith('d')
93
+ }"
94
+ @click="!disabled && !showAnswers && selectChoice(choice)"
95
+ >
96
+ {{ stripHtmlTags(choicesText[index]) }}
97
+ </div>
98
+ </li>
99
+ </ul>
100
+ </template>
101
+
102
+ <style lang="scss" scoped>
103
+ @use '@/styles/breakpoints' as *;
104
+ @use '@/styles/colors' as *;
105
+
106
+ .uikit-mpmc-radio-group {
107
+ list-style: none;
108
+ margin: 0;
109
+ padding: 0;
110
+ display: block;
111
+ background: $white;
112
+ border-radius: 0px 0px 5px 5px;
113
+
114
+ &--dark {
115
+ background: $brand-black;
116
+ }
117
+
118
+ &--show-answer {
119
+ cursor: default;
120
+ }
121
+
122
+ &__option {
123
+ display: flex;
124
+ align-items: flex-start;
125
+ align-self: stretch;
126
+ padding: 11px 15px 12px 15px;
127
+ max-width: 492px;
128
+ border: 0.5px solid rgba($pewter, 0.85);
129
+ border-top: none;
130
+ outline: none;
131
+ transition: background-color 0.2s ease;
132
+
133
+ &:not(&--show-answer) {
134
+ &:focus {
135
+ border: 0.5px solid $brand-blue;
136
+ }
137
+ }
138
+
139
+ &--dark {
140
+ &:focus:not(.uikit-mpmc-radio-group__option--show-answer) {
141
+ border: 0.5px solid $banana-bread;
142
+ }
143
+ }
144
+ }
145
+
146
+ &__choice-text {
147
+ display: block;
148
+ margin-left: 12px;
149
+ color: $brand-black;
150
+ font-size: 16px;
151
+ font-weight: 500;
152
+ line-height: 23px;
153
+ letter-spacing: -0.1px;
154
+
155
+ &--dark {
156
+ color: $barely-background;
157
+ }
158
+
159
+ &--distractor {
160
+ text-decoration: line-through;
161
+ color: $ash;
162
+
163
+ &--dark {
164
+ color: $fog;
165
+ }
166
+ }
167
+ }
168
+ }
169
+ </style>
@@ -162,6 +162,7 @@ import { useQuestionContext } from './composables'
162
162
  import type { IRadioOptions, TMatrixChoiceKey } from './../question'
163
163
  import BrandColors from '../../../pocketprep-export.module.scss'
164
164
  import { computed, onMounted, ref, watch } from 'vue'
165
+ import { stripHtmlTags } from '../../../utils'
165
166
 
166
167
  const emit = defineEmits<{
167
168
  'emitSelectedMatrixChoice': [matrixChoiceKeys: TMatrixChoiceKey[]]
@@ -186,15 +187,6 @@ const expandedRowNumbers = ref<number[]>([])
186
187
  const selectedColumnHeaders = ref<string[][]>([])
187
188
  const brandColors = BrandColors
188
189
 
189
- const stripHtmlTags = (string?: string) => {
190
- if (string) {
191
- const div = document.createElement('div')
192
- div.innerHTML = string
193
- return div.textContent || ''
194
- }
195
- return ''
196
- }
197
-
198
190
  const matrixChoiceLayout = computed(() => {
199
191
  return question.value.matrixChoiceLayout
200
192
  })
@@ -2,6 +2,7 @@
2
2
  import RadioButton from '../../Forms/RadioButton.vue'
3
3
  import { dark as vDark } from '../../../directives'
4
4
  import type { TMatrixChoiceKey } from '../question'
5
+ import { stripHtmlTags } from '../../../utils'
5
6
 
6
7
  interface Props {
7
8
  isDarkMode?: boolean
@@ -42,15 +43,6 @@ const radioButtonColor = (choice: TMatrixChoiceKey) => {
42
43
 
43
44
  return 'blue'
44
45
  }
45
-
46
- const stripHtmlTags = (string?: string) => {
47
- if (string) {
48
- const div = document.createElement('div')
49
- div.innerHTML = string
50
- return div.textContent || ''
51
- }
52
- return ''
53
- }
54
46
  </script>
55
47
 
56
48
  <template>
@@ -4,7 +4,10 @@
4
4
  class="uikit-question-stats-summary"
5
5
  >
6
6
  <div v-dark="isDarkMode" class="uikit-question-stats-summary__stats-summary-total">
7
- <div v-if="isBuildListQuestion" class="uikit-question-stats-summary__stats-summary-value">
7
+ <div v-if="isMPMCQuestion" class="uikit-question-stats-summary__stats-summary-value">
8
+ {{ mpmcChoiceScores.totalAnswered }}
9
+ </div>
10
+ <div v-else-if="isBuildListQuestion" class="uikit-question-stats-summary__stats-summary-value">
8
11
  {{ buildListChoiceScores.totalAnswered }}
9
12
  </div>
10
13
  <div v-else-if="isMatrixQuestion" class="uikit-question-stats-summary__stats-summary-value">
@@ -18,7 +21,10 @@
18
21
  </div>
19
22
  </div>
20
23
  <div v-dark="isDarkMode" class="uikit-question-stats-summary__stats-summary-correct">
21
- <div v-if="isBuildListQuestion" class="uikit-question-stats-summary__stats-summary-value">
24
+ <div v-if="isMPMCQuestion" class="uikit-question-stats-summary__stats-summary-value">
25
+ {{ Math.round((mpmcChoiceScores.answeredCorrectly / mpmcChoiceScores.totalAnswered) * 100) }}%
26
+ </div>
27
+ <div v-else-if="isBuildListQuestion" class="uikit-question-stats-summary__stats-summary-value">
22
28
  {{ Math.round((buildListChoiceScores.answeredCorrectly / buildListChoiceScores.totalAnswered) * 100) }}%
23
29
  </div>
24
30
  <div v-else-if="isMatrixQuestion" class="uikit-question-stats-summary__stats-summary-value">
@@ -43,7 +49,9 @@ const {
43
49
  choiceScores,
44
50
  isDarkMode,
45
51
  isMatrixQuestion,
52
+ isMPMCQuestion,
46
53
  matrixChoiceScores,
54
+ mpmcChoiceScores,
47
55
  buildListChoiceScores,
48
56
  isBuildListQuestion,
49
57
  } = useQuestionContext()
@@ -7,7 +7,10 @@
7
7
  :class="{
8
8
  'uikit-question-summary--matrix-question': isMatrixQuestion,
9
9
  'uikit-question-summary--matrix-question-review-mode': isMatrixQuestion && reviewMode,
10
+ 'uikit-question-summary--mpmc-question': isMPMCQuestion,
11
+ 'uikit-question-summary--mpmc-question-review-mode': isMPMCQuestion && reviewMode,
10
12
  'uikit-question-summary--build-list-question': isBuildListQuestion,
13
+ 'uikit-question-summary--build-list-question-review-mode': isBuildListQuestion && reviewMode,
11
14
  }"
12
15
  >
13
16
  <div class="uikit-question-summary__summary-title">
@@ -45,6 +48,10 @@
45
48
  :class="{
46
49
  'uikit-question-summary__summary-dropdown-explanation--matrix-question-review-mode':
47
50
  isMatrixQuestion && reviewMode,
51
+ 'uikit-question-summary__summary-dropdown-explanation--mpmc-question-review-mode':
52
+ isMPMCQuestion && reviewMode,
53
+ 'uikit-question-summary__summary-dropdown-explanation--build-list-question-review-mode':
54
+ isBuildListQuestion && reviewMode,
48
55
  }"
49
56
  >
50
57
  <slot name="explanationTopExperiment"></slot>
@@ -149,19 +156,22 @@ const {
149
156
  hideReferences,
150
157
  isCorrect,
151
158
  isMatrixQuestionCorrect,
159
+ isMPMCQuestionCorrect,
152
160
  isBuildListOrderCorrect,
153
161
  isUnanswered,
154
162
  reviewMode,
155
163
  isDarkMode,
156
164
  isMatrixQuestion,
165
+ isMPMCQuestion,
157
166
  isBuildListQuestion,
158
167
  breakpointsWithEl,
159
168
  keywordDefinitions,
160
169
  } = useQuestionContext()
161
170
 
162
171
  const isQuestionCorrect = computed(() => {
163
- return (!isMatrixQuestion.value && isCorrect.value) ||
172
+ return ((!isMatrixQuestion.value || !isMPMCQuestion.value || !isBuildListQuestion) && isCorrect.value) ||
164
173
  (isMatrixQuestion.value && isMatrixQuestionCorrect.value && !isUnanswered.value) ||
174
+ (isMPMCQuestion.value && isMPMCQuestionCorrect.value) ||
165
175
  (isBuildListQuestion.value && isBuildListOrderCorrect.value)
166
176
  })
167
177
 
@@ -208,16 +218,21 @@ const toggleSummaryExplanationImageLongAlt = () => {
208
218
  max-width: 518px;
209
219
  }
210
220
 
211
- &--matrix-question-review-mode#{&}--tablet-landscape {
212
- display: inline-block;
213
- }
214
-
215
221
  &--build-list-question {
216
222
  max-width: 452px;
217
223
  margin-left: 38px;
218
224
  display: inline-block;
219
225
  }
220
226
 
227
+ &--matrix-question-review-mode#{&}--tablet-landscape,
228
+ &--mpmc-question-review-mode#{&}--tablet-landscape {
229
+ display: inline-block;
230
+ }
231
+
232
+ &--mpmc-question {
233
+ max-width: 492px;
234
+ }
235
+
221
236
  &--tablet-portrait {
222
237
  display: inline-block;
223
238
  }
@@ -304,7 +319,9 @@ const toggleSummaryExplanationImageLongAlt = () => {
304
319
  &__summary-dropdown-explanation {
305
320
  display: none;
306
321
 
307
- &--matrix-question-review-mode#{&}--tablet-landscape {
322
+ &--matrix-question-review-mode#{&}--tablet-landscape,
323
+ &--mpmc-question-review-mode#{&}--tablet-landscape,
324
+ &--build-list-question-review-mode#{&}--tablet-landscape {
308
325
  display: inline-block;
309
326
  }
310
327
 
@@ -33,6 +33,7 @@ export const useQuestionContext = () => {
33
33
  contextIconType: inject(InjectionKeys.contextIconTypeKey),
34
34
  showAnswers: inject(InjectionKeys.showAnswersKey, ref(false)),
35
35
  showMatrixAnswers: inject(InjectionKeys.showMatrixAnswersKey, ref(false)),
36
+ showMPMCAnswers: inject(InjectionKeys.showMPMCAnswersKey, ref(false)),
36
37
  showBuildListOrder: inject(InjectionKeys.showBuildListOrderKey, ref(false)),
37
38
  imageUrlPrefix: inject(InjectionKeys.imageUrlPrefixKey, ref('')),
38
39
  passageImageUrl: inject(InjectionKeys.passageImageUrlKey, ref<string | null>(null)),
@@ -61,19 +62,26 @@ export const useQuestionContext = () => {
61
62
  showPaywall: inject(InjectionKeys.showPaywallKey, ref(false)),
62
63
  showPassageAndImage: inject(InjectionKeys.showPassageAndImageKey, ref(false)),
63
64
  isMatrixQuestion: inject(InjectionKeys.isMatrixQuestionKey, ref(false)),
65
+ isMPMCQuestion: inject(InjectionKeys.isMPMCQuestionKey, ref(false)),
64
66
  isBuildListQuestion: inject(InjectionKeys.isBuildListQuestionKey, ref(false)),
65
67
  matrixChoiceScores: inject(InjectionKeys.matrixChoiceScoresKey, ref({
66
68
  totalAnswered: 0,
67
69
  answeredCorrectly: 0,
68
70
  })),
71
+ mpmcChoiceScores: inject(InjectionKeys.mpmcChoiceScoresKey, ref({
72
+ totalAnswered: 0,
73
+ answeredCorrectly: 0,
74
+ })),
69
75
  buildListChoiceScores: inject(InjectionKeys.buildListChoiceScoresKey, ref({
70
76
  totalAnswered: 0,
71
77
  answeredCorrectly: 0,
72
78
  })),
73
79
  isMatrixQuestionCorrect: inject(InjectionKeys.isMatrixQuestionCorrectKey, ref(false)),
80
+ isMPMCQuestionCorrect: inject(InjectionKeys.isMPMCQuestionCorrectKey, ref(false)),
74
81
  isBuildListOrderCorrect: inject(InjectionKeys.isBuildListOrderCorrectKey, ref(false)),
75
82
  matrixAnswerKeys: inject(InjectionKeys.matrixAnswerKeysKey, ref<TMatrixChoiceKey[]>([])),
76
83
  selectedMatrixChoices: inject(InjectionKeys.selectedMatrixChoicesKey, ref([])),
84
+ selectedMPMCChoices: inject(InjectionKeys.selectedMPMCChoicesKey, ref([])),
77
85
  selectedBuildListChoiceOrder: inject(InjectionKeys.selectedBuildListChoiceOrderKey, ref([])),
78
86
  isTeachGroupReview: inject(InjectionKeys.isTeachGroupReviewKey, ref(false)),
79
87
  }
@@ -34,6 +34,7 @@ export const isCorrectKey = Symbol('isCorrect') as InjectionKey<ComputedRef<bool
34
34
  export const contextIconTypeKey = Symbol('contextIconType') as InjectionKey<ComputedRef<TContextIcon>>
35
35
  export const showAnswersKey = Symbol('showAnswers') as InjectionKey<Ref<boolean>>
36
36
  export const showMatrixAnswersKey = Symbol('showMatrixAnswers') as InjectionKey<Ref<boolean>>
37
+ export const showMPMCAnswersKey = Symbol('showMPMCAnswers') as InjectionKey<Ref<boolean>>
37
38
  export const showBuildListOrderKey = Symbol('showBuildListOrder') as InjectionKey<Ref<boolean>>
38
39
  export const imageUrlPrefixKey = Symbol('imageUrlPrefix') as InjectionKey<Ref<string>>
39
40
  export const passageImageUrlKey = Symbol('passageImageUrl') as InjectionKey<ComputedRef<string | null>>
@@ -68,12 +69,16 @@ export const keywordDefinitionsKey = Symbol('keywordDefinitions') as InjectionKe
68
69
  export const showPaywallKey = Symbol('showPaywall') as InjectionKey<Ref<boolean>>
69
70
  export const showPassageAndImageKey = Symbol('showPassageAndImage') as InjectionKey<ComputedRef<boolean>>
70
71
  export const isMatrixQuestionKey = Symbol('isMatrixQuestion') as InjectionKey<ComputedRef<boolean>>
72
+ export const isMPMCQuestionKey = Symbol('isMPMCQuestion') as InjectionKey<ComputedRef<boolean>>
71
73
  export const isBuildListQuestionKey = Symbol('isBuildListQuestion') as InjectionKey<ComputedRef<boolean>>
72
74
  export const matrixChoiceScoresKey = Symbol('matrixChoiceScores') as InjectionKey<ComputedRef<TMatrixChoiceScores>>
75
+ export const mpmcChoiceScoresKey = Symbol('mpmcChoiceScores') as InjectionKey<ComputedRef<TChoiceScores>>
73
76
  export const buildListChoiceScoresKey = Symbol('buildListChoiceScores') as InjectionKey<ComputedRef<TBuildListChoiceScores>>
74
77
  export const isMatrixQuestionCorrectKey = Symbol('isMatrixQuestionCorrect') as InjectionKey<ComputedRef<boolean>>
78
+ export const isMPMCQuestionCorrectKey = Symbol('isMPMCQuestionCorrect') as InjectionKey<ComputedRef<boolean>>
75
79
  export const isBuildListOrderCorrectKey = Symbol('isBuildListOrderCorrect') as InjectionKey<ComputedRef<boolean>>
76
80
  export const matrixAnswerKeysKey = Symbol('matrixAnswerKeys') as InjectionKey<ComputedRef<TMatrixChoiceKey[]>>
77
81
  export const selectedMatrixChoicesKey = Symbol('selectedMatrixChoices') as InjectionKey<Ref<TMatrixChoiceKey[]>>
82
+ export const selectedMPMCChoicesKey = Symbol('selectedMPMCChoices') as InjectionKey<Ref<TChoiceKey[]>>
78
83
  export const selectedBuildListChoiceOrderKey = Symbol('selectedBuildListChoiceOrder') as InjectionKey<Ref<TBuildListChoiceKey[]>>
79
- export const isTeachGroupReviewKey = Symbol('isTeachGroupReview') as InjectionKey<Ref<boolean>>
84
+ export const isTeachGroupReviewKey = Symbol('isTeachGroupReview') as InjectionKey<Ref<boolean>>