Skip to content

Commit

Permalink
mobile quiz taking and reporting dropdowns navigation
Browse files Browse the repository at this point in the history
  • Loading branch information
nucleogenesis committed Jun 5, 2024
1 parent dae847e commit f8b27af
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 34 deletions.
141 changes: 114 additions & 27 deletions kolibri/core/assets/src/views/AttemptLogList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,31 @@
{{ $tr('answerHistoryLabel') }}
</h3>

<div v-if="isMobile">
<KSelect
v-if="sectionSelectOptions.length > 1"
class="section-select"
:value="selectedSection"
:label="quizSectionsLabel$()"
:options="sectionSelectOptions"
:disabled="$attrs.disabled"
@change="handleSectionChange($event.value)"
/>

<h2 v-else-if="selectedSection.label" class="section-select">
{{ selectedSection.label }}
</h2>

</div>

<KSelect
v-if="isMobile"
class="history-select"
:value="selected"
aria-labelledby="answer-history-label"
:options="options"
:value="selectedQuestion"
:label="questionsLabel$()"
:options="questionSelectOptions"
:disabled="$attrs.disabled"
@change="handleDropdownChange($event.value)"
@change="handleQuestionChange($event.value)"
>
<template #display>
<AttemptLogItem
Expand Down Expand Up @@ -129,10 +146,11 @@
<script>
import useAccordion from 'kolibri-common/components/useAccordion';
import commonCoreStrings from 'kolibri.coreVue.mixins.commonCoreStrings';
import { coreString } from 'kolibri.coreVue.mixins.commonCoreStrings';
import { enhancedQuizManagementStrings } from 'kolibri-common/strings/enhancedQuizManagementStrings';
import AccordionItem from 'kolibri-common/components/AccordionItem';
import AccordionContainer from 'kolibri-common/components/AccordionContainer';
import { watch } from 'kolibri.lib.vueCompositionApi';
import { computed, watch } from 'kolibri.lib.vueCompositionApi';
import { toRefs } from '@vueuse/core';
import AttemptLogItem from './AttemptLogItem';
Expand All @@ -143,8 +161,8 @@
AccordionContainer,
AccordionItem,
},
mixins: [commonCoreStrings],
setup(props) {
setup(props, { emit }) {
const { questionsLabel$, quizSectionsLabel$ } = enhancedQuizManagementStrings;
const { isSurvey, sections, selectedQuestionNumber } = toRefs(props);
// No need to do this unless "practice quiz" begins to use the same sections structure
// This means expand, isExpanded, and toggle cannot be referenced in any template code
Expand All @@ -169,13 +187,94 @@
}
}
const allQuestionsInOrder = computed(() => {
return sections.value.reduce((a, s) => [...a, ...s.questions], []);
});
const sectionSelectOptions = computed(() => {
return sections.value.map((section, index) => ({
value: index,
label: section.section_title,
}));
});
const currentSectionIndex = computed(() => {
let qCount = 0;
for (let i = 0; i < sections.value.length; i++) {
qCount += sections.value[i].questions.length;
if (qCount >= selectedQuestionNumber.value) {
return i;
}
}
return 0;
});
const currentSection = computed(() => {
return sections.value[currentSectionIndex.value];
});
const questionSelectOptions = computed(() => {
return currentSection.value.questions.map((question, index) => ({
value: question.item,
label: coreString('questionNumberLabel', {
questionNumber: index + 1,
}),
}));
});
// The question itself
const currentQuestion = computed(() => {
return allQuestionsInOrder.value[selectedQuestionNumber.value];
});
// The KSelect-shaped object for the current section
const selectedSection = computed(() => {
return sectionSelectOptions.value[currentSectionIndex.value];
});
// The KSelect-shaped object for the current question
const selectedQuestion = computed(() => {
return questionSelectOptions.value.find(opt => opt.value === currentQuestion.value.item);
});
function handleQuestionChange(item) {
const questionIndex = allQuestionsInOrder.value.findIndex(q => q.item === item);
if (questionIndex !== -1) {
emit('select', questionIndex);
expandCurrentSectionIfNeeded();
}
}
function handleSectionChange(index) {
const questionIndex = sections.value.slice(0, index).reduce((acc, s, i) => {
if (i !== index) {
acc += s.questions.length;
return acc;
} else {
// This will always be the last iteration thanks to slice
return acc + 1;
}
}, 0);
emit('select', questionIndex);
expandCurrentSectionIfNeeded();
}
watch(selectedQuestionNumber, expandCurrentSectionIfNeeded);
expandCurrentSectionIfNeeded();
return {
handleSectionChange,
handleQuestionChange,
quizSectionsLabel$,
questionsLabel$,
expand,
isExpanded,
toggle,
coreString,
selectedSection,
sectionSelectOptions,
selectedQuestion,
questionSelectOptions,
};
},
props: {
Expand Down Expand Up @@ -207,31 +306,13 @@
textDecoration: 'none',
};
},
selected() {
return this.options.find(o => o.value === this.selectedQuestionNumber + 1) || {};
},
options() {
let label = '';
return this.attemptLogs.map(attemptLog => {
label = this.coreString('questionNumberLabel', {
questionNumber: attemptLog.questionNumber,
});
return {
value: attemptLog.questionNumber,
label: label,
};
});
},
},
mounted() {
this.$nextTick(() => {
this.scrollToSelectedAttemptLog(this.selectedQuestionNumber);
});
},
methods: {
handleDropdownChange(value) {
this.$emit('select', value - 1);
},
setSelectedAttemptLog(questionNumber) {
const listOption = this.$refs.attemptListOption[questionNumber];
listOption.focus();
Expand Down Expand Up @@ -294,10 +375,16 @@
list-style-type: none;
}
.section-select {
max-width: 90%;
padding: 0.5em 0;
margin: 1em auto;
}
.history-select {
max-width: 90%;
padding: 0.5em 0;
margin: auto;
margin: 0 auto;
}
/deep/.ui-select-dropdown {
Expand Down
13 changes: 12 additions & 1 deletion kolibri/core/assets/src/views/ExamReport/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@
:class="windowIsSmall ? 'mobile-exercise-container' : ''"
:style="{ backgroundColor: $themeTokens.surface }"
>
<h3>{{ coreString('questionNumberLabel', { questionNumber: questionNumber + 1 }) }}</h3>
<h3>{{ questionNumberInSectionLabel }}</h3>

<div v-if="!isSurvey" data-test="diff-business">
<KCheckbox
Expand Down Expand Up @@ -323,6 +323,17 @@
};
},
computed: {
questionNumberInSectionLabel() {
for (let iSection = 0; iSection < this.sections.length; iSection++) {
const section = this.sections[iSection];
for (let iQuestion = 0; iQuestion < section.questions.length; iQuestion++) {
if (section.questions[iQuestion].item === this.itemId) {
return this.coreString('questionNumberLabel', { questionNumber: iQuestion + 1 });
}
}
}
return '';
},
attemptLogs() {
if (this.isQuiz || this.isSurvey) {
return this.quizAttempts();
Expand Down
20 changes: 14 additions & 6 deletions kolibri/plugins/learn/assets/src/views/ExamPage/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,20 @@
</KGridItem>
</KGrid>
<div v-else style="overflow-x: visible">
<KSelect
v-if="(windowIsSmall || windowIsMedium)"
:value="currentSectionOption"
:options="sectionSelectOptions"
@select="handleSectionOptionChange"
/>
<div v-if="(windowIsSmall || windowIsMedium)">
<KSelect
v-if="sectionSelectOptions.length > 1"
:value="currentSectionOption"
:options="sectionSelectOptions"
@select="handleSectionOptionChange"
/>
<h2
v-else-if="currentSectionOption.label"
class="section-select"
>
{{ currentSectionOption.label }}
</h2>
</div>
<p> {{ currentSection.description }} </p>
<p v-if="content && content.duration">
{{ learnString('suggestedTime') }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,4 +230,8 @@ export const enhancedQuizManagementStrings = createTranslator('EnhancedQuizManag
questionsUnusedInSection: {
message: '{ count, number } { count, plural, one { question } other { questions }} unused',
},
questionsLabel: {
message: 'Questions',
context: 'Label for dropdown list of questions',
},
});

0 comments on commit f8b27af

Please sign in to comment.