From 6e0ed21728715a87b3d86bcb67a6276ae0ec2f6a Mon Sep 17 00:00:00 2001 From: Gordon Nicholas <160246213+GordonNicholasCap@users.noreply.github.com> Date: Tue, 17 Sep 2024 10:54:12 +0200 Subject: [PATCH] N21 2089 placeholder element media shelf (#3401) --- src/locales/de.ts | 1 + src/locales/en.ts | 1 + src/locales/es.ts | 1 + src/locales/uk.ts | 1 + .../DeletedElement.unit.ts | 31 ++---- .../media-shelf/MediaBoardElementDisplay.vue | 35 +++--- ...diaBoardExternalToolDeletedElement.unit.ts | 104 ++++++++++++++++++ .../MediaBoardExternalToolDeletedElement.vue | 46 ++++++++ .../media-shelf/MediaBoardLine.unit.ts | 61 +++++++++- .../feature/media-shelf/MediaBoardLine.vue | 21 +++- .../data/mediaBoardState.composable.ts | 13 ++- src/serverApi/v3/api.ts | 10 +- .../factory/deletedElementContentFactory.ts | 9 ++ .../factory/deletedElementResponseFactory.ts | 13 +++ tests/test-utils/factory/index.ts | 1 + 15 files changed, 304 insertions(+), 44 deletions(-) create mode 100644 src/modules/feature/media-shelf/MediaBoardExternalToolDeletedElement.unit.ts create mode 100644 src/modules/feature/media-shelf/MediaBoardExternalToolDeletedElement.vue create mode 100644 tests/test-utils/factory/deletedElementContentFactory.ts create mode 100644 tests/test-utils/factory/deletedElementResponseFactory.ts diff --git a/src/locales/de.ts b/src/locales/de.ts index ab661e990d..3efc985edd 100644 --- a/src/locales/de.ts +++ b/src/locales/de.ts @@ -97,6 +97,7 @@ export default { "common.medium.chip.deactivated": "Deaktiviert", "common.medium.chip.notLicensed": "Nicht freigeschaltet", "common.medium.chip.incomplete": "Konfiguration unvollständig", + "common.medium.chip.noLongerAvailable": "Nicht mehr verfügbar", "common.medium.information.admin": "Bitte Einstellungen überprüfen.", "common.medium.information.student": "Bitte an eine Lehrkraft wenden.", "common.medium.information.teacher": "Bitte an Schuladministrator:in wenden.", diff --git a/src/locales/en.ts b/src/locales/en.ts index f146b7dc3a..49ce83b3c6 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -97,6 +97,7 @@ export default { "common.medium.chip.deactivated": "Disabled", "common.medium.chip.notLicensed": "Not activated", "common.medium.chip.incomplete": "Configuration incomplete", + "common.medium.chip.noLongerAvailable": "No longer available", "common.medium.information.admin": "Please check settings.", "common.medium.information.student": "Please contact a teacher.", "common.medium.information.teacher": diff --git a/src/locales/es.ts b/src/locales/es.ts index cfd02ecde9..577a8f3423 100644 --- a/src/locales/es.ts +++ b/src/locales/es.ts @@ -97,6 +97,7 @@ export default { "common.medium.chip.deactivated": "Desactivado", "common.medium.chip.notLicensed": "No esta activado", "common.medium.chip.incomplete": "Configuración incompleta", + "common.medium.chip.noLongerAvailable": "Ya no está disponible", "common.medium.information.admin": "Por favor verifique la configuración.", "common.medium.information.student": "Por favor contacte a un maestro.", "common.medium.information.teacher": diff --git a/src/locales/uk.ts b/src/locales/uk.ts index 0bdc71c9f3..677427a7af 100644 --- a/src/locales/uk.ts +++ b/src/locales/uk.ts @@ -97,6 +97,7 @@ export default { "common.medium.chip.deactivated": "Вимкнено", "common.medium.chip.notLicensed": "Не активовано", "common.medium.chip.incomplete": "Конфігурація не завершена", + "common.medium.chip.noLongerAvailable": "Більше не доступний", "common.medium.information.admin": "Перевірте налаштування.", "common.medium.information.student": "Будь ласка, зверніться до вчителя.", "common.medium.information.teacher": diff --git a/src/modules/feature/board-deleted-element/DeletedElement.unit.ts b/src/modules/feature/board-deleted-element/DeletedElement.unit.ts index b573e6c11d..cca12dbf06 100644 --- a/src/modules/feature/board-deleted-element/DeletedElement.unit.ts +++ b/src/modules/feature/board-deleted-element/DeletedElement.unit.ts @@ -1,5 +1,4 @@ -import { ContentElementType, DeletedElementResponse } from "@/serverApi/v3"; -import { timestampsResponseFactory } from "@@/tests/test-utils"; +import { deletedElementResponseFactory } from "@@/tests/test-utils"; import { createTestingI18n, createTestingVuetify, @@ -15,16 +14,6 @@ import DeletedElementMenu from "./DeletedElementMenu.vue"; jest.mock("@data-board"); -const DELETED_ELEMENT: DeletedElementResponse = { - id: "deleted-element-id", - content: { - deletedElementType: ContentElementType.ExternalTool, - title: "Deleted Tool", - }, - type: ContentElementType.Deleted, - timestamps: timestampsResponseFactory.build(), -}; - describe("DeletedElement", () => { let useBoardFocusHandlerMock: DeepMocked< ReturnType @@ -50,7 +39,7 @@ describe("DeletedElement", () => { const getWrapper = ( props: ComponentProps = { - element: DELETED_ELEMENT, + element: deletedElementResponseFactory.build(), isEditMode: false, } ) => { @@ -75,7 +64,7 @@ describe("DeletedElement", () => { useBoardPermissionsMock.isTeacher = false; const { wrapper } = getWrapper({ - element: DELETED_ELEMENT, + element: deletedElementResponseFactory.build(), isEditMode: true, }); @@ -97,7 +86,7 @@ describe("DeletedElement", () => { describe("when in edit mode", () => { const setup = () => { const { wrapper } = getWrapper({ - element: DELETED_ELEMENT, + element: deletedElementResponseFactory.build(), isEditMode: true, }); @@ -118,7 +107,7 @@ describe("DeletedElement", () => { describe("when not in edit mode", () => { const setup = () => { const { wrapper } = getWrapper({ - element: DELETED_ELEMENT, + element: deletedElementResponseFactory.build(), isEditMode: false, }); @@ -138,24 +127,26 @@ describe("DeletedElement", () => { describe("when deleting the element", () => { const setup = () => { + const deletedElement = deletedElementResponseFactory.build(); const { wrapper } = getWrapper({ - element: DELETED_ELEMENT, + element: deletedElement, isEditMode: true, }); return { wrapper, + deletedElement, }; }; it("should emit an event", async () => { - const { wrapper } = setup(); + const { wrapper, deletedElement } = setup(); wrapper.findComponent(DeletedElementMenu).vm.$emit("delete:element"); await nextTick(); expect(wrapper.emitted("delete:element")).toEqual([ - [DELETED_ELEMENT.id], + [deletedElement.id], ]); }); }); @@ -165,7 +156,7 @@ describe("DeletedElement", () => { describe("when the deleted element was an external tool element", () => { const setup = () => { const { wrapper } = getWrapper({ - element: DELETED_ELEMENT, + element: deletedElementResponseFactory.build(), isEditMode: true, }); diff --git a/src/modules/feature/media-shelf/MediaBoardElementDisplay.vue b/src/modules/feature/media-shelf/MediaBoardElementDisplay.vue index 844dc9e25c..93cda800a9 100644 --- a/src/modules/feature/media-shelf/MediaBoardElementDisplay.vue +++ b/src/modules/feature/media-shelf/MediaBoardElementDisplay.vue @@ -18,20 +18,23 @@ :ripple="false" >
- - +
+ + +
+
@@ -70,6 +73,10 @@ defineProps({ element: { type: Object as PropType, }, + isUnavailable: { + type: Boolean, + default: false, + }, }); const card = ref(null); diff --git a/src/modules/feature/media-shelf/MediaBoardExternalToolDeletedElement.unit.ts b/src/modules/feature/media-shelf/MediaBoardExternalToolDeletedElement.unit.ts new file mode 100644 index 0000000000..24abee6797 --- /dev/null +++ b/src/modules/feature/media-shelf/MediaBoardExternalToolDeletedElement.unit.ts @@ -0,0 +1,104 @@ +import { + createTestingI18n, + createTestingVuetify, +} from "@@/tests/test-utils/setup"; +import { deletedElementResponseFactory } from "@@/tests/test-utils"; +import { mount } from "@vue/test-utils"; +import { BoardMenuActionDelete } from "@ui-board"; +import { nextTick } from "vue"; +import { ComponentProps } from "vue-component-type-helpers"; +import { VBtn } from "vuetify/lib/components/index.mjs"; +import MediaBoardExternalToolElementMenu from "./MediaBoardExternalToolElementMenu.vue"; +import MediaBoardDeletedElement from "./MediaBoardExternalToolDeletedElement.vue"; + +describe("MediaBoardDeletedElement", () => { + const getWrapper = ( + props: ComponentProps, + stubThreeDotMenu = true + ) => { + const wrapper = mount(MediaBoardDeletedElement, { + global: { + plugins: [createTestingVuetify(), createTestingI18n()], + }, + props, + stubs: { + MediaBoardExternalToolElementMenu: stubThreeDotMenu, + }, + }); + + return { + wrapper, + }; + }; + + describe("three dot menu", () => { + describe("when clicking on the the three dot menu", () => { + const setupOverlayDiv = () => { + const overlayDiv = document.createElement("div"); + overlayDiv.className = "v-overlay-container"; + document.body.append(); + }; + + const setup = () => { + const deletedElement = deletedElementResponseFactory.build(); + + const { wrapper } = getWrapper( + { + element: deletedElement, + }, + false + ); + + setupOverlayDiv(); + + return { + wrapper, + }; + }; + + afterEach(() => { + document.body.innerHTML = ""; + }); + + it("should show the delete action", async () => { + const { wrapper } = setup(); + + const menuBtn = wrapper + .getComponent(MediaBoardExternalToolElementMenu) + .getComponent(VBtn); + await menuBtn.trigger("click"); + + const deleteAction = wrapper.findComponent(BoardMenuActionDelete); + + expect(deleteAction.exists()).toEqual(true); + }); + }); + + describe("when deleting the element from the menu", () => { + const setup = () => { + const deletedElement = deletedElementResponseFactory.build(); + + const { wrapper } = getWrapper({ + element: deletedElement, + }); + + return { + wrapper, + deletedElement, + }; + }; + + it("should emit a delete event", async () => { + const { wrapper, deletedElement } = setup(); + + const menu = wrapper.getComponent(MediaBoardExternalToolElementMenu); + menu.vm.$emit("delete:element"); + await nextTick(); + + expect(wrapper.emitted("delete:element")).toEqual([ + [deletedElement.id], + ]); + }); + }); + }); +}); diff --git a/src/modules/feature/media-shelf/MediaBoardExternalToolDeletedElement.vue b/src/modules/feature/media-shelf/MediaBoardExternalToolDeletedElement.vue new file mode 100644 index 0000000000..a89fdc3cff --- /dev/null +++ b/src/modules/feature/media-shelf/MediaBoardExternalToolDeletedElement.vue @@ -0,0 +1,46 @@ + + + diff --git a/src/modules/feature/media-shelf/MediaBoardLine.unit.ts b/src/modules/feature/media-shelf/MediaBoardLine.unit.ts index ac7daacce9..a30a07362a 100644 --- a/src/modules/feature/media-shelf/MediaBoardLine.unit.ts +++ b/src/modules/feature/media-shelf/MediaBoardLine.unit.ts @@ -1,5 +1,9 @@ import { MediaBoardLayoutType } from "@/serverApi/v3"; -import { mediaLineResponseFactory } from "@@/tests/test-utils"; +import { + deletedElementResponseFactory, + mediaExternalToolElementResponseFactory, + mediaLineResponseFactory, +} from "@@/tests/test-utils"; import { createTestingI18n, createTestingVuetify, @@ -18,6 +22,7 @@ import MediaBoardExternalToolElement from "./MediaBoardExternalToolElement.vue"; import MediaBoardLine from "./MediaBoardLine.vue"; import MediaBoardLineHeader from "./MediaBoardLineHeader.vue"; import MediaBoardLineMenu from "./MediaBoardLineMenu.vue"; +import MediaBoardExternalToolDeletedElement from "./MediaBoardExternalToolDeletedElement.vue"; jest.mock("@vueuse/core", () => { return { @@ -519,4 +524,58 @@ describe("MediaBoardLine", () => { expect(wrapper.emitted("delete:element")).toEqual([["elementId"]]); }); }); + + describe("when rendering an element", () => { + describe("when the element response is a DeletedElementResponse", () => { + const setup = () => { + const { wrapper } = getWrapper({ + line: mediaLineResponseFactory.build({ + elements: deletedElementResponseFactory.buildList(1), + }), + layout: MediaBoardLayoutType.List, + index: 0, + }); + + return { + wrapper, + }; + }; + + it("should render the element as MediaBoardExternalToolDeletedElement", () => { + const { wrapper } = setup(); + + const deletedElement = wrapper.findComponent( + MediaBoardExternalToolDeletedElement + ); + + expect(deletedElement.exists()).toEqual(true); + }); + }); + + describe("when the element response is a MediaExternalToolElementResponse", () => { + const setup = () => { + const { wrapper } = getWrapper({ + line: mediaLineResponseFactory.build({ + elements: mediaExternalToolElementResponseFactory.buildList(1), + }), + layout: MediaBoardLayoutType.List, + index: 0, + }); + + return { + wrapper, + }; + }; + + it("should render the element as MediaBoardExternalToolElement", () => { + const { wrapper } = setup(); + + const externalToolElement = wrapper.findComponent( + MediaBoardExternalToolElement + ); + + expect(externalToolElement.exists()).toEqual(true); + }); + }); + }); }); diff --git a/src/modules/feature/media-shelf/MediaBoardLine.vue b/src/modules/feature/media-shelf/MediaBoardLine.vue index 32bbe7526e..ba3165ee9a 100644 --- a/src/modules/feature/media-shelf/MediaBoardLine.vue +++ b/src/modules/feature/media-shelf/MediaBoardLine.vue @@ -53,9 +53,15 @@ @end="onElementDragEnd" >