Skip to content

Commit

Permalink
BC-7985 - Adapt sidebar active selection to rooms changes (#3390)
Browse files Browse the repository at this point in the history
* implement selective sidebar selection for /rooms/:id

* fix sidebar selection for board

* move sidebar selection to composable

* add/fix tests
  • Loading branch information
uidp committed Sep 18, 2024
1 parent 6e0ed21 commit de54286
Show file tree
Hide file tree
Showing 18 changed files with 513 additions and 75 deletions.
12 changes: 11 additions & 1 deletion src/layouts/LoggedIn.unit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import { h, nextTick } from "vue";
import { VApp } from "vuetify/lib/components/index.mjs";
import LoggedInLayout from "./LoggedIn.layout.vue";
import { Topbar } from "@ui-layout";
import { createTestingPinia } from "@pinia/testing";
import setupStores from "@@/tests/test-utils/setupStores";

jest.mock("vue-router", () => ({
useRoute: () => ({ path: "rooms/courses-list" }),
Expand All @@ -43,12 +45,20 @@ const setup = () => {
},
});

setupStores({
envConfigModule: EnvConfigModule,
});

const wrapper = mount(VApp, {
slots: {
default: h(LoggedInLayout),
},
global: {
plugins: [createTestingVuetify(), createTestingI18n()],
plugins: [
createTestingVuetify(),
createTestingI18n(),
createTestingPinia(),
],
provide: {
[AUTH_MODULE_KEY.valueOf()]: authModule,
[ENV_CONFIG_MODULE_KEY.valueOf()]: envConfigModule,
Expand Down
4 changes: 3 additions & 1 deletion src/modules/data/board/BoardApi.composable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
CourseRoomsApiFactory,
SubmissionContainerElementContentBody,
} from "@/serverApi/v3";
import { BoardContextType } from "@/types/board/BoardContext";
import { AnyContentElement } from "@/types/board/ContentElement";
import { $axios, mapAxiosErrorToResponseError } from "@/utils/api";
import { createApplicationError } from "@/utils/create-application-error.factory";
Expand Down Expand Up @@ -185,7 +186,7 @@ export const useBoardApi = () => {
});
};

type ContextInfo = { id: string; name: string };
type ContextInfo = { id: string; type: BoardContextType; name: string };

const getContextInfo = async (
boardId: string
Expand All @@ -205,6 +206,7 @@ export const useBoardApi = () => {
}
return {
id: roomResponse.data.roomId,
type: context.type,
name: roomResponse.data.title,
};
};
Expand Down
4 changes: 4 additions & 0 deletions src/modules/data/board/BoardPageInformation.composable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { createSharedComposable } from "@vueuse/core";
import { ref, Ref } from "vue";
import { useI18n } from "vue-i18n";
import { useBoardApi } from "./BoardApi.composable";
import { BoardContextType } from "@/types/board/BoardContext";

const useBoardPageInformation = () => {
const { t } = useI18n();
Expand All @@ -20,6 +21,7 @@ const useBoardPageInformation = () => {
const pageTitle: Ref<string> = ref(getPageTitle());
const roomId: Ref<string | undefined> = ref(undefined);
const breadcrumbs: Ref<Breadcrumb[]> = ref([]);
const contextType: Ref<BoardContextType | undefined> = ref();

function getBreadcrumbs(
contextInfo: { id: string; name: string } | undefined
Expand All @@ -44,12 +46,14 @@ const useBoardPageInformation = () => {
const contextInfo = await getContextInfo(id);
pageTitle.value = getPageTitle(contextInfo?.name);
breadcrumbs.value = getBreadcrumbs(contextInfo);
contextType.value = contextInfo?.type;
roomId.value = contextInfo?.id;
};

return {
createPageInformation,
breadcrumbs,
contextType,
pageTitle,
roomId,
};
Expand Down
31 changes: 27 additions & 4 deletions src/modules/data/board/BoardPageInformation.composable.unit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { mountComposable } from "@@/tests/test-utils/mountComposable";
import { createMock, DeepMocked } from "@golevelup/ts-jest";
import { useBoardApi } from "./BoardApi.composable";
import { useSharedBoardPageInformation } from "./BoardPageInformation.composable";
import { BoardContextType } from "@/types/board/BoardContext";

jest.mock("./BoardApi.composable");
const mockedUseBoardApi = jest.mocked(useBoardApi);
Expand Down Expand Up @@ -36,13 +37,25 @@ describe("BoardPageInformation.composable", () => {
const setup = () => {
mockedBoardApiCalls.getContextInfo.mockResolvedValue({
id: "courseId",
type: BoardContextType.Course,
name: "Course #1",
});

const { createPageInformation, breadcrumbs, pageTitle, roomId } =
mountComposable(() => useSharedBoardPageInformation());

return { createPageInformation, breadcrumbs, pageTitle, roomId };
const {
createPageInformation,
breadcrumbs,
contextType,
pageTitle,
roomId,
} = mountComposable(() => useSharedBoardPageInformation());

return {
createPageInformation,
breadcrumbs,
contextType,
pageTitle,
roomId,
};
};

it("should return two breadcrumbs: 1. course page and and 2. course-overview page", async () => {
Expand Down Expand Up @@ -74,6 +87,16 @@ describe("BoardPageInformation.composable", () => {

expect(roomId.value).toEqual("courseId");
});

it("should set context type", async () => {
const { createPageInformation, contextType } = setup();

const fakeId = "abc123-2";

await createPageInformation(fakeId);

expect(contextType.value).toEqual(BoardContextType.Course);
});
});

describe("when board context does not exist", () => {
Expand Down
25 changes: 0 additions & 25 deletions src/modules/data/room/RoomDetails.state.ts

This file was deleted.

44 changes: 44 additions & 0 deletions src/modules/data/room/RoomDetails.store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Room } from "@/types/room/Room";
import { delay } from "@/utils/helpers";
import { ref } from "vue";
import { roomsData } from "./rooms-mock-data";
import { defineStore } from "pinia";

export enum RoomVariant {
ROOM = "room",
COURSE_ROOM = "courseRoom",
}

export const useRoomDetailsStore = defineStore("roomDetailsStore", () => {
const isLoading = ref(true);
const room = ref<Room>();
const roomVariant = ref<RoomVariant>();

const fetchRoom = async (id: string) => {
await delay(100);
// TODO call API
room.value = roomsData.find((r) => r.id === id);
roomVariant.value =
room.value != null ? RoomVariant.ROOM : RoomVariant.COURSE_ROOM;
isLoading.value = false;
};

const resetState = () => {
isLoading.value = true;
room.value = undefined;
};

const deactivateRoom = () => {
resetState();
isLoading.value = false;
};

return {
deactivateRoom,
fetchRoom,
isLoading,
resetState,
room,
roomVariant,
};
});
2 changes: 1 addition & 1 deletion src/modules/data/room/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export { useCourseApi } from "./courseApi.composable";
export { useRoomsState } from "./Rooms.state";
export { useRoomDetailsState } from "./RoomDetails.state";
export { useRoomDetailsStore, RoomVariant } from "./RoomDetails.store";
export { useCourseInfoApi } from "./courseInfoApi.composable";
export { useCourseList } from "./courseList.composable";
1 change: 1 addition & 0 deletions src/modules/feature/board/board/Board.unit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ describe("Board", () => {
mockedUseSharedBoardPageInformation.mockReturnValue({
createPageInformation: jest.fn(),
breadcrumbs: ref([]),
contextType: ref(),
pageTitle: ref("page-title"),
roomId: ref("room-id"),
});
Expand Down
18 changes: 13 additions & 5 deletions src/modules/page/room/RoomDetails.page.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,34 @@
<script setup lang="ts">
import CourseRoomDetailsPage from "@/pages/course-rooms/CourseRoomDetails.page.vue";
import { ENV_CONFIG_MODULE_KEY, injectStrict } from "@/utils/inject";
import { useRoomDetailsState } from "@data-room";
import { RoomVariant, useRoomDetailsStore } from "@data-room";
import { RoomDetails } from "@feature-room";
import { watch } from "vue";
import { storeToRefs } from "pinia";
import { computed, onUnmounted, watch } from "vue";
import { useRoute } from "vue-router";
const envConfigModule = injectStrict(ENV_CONFIG_MODULE_KEY);
const route = useRoute();
const { fetchRoom, isLoading, isRoom, room } = useRoomDetailsState();
const roomDetailsStore = useRoomDetailsStore();
const { isLoading, room, roomVariant } = storeToRefs(roomDetailsStore);
const { deactivateRoom, fetchRoom, resetState } = roomDetailsStore;
watch(
() => route.params.id,
async () => {
if (envConfigModule.getEnv["FEATURE_ROOMS_ENABLED"]) {
await fetchRoom(route.params.id as string);
} else {
isLoading.value = false;
isRoom.value = false;
deactivateRoom();
}
},
{ immediate: true }
);
const isRoom = computed(() => roomVariant.value === RoomVariant.ROOM);
onUnmounted(() => {
resetState();
});
</script>
8 changes: 7 additions & 1 deletion src/modules/ui/layout/sidebar/Sidebar.unit.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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,
Expand All @@ -43,6 +47,8 @@ const setup = (permissions?: string[]) => {
},
});

mockedUseSidebarSelection.mockReturnValue({ isActive: ref(false) });

const wrapper = mount(VApp, {
global: {
plugins: [createTestingVuetify(), createTestingI18n()],
Expand Down
7 changes: 7 additions & 0 deletions src/modules/ui/layout/sidebar/SidebarCategoryItem.unit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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: {
Expand Down
20 changes: 8 additions & 12 deletions src/modules/ui/layout/sidebar/SidebarItem.unit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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()],
Expand All @@ -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",
Expand All @@ -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");
});
});
Loading

0 comments on commit de54286

Please sign in to comment.