-
-
+
+
+
+
+
@@ -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 @@
+
+
+
+
+
+ {{ $t("common.medium.chip.noLongerAvailable") }}
+
+
+
+
+
+
+
+
+
+
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"
>
+
@@ -69,8 +75,11 @@
diff --git a/src/modules/feature/media-shelf/data/mediaBoardState.composable.ts b/src/modules/feature/media-shelf/data/mediaBoardState.composable.ts
index 10070d3fb8..aac73660d9 100644
--- a/src/modules/feature/media-shelf/data/mediaBoardState.composable.ts
+++ b/src/modules/feature/media-shelf/data/mediaBoardState.composable.ts
@@ -4,6 +4,7 @@ import {
useErrorHandler,
} from "@/components/error-handling/ErrorHandler.composable";
import {
+ DeletedElementResponse,
MediaAvailableLineResponse,
MediaBoardColors,
MediaBoardLayoutType,
@@ -44,8 +45,10 @@ const useMediaBoardState = () => {
}
const lineIndex: number = mediaBoard.value.lines.findIndex((line) => {
- const element: MediaExternalToolElementResponse | undefined =
- line.elements.find((element) => element.id === elementId);
+ const element:
+ | MediaExternalToolElementResponse
+ | DeletedElementResponse
+ | undefined = line.elements.find((element) => element.id === elementId);
return element !== undefined;
});
@@ -456,10 +459,8 @@ const useMediaBoardState = () => {
const tempFromLine = Array.from(
mediaBoard.value.lines[fromLineIndex].elements
);
- const element: MediaExternalToolElementResponse = tempFromLine.splice(
- oldElementIndex,
- 1
- )[0];
+ const element: MediaExternalToolElementResponse | DeletedElementResponse =
+ tempFromLine.splice(oldElementIndex, 1)[0];
mediaBoard.value.lines[fromLineIndex].elements = tempFromLine;
const tempToLine = Array.from(
diff --git a/src/modules/page/room/RoomDetails.page.vue b/src/modules/page/room/RoomDetails.page.vue
index b3e0ef13e8..da5e5abc25 100644
--- a/src/modules/page/room/RoomDetails.page.vue
+++ b/src/modules/page/room/RoomDetails.page.vue
@@ -13,15 +13,18 @@
diff --git a/src/modules/ui/layout/sidebar/Sidebar.unit.ts b/src/modules/ui/layout/sidebar/Sidebar.unit.ts
index a2491bfde1..20d5533fd7 100644
--- a/src/modules/ui/layout/sidebar/Sidebar.unit.ts
+++ b/src/modules/ui/layout/sidebar/Sidebar.unit.ts
@@ -1,5 +1,5 @@
import { mount } from "@vue/test-utils";
-import { h, nextTick } from "vue";
+import { h, nextTick, ref } from "vue";
import { VApp } from "vuetify/lib/components/index.mjs";
import {
createTestingI18n,
@@ -18,11 +18,15 @@ import FilePathsModule from "@/store/filePaths";
import { createModuleMocks } from "@/utils/mock-store-module";
import { SchulcloudTheme } from "@/serverApi/v3";
import { envsFactory } from "@@/tests/test-utils";
+import { useSidebarSelection } from "./SidebarSelection.composable";
jest.mock("vue-router", () => ({
useRoute: () => ({ path: "rooms/courses-list" }),
}));
+jest.mock("./SidebarSelection.composable");
+const mockedUseSidebarSelection = jest.mocked(useSidebarSelection);
+
const setup = (permissions?: string[]) => {
const authModule = createModuleMocks(AuthModule, {
getUserPermissions: permissions,
@@ -43,6 +47,8 @@ const setup = (permissions?: string[]) => {
},
});
+ mockedUseSidebarSelection.mockReturnValue({ isActive: ref(false) });
+
const wrapper = mount(VApp, {
global: {
plugins: [createTestingVuetify(), createTestingI18n()],
diff --git a/src/modules/ui/layout/sidebar/SidebarCategoryItem.unit.ts b/src/modules/ui/layout/sidebar/SidebarCategoryItem.unit.ts
index 2527086313..4b1a036fc7 100644
--- a/src/modules/ui/layout/sidebar/SidebarCategoryItem.unit.ts
+++ b/src/modules/ui/layout/sidebar/SidebarCategoryItem.unit.ts
@@ -5,6 +5,8 @@ import {
} from "@@/tests/test-utils/setup";
import SidebarCategoryItem from "./SidebarCategoryItem.vue";
import { SidebarGroupItem } from "../types";
+import { useSidebarSelection } from "./SidebarSelection.composable";
+import { ref } from "vue";
const groupItem: SidebarGroupItem = {
icon: "mdiOpen",
@@ -33,7 +35,12 @@ jest.mock("vue-router", () => ({
useRoute: () => ({ path: "rooms/courses-list" }),
}));
+jest.mock("./SidebarSelection.composable");
+const mockedUseSidebarSelection = jest.mocked(useSidebarSelection);
+
describe("@ui-layout/SidebarCategoryItem", () => {
+ mockedUseSidebarSelection.mockReturnValue({ isActive: ref(false) });
+
const setup = (sidebarItem: SidebarGroupItem) => {
const wrapper = mount(SidebarCategoryItem, {
global: {
diff --git a/src/modules/ui/layout/sidebar/SidebarItem.unit.ts b/src/modules/ui/layout/sidebar/SidebarItem.unit.ts
index 214735df5d..1424ecb846 100644
--- a/src/modules/ui/layout/sidebar/SidebarItem.unit.ts
+++ b/src/modules/ui/layout/sidebar/SidebarItem.unit.ts
@@ -5,6 +5,8 @@ import {
} from "@@/tests/test-utils/setup";
import SidebarItem from "./SidebarItem.vue";
import { SidebarSingleItem } from "../types";
+import { ref } from "vue";
+import { useSidebarSelection } from "./SidebarSelection.composable";
const iconItem: SidebarSingleItem = {
icon: "mdiOpen",
@@ -23,8 +25,13 @@ jest.mock("vue-router", () => ({
useRoute: () => ({ path: "rooms/courses-list" }),
}));
+jest.mock("./SidebarSelection.composable");
+const mockedUseSidebarSelection = jest.mocked(useSidebarSelection);
+
describe("@ui-layout/SidebarItem", () => {
const setup = (sidebarItem: SidebarSingleItem) => {
+ mockedUseSidebarSelection.mockReturnValue({ isActive: ref(true) });
+
const wrapper = mount(SidebarItem, {
global: {
plugins: [createTestingVuetify(), createTestingI18n()],
@@ -51,7 +58,7 @@ describe("@ui-layout/SidebarItem", () => {
expect(wrapper.findComponent(".v-icon").exists()).toBe(false);
});
- it("should highlight correct sidebar item", () => {
+ it("should highlight item when selection is active", () => {
const { wrapper } = setup({
icon: "mdiOpen",
title: "title",
@@ -61,15 +68,4 @@ describe("@ui-layout/SidebarItem", () => {
expect(wrapper.classes()).toContain("v-list-item--active");
});
-
- it("should not highlight wrong sidebar item", () => {
- const { wrapper } = setup({
- icon: "mdiOpen",
- title: "title",
- testId: "testId",
- to: "/administration/rooms/new",
- });
-
- expect(wrapper.classes()).not.toContain("v-list-item--active");
- });
});
diff --git a/src/modules/ui/layout/sidebar/SidebarItem.vue b/src/modules/ui/layout/sidebar/SidebarItem.vue
index 6ac94950bb..b18df650c8 100644
--- a/src/modules/ui/layout/sidebar/SidebarItem.vue
+++ b/src/modules/ui/layout/sidebar/SidebarItem.vue
@@ -22,7 +22,7 @@