diff --git a/playwright/e2e/left-panel/room-list-view/room-list-header.spec.ts b/playwright/e2e/left-panel/room-list-view/room-list-header.spec.ts index 2f4ef6f001d..daa8d3869ff 100644 --- a/playwright/e2e/left-panel/room-list-view/room-list-header.spec.ts +++ b/playwright/e2e/left-panel/room-list-view/room-list-header.spec.ts @@ -47,12 +47,41 @@ test.describe("Header section of the room list", () => { await app.closeDialog(); }); - test("should render the header section for a space", async ({ page, app, user }) => { + test("should render the header section for a space", { tag: "@screenshot" }, async ({ page, app, user }) => { await app.client.createSpace({ name: "MySpace" }); await page.getByRole("button", { name: "MySpace" }).click(); const roomListHeader = getHeaderSection(page); + await expect(roomListHeader).toMatchScreenshot("room-list-space-header.png"); + await expect(roomListHeader.getByRole("heading", { name: "MySpace" })).toBeVisible(); await expect(roomListHeader.getByRole("button", { name: "Add" })).toBeVisible(); + + const spaceMenu = roomListHeader.getByRole("button", { name: "Open space menu" }); + await spaceMenu.click(); + + await expect(page.getByRole("menu")).toMatchScreenshot("room-list-header-space-menu.png"); + + // It should open the space home + await page.getByRole("menuitem", { name: "Space home" }).click(); + await expect(page.getByRole("main").getByRole("heading", { name: "MySpace" })).toBeVisible(); + + // It should open the invite dialog + await spaceMenu.click(); + await page.getByRole("menuitem", { name: "Invite" }).click(); + await expect(page.getByRole("heading", { name: "Invite to MySpace" })).toBeVisible(); + await app.closeDialog(); + + // It should open the space preferences + await spaceMenu.click(); + await page.getByRole("menuitem", { name: "Preferences" }).click(); + await expect(page.getByRole("heading", { name: "Preferences" })).toBeVisible(); + await app.closeDialog(); + + // It should open the space settings + await spaceMenu.click(); + await page.getByRole("menuitem", { name: "Space Settings" }).click(); + await expect(page.getByRole("heading", { name: "Settings" })).toBeVisible(); + await app.closeDialog(); }); }); diff --git a/playwright/snapshots/left-panel/room-list-view/room-list-header.spec.ts/room-list-header-space-menu-linux.png b/playwright/snapshots/left-panel/room-list-view/room-list-header.spec.ts/room-list-header-space-menu-linux.png new file mode 100644 index 00000000000..d2934c2a769 Binary files /dev/null and b/playwright/snapshots/left-panel/room-list-view/room-list-header.spec.ts/room-list-header-space-menu-linux.png differ diff --git a/playwright/snapshots/left-panel/room-list-view/room-list-header.spec.ts/room-list-space-header-linux.png b/playwright/snapshots/left-panel/room-list-view/room-list-header.spec.ts/room-list-space-header-linux.png new file mode 100644 index 00000000000..bdb7f8fa2a4 Binary files /dev/null and b/playwright/snapshots/left-panel/room-list-view/room-list-header.spec.ts/room-list-space-header-linux.png differ diff --git a/res/css/views/rooms/RoomListView/_RoomListHeaderView.pcss b/res/css/views/rooms/RoomListView/_RoomListHeaderView.pcss index b9c2f7f67bb..6a88e613324 100644 --- a/res/css/views/rooms/RoomListView/_RoomListHeaderView.pcss +++ b/res/css/views/rooms/RoomListView/_RoomListHeaderView.pcss @@ -17,4 +17,16 @@ button { color: var(--cpd-color-icon-secondary); } + + .mx_SpaceMenu_button { + svg { + transition: transform 0.1s linear; + } + } + + .mx_SpaceMenu_button[aria-expanded="true"] { + svg { + transform: rotate(180deg); + } + } } diff --git a/src/components/viewmodels/roomlist/RoomListHeaderViewModel.tsx b/src/components/viewmodels/roomlist/RoomListHeaderViewModel.tsx index a37384b7fac..fb822b81296 100644 --- a/src/components/viewmodels/roomlist/RoomListHeaderViewModel.tsx +++ b/src/components/viewmodels/roomlist/RoomListHeaderViewModel.tsx @@ -6,7 +6,7 @@ */ import { useCallback } from "react"; -import { type Room, RoomEvent, RoomType } from "matrix-js-sdk/src/matrix"; +import { JoinRule, type Room, RoomEvent, RoomType } from "matrix-js-sdk/src/matrix"; import { shouldShowComponent } from "../../../customisations/helpers/UIComponents"; import { UIComponent } from "../../../settings/UIFeature"; @@ -23,7 +23,15 @@ import { UPDATE_SELECTED_SPACE, } from "../../../stores/spaces"; import SpaceStore from "../../../stores/spaces/SpaceStore"; -import { showCreateNewRoom } from "../../../utils/space"; +import { + shouldShowSpaceSettings, + showCreateNewRoom, + showSpaceInvite, + showSpacePreferences, + showSpaceSettings, +} from "../../../utils/space"; +import { useMatrixClientContext } from "../../../contexts/MatrixClientContext"; +import type { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload"; /** * Hook to get the active space and its title. @@ -59,6 +67,11 @@ export interface RoomListHeaderViewState { * True if the user can create rooms */ displayComposeMenu: boolean; + /** + * Whether to display the space menu + * True if there is an active space + */ + displaySpaceMenu: boolean; /** * Whether the user can create rooms */ @@ -67,6 +80,14 @@ export interface RoomListHeaderViewState { * Whether the user can create video rooms */ canCreateVideoRoom: boolean; + /** + * Whether the user can invite in the active space + */ + canInviteInSpace: boolean; + /** + * Whether the user can access space settings + */ + canAccessSpaceSettings: boolean; /** * Create a chat room * @param e - The click event @@ -81,17 +102,39 @@ export interface RoomListHeaderViewState { * Create a video room */ createVideoRoom: () => void; + /** + * Open the active space home + */ + openSpaceHome: () => void; + /** + * Display the space invite dialog + */ + inviteInSpace: () => void; + /** + * Open the space preferences + */ + openSpacePreferences: () => void; + /** + * Open the space settings + */ + openSpaceSettings: () => void; } /** * View model for the RoomListHeader. */ export function useRoomListHeaderViewModel(): RoomListHeaderViewState { + const matrixClient = useMatrixClientContext(); const { activeSpace, title } = useSpace(); const canCreateRoom = shouldShowComponent(UIComponent.CreateRooms); const canCreateVideoRoom = useFeatureEnabled("feature_video_rooms"); const displayComposeMenu = canCreateRoom; + const displaySpaceMenu = Boolean(activeSpace); + const canInviteInSpace = Boolean( + activeSpace?.getJoinRule() === JoinRule.Public || activeSpace?.canInvite(matrixClient.getSafeUserId()), + ); + const canAccessSpaceSettings = Boolean(activeSpace && shouldShowSpaceSettings(activeSpace)); /* Actions */ @@ -125,13 +168,48 @@ export function useRoomListHeaderViewModel(): RoomListHeaderViewState { } }, [activeSpace, elementCallVideoRoomsEnabled]); + const openSpaceHome = useCallback(() => { + // openSpaceHome is only available when there is an active space + if (!activeSpace) return; + defaultDispatcher.dispatch({ + action: Action.ViewRoom, + room_id: activeSpace.roomId, + metricsTrigger: undefined, + }); + }, [activeSpace]); + + const inviteInSpace = useCallback(() => { + // inviteInSpace is only available when there is an active space + if (!activeSpace) return; + showSpaceInvite(activeSpace); + }, [activeSpace]); + + const openSpacePreferences = useCallback(() => { + // openSpacePreferences is only available when there is an active space + if (!activeSpace) return; + showSpacePreferences(activeSpace); + }, [activeSpace]); + + const openSpaceSettings = useCallback(() => { + // openSpaceSettings is only available when there is an active space + if (!activeSpace) return; + showSpaceSettings(activeSpace); + }, [activeSpace]); + return { title, displayComposeMenu, + displaySpaceMenu, canCreateRoom, canCreateVideoRoom, + canInviteInSpace, + canAccessSpaceSettings, createChatRoom, createRoom, createVideoRoom, + openSpaceHome, + inviteInSpace, + openSpacePreferences, + openSpaceSettings, }; } diff --git a/src/components/views/rooms/RoomListView/RoomListHeaderView.tsx b/src/components/views/rooms/RoomListView/RoomListHeaderView.tsx index 478b9b7e704..7c82dabc2a7 100644 --- a/src/components/views/rooms/RoomListView/RoomListHeaderView.tsx +++ b/src/components/views/rooms/RoomListView/RoomListHeaderView.tsx @@ -8,7 +8,11 @@ import React, { type JSX, useState } from "react"; import { IconButton, Menu, MenuItem } from "@vector-im/compound-web"; import ComposeIcon from "@vector-im/compound-design-tokens/assets/web/icons/compose"; import UserAddIcon from "@vector-im/compound-design-tokens/assets/web/icons/user-add"; +import ChevronDownIcon from "@vector-im/compound-design-tokens/assets/web/icons/chevron-down"; import RoomIcon from "@vector-im/compound-design-tokens/assets/web/icons/room"; +import HomeIcon from "@vector-im/compound-design-tokens/assets/web/icons/home"; +import PreferencesIcon from "@vector-im/compound-design-tokens/assets/web/icons/preferences"; +import SettingsIcon from "@vector-im/compound-design-tokens/assets/web/icons/settings"; import VideoCallIcon from "@vector-im/compound-design-tokens/assets/web/icons/video-call"; import { _t } from "../../../../languageHandler"; @@ -34,12 +38,57 @@ export function RoomListHeaderView(): JSX.Element { align="center" data-testid="room-list-header" > -

{vm.title}

+ +

{vm.title}

+ {vm.displaySpaceMenu && } +
{vm.displayComposeMenu && } ); } +interface SpaceMenuProps { + /** + * The view model for the room list header + */ + vm: RoomListHeaderViewState; +} + +/** + * The space menu for the room list header + */ +function SpaceMenu({ vm }: SpaceMenuProps): JSX.Element { + const [open, setOpen] = useState(false); + + return ( + + + + } + > + + {vm.canInviteInSpace && ( + + )} + + {vm.canAccessSpaceSettings && ( + + )} + + ); +} + interface ComposeMenuProps { /** * The view model for the room list header diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index d4ab22771e6..624beab0b88 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2098,6 +2098,7 @@ "other": "Currently joining %(count)s rooms" }, "notification_options": "Notification options", + "open_space_menu": "Open space menu", "redacting_messages_status": { "one": "Currently removing messages in %(count)s room", "other": "Currently removing messages in %(count)s rooms" @@ -2112,6 +2113,10 @@ "sort_by_activity": "Activity", "sort_by_alphabet": "A-Z", "sort_unread_first": "Show rooms with unread messages first", + "space_menu": { + "home": "Space home", + "space_settings": "Space Settings" + }, "space_menu_label": "%(spaceName)s menu", "sublist_options": "List options", "suggested_rooms_heading": "Suggested Rooms" diff --git a/test/unit-tests/components/viewmodels/roomlist/RoomListHeaderViewModel-test.tsx b/test/unit-tests/components/viewmodels/roomlist/RoomListHeaderViewModel-test.tsx index fe96e268614..43b7cb2b7d2 100644 --- a/test/unit-tests/components/viewmodels/roomlist/RoomListHeaderViewModel-test.tsx +++ b/test/unit-tests/components/viewmodels/roomlist/RoomListHeaderViewModel-test.tsx @@ -6,24 +6,34 @@ */ import { renderHook } from "jest-matrix-react"; -import { type MatrixClient, type Room, RoomType } from "matrix-js-sdk/src/matrix"; +import { JoinRule, type MatrixClient, type Room, RoomType } from "matrix-js-sdk/src/matrix"; import { mocked } from "jest-mock"; import { useRoomListHeaderViewModel } from "../../../../../src/components/viewmodels/roomlist/RoomListHeaderViewModel"; import SpaceStore from "../../../../../src/stores/spaces/SpaceStore"; -import { mkStubRoom, stubClient } from "../../../../test-utils"; +import { mkStubRoom, stubClient, withClientContextRenderOptions } from "../../../../test-utils"; import { shouldShowComponent } from "../../../../../src/customisations/helpers/UIComponents"; import SettingsStore from "../../../../../src/settings/SettingsStore"; import defaultDispatcher from "../../../../../src/dispatcher/dispatcher"; import { Action } from "../../../../../src/dispatcher/actions"; -import { showCreateNewRoom } from "../../../../../src/utils/space"; +import { + shouldShowSpaceSettings, + showCreateNewRoom, + showSpaceInvite, + showSpacePreferences, + showSpaceSettings, +} from "../../../../../src/utils/space"; jest.mock("../../../../../src/customisations/helpers/UIComponents", () => ({ shouldShowComponent: jest.fn(), })); jest.mock("../../../../../src/utils/space", () => ({ + shouldShowSpaceSettings: jest.fn(), showCreateNewRoom: jest.fn(), + showSpaceInvite: jest.fn(), + showSpacePreferences: jest.fn(), + showSpaceSettings: jest.fn(), })); describe("useRoomListHeaderViewModel", () => { @@ -39,15 +49,19 @@ describe("useRoomListHeaderViewModel", () => { jest.resetAllMocks(); }); + function render() { + return renderHook(() => useRoomListHeaderViewModel(), withClientContextRenderOptions(matrixClient)); + } + describe("title", () => { it("should return Home as title", () => { - const { result } = renderHook(() => useRoomListHeaderViewModel()); + const { result } = render(); expect(result.current.title).toStrictEqual("Home"); }); it("should return the current space name as title", () => { jest.spyOn(SpaceStore.instance, "activeSpaceRoom", "get").mockReturnValue(space); - const { result } = renderHook(() => useRoomListHeaderViewModel()); + const { result } = render(); expect(result.current.title).toStrictEqual("spaceName"); }); @@ -55,7 +69,7 @@ describe("useRoomListHeaderViewModel", () => { it("should be displayComposeMenu=true and canCreateRoom=true if the user can creates room", () => { mocked(shouldShowComponent).mockReturnValue(false); - const { result, rerender } = renderHook(() => useRoomListHeaderViewModel()); + const { result, rerender } = render(); expect(result.current.displayComposeMenu).toBe(false); expect(result.current.canCreateRoom).toBe(false); @@ -65,15 +79,45 @@ describe("useRoomListHeaderViewModel", () => { expect(result.current.canCreateRoom).toBe(true); }); + it("should be displaySpaceMenu=true if the user is in a space", () => { + jest.spyOn(SpaceStore.instance, "activeSpaceRoom", "get").mockReturnValue(space); + const { result } = render(); + expect(result.current.displaySpaceMenu).toBe(true); + }); + + it("should be canInviteInSpace=true if the space join rule is public", () => { + jest.spyOn(SpaceStore.instance, "activeSpaceRoom", "get").mockReturnValue(space); + jest.spyOn(space, "getJoinRule").mockReturnValue(JoinRule.Public); + + const { result } = render(); + expect(result.current.displaySpaceMenu).toBe(true); + }); + + it("should be canInviteInSpace=true if the user has the right", () => { + jest.spyOn(SpaceStore.instance, "activeSpaceRoom", "get").mockReturnValue(space); + jest.spyOn(space, "canInvite").mockReturnValue(true); + + const { result } = render(); + expect(result.current.displaySpaceMenu).toBe(true); + }); + + it("should be canAccessSpaceSettings=true if the user has the right", () => { + jest.spyOn(SpaceStore.instance, "activeSpaceRoom", "get").mockReturnValue(space); + mocked(shouldShowSpaceSettings).mockReturnValue(true); + + const { result } = render(); + expect(result.current.canAccessSpaceSettings).toBe(true); + }); + it("should be canCreateVideoRoom=true if feature_video_rooms is enabled", () => { jest.spyOn(SettingsStore, "getValue").mockReturnValue(true); - const { result } = renderHook(() => useRoomListHeaderViewModel()); + const { result } = render(); expect(result.current.canCreateVideoRoom).toBe(true); }); it("should fire Action.CreateChat when createChatRoom is called", () => { const spy = jest.spyOn(defaultDispatcher, "fire"); - const { result } = renderHook(() => useRoomListHeaderViewModel()); + const { result } = render(); result.current.createChatRoom(new Event("click")); expect(spy).toHaveBeenCalledWith(Action.CreateChat); @@ -81,7 +125,7 @@ describe("useRoomListHeaderViewModel", () => { it("should fire Action.CreateRoom when createRoom is called", () => { const spy = jest.spyOn(defaultDispatcher, "fire"); - const { result } = renderHook(() => useRoomListHeaderViewModel()); + const { result } = render(); result.current.createRoom(new Event("click")); expect(spy).toHaveBeenCalledWith(Action.CreateRoom); @@ -89,7 +133,7 @@ describe("useRoomListHeaderViewModel", () => { it("should call showCreateNewRoom when createRoom is called in a space", () => { jest.spyOn(SpaceStore.instance, "activeSpaceRoom", "get").mockReturnValue(space); - const { result } = renderHook(() => useRoomListHeaderViewModel()); + const { result } = render(); result.current.createRoom(new Event("click")); expect(showCreateNewRoom).toHaveBeenCalledWith(space); @@ -98,7 +142,7 @@ describe("useRoomListHeaderViewModel", () => { it("should fire Action.CreateRoom with RoomType.UnstableCall when createVideoRoom is called and feature_element_call_video_rooms is enabled", () => { jest.spyOn(SettingsStore, "getValue").mockReturnValue(true); const spy = jest.spyOn(defaultDispatcher, "dispatch"); - const { result } = renderHook(() => useRoomListHeaderViewModel()); + const { result } = render(); result.current.createVideoRoom(); expect(spy).toHaveBeenCalledWith({ action: Action.CreateRoom, type: RoomType.UnstableCall }); @@ -107,7 +151,7 @@ describe("useRoomListHeaderViewModel", () => { it("should fire Action.CreateRoom with RoomType.ElementVideo when createVideoRoom is called and feature_element_call_video_rooms is disabled", () => { jest.spyOn(SettingsStore, "getValue").mockReturnValue(false); const spy = jest.spyOn(defaultDispatcher, "dispatch"); - const { result } = renderHook(() => useRoomListHeaderViewModel()); + const { result } = render(); result.current.createVideoRoom(); expect(spy).toHaveBeenCalledWith({ action: Action.CreateRoom, type: RoomType.ElementVideo }); @@ -115,9 +159,42 @@ describe("useRoomListHeaderViewModel", () => { it("should call showCreateNewRoom when createVideoRoom is called in a space", () => { jest.spyOn(SpaceStore.instance, "activeSpaceRoom", "get").mockReturnValue(space); - const { result } = renderHook(() => useRoomListHeaderViewModel()); + const { result } = render(); result.current.createVideoRoom(); expect(showCreateNewRoom).toHaveBeenCalledWith(space, RoomType.ElementVideo); }); + + it("should fire Action.ViewRoom when openSpaceHome is called in a space", () => { + jest.spyOn(SpaceStore.instance, "activeSpaceRoom", "get").mockReturnValue(space); + const spy = jest.spyOn(defaultDispatcher, "dispatch"); + const { result } = render(); + result.current.openSpaceHome(); + + expect(spy).toHaveBeenCalledWith({ action: Action.ViewRoom, room_id: space.roomId, metricsTrigger: undefined }); + }); + + it("should call showSpaceInvite when inviteInSpace is called in a space", () => { + jest.spyOn(SpaceStore.instance, "activeSpaceRoom", "get").mockReturnValue(space); + const { result } = render(); + result.current.inviteInSpace(); + + expect(showSpaceInvite).toHaveBeenCalledWith(space); + }); + + it("should call showSpacePreferences when openSpacePreferences is called in a space", () => { + jest.spyOn(SpaceStore.instance, "activeSpaceRoom", "get").mockReturnValue(space); + const { result } = render(); + result.current.openSpacePreferences(); + + expect(showSpacePreferences).toHaveBeenCalledWith(space); + }); + + it("should call showSpaceSettings when openSpaceSettings is called in a space", () => { + jest.spyOn(SpaceStore.instance, "activeSpaceRoom", "get").mockReturnValue(space); + const { result } = render(); + result.current.openSpaceSettings(); + + expect(showSpaceSettings).toHaveBeenCalledWith(space); + }); }); diff --git a/test/unit-tests/components/views/rooms/RoomListView/RoomListHeaderView-test.tsx b/test/unit-tests/components/views/rooms/RoomListView/RoomListHeaderView-test.tsx index fa81db04b0d..97195556c3b 100644 --- a/test/unit-tests/components/views/rooms/RoomListView/RoomListHeaderView-test.tsx +++ b/test/unit-tests/components/views/rooms/RoomListView/RoomListHeaderView-test.tsx @@ -24,64 +24,129 @@ describe("", () => { const defaultValue: RoomListHeaderViewState = { title: "title", displayComposeMenu: true, + displaySpaceMenu: true, canCreateRoom: true, canCreateVideoRoom: true, + canInviteInSpace: true, + canAccessSpaceSettings: true, createRoom: jest.fn(), createVideoRoom: jest.fn(), createChatRoom: jest.fn(), + openSpaceHome: jest.fn(), + inviteInSpace: jest.fn(), + openSpacePreferences: jest.fn(), + openSpaceSettings: jest.fn(), }; afterEach(() => { jest.resetAllMocks(); }); - it("should display the compose menu", () => { - mocked(useRoomListHeaderViewModel).mockReturnValue(defaultValue); + describe("compose menu", () => { + it("should display the compose menu", () => { + mocked(useRoomListHeaderViewModel).mockReturnValue(defaultValue); - const { asFragment } = render(); - expect(screen.queryByRole("button", { name: "Add" })).toBeInTheDocument(); - expect(asFragment()).toMatchSnapshot(); - }); + const { asFragment } = render(); + expect(screen.queryByRole("button", { name: "Add" })).toBeInTheDocument(); + expect(asFragment()).toMatchSnapshot(); + }); - it("should not display the compose menu", () => { - mocked(useRoomListHeaderViewModel).mockReturnValue({ ...defaultValue, displayComposeMenu: false }); + it("should not display the compose menu", () => { + mocked(useRoomListHeaderViewModel).mockReturnValue({ ...defaultValue, displayComposeMenu: false }); - const { asFragment } = render(); - expect(screen.queryByRole("button", { name: "Add" })).toBeNull(); - expect(asFragment()).toMatchSnapshot(); - }); + const { asFragment } = render(); + expect(screen.queryByRole("button", { name: "Add" })).toBeNull(); + expect(asFragment()).toMatchSnapshot(); + }); + + it("should display all the buttons when the menu is opened", async () => { + const user = userEvent.setup(); + mocked(useRoomListHeaderViewModel).mockReturnValue(defaultValue); + render(); + const openMenu = screen.getByRole("button", { name: "Add" }); + await user.click(openMenu); - it("should display all the buttons when the menu is opened", async () => { - const user = userEvent.setup(); - mocked(useRoomListHeaderViewModel).mockReturnValue(defaultValue); - render(); - const openMenu = screen.getByRole("button", { name: "Add" }); - await user.click(openMenu); + await user.click(screen.getByRole("menuitem", { name: "New message" })); + expect(defaultValue.createChatRoom).toHaveBeenCalled(); - await user.click(screen.getByRole("menuitem", { name: "New message" })); - expect(defaultValue.createChatRoom).toHaveBeenCalled(); + await user.click(openMenu); + await user.click(screen.getByRole("menuitem", { name: "New room" })); + expect(defaultValue.createRoom).toHaveBeenCalled(); - await user.click(openMenu); - await user.click(screen.getByRole("menuitem", { name: "New room" })); - expect(defaultValue.createRoom).toHaveBeenCalled(); + await user.click(openMenu); + await user.click(screen.getByRole("menuitem", { name: "New video room" })); + expect(defaultValue.createVideoRoom).toHaveBeenCalled(); + }); - await user.click(openMenu); - await user.click(screen.getByRole("menuitem", { name: "New video room" })); - expect(defaultValue.createVideoRoom).toHaveBeenCalled(); + it("should display only the new message button", async () => { + const user = userEvent.setup(); + mocked(useRoomListHeaderViewModel).mockReturnValue({ + ...defaultValue, + canCreateRoom: false, + canCreateVideoRoom: false, + }); + + render(); + await user.click(screen.getByRole("button", { name: "Add" })); + + expect(screen.queryByRole("menuitem", { name: "New room" })).toBeNull(); + expect(screen.queryByRole("menuitem", { name: "New video room" })).toBeNull(); + }); }); - it("should display only the new message button", async () => { - const user = userEvent.setup(); - mocked(useRoomListHeaderViewModel).mockReturnValue({ - ...defaultValue, - canCreateRoom: false, - canCreateVideoRoom: false, + describe("space menu", () => { + it("should display the space menu", () => { + mocked(useRoomListHeaderViewModel).mockReturnValue(defaultValue); + + const { asFragment } = render(); + expect(screen.queryByRole("button", { name: "Open space menu" })).toBeInTheDocument(); + expect(asFragment()).toMatchSnapshot(); }); - render(); - await user.click(screen.getByRole("button", { name: "Add" })); + it("should not display the space menu", () => { + mocked(useRoomListHeaderViewModel).mockReturnValue({ ...defaultValue, displaySpaceMenu: false }); - expect(screen.queryByRole("menuitem", { name: "New room" })).toBeNull(); - expect(screen.queryByRole("menuitem", { name: "New video room" })).toBeNull(); + const { asFragment } = render(); + expect(screen.queryByRole("button", { name: "Open space menu" })).toBeNull(); + expect(asFragment()).toMatchSnapshot(); + }); + + it("should display all the buttons when the space menu is opened", async () => { + const user = userEvent.setup(); + mocked(useRoomListHeaderViewModel).mockReturnValue(defaultValue); + render(); + const openMenu = screen.getByRole("button", { name: "Open space menu" }); + await user.click(openMenu); + + await user.click(screen.getByRole("menuitem", { name: "Space home" })); + expect(defaultValue.openSpaceHome).toHaveBeenCalled(); + + await user.click(openMenu); + await user.click(screen.getByRole("menuitem", { name: "Invite" })); + expect(defaultValue.inviteInSpace).toHaveBeenCalled(); + + await user.click(openMenu); + await user.click(screen.getByRole("menuitem", { name: "Preferences" })); + expect(defaultValue.openSpacePreferences).toHaveBeenCalled(); + + await user.click(openMenu); + await user.click(screen.getByRole("menuitem", { name: "Space Settings" })); + expect(defaultValue.openSpaceSettings).toHaveBeenCalled(); + }); + + it("should display only the home and preference buttons", async () => { + const user = userEvent.setup(); + mocked(useRoomListHeaderViewModel).mockReturnValue({ + ...defaultValue, + canInviteInSpace: false, + canAccessSpaceSettings: false, + }); + + render(); + await user.click(screen.getByRole("button", { name: "Add" })); + + expect(screen.queryByRole("menuitem", { name: "Invite" })).toBeNull(); + expect(screen.queryByRole("menuitem", { name: "Space Setting" })).toBeNull(); + }); }); }); diff --git a/test/unit-tests/components/views/rooms/RoomListView/__snapshots__/RoomListHeaderView-test.tsx.snap b/test/unit-tests/components/views/rooms/RoomListView/__snapshots__/RoomListHeaderView-test.tsx.snap index 36ae6d2b10a..eee7df2dd75 100644 --- a/test/unit-tests/components/views/rooms/RoomListView/__snapshots__/RoomListHeaderView-test.tsx.snap +++ b/test/unit-tests/components/views/rooms/RoomListView/__snapshots__/RoomListHeaderView-test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[` should display the compose menu 1`] = ` +exports[` compose menu should display the compose menu 1`] = `
should display the compose menu 1`] = ` data-testid="room-list-header" style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: space-between; --mx-flex-gap: 0;" > -

- title -

+
+

+ title +

+ +
+ +
+
+`; + +exports[` compose menu should not display the compose menu 1`] = ` + +
+
+

+ title +

+ +
+
+
+`; + +exports[` space menu should display the space menu 1`] = ` + +
+
+

+ title +

+ +
`; diff --git a/test/unit-tests/components/views/rooms/RoomListView/__snapshots__/RoomListView-test.tsx.snap b/test/unit-tests/components/views/rooms/RoomListView/__snapshots__/RoomListView-test.tsx.snap index 669dffd48a9..ac40cc465cf 100644 --- a/test/unit-tests/components/views/rooms/RoomListView/__snapshots__/RoomListView-test.tsx.snap +++ b/test/unit-tests/components/views/rooms/RoomListView/__snapshots__/RoomListView-test.tsx.snap @@ -12,9 +12,14 @@ exports[` should not render the RoomListSearch component when UI data-testid="room-list-header" style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: space-between; --mx-flex-gap: 0;" > -

- Home -

+
+

+ Home +

+
@@ -88,9 +93,14 @@ exports[` should render the RoomListSearch component when UIComp data-testid="room-list-header" style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: space-between; --mx-flex-gap: 0;" > -

- Home -

+
+

+ Home +

+