Skip to content

Commit

Permalink
Merge pull request #11562 from ozer550/feature--quizForgeBegone
Browse files Browse the repository at this point in the history
Migrate quizForge object references to composition API standard
  • Loading branch information
nucleogenesis authored Dec 12, 2023
2 parents 08da061 + 5af7475 commit c3995f8
Show file tree
Hide file tree
Showing 6 changed files with 208 additions and 69 deletions.
84 changes: 81 additions & 3 deletions kolibri/plugins/coach/assets/src/composables/useQuizCreation.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { ContentNodeKinds } from 'kolibri.coreVue.vuex.constants';
import { ChannelResource, ExamResource } from 'kolibri.resources';
import { validateObject, objectWithDefaults } from 'kolibri.utils.objectSpecs';
import { get, set } from '@vueuse/core';
import { computed, ref } from 'kolibri.lib.vueCompositionApi';
import { computed, ref, provide, inject } from 'kolibri.lib.vueCompositionApi';
// TODO: Probably move this to this file's local dir
import selectQuestions from '../modules/examCreation/selectQuestions.js';
import { Quiz, QuizSection, QuizQuestion } from './quizCreationSpecs.js';
Expand All @@ -31,7 +31,7 @@ function isExercise(o) {
/**
* Composable function presenting primary interface for Quiz Creation
*/
export default (DEBUG = false) => {
export default function useQuizCreation(DEBUG = false) {
// -----------
// Local state
// -----------
Expand Down Expand Up @@ -380,6 +380,26 @@ export default (DEBUG = false) => {
return !get(allQuestionsSelected) && !get(noQuestionsSelected);
});

provide('saveQuiz', saveQuiz);
provide('updateSection', updateSection);
provide('replaceSelectedQuestions', replaceSelectedQuestions);
provide('addSection', addSection);
provide('removeSection', removeSection);
provide('setActiveSection', setActiveSection);
provide('initializeQuiz', initializeQuiz);
provide('updateQuiz', updateQuiz);
provide('addQuestionToSelection', addQuestionToSelection);
provide('removeQuestionFromSelection', removeQuestionFromSelection);
provide('channels', channels);
provide('quiz', quiz);
provide('allSections', allSections);
provide('activeSection', activeSection);
provide('inactiveSections', inactiveSections);
provide('activeExercisePool', activeExercisePool);
provide('activeQuestionsPool', activeQuestionsPool);
provide('activeQuestions', activeQuestions);
provide('selectedActiveQuestions', selectedActiveQuestions);
provide('replacementQuestionPool', replacementQuestionPool);
return {
// Methods
saveQuiz,
Expand Down Expand Up @@ -412,4 +432,62 @@ export default (DEBUG = false) => {
allQuestionsSelected,
noQuestionsSelected,
};
};

/*
return {
// Only what is needed where we want the rest of the module to be
// provided
saveQuiz,
initializeQuiz,
};
*/
}

export function injectQuizCreation() {
const saveQuiz = inject('saveQuiz');
const updateSection = inject('updateSection');
const replaceSelectedQuestions = inject('replaceSelectedQuestions');
const addSection = inject('addSection');
const removeSection = inject('removeSection');
const setActiveSection = inject('setActiveSection');
const initializeQuiz = inject('initializeQuiz');
const updateQuiz = inject('updateQuiz');
const addQuestionToSelection = inject('addQuestionToSelection');
const removeQuestionFromSelection = inject('removeQuestionFromSelection');
const channels = inject('channels');
const quiz = inject('quiz');
const allSections = inject('allSections');
const activeSection = inject('activeSection');
const inactiveSections = inject('inactiveSections');
const activeExercisePool = inject('activeExercisePool');
const activeQuestionsPool = inject('activeQuestionsPool');
const activeQuestions = inject('activeQuestions');
const selectedActiveQuestions = inject('selectedActiveQuestions');
const replacementQuestionPool = inject('replacementQuestionPool');

return {
// Methods
saveQuiz,
updateSection,
replaceSelectedQuestions,
addSection,
removeSection,
setActiveSection,
initializeQuiz,
updateQuiz,
addQuestionToSelection,
removeQuestionFromSelection,

// Computed
channels,
quiz,
allSections,
activeSection,
inactiveSections,
activeExercisePool,
activeQuestionsPool,
activeQuestions,
selectedActiveQuestions,
replacementQuestionPool,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
:label="quizTitle$()"
:autofocus="true"
:maxlength="100"
@blur="e => quizForge.updateQuiz({ title: e.target.value })"
@change="title => quizForge.updateQuiz({ title })"
@blur="e => updateQuiz({ title: e.target })"
@change="title => updateQuiz({ title })"
/>
</KGridItem>
</KGrid>
Expand All @@ -43,13 +43,14 @@
tabsId="quizSectionTabs"
class="section-tabs"
:tabs="tabs"
:activeTabId="quizForge.activeSection.value ?
quizForge.activeSection.value.section_id :
:appearanceOverrides="{ padding: '0px', overflow: 'hidden' }"
:activeTabId="activeSection ?
activeSection.section_id :
'' "
backgroundColor="transparent"
hoverBackgroundColor="transparent"
:aria-label="quizSectionsLabel$()"
@click="id => quizForge.setActiveSection(id)"
@click="id => setActiveSection(id)"
>
<template #tab="{ tab }">
<span
Expand All @@ -76,7 +77,7 @@
:disabled="false"
:hasIcons="true"
:options="overflowTabs"
@select="opt => quizForge.setActiveSection(opt.id)"
@select="opt => setActiveSection(opt.id)"
/>
</template>
</KIconButton>
Expand All @@ -103,12 +104,13 @@
</KGrid>

<KTabsPanel
v-if="quizForge.activeSection.value"
v-if="activeSection"
tabsId="quizSectionTabs"
:activeTabId="quizForge.activeSection.value ? quizForge.activeSection.value.section_id : ''"
:activeTabId="activeSection ? activeSection.section_id : ''"
>
<p>{{ activeSection.section_id }}</p>
<!-- TODO This should be a separate component like "empty section container" or something -->
<div v-if="!quizForge.activeQuestions.value.length" class="no-question-style">
<div v-if="!activeQuestions.length" class="no-question-style">
<KGrid class="questions-list-label-row">
<KGridItem
class="right-side-heading"
Expand Down Expand Up @@ -144,13 +146,11 @@
<KButton
primary
icon="plus"
@click="openSelectResources(quizForge.activeSection.value.section_id)"
@click="openSelectResources(activeSection.section_id)"
>
{{ addQuestionsLabel$() }}
</KButton>
</div>
<!-- END TODO -->


<div v-else>
<KGrid class="questions-list-label-row">
Expand Down Expand Up @@ -217,7 +217,7 @@
<template #default="{ toggleItemState, isItemExpanded }">
<DragContainer
key="drag-container"
:items="quizForge.activeQuestions.value"
:items="activeQuestions"
@sort="handleQuestionOrderChange"
@dragStart="handleDragStart"
>
Expand All @@ -227,7 +227,7 @@
class="wrapper"
>
<Draggable
v-for="(question, index) in quizForge.activeQuestions.value"
v-for="(question, index) in activeQuestions"
:key="`drag-${question.question_id}`"
tabindex="-1"
style="background: white"
Expand All @@ -245,18 +245,18 @@
moveDownText="down"
:noDrag="true"
:isFirst="index === 0"
:isLast="index === quizForge.activeQuestions.value.length - 1"
:isLast="index === activeQuestions.length - 1"
@moveUp="shiftOne(index, -1)"
@moveDown="shiftOne(index, +1)"
/>
</div>
</DragHandle>
<KCheckbox
style="padding-left: 0.5em"
:checked="quizForge.selectedActiveQuestions.value.includes(
:checked="selectedActiveQuestions.includes(
question.question_id
)"
@change="() => quizForge.toggleQuestionInSelection(question.question_id)"
@change="() => toggleQuestionInSelection(question.question_id)"
/>
<KButton
tabindex="0"
Expand All @@ -280,10 +280,10 @@
<div
:id="`question-panel-${question.question_id}`"
:ref="`question-panel-${question.question_id}`"
:style="{ userSelect: dragActive.value ? 'none!important' : 'text' }"
:style="{ userSelect: dragActive ? 'none!important' : 'text' }"
>
<p
v-if="isItemExpanded(question.question_id) && !dragActive.value"
v-if="isItemExpanded(question.question_id) && !dragActive"
class="question-content-panel"
>
CONTENT OF {{ question.title }}
Expand Down Expand Up @@ -316,6 +316,7 @@
import DragHandle from 'kolibri.coreVue.components.DragHandle';
import DragSortWidget from 'kolibri.coreVue.components.DragSortWidget';
import Draggable from 'kolibri.coreVue.components.Draggable';
import { injectQuizCreation } from '../../../composables/useQuizCreation';
import commonCoach from '../../common';
import SectionSidePanel from './SectionSidePanel';
import TabsWithOverflow from './TabsWithOverflow';
Expand Down Expand Up @@ -351,6 +352,32 @@
questionList$,
} = enhancedQuizManagementStrings;
const {
// Methods
saveQuiz,
updateSection,
replaceSelectedQuestions,
addSection,
removeSection,
setActiveSection,
initializeQuiz,
updateQuiz,
addQuestionToSelection,
removeQuestionFromSelection,
// Computed
channels,
quiz,
allSections,
activeSection,
inactiveSections,
activeExercisePool,
activeQuestionsPool,
activeQuestions,
selectedActiveQuestions,
replacementQuestionPool,
} = injectQuizCreation();
// The number we use for the default section title
const sectionCreationCount = ref(1);
const dragActive = ref(false);
Expand All @@ -370,9 +397,31 @@
deleteSectionLabel$,
replaceAction$,
questionList$,
saveQuiz,
updateSection,
replaceSelectedQuestions,
addSection,
removeSection,
setActiveSection,
initializeQuiz,
updateQuiz,
addQuestionToSelection,
removeQuestionFromSelection,
// Computed
channels,
quiz,
allSections,
activeSection,
inactiveSections,
activeExercisePool,
activeQuestionsPool,
activeQuestions,
selectedActiveQuestions,
replacementQuestionPool,
};
},
inject: ['quizForge'],
computed: {
accordionStyleOverrides() {
return {
Expand All @@ -397,7 +446,7 @@
};
},
tabs() {
return get(this.quizForge.allSections).map(section => {
return get(this.allSections).map(section => {
const id = section.section_id;
const label = section.section_title;
return { id, label };
Expand Down Expand Up @@ -429,17 +478,17 @@
},
methods: {
handleReplaceSelection() {
const section_id = get(this.quizForge.activeSection).section_id;
const section_id = get(this.activeSection).section_id;
this.$router.replace({ path: 'new/' + section_id + '/replace-questions' });
},
handleActiveSectionAction(opt) {
const section_id = this.quizForge.activeSection.value.section_id;
const section_id = this.activeSection.section_id;
switch (opt.label) {
case this.editSectionLabel$():
this.$router.replace({ path: 'new/' + section_id + '/edit' });
break;
case this.deleteSectionLabel$():
this.quizForge.removeSection(this.quizForge.activeSection.value.section_id);
this.removeSection(this.activeSection.section_id);
this.focusActiveSectionTab();
break;
}
Expand All @@ -448,7 +497,7 @@
return `section-tab-${section_id}`;
},
focusActiveSectionTab() {
const label = this.tabRefLabel(this.quizForge.activeSection.value.section_id);
const label = this.tabRefLabel(this.activeSection.section_id);
const tabRef = this.$refs[label];
// TODO Consider the "Delete section" button on the side panel; maybe we need to await
// nextTick if we're getting the error
Expand All @@ -465,7 +514,7 @@
},
activeSectionIsHidden(overflow) {
const ids = overflow.map(i => i.id);
return ids.includes(get(this.quizForge.activeSection).section_id);
return ids.includes(get(this.activeSection).section_id);
},
overflowButtonStyles(overflow) {
return {
Expand All @@ -479,14 +528,14 @@
handleQuestionOrderChange({ newArray }) {
set(this.dragActive, false);
const payload = {
section_id: get(this.quizForge.activeSection).section_id,
section_id: get(this.activeSection).section_id,
questions: newArray,
};
this.quizForge.updateSection(payload);
this.updateSection(payload);
},
handleAddSection() {
const newSection = this.quizForge.addSection();
this.quizForge.setActiveSection(get(newSection).section_id);
const newSection = this.addSection();
this.setActiveSection(get(newSection).section_id);
this.sectionCreationCount++;
},
handleDragStart() {
Expand Down
Loading

0 comments on commit c3995f8

Please sign in to comment.