Skip to content

Commit

Permalink
fix: checkbox logic to setup(); topic selection logic implemented
Browse files Browse the repository at this point in the history
- Implements topic selection logic (can select topic when all children are fetched and are exercises)
- [De]select all accounts for possibility of topics, using new `hasCheckbox` logic
- Only shows "Select all" when every child `hasCheckbox` based on the new logic
- Generally begins moving logic into relevant blocks within setup()
  • Loading branch information
nucleogenesis committed Feb 16, 2024
1 parent 4f12ce5 commit 60d9864
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 38 deletions.
12 changes: 9 additions & 3 deletions kolibri/plugins/coach/assets/src/composables/useQuizResources.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,11 +128,17 @@ export default function useQuizResources({ topicId } = {}) {
}

/** @returns {Boolean} Whether the given node should be displayed with a checkbox
* @description Returns whether the given node is an exercise or not -- although, could be
* extended in the future to permit topic-level selection if desired
* @description Returns true for exercises and for topics that have no topic children and no
* more children to load
*/
function hasCheckbox(node) {
return node.kind === ContentNodeKinds.EXERCISE;
return (
node.kind === ContentNodeKinds.EXERCISE ||
// Has children, no more to load, and no children are topics
(node.children &&
!node.children.more &&
!node.children.results.some(c => c.kind === ContentNodeKinds.TOPIC))
);
}

function setResources(r) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,16 @@
/>
<ContentCardList
:contentList="contentList"
:showSelectAll="true"
:showSelectAll="showSelectAll"
:viewMoreButtonState="viewMoreButtonState"
:selectAllChecked="isSelectAllChecked"
:selectAllChecked="selectAllChecked"
:selectAllIndeterminate="selectAllIndeterminate"
:contentIsChecked="contentPresentInWorkingResourcePool"
:contentHasCheckbox="hasCheckbox"
:contentCardMessage="selectionMetadata"
:contentCardLink="contentLink"
:selectAllIndeterminate="selectAllIndeterminate"
:loadingMoreState="loadingMore"
@changeselectall="toggleTopicInWorkingResources"
@changeselectall="handleSelectAll"
@change_content_card="toggleSelected"
@moreresults="fetchMoreQuizResources"
/>
Expand Down Expand Up @@ -169,7 +169,29 @@
* a list of unique resources to avoid unnecessary duplication
*/
function addToWorkingResourcePool(resources = []) {
workingResourcePool.value = uniqWith([...workingResourcePool.value, ...resources], isEqual);
workingResourcePool.value = uniqWith(
[
...workingResourcePool.value,
...resources.filter(r => r.kind === ContentNodeKinds.EXERCISE),
],
isEqual
);
}
/**
* @description Returns the list of Exercises which can possibly be selected from the current
* contentList taking into consideration the logic for whether a topic can be selected or not.
* @returns {QuizExercise[]} - All contents which can be selected
*/
function selectableContentList() {
return contentList.value.reduce((newList, content) => {
if (content.kind === ContentNodeKinds.TOPIC && hasCheckbox(content)) {
newList = [...newList, ...content.children.results];
} else {
newList.push(content);
}
return newList;
}, []);
}
/**
Expand All @@ -193,9 +215,69 @@
*/
function contentPresentInWorkingResourcePool(content) {
const workingResourceIds = workingResourcePool.value.map(wr => wr.id);
if (content.kind === ContentNodeKinds.TOPIC) {
return content.children.results.every(child => workingResourceIds.includes(child.id));
}
return workingResourceIds.includes(content.id);
}
const selectAllChecked = computed(() => {
// Returns true if all the resources in the topic are in the working resource pool
const workingResourceIds = workingResourcePool.value.map(wr => wr.id);
const selectableIds = selectableContentList().map(content => content.id);
return selectableIds.every(id => workingResourceIds.includes(id));
});
const selectAllIndeterminate = computed(() => {
// Returns true if some, but not all, of the resources in the topic are in the working
// resource
const workingResourceIds = workingResourcePool.value.map(wr => wr.id);
const selectableIds = selectableContentList().map(content => content.id);
return !selectAllChecked.value && selectableIds.some(id => workingResourceIds.includes(id));
});
const showSelectAll = computed(() => {
return contentList.value.every(content => hasCheckbox(content));
});
function handleSelectAll(isChecked) {
if (isChecked) {
this.addToWorkingResourcePool(this.contentList);
} else {
this.contentList.forEach(content => {
var contentToRemove = [];
if (content.kind === ContentNodeKinds.TOPIC) {
contentToRemove = content.children.results;
} else {
contentToRemove.push(content);
}
contentToRemove.forEach(c => {
this.removeFromWorkingResourcePool(c);
});
});
}
}
/**
* @param {Object} param
* @param {ContentNode} param.content
* @param {boolean} param.checked
* @affects workingResourcePool - Adds or removes the content from the workingResourcePool
* When given a topic, it adds or removes all the exercises in the topic from the
* workingResourcePool. This assumes that topics which should not be added are not able to
* be checked and does not do any additional checks.
*/
function toggleSelected({ content, checked }) {
content = content.kind === ContentNodeKinds.TOPIC ? content.children.results : [content];
if (checked) {
this.addToWorkingResourcePool(content);
} else {
content.forEach(c => {
this.removeFromWorkingResourcePool(c);
});
}
}
const {
hasCheckbox,
topic,
Expand Down Expand Up @@ -284,6 +366,11 @@
});
return {
selectAllChecked,
selectAllIndeterminate,
showSelectAll,
handleSelectAll,
toggleSelected,
topic,
topicId,
contentList,
Expand Down Expand Up @@ -325,20 +412,6 @@
isTopicIdSet() {
return this.$route.params.topic_id;
},
isSelectAllChecked() {
// Returns true if all the resources in the topic are in the working resource pool
const workingResourceIds = this.workingResourcePool.map(wr => wr.id);
return this.contentList.every(content => workingResourceIds.includes(content.id));
},
selectAllIndeterminate() {
// Returns true if some, but not all, of the resources in the topic are in the working
// resource
const workingResourceIds = this.workingResourcePool.map(wr => wr.id);
return (
!this.isSelectAllChecked &&
this.contentList.some(content => workingResourceIds.includes(content.id))
);
},
getBookmarksLink() {
// Inject the showBookmarks parameter so that
Expand Down Expand Up @@ -389,22 +462,6 @@
return {}; // or return {} if you prefer an empty object
},
toggleSelected({ content, checked }) {
if (checked) {
this.addToWorkingResourcePool([content]);
} else {
this.removeFromWorkingResourcePool(content);
}
},
toggleTopicInWorkingResources(isChecked) {
if (isChecked) {
this.addToWorkingResourcePool(this.contentList);
} else {
this.contentList.forEach(content => {
this.removeFromWorkingResourcePool(content);
});
}
},
topicListingLink({ topicId }) {
return this.$router.getRoute(
PageNames.QUIZ_SELECT_RESOURCES,
Expand Down

0 comments on commit 60d9864

Please sign in to comment.