Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate quizForge object references to composition API standard #11562

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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