From cc15cd63cc415c0a2ab838a537d5f08a48ab3b12 Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Tue, 12 Apr 2022 12:07:28 +0100
Subject: [PATCH 1/3] Prevent soft crash around room list header context menu
when space changes under it
---
src/components/views/rooms/RoomListHeader.tsx | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/src/components/views/rooms/RoomListHeader.tsx b/src/components/views/rooms/RoomListHeader.tsx
index 6b97e8a660a..076a6b483d4 100644
--- a/src/components/views/rooms/RoomListHeader.tsx
+++ b/src/components/views/rooms/RoomListHeader.tsx
@@ -140,6 +140,13 @@ const RoomListHeader = ({ onVisibilityChange }: IProps) => {
}
});
+ useEffect(() => {
+ if (mainMenuDisplayed && !activeSpace && spaceKey !== MetaSpace.Home) {
+ // Space changed under us and we no longer has a main menu to draw
+ closeMainMenu();
+ }
+ }, [closeMainMenu, activeSpace, spaceKey, mainMenuDisplayed]);
+
// we pass null for the queryLength to inhibit the metrics hook for when there is no filterCondition
useWebSearchMetrics(count, filterCondition ? filterCondition.search.length : null, false);
@@ -168,7 +175,7 @@ const RoomListHeader = ({ onVisibilityChange }: IProps) => {
const canShowPlusMenu = canCreateRooms || canExploreRooms || activeSpace;
let contextMenu: JSX.Element;
- if (mainMenuDisplayed) {
+ if (mainMenuDisplayed && mainMenuHandle.current) {
let ContextMenuComponent;
if (activeSpace) {
ContextMenuComponent = SpaceContextMenu;
From c6e8f0a68d44f6b7f05646e34d37551e6b99d946 Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Tue, 12 Apr 2022 12:10:49 +0100
Subject: [PATCH 2/3] Tidy
---
src/components/views/rooms/RoomListHeader.tsx | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/src/components/views/rooms/RoomListHeader.tsx b/src/components/views/rooms/RoomListHeader.tsx
index 076a6b483d4..6a892df2386 100644
--- a/src/components/views/rooms/RoomListHeader.tsx
+++ b/src/components/views/rooms/RoomListHeader.tsx
@@ -140,12 +140,14 @@ const RoomListHeader = ({ onVisibilityChange }: IProps) => {
}
});
+ const canShowMainMenu = activeSpace || spaceKey === MetaSpace.Home;
+
useEffect(() => {
- if (mainMenuDisplayed && !activeSpace && spaceKey !== MetaSpace.Home) {
+ if (mainMenuDisplayed && !canShowMainMenu) {
// Space changed under us and we no longer has a main menu to draw
closeMainMenu();
}
- }, [closeMainMenu, activeSpace, spaceKey, mainMenuDisplayed]);
+ }, [closeMainMenu, canShowMainMenu, mainMenuDisplayed]);
// we pass null for the queryLength to inhibit the metrics hook for when there is no filterCondition
useWebSearchMetrics(count, filterCondition ? filterCondition.search.length : null, false);
@@ -371,7 +373,7 @@ const RoomListHeader = ({ onVisibilityChange }: IProps) => {
.join("\n");
let contextMenuButton: JSX.Element =
{ title }
;
- if (activeSpace || spaceKey === MetaSpace.Home) {
+ if (canShowMainMenu) {
contextMenuButton =
Date: Tue, 12 Apr 2022 15:16:16 +0100
Subject: [PATCH 3/3] Add tests
---
.../views/rooms/RoomListHeader-test.tsx | 136 ++++++++++++++++++
test/test-utils/test-utils.ts | 1 +
2 files changed, 137 insertions(+)
create mode 100644 test/components/views/rooms/RoomListHeader-test.tsx
diff --git a/test/components/views/rooms/RoomListHeader-test.tsx b/test/components/views/rooms/RoomListHeader-test.tsx
new file mode 100644
index 00000000000..1baabebbb8a
--- /dev/null
+++ b/test/components/views/rooms/RoomListHeader-test.tsx
@@ -0,0 +1,136 @@
+/*
+Copyright 2022 The Matrix.org Foundation C.I.C.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import React from 'react';
+import { mount } from 'enzyme';
+import { MatrixClient } from 'matrix-js-sdk/src/client';
+import { act } from "react-dom/test-utils";
+
+import SpaceStore from "../../../../src/stores/spaces/SpaceStore";
+import { MetaSpace } from "../../../../src/stores/spaces";
+import RoomListHeader from "../../../../src/components/views/rooms/RoomListHeader";
+import * as testUtils from "../../../test-utils";
+import { createTestClient, mkSpace } from "../../../test-utils";
+import DMRoomMap from "../../../../src/utils/DMRoomMap";
+import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
+import SettingsStore from "../../../../src/settings/SettingsStore";
+import { SettingLevel } from "../../../../src/settings/SettingLevel";
+
+describe("RoomListHeader", () => {
+ let client: MatrixClient;
+
+ beforeEach(() => {
+ client = createTestClient();
+ });
+
+ it("renders a main menu for the home space", () => {
+ act(() => {
+ SpaceStore.instance.setActiveSpace(MetaSpace.Home);
+ });
+
+ const wrapper = mount(
+
+ );
+
+ expect(wrapper.text()).toBe("Home");
+ act(() => {
+ wrapper.find('[aria-label="Home options"]').hostNodes().simulate("click");
+ });
+ wrapper.update();
+
+ const menu = wrapper.find(".mx_IconizedContextMenu");
+ const items = menu.find(".mx_IconizedContextMenu_item").hostNodes();
+ expect(items).toHaveLength(1);
+ expect(items.at(0).text()).toBe("Show all rooms");
+ });
+
+ it("renders a main menu for spaces", async () => {
+ const testSpace = mkSpace(client, "!space:server");
+ testSpace.name = "Test Space";
+ client.getRoom = () => testSpace;
+
+ const getUserIdForRoomId = jest.fn();
+ const getDMRoomsForUserId = jest.fn();
+ // @ts-ignore
+ DMRoomMap.sharedInstance = { getUserIdForRoomId, getDMRoomsForUserId };
+
+ await testUtils.setupAsyncStoreWithClient(SpaceStore.instance, client);
+ act(() => {
+ SpaceStore.instance.setActiveSpace(testSpace.roomId);
+ });
+
+ const wrapper = mount(
+
+ );
+
+ expect(wrapper.text()).toBe("Test Space");
+ act(() => {
+ wrapper.find('[aria-label="Test Space menu"]').hostNodes().simulate("click");
+ });
+ wrapper.update();
+
+ const menu = wrapper.find(".mx_IconizedContextMenu");
+ const items = menu.find(".mx_IconizedContextMenu_item").hostNodes();
+ expect(items).toHaveLength(6);
+ expect(items.at(0).text()).toBe("Space home");
+ expect(items.at(1).text()).toBe("Manage & explore rooms");
+ expect(items.at(2).text()).toBe("Preferences");
+ expect(items.at(3).text()).toBe("Settings");
+ expect(items.at(4).text()).toBe("Room");
+ expect(items.at(4).text()).toBe("Room");
+ });
+
+ it("closes menu if space changes from under it", async () => {
+ await SettingsStore.setValue("Spaces.enabledMetaSpaces", null, SettingLevel.DEVICE, {
+ [MetaSpace.Home]: true,
+ [MetaSpace.Favourites]: true,
+ });
+
+ const testSpace = mkSpace(client, "!space:server");
+ testSpace.name = "Test Space";
+ client.getRoom = () => testSpace;
+
+ const getUserIdForRoomId = jest.fn();
+ const getDMRoomsForUserId = jest.fn();
+ // @ts-ignore
+ DMRoomMap.sharedInstance = { getUserIdForRoomId, getDMRoomsForUserId };
+
+ await testUtils.setupAsyncStoreWithClient(SpaceStore.instance, client);
+ act(() => {
+ SpaceStore.instance.setActiveSpace(testSpace.roomId);
+ });
+
+ const wrapper = mount(
+
+ );
+
+ expect(wrapper.text()).toBe("Test Space");
+ act(() => {
+ wrapper.find('[aria-label="Test Space menu"]').hostNodes().simulate("click");
+ });
+ wrapper.update();
+
+ act(() => {
+ SpaceStore.instance.setActiveSpace(MetaSpace.Favourites);
+ });
+ wrapper.update();
+
+ expect(wrapper.text()).toBe("Favourites");
+
+ const menu = wrapper.find(".mx_IconizedContextMenu");
+ expect(menu).toHaveLength(0);
+ });
+});
diff --git a/test/test-utils/test-utils.ts b/test/test-utils/test-utils.ts
index 14bd85bc5c2..fc85a825f31 100644
--- a/test/test-utils/test-utils.ts
+++ b/test/test-utils/test-utils.ts
@@ -379,6 +379,7 @@ export function mkStubRoom(roomId: string = null, name: string, client: MatrixCl
getJoinRule: jest.fn().mockReturnValue("invite"),
loadMembersIfNeeded: jest.fn(),
client,
+ canInvite: jest.fn(),
} as unknown as Room;
}