diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/EditBooleanMapModal.vue b/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/EditBooleanMapModal.vue
index a8b621bd13..93f34bc587 100644
--- a/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/EditBooleanMapModal.vue
+++ b/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/EditBooleanMapModal.vue
@@ -95,7 +95,7 @@
},
data() {
return {
- updateDescendants: false,
+ updateDescendants: true,
error: '',
/**
* selectedValues is an object with the following structure:
diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/EditLanguageModal.vue b/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/EditLanguageModal.vue
index 6287b14dd5..381afcf60f 100644
--- a/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/EditLanguageModal.vue
+++ b/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/EditLanguageModal.vue
@@ -92,7 +92,7 @@
return {
selectedLanguage: '',
searchQuery: '',
- updateDescendants: false,
+ updateDescendants: true,
isMultipleNodeLanguages: false,
changed: false,
};
diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/EditTitleDescriptionModal.vue b/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/EditTitleDescriptionModal.vue
index 3acdde0ad2..15b271664e 100644
--- a/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/EditTitleDescriptionModal.vue
+++ b/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/EditTitleDescriptionModal.vue
@@ -98,6 +98,7 @@
id: nodeId,
title: title.trim(),
description: description.trim(),
+ checkComplete: true,
});
/* eslint-disable-next-line kolibri/vue-no-undefined-string-uses */
this.$store.dispatch('showSnackbarSimple', commonStrings.$tr('changesSaved'));
diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/__tests__/EditBooleanMapModal.spec.js b/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/__tests__/EditBooleanMapModal.spec.js
index 2d4306846b..aa96754a67 100644
--- a/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/__tests__/EditBooleanMapModal.spec.js
+++ b/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/__tests__/EditBooleanMapModal.spec.js
@@ -275,10 +275,11 @@ describe('EditBooleanMapModal', () => {
expect(wrapper.find('[data-test="update-descendants-checkbox"]').exists()).toBeFalsy();
});
- test('should call updateContentNode on success submit if the user does not check the update descendants checkbox', async () => {
+ test('should call updateContentNode on success submit if the user uncheck the update descendants checkbox', async () => {
nodes['node1'].kind = ContentKindsNames.TOPIC;
const wrapper = makeWrapper({ nodeIds: ['node1'], isDescendantsUpdatable: true });
+ wrapper.find('[data-test="update-descendants-checkbox"]').element.click();
await wrapper.vm.handleSave();
expect(contentNodeActions.updateContentNode).toHaveBeenCalledWith(expect.anything(), {
@@ -287,11 +288,10 @@ describe('EditBooleanMapModal', () => {
});
});
- test('should call updateContentNodeDescendants on success submit if the user checks the descendants checkbox', async () => {
+ test('should call updateContentNodeDescendants on success submit if the user does not uncheck the update descendants checkbox', async () => {
nodes['node1'].kind = ContentKindsNames.TOPIC;
const wrapper = makeWrapper({ nodeIds: ['node1'], isDescendantsUpdatable: true });
- wrapper.find('[data-test="update-descendants-checkbox"]').element.click();
await wrapper.vm.handleSave();
expect(contentNodeActions.updateContentNodeDescendants).toHaveBeenCalledWith(
diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/__tests__/EditLanguageModal.spec.js b/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/__tests__/EditLanguageModal.spec.js
index c9716d55da..2c2cb791ff 100644
--- a/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/__tests__/EditLanguageModal.spec.js
+++ b/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/__tests__/EditLanguageModal.spec.js
@@ -220,8 +220,8 @@ describe('EditLanguageModal', () => {
});
describe('topic nodes present', () => {
- it('should display the checkbox to apply change to descendants if a topic is present', () => {
- [wrapper] = makeWrapper(['test-en-topic', 'test-en-res']);
+ test('should display a selected checkbox to apply change to descendants if a topic is present', () => {
+ const [wrapper] = makeWrapper(['test-en-topic', 'test-en-res']);
expect(
wrapper.findComponent('[data-test="update-descendants-checkbox"]').exists(),
@@ -236,30 +236,33 @@ describe('EditLanguageModal', () => {
).toBeFalsy();
});
- it('should call updateContentNode with the right language on success submit if the user does not check the checkbox', async () => {
- [wrapper, mocks] = makeWrapper(['test-en-topic', 'test-en-res']);
+ test('should call updateContentNodeDescendants with the right language on success submit by default', async () => {
+ const [wrapper, mocks] = makeWrapper(['test-en-topic', 'test-en-res']);
await chooseLanguage(wrapper, 'es');
await wrapper.vm.handleSave();
- await wrapper.vm.$nextTick();
- expect(mocks.updateContentNode).toHaveBeenCalledWith({
+ expect(mocks.updateContentNodeDescendants).toHaveBeenCalledWith({
id: 'test-en-topic',
language: 'es',
});
});
- it('should call updateContentNodeDescendants with the right language on success submit if the user checks the checkbox', async () => {
- [wrapper, mocks] = makeWrapper(['test-en-topic', 'test-en-res']);
+ test('should call updateContentNode with the right language on success submit if the user unchecks check the checkbox', async () => {
+ const [wrapper, mocks] = makeWrapper(['test-en-topic', 'test-en-res']);
await chooseLanguage(wrapper, 'es');
- wrapper.findComponent('[data-test="update-descendants-checkbox"]').vm.$emit('change', true);
+
+ // Uncheck the descendants checkbox
+ const descendantsCheckbox = wrapper.findComponent(
+ '[data-test="update-descendants-checkbox"]',
+ );
+ descendantsCheckbox.vm.$emit('change', false);
await wrapper.vm.$nextTick();
- expect(wrapper.vm.updateDescendants).toBe(true);
+
await wrapper.vm.handleSave();
- await wrapper.vm.$nextTick();
- expect(mocks.updateContentNodeDescendants).toHaveBeenCalledWith({
+ expect(mocks.updateContentNode).toHaveBeenCalledWith({
id: 'test-en-topic',
language: 'es',
});
diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/__tests__/EditTitleDescriptionModal.spec.js b/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/__tests__/EditTitleDescriptionModal.spec.js
index 464a8e9026..632f364fe1 100644
--- a/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/__tests__/EditTitleDescriptionModal.spec.js
+++ b/contentcuration/contentcuration/frontend/channelEdit/components/QuickEditModal/__tests__/EditTitleDescriptionModal.spec.js
@@ -33,9 +33,7 @@ describe('EditTitleDescriptionModal', () => {
},
},
}),
- propsData: {
- nodeId,
- },
+ propsData: { nodeId },
});
updateContentNode = jest.spyOn(wrapper.vm, 'updateContentNode').mockImplementation(() => {});
@@ -70,7 +68,8 @@ describe('EditTitleDescriptionModal', () => {
expect(updateContentNode).toHaveBeenCalledWith({
id: nodeId,
title: newTitle,
- description: newDescription,
+ description: newDescription ?? '',
+ checkComplete: true,
});
});
@@ -80,11 +79,11 @@ describe('EditTitleDescriptionModal', () => {
descriptionInput.vm.$emit('input', '');
modal.vm.$emit('submit');
-
expect(updateContentNode).toHaveBeenCalledWith({
id: nodeId,
title: newTitle,
description: '',
+ checkComplete: true,
});
});
diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/ResourcePanel.vue b/contentcuration/contentcuration/frontend/channelEdit/components/ResourcePanel.vue
index 674bcfa160..e8616b3c1e 100644
--- a/contentcuration/contentcuration/frontend/channelEdit/components/ResourcePanel.vue
+++ b/contentcuration/contentcuration/frontend/channelEdit/components/ResourcePanel.vue
@@ -74,7 +74,8 @@
slider-color="primary"
>
@@ -86,7 +87,8 @@
/>
diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/edit/DetailsTabView.vue b/contentcuration/contentcuration/frontend/channelEdit/components/edit/DetailsTabView.vue
index 3002797c94..d7e6021ee9 100644
--- a/contentcuration/contentcuration/frontend/channelEdit/components/edit/DetailsTabView.vue
+++ b/contentcuration/contentcuration/frontend/channelEdit/components/edit/DetailsTabView.vue
@@ -499,6 +499,7 @@
} from 'shared/constants';
import { constantsTranslationMixin, metadataTranslationMixin } from 'shared/mixins';
import { crossComponentTranslator } from 'shared/i18n';
+ import { LanguagesNames } from 'shared/leUtils/Languages';
function getValueFromResults(results) {
if (results.length === 0) {
@@ -715,7 +716,17 @@
},
},
role: generateGetterSetter('role_visibility'),
- language: generateGetterSetter('language'),
+ language: {
+ get() {
+ const value = this.getValueFromNodes('language');
+ return value === nonUniqueValue ? LanguagesNames.MUL : value;
+ },
+ set(value) {
+ if (!(value === LanguagesNames.MUL && this.language === LanguagesNames.MUL)) {
+ this.update({ language: value });
+ }
+ },
+ },
accessibility: generateNestedNodesGetterSetter('accessibility_labels'),
contentLevel: generateNestedNodesGetterSetterObject('grade_levels'),
resourcesNeeded: generateNestedNodesGetterSetterObject('learner_needs'),
diff --git a/contentcuration/contentcuration/frontend/channelEdit/views/ImportFromChannels/SearchResultsList.vue b/contentcuration/contentcuration/frontend/channelEdit/views/ImportFromChannels/SearchResultsList.vue
index 4f95c2cd99..2862ff2146 100644
--- a/contentcuration/contentcuration/frontend/channelEdit/views/ImportFromChannels/SearchResultsList.vue
+++ b/contentcuration/contentcuration/frontend/channelEdit/views/ImportFromChannels/SearchResultsList.vue
@@ -3,13 +3,22 @@
-
-
-
-
-
-
-
+
+
+
+
+
@@ -105,8 +114,8 @@
-
-
+
+
diff --git a/contentcuration/contentcuration/frontend/channelEdit/views/trash/TrashModal.vue b/contentcuration/contentcuration/frontend/channelEdit/views/trash/TrashModal.vue
index 9052cf5720..271d12a8d0 100644
--- a/contentcuration/contentcuration/frontend/channelEdit/views/trash/TrashModal.vue
+++ b/contentcuration/contentcuration/frontend/channelEdit/views/trash/TrashModal.vue
@@ -281,19 +281,24 @@
'moveContentNodes',
'loadContentNodes',
'loadAncestors',
+ 'removeContentNodes',
]),
loadNodes() {
this.loading = true;
+ this.more = null;
+ this.moreLoading = false;
if (!this.trashId) {
this.loading = false;
return;
}
- this.loadChildren({ parent: this.trashId, ordering: '-modified' }).then(
- childrenResponse => {
- this.loading = false;
- this.more = childrenResponse.more || null;
- },
- );
+ this.removeContentNodes({ parentId: this.trashId }).then(() => {
+ this.loadChildren({ parent: this.trashId, ordering: '-modified' }).then(
+ childrenResponse => {
+ this.loading = false;
+ this.more = childrenResponse.more || null;
+ },
+ );
+ });
},
moveNodes(target) {
return this.moveContentNodes({
diff --git a/contentcuration/contentcuration/frontend/shared/data/__tests__/ContentNodeResource.spec.js b/contentcuration/contentcuration/frontend/shared/data/__tests__/ContentNodeResource.spec.js
index 3f16719252..31bcb53bb4 100644
--- a/contentcuration/contentcuration/frontend/shared/data/__tests__/ContentNodeResource.spec.js
+++ b/contentcuration/contentcuration/frontend/shared/data/__tests__/ContentNodeResource.spec.js
@@ -574,8 +574,12 @@ describe('ContentNode methods', () => {
it('should update the node with the payload', async () => {
node.parent = parent.id;
- await expect(ContentNode.tableMove({ node, parent, payload, change })).resolves.toBe(payload);
- expect(table.update).toHaveBeenCalledWith(node.id, payload);
+ const result = await ContentNode.tableMove({ node, parent, payload, change });
+ expect(result).toMatchObject({ ...payload, modified: expect.any(String) });
+ expect(table.update).toHaveBeenCalledTimes(1);
+ const [updateId, updatePayload] = table.update.mock.calls[0];
+ expect(updateId).toBe(node.id);
+ expect(updatePayload).toBe(result);
expect(table.put).not.toBeCalled();
expect(table.update).not.toHaveBeenCalledWith(node.parent, { changed: true });
});
@@ -584,19 +588,23 @@ describe('ContentNode methods', () => {
node.parent = parent.id;
updated = false;
const newPayload = { ...payload, root_id: parent.root_id };
- await expect(ContentNode.tableMove({ node, parent, payload, change })).resolves.toMatchObject(
- newPayload,
+ const result = await ContentNode.tableMove({ node, parent, payload, change });
+ expect(result).toMatchObject({ ...newPayload, modified: expect.any(String) });
+ expect(table.update).toHaveBeenCalledWith(
+ node.id,
+ expect.objectContaining({ ...payload, modified: expect.any(String) }),
);
- expect(table.update).toHaveBeenCalledWith(node.id, payload);
- expect(table.put).toHaveBeenCalledWith(newPayload);
+ expect(table.put).toHaveBeenCalledWith(result);
expect(table.update).not.toHaveBeenCalledWith(node.parent, { changed: true });
});
it('should mark the old parent as changed', async () => {
- await expect(ContentNode.tableMove({ node, parent, payload, change })).resolves.toMatchObject(
- payload,
+ const result = await ContentNode.tableMove({ node, parent, payload, change });
+ expect(result).toMatchObject({ ...payload, modified: expect.any(String) });
+ expect(table.update).toHaveBeenCalledWith(
+ node.id,
+ expect.objectContaining({ ...payload, modified: expect.any(String) }),
);
- expect(table.update).toHaveBeenCalledWith(node.id, payload);
expect(table.put).not.toBeCalled();
expect(table.update).toHaveBeenCalledWith(node.parent, { changed: true });
});
diff --git a/contentcuration/contentcuration/frontend/shared/data/resources.js b/contentcuration/contentcuration/frontend/shared/data/resources.js
index 37c307d850..1ddc0cdbfe 100644
--- a/contentcuration/contentcuration/frontend/shared/data/resources.js
+++ b/contentcuration/contentcuration/frontend/shared/data/resources.js
@@ -1687,6 +1687,10 @@ export const ContentNode = new TreeResource({
async tableMove({ node, parent, payload }) {
// Do direct table writes here rather than using add/update methods to avoid
// creating unnecessary additional change events.
+ payload = {
+ ...payload,
+ modified: new Date().toISOString(),
+ };
const updated = await this.table.update(node.id, payload);
// Update didn't succeed, this node probably doesn't exist, do a put instead,
// but need to add in other parent info.
diff --git a/contentcuration/contentcuration/frontend/shared/views/__tests__/languageDropdown.spec.js b/contentcuration/contentcuration/frontend/shared/views/__tests__/languageDropdown.spec.js
index f478ebcd9b..8bdcd165fd 100644
--- a/contentcuration/contentcuration/frontend/shared/views/__tests__/languageDropdown.spec.js
+++ b/contentcuration/contentcuration/frontend/shared/views/__tests__/languageDropdown.spec.js
@@ -1,4 +1,4 @@
-import { mount } from '@vue/test-utils';
+import { mount, shallowMount } from '@vue/test-utils';
import LanguageDropdown from '../LanguageDropdown.vue';
import TestForm from './TestForm.vue';
import { LanguagesList } from 'shared/leUtils/Languages';
@@ -61,4 +61,24 @@ describe('languageDropdown', () => {
await wrapper.vm.$nextTick();
expect(wrapper.find('.error--text').exists()).toBe(true);
});
+
+ it('returns formatted language text when native_name is present', () => {
+ const wrapper = shallowMount(LanguageDropdown, {
+ mocks: {
+ $tr: (key, params) => `${params.language} (${params.code})`,
+ },
+ });
+ const item = { native_name: 'Español,Spanish', id: 'es' };
+ expect(wrapper.vm.languageText(item)).toBe('Español (es)');
+ });
+
+ it('returns formatted language text when native_name is an empty string', () => {
+ const wrapper = shallowMount(LanguageDropdown, {
+ mocks: {
+ $tr: (key, params) => `${params.language} (${params.code})`,
+ },
+ });
+ const item = { native_name: '', id: 'de' };
+ expect(wrapper.vm.languageText(item)).toBe(' (de)');
+ });
});