From 1319e1f3fad15d3be16b8d0fe543b2a28cb297fa Mon Sep 17 00:00:00 2001 From: Alex Velez Date: Fri, 8 Dec 2023 10:57:41 -0500 Subject: [PATCH 01/11] Add edit title and description modal --- .../components/ContentNodeEditListItem.vue | 166 +++++++++++------- .../quickEdit/EditTitleDescriptionModal.vue | 94 ++++++++++ 2 files changed, 192 insertions(+), 68 deletions(-) create mode 100644 contentcuration/contentcuration/frontend/channelEdit/components/quickEdit/EditTitleDescriptionModal.vue diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/ContentNodeEditListItem.vue b/contentcuration/contentcuration/frontend/channelEdit/components/ContentNodeEditListItem.vue index 3e57f48587..310a2af09c 100644 --- a/contentcuration/contentcuration/frontend/channelEdit/components/ContentNodeEditListItem.vue +++ b/contentcuration/contentcuration/frontend/channelEdit/components/ContentNodeEditListItem.vue @@ -1,80 +1,99 @@ @@ -89,6 +108,7 @@ import Checkbox from 'shared/views/form/Checkbox'; import IconButton from 'shared/views/IconButton'; import DraggableItem from 'shared/views/draggable/DraggableItem'; + import EditTitleDescriptionModal from './quickEdit/EditTitleDescriptionModal.vue'; import { COPYING_FLAG } from 'shared/data/constants'; import { DragEffect, DropEffect, EffectAllowed } from 'shared/mixins/draggable/constants'; import { DraggableRegions } from 'frontend/channelEdit/constants'; @@ -102,6 +122,7 @@ ContentNodeContextMenu, Checkbox, IconButton, + EditTitleDescriptionModal, }, props: { nodeId: { @@ -137,6 +158,9 @@ data() { return { activated: false, + showQuickEdit: { + titleDescription: false, + } }; }, computed: { @@ -200,6 +224,11 @@ }); }, }, + methods: { + showTitleDescriptionModal() { + this.showQuickEdit.titleDescription = true; + }, + }, beforeDestroy() { // Unselect before removing if (this.selected) { @@ -217,6 +246,7 @@ creatingCopies: 'Copying...', copiedSnackbar: 'Copy operation complete', undo: 'Undo', + editTooltip: 'Edit Title & Description', /* eslint-enable kolibri/vue-no-unused-translations */ }, }; diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/quickEdit/EditTitleDescriptionModal.vue b/contentcuration/contentcuration/frontend/channelEdit/components/quickEdit/EditTitleDescriptionModal.vue new file mode 100644 index 0000000000..19024a8504 --- /dev/null +++ b/contentcuration/contentcuration/frontend/channelEdit/components/quickEdit/EditTitleDescriptionModal.vue @@ -0,0 +1,94 @@ + + + + + + + \ No newline at end of file From 780174c6f4ba44554a2c3e628aa5357d2ea2b5b9 Mon Sep 17 00:00:00 2001 From: Alex Velez Date: Fri, 8 Dec 2023 15:59:02 -0500 Subject: [PATCH 02/11] Refactor and maxlength --- .../quickEdit/EditTitleDescriptionModal.vue | 52 +++++++++---------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/quickEdit/EditTitleDescriptionModal.vue b/contentcuration/contentcuration/frontend/channelEdit/components/quickEdit/EditTitleDescriptionModal.vue index 19024a8504..202622e7e1 100644 --- a/contentcuration/contentcuration/frontend/channelEdit/components/quickEdit/EditTitleDescriptionModal.vue +++ b/contentcuration/contentcuration/frontend/channelEdit/components/quickEdit/EditTitleDescriptionModal.vue @@ -11,22 +11,22 @@ @cancel="close" > @@ -35,7 +35,7 @@ - - - \ No newline at end of file From d34108e4b1823ea7e18f78efea72d375629518c4 Mon Sep 17 00:00:00 2001 From: Alex Velez Date: Fri, 8 Dec 2023 16:18:01 -0500 Subject: [PATCH 03/11] Show snackbar on edit --- .../components/quickEdit/EditTitleDescriptionModal.vue | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/quickEdit/EditTitleDescriptionModal.vue b/contentcuration/contentcuration/frontend/channelEdit/components/quickEdit/EditTitleDescriptionModal.vue index 202622e7e1..8d759d1059 100644 --- a/contentcuration/contentcuration/frontend/channelEdit/components/quickEdit/EditTitleDescriptionModal.vue +++ b/contentcuration/contentcuration/frontend/channelEdit/components/quickEdit/EditTitleDescriptionModal.vue @@ -77,6 +77,7 @@ description: description.trim(), }); + this.$store.dispatch('showSnackbarSimple', this.$tr('editedTitleDescription')); this.close(); }, }, @@ -87,6 +88,7 @@ saveAction: 'Save', cancelAction: 'Cancel', fieldRequired: 'Field is required', + editedTitleDescription: 'Edited title and description', }, } From 82ce6bc858585832e96f4a44c53f59d1e5ab3844 Mon Sep 17 00:00:00 2001 From: Alex Velez Date: Fri, 8 Dec 2023 16:21:33 -0500 Subject: [PATCH 04/11] Linting files --- .../components/ContentNodeEditListItem.vue | 14 +++++++------- .../quickEdit/EditTitleDescriptionModal.vue | 14 ++++++++------ 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/ContentNodeEditListItem.vue b/contentcuration/contentcuration/frontend/channelEdit/components/ContentNodeEditListItem.vue index 310a2af09c..b44fd3cb67 100644 --- a/contentcuration/contentcuration/frontend/channelEdit/components/ContentNodeEditListItem.vue +++ b/contentcuration/contentcuration/frontend/channelEdit/components/ContentNodeEditListItem.vue @@ -105,10 +105,10 @@ import ContentNodeListItem from './ContentNodeListItem'; import ContentNodeOptions from './ContentNodeOptions'; import ContentNodeContextMenu from './ContentNodeContextMenu'; + import EditTitleDescriptionModal from './quickEdit/EditTitleDescriptionModal.vue'; import Checkbox from 'shared/views/form/Checkbox'; import IconButton from 'shared/views/IconButton'; import DraggableItem from 'shared/views/draggable/DraggableItem'; - import EditTitleDescriptionModal from './quickEdit/EditTitleDescriptionModal.vue'; import { COPYING_FLAG } from 'shared/data/constants'; import { DragEffect, DropEffect, EffectAllowed } from 'shared/mixins/draggable/constants'; import { DraggableRegions } from 'frontend/channelEdit/constants'; @@ -160,7 +160,7 @@ activated: false, showQuickEdit: { titleDescription: false, - } + }, }; }, computed: { @@ -224,17 +224,17 @@ }); }, }, - methods: { - showTitleDescriptionModal() { - this.showQuickEdit.titleDescription = true; - }, - }, beforeDestroy() { // Unselect before removing if (this.selected) { this.selected = false; } }, + methods: { + showTitleDescriptionModal() { + this.showQuickEdit.titleDescription = true; + }, + }, $trs: { optionsTooltip: 'Options', /* eslint-disable kolibri/vue-no-unused-translations */ diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/quickEdit/EditTitleDescriptionModal.vue b/contentcuration/contentcuration/frontend/channelEdit/components/quickEdit/EditTitleDescriptionModal.vue index 8d759d1059..2540807576 100644 --- a/contentcuration/contentcuration/frontend/channelEdit/components/quickEdit/EditTitleDescriptionModal.vue +++ b/contentcuration/contentcuration/frontend/channelEdit/components/quickEdit/EditTitleDescriptionModal.vue @@ -1,4 +1,5 @@ @@ -38,6 +40,7 @@ import { mapActions } from 'vuex'; export default { + name: 'EditTitleDescriptionModal', props: { node: { type: Object, @@ -52,9 +55,7 @@ }; }, methods: { - ...mapActions('contentNode', [ - 'updateContentNode', - ]), + ...mapActions('contentNode', ['updateContentNode']), validateTitle() { if (this.title.trim().length === 0) { this.titleError = this.$tr('fieldRequired'); @@ -90,5 +91,6 @@ fieldRequired: 'Field is required', editedTitleDescription: 'Edited title and description', }, - } + }; + From dfb8c44ea22c75606fb1a87b8202c9db3a9eef04 Mon Sep 17 00:00:00 2001 From: Alex Velez Date: Mon, 11 Dec 2023 05:43:37 -0500 Subject: [PATCH 05/11] Add tests to EditTitleDescriptionModal component --- .../quickEdit/EditTitleDescriptionModal.vue | 3 + .../EditTitleDescriptionModal.spec.js | 175 ++++++++++++++++++ package.json | 6 +- 3 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 contentcuration/contentcuration/frontend/channelEdit/components/quickEdit/__tests__/EditTitleDescriptionModal.spec.js diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/quickEdit/EditTitleDescriptionModal.vue b/contentcuration/contentcuration/frontend/channelEdit/components/quickEdit/EditTitleDescriptionModal.vue index 2540807576..18972d6b22 100644 --- a/contentcuration/contentcuration/frontend/channelEdit/components/quickEdit/EditTitleDescriptionModal.vue +++ b/contentcuration/contentcuration/frontend/channelEdit/components/quickEdit/EditTitleDescriptionModal.vue @@ -8,11 +8,13 @@ :title="$tr('editTitleDescription')" :submitText="$tr('saveAction')" :cancelText="$tr('cancelAction')" + data-test="edit-title-description-modal" @submit="handleSave" @cancel="close" > { + beforeEach(() => { + contentNodeActions = { + updateContentNode: jest.fn(), + }; + generalActions = { + showSnackbarSimple: jest.fn(), + }; + store = new Vuex.Store({ + actions: generalActions, + modules: { + contentNode: { + namespaced: true, + actions: contentNodeActions, + }, + }, + }); + }); + + test('smoke test', () => { + const wrapper = mount(EditTitleDescriptionModal, { + propsData: { + node, + }, + }); + expect(wrapper.isVueInstance()).toBe(true); + }); + + test('should display the correct title and description on first render', () => { + const wrapper = mount(EditTitleDescriptionModal, { + propsData: { + node, + }, + }); + + expect(wrapper.find('[data-test="title-input"]').vm.$props.value).toBe(node.title); + expect(wrapper.find('[data-test="description-input"]').vm.$props.value).toBe(node.description); + }); + + test('should call updateContentNode on success submit', () => { + const wrapper = mount(EditTitleDescriptionModal, { + store, + propsData: { + node, + }, + }); + + wrapper.find('[data-test="edit-title-description-modal"]').vm.$emit('submit'); + expect(contentNodeActions.updateContentNode).toHaveBeenCalled(); + }); + + test('should call updateContentNode with the correct parameters on success submit', () => { + const wrapper = mount(EditTitleDescriptionModal, { + store, + propsData: { + node, + }, + }); + + const newTitle = 'new-title'; + const newDescription = 'new-description'; + wrapper.find('[data-test="title-input"]').vm.$emit('input', 'new-title'); + wrapper.find('[data-test="description-input"]').vm.$emit('input', 'new-description'); + + wrapper.find('[data-test="edit-title-description-modal"]').vm.$emit('submit'); + + expect(contentNodeActions.updateContentNode).toHaveBeenCalledWith(expect.anything(), { + id: node.id, + title: newTitle, + description: newDescription, + }); + }); + + test('should let update even if description is empty', () => { + const wrapper = mount(EditTitleDescriptionModal, { + store, + propsData: { + node, + }, + }); + + const newTitle = 'new-title'; + wrapper.find('[data-test="title-input"]').vm.$emit('input', 'new-title'); + wrapper.find('[data-test="description-input"]').vm.$emit('input', ''); + + wrapper.find('[data-test="edit-title-description-modal"]').vm.$emit('submit'); + + expect(contentNodeActions.updateContentNode).toHaveBeenCalledWith(expect.anything(), { + id: node.id, + title: newTitle, + description: '', + }); + }); + + test('should validate title on blur', () => { + const wrapper = mount(EditTitleDescriptionModal, { + store, + propsData: { + node, + }, + }); + + wrapper.find('[data-test="title-input"]').vm.$emit('input', ''); + wrapper.find('[data-test="title-input"]').vm.$emit('blur'); + + expect(wrapper.find('[data-test="title-input"]').vm.$props.invalidText).toBeTruthy(); + }); + + test('should validate title on submit', () => { + const wrapper = mount(EditTitleDescriptionModal, { + store, + propsData: { + node, + }, + }); + + wrapper.find('[data-test="title-input"]').vm.$emit('input', ''); + wrapper.find('[data-test="edit-title-description-modal"]').vm.$emit('submit'); + + expect(wrapper.find('[data-test="title-input"]').vm.$props.invalidText).toBeTruthy(); + }); + + test("should show 'Edited title and description' on a snackbar on success submit", () => { + const wrapper = mount(EditTitleDescriptionModal, { + store, + propsData: { + node, + }, + }); + + wrapper.find('[data-test="edit-title-description-modal"]').vm.$emit('submit'); + expect(generalActions.showSnackbarSimple).toHaveBeenCalledWith( + expect.anything(), + 'Edited title and description' + ); + }); + + test("should emit 'close' event on success submit", () => { + const wrapper = mount(EditTitleDescriptionModal, { + store, + propsData: { + node, + }, + }); + + wrapper.find('[data-test="edit-title-description-modal"]').vm.$emit('submit'); + expect(wrapper.emitted().close).toBeTruthy(); + }); + + test('should emit close event on cancel', () => { + const wrapper = mount(EditTitleDescriptionModal, { + store, + propsData: { + node, + }, + }); + + wrapper.find('[data-test="edit-title-description-modal"]').vm.$emit('cancel'); + expect(wrapper.emitted().close).toBeTruthy(); + }); +}); diff --git a/package.json b/package.json index 2c7ebd30dc..44d777db25 100644 --- a/package.json +++ b/package.json @@ -129,5 +129,9 @@ "browserslist": [ "> 1%", "Firefox ESR" - ] + ], + "volta": { + "node": "16.20.2", + "yarn": "1.22.21" + } } From d2a7c315d05b6c516a41fe770bbfed23e435c0c4 Mon Sep 17 00:00:00 2001 From: Alex Velez Date: Mon, 11 Dec 2023 05:47:00 -0500 Subject: [PATCH 06/11] Restore package.json --- package.json | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/package.json b/package.json index 44d777db25..2c7ebd30dc 100644 --- a/package.json +++ b/package.json @@ -129,9 +129,5 @@ "browserslist": [ "> 1%", "Firefox ESR" - ], - "volta": { - "node": "16.20.2", - "yarn": "1.22.21" - } + ] } From 87ad48f1d3acd551e8cd483e883bc054e883a1e8 Mon Sep 17 00:00:00 2001 From: Alex Velez Date: Thu, 14 Dec 2023 07:29:45 -0500 Subject: [PATCH 07/11] Refactor modal validations --- .../quickEdit/EditTitleDescriptionModal.vue | 11 ++++------- .../frontend/shared/utils/validation.js | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/contentcuration/contentcuration/frontend/channelEdit/components/quickEdit/EditTitleDescriptionModal.vue b/contentcuration/contentcuration/frontend/channelEdit/components/quickEdit/EditTitleDescriptionModal.vue index 18972d6b22..5a67c7a883 100644 --- a/contentcuration/contentcuration/frontend/channelEdit/components/quickEdit/EditTitleDescriptionModal.vue +++ b/contentcuration/contentcuration/frontend/channelEdit/components/quickEdit/EditTitleDescriptionModal.vue @@ -41,6 +41,7 @@