New room list: add more options menu on room list item (#29445)
* refactor(room list item): rename `RoomListCell` into `RoomListItemView` * refactor(room list item): move open room action to new room list item view model * feat(hover menu): add `hasAccessToOptionsMenu` * feat(hover menu): add to `RoomListItemViewModel` the condition to display or not the hover menu * feat(hover menu): add view model for the hover menu * feat(hover menu): add hover menu view * feat(hover menu): add hover menu to room list item * feat(hover menu): update i18n * test(view model list item): update test and add test to `showHoverMenu` * test(room list): update snapshot * test(room list item menu): add tests for view model * test(room list item menu): add tests for view * test(room list item): add tests * test(e2e): add tests for more options menu * chore: update compound web * test(e2e): fix typo
This commit is contained in:
@@ -92,7 +92,7 @@
|
|||||||
"@types/png-chunks-extract": "^1.0.2",
|
"@types/png-chunks-extract": "^1.0.2",
|
||||||
"@types/react-virtualized": "^9.21.30",
|
"@types/react-virtualized": "^9.21.30",
|
||||||
"@vector-im/compound-design-tokens": "^4.0.0",
|
"@vector-im/compound-design-tokens": "^4.0.0",
|
||||||
"@vector-im/compound-web": "^7.6.4",
|
"@vector-im/compound-web": "^7.7.2",
|
||||||
"@vector-im/matrix-wysiwyg": "2.38.2",
|
"@vector-im/matrix-wysiwyg": "2.38.2",
|
||||||
"@zxcvbn-ts/core": "^3.0.4",
|
"@zxcvbn-ts/core": "^3.0.4",
|
||||||
"@zxcvbn-ts/language-common": "^3.0.4",
|
"@zxcvbn-ts/language-common": "^3.0.4",
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { test, expect } from "../../../element-web-test";
|
|||||||
|
|
||||||
test.describe("Room list", () => {
|
test.describe("Room list", () => {
|
||||||
test.use({
|
test.use({
|
||||||
|
displayName: "Alice",
|
||||||
labsFlags: ["feature_new_room_list"],
|
labsFlags: ["feature_new_room_list"],
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -47,4 +48,33 @@ test.describe("Room list", () => {
|
|||||||
await roomListView.getByRole("gridcell", { name: "Open room room29" }).click();
|
await roomListView.getByRole("gridcell", { name: "Open room room29" }).click();
|
||||||
await expect(page.getByRole("heading", { name: "room29", level: 1 })).toBeVisible();
|
await expect(page.getByRole("heading", { name: "room29", level: 1 })).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("should open the more options menu", { tag: "@screenshot" }, async ({ page, app, user }) => {
|
||||||
|
const roomListView = getRoomList(page);
|
||||||
|
const roomItem = roomListView.getByRole("gridcell", { name: "Open room room29" });
|
||||||
|
await roomItem.hover();
|
||||||
|
|
||||||
|
await expect(roomItem).toMatchScreenshot("room-list-item-hover.png");
|
||||||
|
const roomItemMenu = roomItem.getByRole("button", { name: "More Options" });
|
||||||
|
await roomItemMenu.click();
|
||||||
|
await expect(page).toMatchScreenshot("room-list-item-open-more-options.png");
|
||||||
|
|
||||||
|
// It should make the room favourited
|
||||||
|
await page.getByRole("menuitemcheckbox", { name: "Favourited" }).click();
|
||||||
|
|
||||||
|
// Check that the room is favourited
|
||||||
|
await roomItem.hover();
|
||||||
|
await roomItemMenu.click();
|
||||||
|
await expect(page.getByRole("menuitemcheckbox", { name: "Favourited" })).toBeChecked();
|
||||||
|
// It should show the invite dialog
|
||||||
|
await page.getByRole("menuitem", { name: "invite" }).click();
|
||||||
|
await expect(page.getByRole("heading", { name: "Invite to room29" })).toBeVisible();
|
||||||
|
await app.closeDialog();
|
||||||
|
|
||||||
|
// It should leave the room
|
||||||
|
await roomItem.hover();
|
||||||
|
await roomItemMenu.click();
|
||||||
|
await page.getByRole("menuitem", { name: "leave room" }).click();
|
||||||
|
await expect(roomItem).not.toBeVisible();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 80 KiB |
@@ -271,8 +271,9 @@
|
|||||||
@import "./views/right_panel/_WidgetCard.pcss";
|
@import "./views/right_panel/_WidgetCard.pcss";
|
||||||
@import "./views/room_settings/_AliasSettings.pcss";
|
@import "./views/room_settings/_AliasSettings.pcss";
|
||||||
@import "./views/rooms/RoomListPanel/_RoomList.pcss";
|
@import "./views/rooms/RoomListPanel/_RoomList.pcss";
|
||||||
@import "./views/rooms/RoomListPanel/_RoomListCell.pcss";
|
|
||||||
@import "./views/rooms/RoomListPanel/_RoomListHeaderView.pcss";
|
@import "./views/rooms/RoomListPanel/_RoomListHeaderView.pcss";
|
||||||
|
@import "./views/rooms/RoomListPanel/_RoomListItemMenuView.pcss";
|
||||||
|
@import "./views/rooms/RoomListPanel/_RoomListItemView.pcss";
|
||||||
@import "./views/rooms/RoomListPanel/_RoomListPanel.pcss";
|
@import "./views/rooms/RoomListPanel/_RoomListPanel.pcss";
|
||||||
@import "./views/rooms/RoomListPanel/_RoomListPrimaryFilters.pcss";
|
@import "./views/rooms/RoomListPanel/_RoomListPrimaryFilters.pcss";
|
||||||
@import "./views/rooms/RoomListPanel/_RoomListSearch.pcss";
|
@import "./views/rooms/RoomListPanel/_RoomListSearch.pcss";
|
||||||
|
|||||||
12
res/css/views/rooms/RoomListPanel/_RoomListItemMenuView.pcss
Normal file
12
res/css/views/rooms/RoomListPanel/_RoomListItemMenuView.pcss
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2025 New Vector Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.mx_RoomListItemMenuView {
|
||||||
|
svg {
|
||||||
|
fill: var(--cpd-color-icon-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The RoomCell has the following structure:
|
* The RoomListItemView has the following structure:
|
||||||
* button----------------------------------------|
|
* button----------------------------------------|
|
||||||
* | <-12px-> container--------------------------|
|
* | <-12px-> container--------------------------|
|
||||||
* | | room avatar <-12px-> content-----|
|
* | | room avatar <-12px-> content-----|
|
||||||
@@ -14,19 +14,20 @@
|
|||||||
* | | | ----------| <-- border
|
* | | | ----------| <-- border
|
||||||
* |---------------------------------------------|
|
* |---------------------------------------------|
|
||||||
*/
|
*/
|
||||||
.mx_RoomListCell {
|
.mx_RoomListItemView {
|
||||||
all: unset;
|
all: unset;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: var(--cpd-color-bg-action-secondary-hovered);
|
background-color: var(--cpd-color-bg-action-secondary-hovered);
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomListCell_container {
|
.mx_RoomListItemView_container {
|
||||||
padding-left: var(--cpd-space-3x);
|
padding-left: var(--cpd-space-3x);
|
||||||
font: var(--cpd-font-body-md-regular);
|
font: var(--cpd-font-body-md-regular);
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
.mx_RoomListCell_content {
|
.mx_RoomListItemView_content {
|
||||||
|
padding-right: var(--cpd-space-3x);
|
||||||
height: 100%;
|
height: 100%;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
/* The border is only under the room name and the future hover menu */
|
/* The border is only under the room name and the future hover menu */
|
||||||
@@ -42,3 +43,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_RoomListItemView_menu_open {
|
||||||
|
background-color: var(--cpd-color-bg-action-secondary-hovered);
|
||||||
|
}
|
||||||
180
src/components/viewmodels/roomlist/RoomListItemMenuViewModel.tsx
Normal file
180
src/components/viewmodels/roomlist/RoomListItemMenuViewModel.tsx
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2025 New Vector Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { useCallback } from "react";
|
||||||
|
import { type Room, RoomEvent } from "matrix-js-sdk/src/matrix";
|
||||||
|
|
||||||
|
import { useMatrixClientContext } from "../../../contexts/MatrixClientContext";
|
||||||
|
import { useEventEmitterState } from "../../../hooks/useEventEmitter";
|
||||||
|
import { useUnreadNotifications } from "../../../hooks/useUnreadNotifications";
|
||||||
|
import { hasAccessToOptionsMenu } from "./utils";
|
||||||
|
import DMRoomMap from "../../../utils/DMRoomMap";
|
||||||
|
import { DefaultTagID } from "../../../stores/room-list/models";
|
||||||
|
import { NotificationLevel } from "../../../stores/notifications/NotificationLevel";
|
||||||
|
import { shouldShowComponent } from "../../../customisations/helpers/UIComponents";
|
||||||
|
import { UIComponent } from "../../../settings/UIFeature";
|
||||||
|
import dispatcher from "../../../dispatcher/dispatcher";
|
||||||
|
import { clearRoomNotification, setMarkedUnreadState } from "../../../utils/notifications";
|
||||||
|
import PosthogTrackers from "../../../PosthogTrackers";
|
||||||
|
import { tagRoom } from "../../../utils/room/tagRoom";
|
||||||
|
|
||||||
|
export interface RoomListItemMenuViewState {
|
||||||
|
/**
|
||||||
|
* Whether the more options menu should be shown.
|
||||||
|
*/
|
||||||
|
showMoreOptionsMenu: boolean;
|
||||||
|
/**
|
||||||
|
* Whether the room is a favourite room.
|
||||||
|
*/
|
||||||
|
isFavourite: boolean;
|
||||||
|
/**
|
||||||
|
* Can invite other user's in the room.
|
||||||
|
*/
|
||||||
|
canInvite: boolean;
|
||||||
|
/**
|
||||||
|
* Can copy the room link.
|
||||||
|
*/
|
||||||
|
canCopyRoomLink: boolean;
|
||||||
|
/**
|
||||||
|
* Can mark the room as read.
|
||||||
|
*/
|
||||||
|
canMarkAsRead: boolean;
|
||||||
|
/**
|
||||||
|
* Can mark the room as unread.
|
||||||
|
*/
|
||||||
|
canMarkAsUnread: boolean;
|
||||||
|
/**
|
||||||
|
* Mark the room as read.
|
||||||
|
* @param evt
|
||||||
|
*/
|
||||||
|
markAsRead: (evt: Event) => void;
|
||||||
|
/**
|
||||||
|
* Mark the room as unread.
|
||||||
|
* @param evt
|
||||||
|
*/
|
||||||
|
markAsUnread: (evt: Event) => void;
|
||||||
|
/**
|
||||||
|
* Toggle the room as favourite.
|
||||||
|
* @param evt
|
||||||
|
*/
|
||||||
|
toggleFavorite: (evt: Event) => void;
|
||||||
|
/**
|
||||||
|
* Toggle the room as low priority.
|
||||||
|
*/
|
||||||
|
toggleLowPriority: () => void;
|
||||||
|
/**
|
||||||
|
* Invite other users in the room.
|
||||||
|
* @param evt
|
||||||
|
*/
|
||||||
|
invite: (evt: Event) => void;
|
||||||
|
/**
|
||||||
|
* Copy the room link in the clipboard.
|
||||||
|
* @param evt
|
||||||
|
*/
|
||||||
|
copyRoomLink: (evt: Event) => void;
|
||||||
|
/**
|
||||||
|
* Leave the room.
|
||||||
|
* @param evt
|
||||||
|
*/
|
||||||
|
leaveRoom: (evt: Event) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useRoomListItemMenuViewModel(room: Room): RoomListItemMenuViewState {
|
||||||
|
const matrixClient = useMatrixClientContext();
|
||||||
|
const roomTags = useEventEmitterState(room, RoomEvent.Tags, () => room.tags);
|
||||||
|
const { level: notificationLevel } = useUnreadNotifications(room);
|
||||||
|
|
||||||
|
const showMoreOptionsMenu = hasAccessToOptionsMenu(room);
|
||||||
|
|
||||||
|
const isDm = Boolean(DMRoomMap.shared().getUserIdForRoomId(room.roomId));
|
||||||
|
const isFavourite = Boolean(roomTags[DefaultTagID.Favourite]);
|
||||||
|
const isArchived = Boolean(roomTags[DefaultTagID.Archived]);
|
||||||
|
|
||||||
|
const canMarkAsRead = notificationLevel > NotificationLevel.None;
|
||||||
|
const canMarkAsUnread = !canMarkAsRead && !isArchived;
|
||||||
|
|
||||||
|
const canInvite =
|
||||||
|
room.canInvite(matrixClient.getUserId()!) && !isDm && shouldShowComponent(UIComponent.InviteUsers);
|
||||||
|
const canCopyRoomLink = !isDm;
|
||||||
|
|
||||||
|
// Actions
|
||||||
|
|
||||||
|
const markAsRead = useCallback(
|
||||||
|
async (evt: Event): Promise<void> => {
|
||||||
|
await clearRoomNotification(room, matrixClient);
|
||||||
|
PosthogTrackers.trackInteraction("WebRoomListRoomTileContextMenuMarkRead", evt);
|
||||||
|
},
|
||||||
|
[room, matrixClient],
|
||||||
|
);
|
||||||
|
|
||||||
|
const markAsUnread = useCallback(
|
||||||
|
async (evt: Event): Promise<void> => {
|
||||||
|
await setMarkedUnreadState(room, matrixClient, true);
|
||||||
|
PosthogTrackers.trackInteraction("WebRoomListRoomTileContextMenuMarkUnread", evt);
|
||||||
|
},
|
||||||
|
[room, matrixClient],
|
||||||
|
);
|
||||||
|
|
||||||
|
const toggleFavorite = useCallback(
|
||||||
|
(evt: Event): void => {
|
||||||
|
tagRoom(room, DefaultTagID.Favourite);
|
||||||
|
PosthogTrackers.trackInteraction("WebRoomListRoomTileContextMenuFavouriteToggle", evt);
|
||||||
|
},
|
||||||
|
[room],
|
||||||
|
);
|
||||||
|
|
||||||
|
const toggleLowPriority = useCallback((): void => tagRoom(room, DefaultTagID.LowPriority), [room]);
|
||||||
|
|
||||||
|
const invite = useCallback(
|
||||||
|
(evt: Event): void => {
|
||||||
|
dispatcher.dispatch({
|
||||||
|
action: "view_invite",
|
||||||
|
roomId: room.roomId,
|
||||||
|
});
|
||||||
|
PosthogTrackers.trackInteraction("WebRoomListRoomTileContextMenuInviteItem", evt);
|
||||||
|
},
|
||||||
|
[room],
|
||||||
|
);
|
||||||
|
|
||||||
|
const copyRoomLink = useCallback(
|
||||||
|
(evt: Event): void => {
|
||||||
|
dispatcher.dispatch({
|
||||||
|
action: "copy_room",
|
||||||
|
room_id: room.roomId,
|
||||||
|
});
|
||||||
|
PosthogTrackers.trackInteraction("WebRoomListRoomTileContextMenuFavouriteToggle", evt);
|
||||||
|
},
|
||||||
|
[room],
|
||||||
|
);
|
||||||
|
|
||||||
|
const leaveRoom = useCallback(
|
||||||
|
(evt: Event): void => {
|
||||||
|
dispatcher.dispatch({
|
||||||
|
action: isArchived ? "forget_room" : "leave_room",
|
||||||
|
room_id: room.roomId,
|
||||||
|
});
|
||||||
|
PosthogTrackers.trackInteraction("WebRoomListRoomTileContextMenuLeaveItem", evt);
|
||||||
|
},
|
||||||
|
[room, isArchived],
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
showMoreOptionsMenu,
|
||||||
|
isFavourite,
|
||||||
|
canInvite,
|
||||||
|
canCopyRoomLink,
|
||||||
|
canMarkAsRead,
|
||||||
|
canMarkAsUnread,
|
||||||
|
markAsRead,
|
||||||
|
markAsUnread,
|
||||||
|
toggleFavorite,
|
||||||
|
toggleLowPriority,
|
||||||
|
invite,
|
||||||
|
copyRoomLink,
|
||||||
|
leaveRoom,
|
||||||
|
};
|
||||||
|
}
|
||||||
49
src/components/viewmodels/roomlist/RoomListItemViewModel.tsx
Normal file
49
src/components/viewmodels/roomlist/RoomListItemViewModel.tsx
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2025 New Vector Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { useCallback } from "react";
|
||||||
|
import { type Room } from "matrix-js-sdk/src/matrix";
|
||||||
|
|
||||||
|
import dispatcher from "../../../dispatcher/dispatcher";
|
||||||
|
import type { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
|
||||||
|
import { Action } from "../../../dispatcher/actions";
|
||||||
|
import { hasAccessToOptionsMenu } from "./utils";
|
||||||
|
|
||||||
|
export interface RoomListItemViewState {
|
||||||
|
/**
|
||||||
|
* Whether the hover menu should be shown.
|
||||||
|
*/
|
||||||
|
showHoverMenu: boolean;
|
||||||
|
/**
|
||||||
|
* Open the room having given roomId.
|
||||||
|
*/
|
||||||
|
openRoom: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* View model for the room list item
|
||||||
|
* @see {@link RoomListItemViewState} for more information about what this view model returns.
|
||||||
|
*/
|
||||||
|
export function useRoomListItemViewModel(room: Room): RoomListItemViewState {
|
||||||
|
// incoming: Check notification menu rights
|
||||||
|
const showHoverMenu = hasAccessToOptionsMenu(room);
|
||||||
|
|
||||||
|
// Actions
|
||||||
|
|
||||||
|
const openRoom = useCallback((): void => {
|
||||||
|
dispatcher.dispatch<ViewRoomPayload>({
|
||||||
|
action: Action.ViewRoom,
|
||||||
|
room_id: room.roomId,
|
||||||
|
metricsTrigger: "RoomList",
|
||||||
|
});
|
||||||
|
}, [room]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
showHoverMenu,
|
||||||
|
openRoom,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -5,12 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
|
|||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { useCallback } from "react";
|
|
||||||
|
|
||||||
import type { Room } from "matrix-js-sdk/src/matrix";
|
import type { Room } from "matrix-js-sdk/src/matrix";
|
||||||
import type { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
|
|
||||||
import dispatcher from "../../../dispatcher/dispatcher";
|
|
||||||
import { Action } from "../../../dispatcher/actions";
|
|
||||||
import { type PrimaryFilter, type SecondaryFilters, useFilteredRooms } from "./useFilteredRooms";
|
import { type PrimaryFilter, type SecondaryFilters, useFilteredRooms } from "./useFilteredRooms";
|
||||||
import { type SortOption, useSorter } from "./useSorter";
|
import { type SortOption, useSorter } from "./useSorter";
|
||||||
|
|
||||||
@@ -19,12 +14,6 @@ export interface RoomListViewState {
|
|||||||
* A list of rooms to be displayed in the left panel.
|
* A list of rooms to be displayed in the left panel.
|
||||||
*/
|
*/
|
||||||
rooms: Room[];
|
rooms: Room[];
|
||||||
|
|
||||||
/**
|
|
||||||
* Open the room having given roomId.
|
|
||||||
*/
|
|
||||||
openRoom: (roomId: string) => void;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A list of objects that provide the view enough information
|
* A list of objects that provide the view enough information
|
||||||
* to render primary room filters.
|
* to render primary room filters.
|
||||||
@@ -60,17 +49,8 @@ export function useRoomListViewModel(): RoomListViewState {
|
|||||||
const { primaryFilters, rooms, activateSecondaryFilter, activeSecondaryFilter } = useFilteredRooms();
|
const { primaryFilters, rooms, activateSecondaryFilter, activeSecondaryFilter } = useFilteredRooms();
|
||||||
const { activeSortOption, sort } = useSorter();
|
const { activeSortOption, sort } = useSorter();
|
||||||
|
|
||||||
const openRoom = useCallback((roomId: string): void => {
|
|
||||||
dispatcher.dispatch<ViewRoomPayload>({
|
|
||||||
action: Action.ViewRoom,
|
|
||||||
room_id: roomId,
|
|
||||||
metricsTrigger: "RoomList",
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
rooms,
|
rooms,
|
||||||
openRoom,
|
|
||||||
primaryFilters,
|
primaryFilters,
|
||||||
activateSecondaryFilter,
|
activateSecondaryFilter,
|
||||||
activeSecondaryFilter,
|
activeSecondaryFilter,
|
||||||
|
|||||||
25
src/components/viewmodels/roomlist/utils.ts
Normal file
25
src/components/viewmodels/roomlist/utils.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2025 New Vector Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { type Room, KnownMembership } from "matrix-js-sdk/src/matrix";
|
||||||
|
|
||||||
|
import { isKnockDenied } from "../../../utils/membership";
|
||||||
|
import { shouldShowComponent } from "../../../customisations/helpers/UIComponents";
|
||||||
|
import { UIComponent } from "../../../settings/UIFeature";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the user has access to the options menu.
|
||||||
|
* @param room
|
||||||
|
*/
|
||||||
|
export function hasAccessToOptionsMenu(room: Room): boolean {
|
||||||
|
return (
|
||||||
|
room.getMyMembership() === KnownMembership.Invite ||
|
||||||
|
(room.getMyMembership() !== KnownMembership.Knock &&
|
||||||
|
!isKnockDenied(room) &&
|
||||||
|
shouldShowComponent(UIComponent.RoomOptionsMenu))
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -10,7 +10,7 @@ import { AutoSizer, List, type ListRowProps } from "react-virtualized";
|
|||||||
|
|
||||||
import { type RoomListViewState } from "../../../viewmodels/roomlist/RoomListViewModel";
|
import { type RoomListViewState } from "../../../viewmodels/roomlist/RoomListViewModel";
|
||||||
import { _t } from "../../../../languageHandler";
|
import { _t } from "../../../../languageHandler";
|
||||||
import { RoomListCell } from "./RoomListCell";
|
import { RoomListItemView } from "./RoomListItemView";
|
||||||
|
|
||||||
interface RoomListProps {
|
interface RoomListProps {
|
||||||
/**
|
/**
|
||||||
@@ -22,12 +22,10 @@ interface RoomListProps {
|
|||||||
/**
|
/**
|
||||||
* A virtualized list of rooms.
|
* A virtualized list of rooms.
|
||||||
*/
|
*/
|
||||||
export function RoomList({ vm: { rooms, openRoom } }: RoomListProps): JSX.Element {
|
export function RoomList({ vm: { rooms } }: RoomListProps): JSX.Element {
|
||||||
const roomRendererMemoized = useCallback(
|
const roomRendererMemoized = useCallback(
|
||||||
({ key, index, style }: ListRowProps) => (
|
({ key, index, style }: ListRowProps) => <RoomListItemView room={rooms[index]} key={key} style={style} />,
|
||||||
<RoomListCell room={rooms[index]} key={key} style={style} onClick={() => openRoom(rooms[index].roomId)} />
|
[rooms],
|
||||||
),
|
|
||||||
[rooms, openRoom],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// The first div is needed to make the virtualized list take all the remaining space and scroll correctly
|
// The first div is needed to make the virtualized list take all the remaining space and scroll correctly
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2025 New Vector Ltd.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
|
||||||
* Please see LICENSE files in the repository root for full details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import React, { type JSX } from "react";
|
|
||||||
import { type Room } from "matrix-js-sdk/src/matrix";
|
|
||||||
|
|
||||||
import { _t } from "../../../../languageHandler";
|
|
||||||
import { Flex } from "../../../utils/Flex";
|
|
||||||
import DecoratedRoomAvatar from "../../avatars/DecoratedRoomAvatar";
|
|
||||||
|
|
||||||
interface RoomListCellProps extends React.HTMLAttributes<HTMLButtonElement> {
|
|
||||||
/**
|
|
||||||
* The room to display
|
|
||||||
*/
|
|
||||||
room: Room;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A cell in the room list
|
|
||||||
*/
|
|
||||||
export function RoomListCell({ room, ...props }: RoomListCellProps): JSX.Element {
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
className="mx_RoomListCell"
|
|
||||||
type="button"
|
|
||||||
aria-label={_t("room_list|room|open_room", { roomName: room.name })}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
{/* We need this extra div between the button and the content in order to add a padding which is not messing with the virtualized list */}
|
|
||||||
<Flex className="mx_RoomListCell_container" gap="var(--cpd-space-3x)" align="center">
|
|
||||||
<DecoratedRoomAvatar room={room} size="32px" />
|
|
||||||
<Flex className="mx_RoomListCell_content" align="center">
|
|
||||||
{/* We truncate the room name when too long. Title here is to show the full name on hover */}
|
|
||||||
<span title={room.name}>{room.name}</span>
|
|
||||||
{/* Future hover menu et notification badges */}
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,154 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2025 New Vector Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { type ComponentProps, forwardRef, type JSX, useState } from "react";
|
||||||
|
import { IconButton, Menu, MenuItem, Separator, ToggleMenuItem, Tooltip } from "@vector-im/compound-web";
|
||||||
|
import MarkAsReadIcon from "@vector-im/compound-design-tokens/assets/web/icons/mark-as-read";
|
||||||
|
import MarkAsUnreadIcon from "@vector-im/compound-design-tokens/assets/web/icons/mark-as-unread";
|
||||||
|
import FavouriteIcon from "@vector-im/compound-design-tokens/assets/web/icons/favourite";
|
||||||
|
import ArrowDownIcon from "@vector-im/compound-design-tokens/assets/web/icons/arrow-down";
|
||||||
|
import UserAddIcon from "@vector-im/compound-design-tokens/assets/web/icons/user-add";
|
||||||
|
import LinkIcon from "@vector-im/compound-design-tokens/assets/web/icons/link";
|
||||||
|
import LeaveIcon from "@vector-im/compound-design-tokens/assets/web/icons/leave";
|
||||||
|
import OverflowIcon from "@vector-im/compound-design-tokens/assets/web/icons/overflow-horizontal";
|
||||||
|
import { type Room } from "matrix-js-sdk/src/matrix";
|
||||||
|
|
||||||
|
import { _t } from "../../../../languageHandler";
|
||||||
|
import { Flex } from "../../../utils/Flex";
|
||||||
|
import {
|
||||||
|
type RoomListItemMenuViewState,
|
||||||
|
useRoomListItemMenuViewModel,
|
||||||
|
} from "../../../viewmodels/roomlist/RoomListItemMenuViewModel";
|
||||||
|
|
||||||
|
interface RoomListItemMenuViewProps {
|
||||||
|
/**
|
||||||
|
* The room to display the menu for.
|
||||||
|
*/
|
||||||
|
room: Room;
|
||||||
|
/**
|
||||||
|
* Set the menu open state.
|
||||||
|
* @param isOpen
|
||||||
|
*/
|
||||||
|
setMenuOpen: (isOpen: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A view for the room list item menu.
|
||||||
|
*/
|
||||||
|
export function RoomListItemMenuView({ room, setMenuOpen }: RoomListItemMenuViewProps): JSX.Element {
|
||||||
|
const vm = useRoomListItemMenuViewModel(room);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex className="mx_RoomListItemMenuView" align="center" gap="var(--cpd-space-1x)">
|
||||||
|
{vm.showMoreOptionsMenu && <MoreOptionsMenu setMenuOpen={setMenuOpen} vm={vm} />}
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MoreOptionsMenuProps {
|
||||||
|
/**
|
||||||
|
* The view model state for the menu.
|
||||||
|
*/
|
||||||
|
vm: RoomListItemMenuViewState;
|
||||||
|
/**
|
||||||
|
* Set the menu open state.
|
||||||
|
* @param isOpen
|
||||||
|
*/
|
||||||
|
setMenuOpen: (isOpen: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The more options menu for the room list item.
|
||||||
|
*/
|
||||||
|
function MoreOptionsMenu({ vm, setMenuOpen }: MoreOptionsMenuProps): JSX.Element {
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Menu
|
||||||
|
open={open}
|
||||||
|
onOpenChange={(isOpen) => {
|
||||||
|
setOpen(isOpen);
|
||||||
|
setMenuOpen(isOpen);
|
||||||
|
}}
|
||||||
|
title={_t("room_list|room|more_options")}
|
||||||
|
showTitle={false}
|
||||||
|
align="start"
|
||||||
|
trigger={<MoreOptionsButton />}
|
||||||
|
>
|
||||||
|
{vm.canMarkAsRead && (
|
||||||
|
<MenuItem
|
||||||
|
Icon={MarkAsReadIcon}
|
||||||
|
label={_t("room_list|more_options|mark_read")}
|
||||||
|
onSelect={vm.markAsRead}
|
||||||
|
onClick={(evt) => evt.stopPropagation()}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{vm.canMarkAsUnread && (
|
||||||
|
<MenuItem
|
||||||
|
Icon={MarkAsUnreadIcon}
|
||||||
|
label={_t("room_list|more_options|mark_unread")}
|
||||||
|
onSelect={vm.markAsUnread}
|
||||||
|
onClick={(evt) => evt.stopPropagation()}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<ToggleMenuItem
|
||||||
|
checked={vm.isFavourite}
|
||||||
|
Icon={FavouriteIcon}
|
||||||
|
label={_t("room_list|more_options|favourited")}
|
||||||
|
onSelect={vm.toggleFavorite}
|
||||||
|
onClick={(evt) => evt.stopPropagation()}
|
||||||
|
/>
|
||||||
|
<MenuItem
|
||||||
|
Icon={ArrowDownIcon}
|
||||||
|
label={_t("room_list|more_options|low_priority")}
|
||||||
|
onSelect={vm.toggleLowPriority}
|
||||||
|
onClick={(evt) => evt.stopPropagation()}
|
||||||
|
/>
|
||||||
|
{vm.canInvite && (
|
||||||
|
<MenuItem
|
||||||
|
Icon={UserAddIcon}
|
||||||
|
label={_t("action|invite")}
|
||||||
|
onSelect={vm.invite}
|
||||||
|
onClick={(evt) => evt.stopPropagation()}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{vm.canCopyRoomLink && (
|
||||||
|
<MenuItem
|
||||||
|
Icon={LinkIcon}
|
||||||
|
label={_t("room_list|more_options|copy_link")}
|
||||||
|
onSelect={vm.copyRoomLink}
|
||||||
|
onClick={(evt) => evt.stopPropagation()}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Separator />
|
||||||
|
<MenuItem
|
||||||
|
kind="critical"
|
||||||
|
Icon={LeaveIcon}
|
||||||
|
label={_t("room_list|more_options|leave_room")}
|
||||||
|
onSelect={vm.leaveRoom}
|
||||||
|
onClick={(evt) => evt.stopPropagation()}
|
||||||
|
/>
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MoreOptionsButtonProps extends ComponentProps<typeof IconButton> {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A button to trigger the more options menu.
|
||||||
|
*/
|
||||||
|
export const MoreOptionsButton = forwardRef<HTMLButtonElement, MoreOptionsButtonProps>(
|
||||||
|
function MoreOptionsButton(props, ref) {
|
||||||
|
return (
|
||||||
|
<Tooltip label={_t("room_list|room|more_options")}>
|
||||||
|
<IconButton aria-label={_t("room_list|room|more_options")} {...props} ref={ref}>
|
||||||
|
<OverflowIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2025 New Vector Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { type JSX, useState } from "react";
|
||||||
|
import { type Room } from "matrix-js-sdk/src/matrix";
|
||||||
|
import classNames from "classnames";
|
||||||
|
|
||||||
|
import { useRoomListItemViewModel } from "../../../viewmodels/roomlist/RoomListItemViewModel";
|
||||||
|
import DecoratedRoomAvatar from "../../avatars/DecoratedRoomAvatar";
|
||||||
|
import { Flex } from "../../../utils/Flex";
|
||||||
|
import { _t } from "../../../../languageHandler";
|
||||||
|
import { RoomListItemMenuView } from "./RoomListItemMenuView";
|
||||||
|
|
||||||
|
interface RoomListItemViewPropsProps extends React.HTMLAttributes<HTMLButtonElement> {
|
||||||
|
/**
|
||||||
|
* The room to display
|
||||||
|
*/
|
||||||
|
room: Room;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An item in the room list
|
||||||
|
*/
|
||||||
|
export function RoomListItemView({ room, ...props }: RoomListItemViewPropsProps): JSX.Element {
|
||||||
|
const vm = useRoomListItemViewModel(room);
|
||||||
|
|
||||||
|
const [isHover, setIsHover] = useState(false);
|
||||||
|
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||||
|
// The compound menu in RoomListItemMenuView needs to be rendered when the hover menu is shown
|
||||||
|
// Using display: none; and then display:flex when hovered in CSS causes the menu to be misaligned
|
||||||
|
const showHoverDecoration = (isMenuOpen || isHover) && vm.showHoverMenu;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className={classNames("mx_RoomListItemView", {
|
||||||
|
mx_RoomListItemView_menu_open: showHoverDecoration,
|
||||||
|
})}
|
||||||
|
type="button"
|
||||||
|
aria-label={_t("room_list|room|open_room", { roomName: room.name })}
|
||||||
|
onClick={() => vm.openRoom()}
|
||||||
|
onMouseOver={() => setIsHover(true)}
|
||||||
|
onMouseOut={() => setIsHover(false)}
|
||||||
|
onFocus={() => setIsHover(true)}
|
||||||
|
onBlur={() => setIsHover(false)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{/* We need this extra div between the button and the content in order to add a padding which is not messing with the virtualized list */}
|
||||||
|
<Flex className="mx_RoomListItemView_container" gap="var(--cpd-space-3x)" align="center">
|
||||||
|
<DecoratedRoomAvatar room={room} size="32px" />
|
||||||
|
<Flex
|
||||||
|
className="mx_RoomListItemView_content"
|
||||||
|
gap="var(--cpd-space-3x)"
|
||||||
|
align="center"
|
||||||
|
justify="space-between"
|
||||||
|
>
|
||||||
|
{/* We truncate the room name when too long. Title here is to show the full name on hover */}
|
||||||
|
<span title={room.name}>{room.name}</span>
|
||||||
|
{showHoverDecoration && (
|
||||||
|
<RoomListItemMenuView
|
||||||
|
room={room}
|
||||||
|
setMenuOpen={(isOpen) => {
|
||||||
|
if (isOpen) setIsMenuOpen(isOpen);
|
||||||
|
// To avoid icon blinking when closing the menu, we delay the state update
|
||||||
|
else setTimeout(() => setIsMenuOpen(isOpen), 0);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -2112,6 +2112,14 @@
|
|||||||
"other": "Currently joining %(count)s rooms"
|
"other": "Currently joining %(count)s rooms"
|
||||||
},
|
},
|
||||||
"list_title": "Room list",
|
"list_title": "Room list",
|
||||||
|
"more_options": {
|
||||||
|
"copy_link": "Copy room link",
|
||||||
|
"favourited": "Favourited",
|
||||||
|
"leave_room": "Leave room",
|
||||||
|
"low_priority": "Low priority",
|
||||||
|
"mark_read": "Mark as read",
|
||||||
|
"mark_unread": "Mark as unread"
|
||||||
|
},
|
||||||
"notification_options": "Notification options",
|
"notification_options": "Notification options",
|
||||||
"open_space_menu": "Open space menu",
|
"open_space_menu": "Open space menu",
|
||||||
"primary_filters": "Room list filters",
|
"primary_filters": "Room list filters",
|
||||||
@@ -2120,6 +2128,7 @@
|
|||||||
"other": "Currently removing messages in %(count)s rooms"
|
"other": "Currently removing messages in %(count)s rooms"
|
||||||
},
|
},
|
||||||
"room": {
|
"room": {
|
||||||
|
"more_options": "More Options",
|
||||||
"open_room": "Open room %(roomName)s"
|
"open_room": "Open room %(roomName)s"
|
||||||
},
|
},
|
||||||
"show_less": "Show less",
|
"show_less": "Show less",
|
||||||
|
|||||||
@@ -606,7 +606,7 @@ export function mkStubRoom(
|
|||||||
getState: (): RoomState | undefined => undefined,
|
getState: (): RoomState | undefined => undefined,
|
||||||
} as unknown as EventTimeline;
|
} as unknown as EventTimeline;
|
||||||
return {
|
return {
|
||||||
canInvite: jest.fn(),
|
canInvite: jest.fn().mockReturnValue(false),
|
||||||
client,
|
client,
|
||||||
findThreadForEvent: jest.fn(),
|
findThreadForEvent: jest.fn(),
|
||||||
createThreadsTimelineSets: jest.fn().mockReturnValue(new Promise(() => {})),
|
createThreadsTimelineSets: jest.fn().mockReturnValue(new Promise(() => {})),
|
||||||
|
|||||||
@@ -0,0 +1,173 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2025 New Vector Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { renderHook } from "jest-matrix-react";
|
||||||
|
import { mocked } from "jest-mock";
|
||||||
|
import { type MatrixClient, type Room } from "matrix-js-sdk/src/matrix";
|
||||||
|
|
||||||
|
import { mkStubRoom, stubClient, withClientContextRenderOptions } from "../../../../test-utils";
|
||||||
|
import { useRoomListItemMenuViewModel } from "../../../../../src/components/viewmodels/roomlist/RoomListItemMenuViewModel";
|
||||||
|
import { hasAccessToOptionsMenu } from "../../../../../src/components/viewmodels/roomlist/utils";
|
||||||
|
import DMRoomMap from "../../../../../src/utils/DMRoomMap";
|
||||||
|
import { DefaultTagID } from "../../../../../src/stores/room-list/models";
|
||||||
|
import { useUnreadNotifications } from "../../../../../src/hooks/useUnreadNotifications";
|
||||||
|
import { NotificationLevel } from "../../../../../src/stores/notifications/NotificationLevel";
|
||||||
|
import { clearRoomNotification, setMarkedUnreadState } from "../../../../../src/utils/notifications";
|
||||||
|
import { tagRoom } from "../../../../../src/utils/room/tagRoom";
|
||||||
|
import dispatcher from "../../../../../src/dispatcher/dispatcher";
|
||||||
|
|
||||||
|
jest.mock("../../../../../src/components/viewmodels/roomlist/utils", () => ({
|
||||||
|
hasAccessToOptionsMenu: jest.fn().mockReturnValue(false),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock("../../../../../src/hooks/useUnreadNotifications", () => ({
|
||||||
|
useUnreadNotifications: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock("../../../../../src/utils/notifications", () => ({
|
||||||
|
clearRoomNotification: jest.fn(),
|
||||||
|
setMarkedUnreadState: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock("../../../../../src/utils/room/tagRoom", () => ({
|
||||||
|
tagRoom: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe("RoomListItemMenuViewModel", () => {
|
||||||
|
let matrixClient: MatrixClient;
|
||||||
|
let room: Room;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
matrixClient = stubClient();
|
||||||
|
room = mkStubRoom("roomId", "roomName", matrixClient);
|
||||||
|
|
||||||
|
DMRoomMap.makeShared(matrixClient);
|
||||||
|
jest.spyOn(DMRoomMap.shared(), "getUserIdForRoomId").mockReturnValue(null);
|
||||||
|
|
||||||
|
mocked(useUnreadNotifications).mockReturnValue({ symbol: null, count: 0, level: NotificationLevel.None });
|
||||||
|
jest.spyOn(dispatcher, "dispatch");
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.resetAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
function render() {
|
||||||
|
return renderHook(() => useRoomListItemMenuViewModel(room), withClientContextRenderOptions(matrixClient));
|
||||||
|
}
|
||||||
|
|
||||||
|
it("default", () => {
|
||||||
|
const { result } = render();
|
||||||
|
expect(result.current.showMoreOptionsMenu).toBe(false);
|
||||||
|
expect(result.current.canInvite).toBe(false);
|
||||||
|
expect(result.current.isFavourite).toBe(false);
|
||||||
|
expect(result.current.canCopyRoomLink).toBe(true);
|
||||||
|
expect(result.current.canMarkAsRead).toBe(false);
|
||||||
|
expect(result.current.canMarkAsUnread).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should has showMoreOptionsMenu to be true", () => {
|
||||||
|
mocked(hasAccessToOptionsMenu).mockReturnValue(true);
|
||||||
|
const { result } = render();
|
||||||
|
expect(result.current.showMoreOptionsMenu).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should be able to invite", () => {
|
||||||
|
jest.spyOn(room, "canInvite").mockReturnValue(true);
|
||||||
|
const { result } = render();
|
||||||
|
expect(result.current.canInvite).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should be a favourite", () => {
|
||||||
|
room.tags = { [DefaultTagID.Favourite]: { order: 0 } };
|
||||||
|
const { result } = render();
|
||||||
|
expect(result.current.isFavourite).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not be able to copy the room link", () => {
|
||||||
|
jest.spyOn(DMRoomMap.shared(), "getUserIdForRoomId").mockReturnValue("userId");
|
||||||
|
const { result } = render();
|
||||||
|
expect(result.current.canCopyRoomLink).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should be able to mark as read", () => {
|
||||||
|
// Add a notification
|
||||||
|
mocked(useUnreadNotifications).mockReturnValue({
|
||||||
|
symbol: null,
|
||||||
|
count: 1,
|
||||||
|
level: NotificationLevel.Notification,
|
||||||
|
});
|
||||||
|
const { result } = render();
|
||||||
|
expect(result.current.canMarkAsRead).toBe(true);
|
||||||
|
expect(result.current.canMarkAsUnread).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Actions
|
||||||
|
|
||||||
|
it("should mark as read", () => {
|
||||||
|
const { result } = render();
|
||||||
|
result.current.markAsRead(new Event("click"));
|
||||||
|
expect(mocked(clearRoomNotification)).toHaveBeenCalledWith(room, matrixClient);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should mark as unread", () => {
|
||||||
|
const { result } = render();
|
||||||
|
result.current.markAsUnread(new Event("click"));
|
||||||
|
expect(mocked(setMarkedUnreadState)).toHaveBeenCalledWith(room, matrixClient, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should tag a room as favourite", () => {
|
||||||
|
const { result } = render();
|
||||||
|
result.current.toggleFavorite(new Event("click"));
|
||||||
|
expect(mocked(tagRoom)).toHaveBeenCalledWith(room, DefaultTagID.Favourite);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should tag a room as low priority", () => {
|
||||||
|
const { result } = render();
|
||||||
|
result.current.toggleLowPriority();
|
||||||
|
expect(mocked(tagRoom)).toHaveBeenCalledWith(room, DefaultTagID.LowPriority);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should dispatch invite action", () => {
|
||||||
|
const { result } = render();
|
||||||
|
result.current.invite(new Event("click"));
|
||||||
|
expect(dispatcher.dispatch).toHaveBeenCalledWith({
|
||||||
|
action: "view_invite",
|
||||||
|
roomId: room.roomId,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should dispatch a copy room action", () => {
|
||||||
|
const { result } = render();
|
||||||
|
result.current.copyRoomLink(new Event("click"));
|
||||||
|
expect(dispatcher.dispatch).toHaveBeenCalledWith({
|
||||||
|
action: "copy_room",
|
||||||
|
room_id: room.roomId,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should dispatch forget room action", () => {
|
||||||
|
// forget room is only available for archived rooms
|
||||||
|
room.tags = { [DefaultTagID.Archived]: { order: 0 } };
|
||||||
|
|
||||||
|
const { result } = render();
|
||||||
|
result.current.leaveRoom(new Event("click"));
|
||||||
|
expect(dispatcher.dispatch).toHaveBeenCalledWith({
|
||||||
|
action: "forget_room",
|
||||||
|
room_id: room.roomId,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should dispatch leave room action", () => {
|
||||||
|
const { result } = render();
|
||||||
|
result.current.leaveRoom(new Event("click"));
|
||||||
|
expect(dispatcher.dispatch).toHaveBeenCalledWith({
|
||||||
|
action: "leave_room",
|
||||||
|
room_id: room.roomId,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2025 New Vector Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { renderHook } from "jest-matrix-react";
|
||||||
|
import { type Room } from "matrix-js-sdk/src/matrix";
|
||||||
|
import { mocked } from "jest-mock";
|
||||||
|
|
||||||
|
import dispatcher from "../../../../../src/dispatcher/dispatcher";
|
||||||
|
import { Action } from "../../../../../src/dispatcher/actions";
|
||||||
|
import { useRoomListItemViewModel } from "../../../../../src/components/viewmodels/roomlist/RoomListItemViewModel";
|
||||||
|
import { createTestClient, mkStubRoom } from "../../../../test-utils";
|
||||||
|
import { hasAccessToOptionsMenu } from "../../../../../src/components/viewmodels/roomlist/utils";
|
||||||
|
|
||||||
|
jest.mock("../../../../../src/components/viewmodels/roomlist/utils", () => ({
|
||||||
|
hasAccessToOptionsMenu: jest.fn().mockReturnValue(false),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe("RoomListItemViewModel", () => {
|
||||||
|
let room: Room;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
const matrixClient = createTestClient();
|
||||||
|
room = mkStubRoom("roomId", "roomName", matrixClient);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should dispatch view room action on openRoom", async () => {
|
||||||
|
const { result: vm } = renderHook(() => useRoomListItemViewModel(room));
|
||||||
|
|
||||||
|
const fn = jest.spyOn(dispatcher, "dispatch");
|
||||||
|
vm.current.openRoom();
|
||||||
|
expect(fn).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
action: Action.ViewRoom,
|
||||||
|
room_id: room.roomId,
|
||||||
|
metricsTrigger: "RoomList",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should show hover menu if user has access to options menu", async () => {
|
||||||
|
mocked(hasAccessToOptionsMenu).mockReturnValue(true);
|
||||||
|
const { result: vm } = renderHook(() => useRoomListItemViewModel(room));
|
||||||
|
expect(vm.current.showHoverMenu).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -12,8 +12,6 @@ import RoomListStoreV3 from "../../../../../src/stores/room-list-v3/RoomListStor
|
|||||||
import { mkStubRoom } from "../../../../test-utils";
|
import { mkStubRoom } from "../../../../test-utils";
|
||||||
import { LISTS_UPDATE_EVENT } from "../../../../../src/stores/room-list/SlidingRoomListStore";
|
import { LISTS_UPDATE_EVENT } from "../../../../../src/stores/room-list/SlidingRoomListStore";
|
||||||
import { useRoomListViewModel } from "../../../../../src/components/viewmodels/roomlist/RoomListViewModel";
|
import { useRoomListViewModel } from "../../../../../src/components/viewmodels/roomlist/RoomListViewModel";
|
||||||
import dispatcher from "../../../../../src/dispatcher/dispatcher";
|
|
||||||
import { Action } from "../../../../../src/dispatcher/actions";
|
|
||||||
import { FilterKey } from "../../../../../src/stores/room-list-v3/skip-list/filters";
|
import { FilterKey } from "../../../../../src/stores/room-list-v3/skip-list/filters";
|
||||||
import { SecondaryFilters } from "../../../../../src/components/viewmodels/roomlist/useFilteredRooms";
|
import { SecondaryFilters } from "../../../../../src/components/viewmodels/roomlist/useFilteredRooms";
|
||||||
import { SortingAlgorithm } from "../../../../../src/stores/room-list-v3/skip-list/sorters";
|
import { SortingAlgorithm } from "../../../../../src/stores/room-list-v3/skip-list/sorters";
|
||||||
@@ -56,21 +54,6 @@ describe("RoomListViewModel", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should dispatch view room action on openRoom", async () => {
|
|
||||||
const { rooms } = mockAndCreateRooms();
|
|
||||||
const { result: vm } = renderHook(() => useRoomListViewModel());
|
|
||||||
|
|
||||||
const fn = jest.spyOn(dispatcher, "dispatch");
|
|
||||||
act(() => vm.current.openRoom(rooms[7].roomId));
|
|
||||||
expect(fn).toHaveBeenCalledWith(
|
|
||||||
expect.objectContaining({
|
|
||||||
action: Action.ViewRoom,
|
|
||||||
room_id: rooms[7].roomId,
|
|
||||||
metricsTrigger: "RoomList",
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("Filters", () => {
|
describe("Filters", () => {
|
||||||
it("should provide list of available filters", () => {
|
it("should provide list of available filters", () => {
|
||||||
mockAndCreateRooms();
|
mockAndCreateRooms();
|
||||||
|
|||||||
@@ -7,8 +7,7 @@
|
|||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { type MatrixClient } from "matrix-js-sdk/src/matrix";
|
import { type MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||||
import { render, screen, waitFor } from "jest-matrix-react";
|
import { render } from "jest-matrix-react";
|
||||||
import userEvent from "@testing-library/user-event";
|
|
||||||
|
|
||||||
import { mkRoom, stubClient } from "../../../../../test-utils";
|
import { mkRoom, stubClient } from "../../../../../test-utils";
|
||||||
import { type RoomListViewState } from "../../../../../../src/components/viewmodels/roomlist/RoomListViewModel";
|
import { type RoomListViewState } from "../../../../../../src/components/viewmodels/roomlist/RoomListViewModel";
|
||||||
@@ -31,7 +30,6 @@ describe("<RoomList />", () => {
|
|||||||
const rooms = Array.from({ length: 10 }, (_, i) => mkRoom(matrixClient, `room${i}`));
|
const rooms = Array.from({ length: 10 }, (_, i) => mkRoom(matrixClient, `room${i}`));
|
||||||
vm = {
|
vm = {
|
||||||
rooms,
|
rooms,
|
||||||
openRoom: jest.fn(),
|
|
||||||
primaryFilters: [],
|
primaryFilters: [],
|
||||||
activateSecondaryFilter: () => {},
|
activateSecondaryFilter: () => {},
|
||||||
activeSecondaryFilter: SecondaryFilters.AllActivity,
|
activeSecondaryFilter: SecondaryFilters.AllActivity,
|
||||||
@@ -48,15 +46,4 @@ describe("<RoomList />", () => {
|
|||||||
const { asFragment } = render(<RoomList vm={vm} />);
|
const { asFragment } = render(<RoomList vm={vm} />);
|
||||||
expect(asFragment()).toMatchSnapshot();
|
expect(asFragment()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should open the room", async () => {
|
|
||||||
const user = userEvent.setup();
|
|
||||||
|
|
||||||
render(<RoomList vm={vm} />);
|
|
||||||
await waitFor(async () => {
|
|
||||||
expect(screen.getByRole("gridcell", { name: "Open room room9" })).toBeVisible();
|
|
||||||
await user.click(screen.getByRole("gridcell", { name: "Open room room9" }));
|
|
||||||
});
|
|
||||||
expect(vm.openRoom).toHaveBeenCalledWith(vm.rooms[9].roomId);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2025 New Vector Ltd.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
|
||||||
* Please see LICENSE files in the repository root for full details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import React from "react";
|
|
||||||
import { type MatrixClient, type Room } from "matrix-js-sdk/src/matrix";
|
|
||||||
import { render, screen } from "jest-matrix-react";
|
|
||||||
import userEvent from "@testing-library/user-event";
|
|
||||||
|
|
||||||
import { mkRoom, stubClient } from "../../../../../test-utils";
|
|
||||||
import { RoomListCell } from "../../../../../../src/components/views/rooms/RoomListPanel/RoomListCell";
|
|
||||||
import DMRoomMap from "../../../../../../src/utils/DMRoomMap";
|
|
||||||
|
|
||||||
describe("<RoomListCell />", () => {
|
|
||||||
let matrixClient: MatrixClient;
|
|
||||||
let room: Room;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
matrixClient = stubClient();
|
|
||||||
room = mkRoom(matrixClient, "room1");
|
|
||||||
|
|
||||||
DMRoomMap.makeShared(matrixClient);
|
|
||||||
jest.spyOn(DMRoomMap.shared(), "getUserIdForRoomId").mockReturnValue(null);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("should render a room cell", () => {
|
|
||||||
const onClick = jest.fn();
|
|
||||||
const { asFragment } = render(<RoomListCell room={room} onClick={onClick} />);
|
|
||||||
expect(asFragment()).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("should call onClick when clicked", async () => {
|
|
||||||
const user = userEvent.setup();
|
|
||||||
|
|
||||||
const onClick = jest.fn();
|
|
||||||
render(<RoomListCell room={room} onClick={onClick} />);
|
|
||||||
|
|
||||||
await user.click(screen.getByRole("button", { name: `Open room ${room.name}` }));
|
|
||||||
expect(onClick).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2025 New Vector Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import { mocked } from "jest-mock";
|
||||||
|
import { render, screen } from "jest-matrix-react";
|
||||||
|
import userEvent from "@testing-library/user-event";
|
||||||
|
|
||||||
|
import {
|
||||||
|
type RoomListItemMenuViewState,
|
||||||
|
useRoomListItemMenuViewModel,
|
||||||
|
} from "../../../../../../src/components/viewmodels/roomlist/RoomListItemMenuViewModel";
|
||||||
|
import type { MatrixClient, Room } from "matrix-js-sdk/src/matrix";
|
||||||
|
import { mkRoom, stubClient } from "../../../../../test-utils";
|
||||||
|
import { RoomListItemMenuView } from "../../../../../../src/components/views/rooms/RoomListPanel/RoomListItemMenuView";
|
||||||
|
|
||||||
|
jest.mock("../../../../../../src/components/viewmodels/roomlist/RoomListItemMenuViewModel", () => ({
|
||||||
|
useRoomListItemMenuViewModel: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe("<RoomListItemMenuView />", () => {
|
||||||
|
const defaultValue: RoomListItemMenuViewState = {
|
||||||
|
showMoreOptionsMenu: true,
|
||||||
|
isFavourite: true,
|
||||||
|
canInvite: true,
|
||||||
|
canMarkAsUnread: true,
|
||||||
|
canMarkAsRead: true,
|
||||||
|
canCopyRoomLink: true,
|
||||||
|
copyRoomLink: jest.fn(),
|
||||||
|
markAsUnread: jest.fn(),
|
||||||
|
markAsRead: jest.fn(),
|
||||||
|
leaveRoom: jest.fn(),
|
||||||
|
toggleLowPriority: jest.fn(),
|
||||||
|
toggleFavorite: jest.fn(),
|
||||||
|
invite: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let matrixClient: MatrixClient;
|
||||||
|
let room: Room;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
mocked(useRoomListItemMenuViewModel).mockReturnValue(defaultValue);
|
||||||
|
matrixClient = stubClient();
|
||||||
|
room = mkRoom(matrixClient, "room1");
|
||||||
|
});
|
||||||
|
|
||||||
|
function renderMenu(setMenuOpen = jest.fn()) {
|
||||||
|
return render(<RoomListItemMenuView room={room} setMenuOpen={setMenuOpen} />);
|
||||||
|
}
|
||||||
|
|
||||||
|
it("should render the more options menu", () => {
|
||||||
|
const { asFragment } = renderMenu();
|
||||||
|
expect(screen.getByRole("button", { name: "More Options" })).toBeInTheDocument();
|
||||||
|
expect(asFragment()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not render the more options menu when showMoreOptionsMenu is false", () => {
|
||||||
|
mocked(useRoomListItemMenuViewModel).mockReturnValue({ ...defaultValue, showMoreOptionsMenu: false });
|
||||||
|
renderMenu();
|
||||||
|
expect(screen.queryByRole("button", { name: "More Options" })).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should call setMenuOpen when the menu is opened", async () => {
|
||||||
|
const user = userEvent.setup();
|
||||||
|
const setMenuOpen = jest.fn();
|
||||||
|
renderMenu(setMenuOpen);
|
||||||
|
|
||||||
|
await user.click(screen.getByRole("button", { name: "More Options" }));
|
||||||
|
expect(setMenuOpen).toHaveBeenCalledWith(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should display all the buttons and have the actions linked", async () => {
|
||||||
|
const user = userEvent.setup();
|
||||||
|
renderMenu();
|
||||||
|
|
||||||
|
const openMenu = screen.getByRole("button", { name: "More Options" });
|
||||||
|
await user.click(openMenu);
|
||||||
|
|
||||||
|
await user.click(screen.getByRole("menuitem", { name: "Mark as read" }));
|
||||||
|
expect(defaultValue.markAsRead).toHaveBeenCalled();
|
||||||
|
|
||||||
|
await user.click(openMenu);
|
||||||
|
await user.click(screen.getByRole("menuitem", { name: "Mark as unread" }));
|
||||||
|
expect(defaultValue.markAsUnread).toHaveBeenCalled();
|
||||||
|
|
||||||
|
await user.click(openMenu);
|
||||||
|
await user.click(screen.getByRole("menuitemcheckbox", { name: "Favourited" }));
|
||||||
|
expect(defaultValue.toggleFavorite).toHaveBeenCalled();
|
||||||
|
|
||||||
|
await user.click(openMenu);
|
||||||
|
await user.click(screen.getByRole("menuitem", { name: "Low priority" }));
|
||||||
|
expect(defaultValue.toggleLowPriority).toHaveBeenCalled();
|
||||||
|
|
||||||
|
await user.click(openMenu);
|
||||||
|
await user.click(screen.getByRole("menuitem", { name: "Invite" }));
|
||||||
|
expect(defaultValue.invite).toHaveBeenCalled();
|
||||||
|
|
||||||
|
await user.click(openMenu);
|
||||||
|
await user.click(screen.getByRole("menuitem", { name: "Copy room link" }));
|
||||||
|
expect(defaultValue.copyRoomLink).toHaveBeenCalled();
|
||||||
|
|
||||||
|
await user.click(openMenu);
|
||||||
|
await user.click(screen.getByRole("menuitem", { name: "Leave room" }));
|
||||||
|
expect(defaultValue.leaveRoom).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2025 New Vector Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import { type MatrixClient, type Room } from "matrix-js-sdk/src/matrix";
|
||||||
|
import { render, screen, waitFor } from "jest-matrix-react";
|
||||||
|
import userEvent from "@testing-library/user-event";
|
||||||
|
import { mocked } from "jest-mock";
|
||||||
|
|
||||||
|
import { mkRoom, stubClient, withClientContextRenderOptions } from "../../../../../test-utils";
|
||||||
|
import { RoomListItemView } from "../../../../../../src/components/views/rooms/RoomListPanel/RoomListItemView";
|
||||||
|
import DMRoomMap from "../../../../../../src/utils/DMRoomMap";
|
||||||
|
import {
|
||||||
|
type RoomListItemViewState,
|
||||||
|
useRoomListItemViewModel,
|
||||||
|
} from "../../../../../../src/components/viewmodels/roomlist/RoomListItemViewModel";
|
||||||
|
|
||||||
|
jest.mock("../../../../../../src/components/viewmodels/roomlist/RoomListItemViewModel", () => ({
|
||||||
|
useRoomListItemViewModel: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe("<RoomListItemView />", () => {
|
||||||
|
const defaultValue: RoomListItemViewState = {
|
||||||
|
openRoom: jest.fn(),
|
||||||
|
showHoverMenu: false,
|
||||||
|
};
|
||||||
|
let matrixClient: MatrixClient;
|
||||||
|
let room: Room;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
mocked(useRoomListItemViewModel).mockReturnValue(defaultValue);
|
||||||
|
matrixClient = stubClient();
|
||||||
|
room = mkRoom(matrixClient, "room1");
|
||||||
|
|
||||||
|
DMRoomMap.makeShared(matrixClient);
|
||||||
|
jest.spyOn(DMRoomMap.shared(), "getUserIdForRoomId").mockReturnValue(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should render a room item", () => {
|
||||||
|
const onClick = jest.fn();
|
||||||
|
const { asFragment } = render(<RoomListItemView room={room} onClick={onClick} />);
|
||||||
|
expect(asFragment()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should call openRoom when clicked", async () => {
|
||||||
|
const user = userEvent.setup();
|
||||||
|
render(<RoomListItemView room={room} />);
|
||||||
|
|
||||||
|
await user.click(screen.getByRole("button", { name: `Open room ${room.name}` }));
|
||||||
|
expect(defaultValue.openRoom).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should hover decoration if hovered", async () => {
|
||||||
|
mocked(useRoomListItemViewModel).mockReturnValue({ ...defaultValue, showHoverMenu: true });
|
||||||
|
|
||||||
|
const user = userEvent.setup();
|
||||||
|
render(<RoomListItemView room={room} />, withClientContextRenderOptions(matrixClient));
|
||||||
|
const listItem = screen.getByRole("button", { name: `Open room ${room.name}` });
|
||||||
|
expect(screen.queryByRole("button", { name: "More Options" })).toBeNull();
|
||||||
|
|
||||||
|
await user.hover(listItem);
|
||||||
|
await waitFor(() => expect(screen.getByRole("button", { name: "More Options" })).toBeInTheDocument());
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -20,7 +20,6 @@ describe("<RoomListPrimaryFilters />", () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vm = {
|
vm = {
|
||||||
rooms: [],
|
rooms: [],
|
||||||
openRoom: jest.fn(),
|
|
||||||
primaryFilters: [
|
primaryFilters: [
|
||||||
{ name: "People", active: false, toggle: jest.fn() },
|
{ name: "People", active: false, toggle: jest.fn() },
|
||||||
{ name: "Rooms", active: true, toggle: jest.fn() },
|
{ name: "Rooms", active: true, toggle: jest.fn() },
|
||||||
|
|||||||
@@ -24,13 +24,13 @@ exports[`<RoomList /> should render a room list 1`] = `
|
|||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
aria-label="Open room room0"
|
aria-label="Open room room0"
|
||||||
class="mx_RoomListCell"
|
class="mx_RoomListItemView"
|
||||||
role="gridcell"
|
role="gridcell"
|
||||||
style="height: 48px; left: 0px; position: absolute; top: 0px; width: 100%;"
|
style="height: 48px; left: 0px; position: absolute; top: 0px; width: 100%;"
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="mx_Flex mx_RoomListCell_container"
|
class="mx_Flex mx_RoomListItemView_container"
|
||||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
|
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@@ -57,8 +57,8 @@ exports[`<RoomList /> should render a room list 1`] = `
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="mx_Flex mx_RoomListCell_content"
|
class="mx_Flex mx_RoomListItemView_content"
|
||||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: 0; --mx-flex-wrap: nowrap;"
|
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: space-between; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
title="room0"
|
title="room0"
|
||||||
@@ -70,13 +70,13 @@ exports[`<RoomList /> should render a room list 1`] = `
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
aria-label="Open room room1"
|
aria-label="Open room room1"
|
||||||
class="mx_RoomListCell"
|
class="mx_RoomListItemView"
|
||||||
role="gridcell"
|
role="gridcell"
|
||||||
style="height: 48px; left: 0px; position: absolute; top: 48px; width: 100%;"
|
style="height: 48px; left: 0px; position: absolute; top: 48px; width: 100%;"
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="mx_Flex mx_RoomListCell_container"
|
class="mx_Flex mx_RoomListItemView_container"
|
||||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
|
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@@ -103,8 +103,8 @@ exports[`<RoomList /> should render a room list 1`] = `
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="mx_Flex mx_RoomListCell_content"
|
class="mx_Flex mx_RoomListItemView_content"
|
||||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: 0; --mx-flex-wrap: nowrap;"
|
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: space-between; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
title="room1"
|
title="room1"
|
||||||
@@ -116,13 +116,13 @@ exports[`<RoomList /> should render a room list 1`] = `
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
aria-label="Open room room2"
|
aria-label="Open room room2"
|
||||||
class="mx_RoomListCell"
|
class="mx_RoomListItemView"
|
||||||
role="gridcell"
|
role="gridcell"
|
||||||
style="height: 48px; left: 0px; position: absolute; top: 96px; width: 100%;"
|
style="height: 48px; left: 0px; position: absolute; top: 96px; width: 100%;"
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="mx_Flex mx_RoomListCell_container"
|
class="mx_Flex mx_RoomListItemView_container"
|
||||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
|
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@@ -149,8 +149,8 @@ exports[`<RoomList /> should render a room list 1`] = `
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="mx_Flex mx_RoomListCell_content"
|
class="mx_Flex mx_RoomListItemView_content"
|
||||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: 0; --mx-flex-wrap: nowrap;"
|
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: space-between; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
title="room2"
|
title="room2"
|
||||||
@@ -162,13 +162,13 @@ exports[`<RoomList /> should render a room list 1`] = `
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
aria-label="Open room room3"
|
aria-label="Open room room3"
|
||||||
class="mx_RoomListCell"
|
class="mx_RoomListItemView"
|
||||||
role="gridcell"
|
role="gridcell"
|
||||||
style="height: 48px; left: 0px; position: absolute; top: 144px; width: 100%;"
|
style="height: 48px; left: 0px; position: absolute; top: 144px; width: 100%;"
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="mx_Flex mx_RoomListCell_container"
|
class="mx_Flex mx_RoomListItemView_container"
|
||||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
|
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@@ -195,8 +195,8 @@ exports[`<RoomList /> should render a room list 1`] = `
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="mx_Flex mx_RoomListCell_content"
|
class="mx_Flex mx_RoomListItemView_content"
|
||||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: 0; --mx-flex-wrap: nowrap;"
|
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: space-between; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
title="room3"
|
title="room3"
|
||||||
@@ -208,13 +208,13 @@ exports[`<RoomList /> should render a room list 1`] = `
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
aria-label="Open room room4"
|
aria-label="Open room room4"
|
||||||
class="mx_RoomListCell"
|
class="mx_RoomListItemView"
|
||||||
role="gridcell"
|
role="gridcell"
|
||||||
style="height: 48px; left: 0px; position: absolute; top: 192px; width: 100%;"
|
style="height: 48px; left: 0px; position: absolute; top: 192px; width: 100%;"
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="mx_Flex mx_RoomListCell_container"
|
class="mx_Flex mx_RoomListItemView_container"
|
||||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
|
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@@ -241,8 +241,8 @@ exports[`<RoomList /> should render a room list 1`] = `
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="mx_Flex mx_RoomListCell_content"
|
class="mx_Flex mx_RoomListItemView_content"
|
||||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: 0; --mx-flex-wrap: nowrap;"
|
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: space-between; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
title="room4"
|
title="room4"
|
||||||
@@ -254,13 +254,13 @@ exports[`<RoomList /> should render a room list 1`] = `
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
aria-label="Open room room5"
|
aria-label="Open room room5"
|
||||||
class="mx_RoomListCell"
|
class="mx_RoomListItemView"
|
||||||
role="gridcell"
|
role="gridcell"
|
||||||
style="height: 48px; left: 0px; position: absolute; top: 240px; width: 100%;"
|
style="height: 48px; left: 0px; position: absolute; top: 240px; width: 100%;"
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="mx_Flex mx_RoomListCell_container"
|
class="mx_Flex mx_RoomListItemView_container"
|
||||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
|
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@@ -287,8 +287,8 @@ exports[`<RoomList /> should render a room list 1`] = `
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="mx_Flex mx_RoomListCell_content"
|
class="mx_Flex mx_RoomListItemView_content"
|
||||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: 0; --mx-flex-wrap: nowrap;"
|
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: space-between; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
title="room5"
|
title="room5"
|
||||||
@@ -300,13 +300,13 @@ exports[`<RoomList /> should render a room list 1`] = `
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
aria-label="Open room room6"
|
aria-label="Open room room6"
|
||||||
class="mx_RoomListCell"
|
class="mx_RoomListItemView"
|
||||||
role="gridcell"
|
role="gridcell"
|
||||||
style="height: 48px; left: 0px; position: absolute; top: 288px; width: 100%;"
|
style="height: 48px; left: 0px; position: absolute; top: 288px; width: 100%;"
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="mx_Flex mx_RoomListCell_container"
|
class="mx_Flex mx_RoomListItemView_container"
|
||||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
|
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@@ -333,8 +333,8 @@ exports[`<RoomList /> should render a room list 1`] = `
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="mx_Flex mx_RoomListCell_content"
|
class="mx_Flex mx_RoomListItemView_content"
|
||||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: 0; --mx-flex-wrap: nowrap;"
|
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: space-between; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
title="room6"
|
title="room6"
|
||||||
@@ -346,13 +346,13 @@ exports[`<RoomList /> should render a room list 1`] = `
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
aria-label="Open room room7"
|
aria-label="Open room room7"
|
||||||
class="mx_RoomListCell"
|
class="mx_RoomListItemView"
|
||||||
role="gridcell"
|
role="gridcell"
|
||||||
style="height: 48px; left: 0px; position: absolute; top: 336px; width: 100%;"
|
style="height: 48px; left: 0px; position: absolute; top: 336px; width: 100%;"
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="mx_Flex mx_RoomListCell_container"
|
class="mx_Flex mx_RoomListItemView_container"
|
||||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
|
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@@ -379,8 +379,8 @@ exports[`<RoomList /> should render a room list 1`] = `
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="mx_Flex mx_RoomListCell_content"
|
class="mx_Flex mx_RoomListItemView_content"
|
||||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: 0; --mx-flex-wrap: nowrap;"
|
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: space-between; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
title="room7"
|
title="room7"
|
||||||
@@ -392,13 +392,13 @@ exports[`<RoomList /> should render a room list 1`] = `
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
aria-label="Open room room8"
|
aria-label="Open room room8"
|
||||||
class="mx_RoomListCell"
|
class="mx_RoomListItemView"
|
||||||
role="gridcell"
|
role="gridcell"
|
||||||
style="height: 48px; left: 0px; position: absolute; top: 384px; width: 100%;"
|
style="height: 48px; left: 0px; position: absolute; top: 384px; width: 100%;"
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="mx_Flex mx_RoomListCell_container"
|
class="mx_Flex mx_RoomListItemView_container"
|
||||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
|
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@@ -425,8 +425,8 @@ exports[`<RoomList /> should render a room list 1`] = `
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="mx_Flex mx_RoomListCell_content"
|
class="mx_Flex mx_RoomListItemView_content"
|
||||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: 0; --mx-flex-wrap: nowrap;"
|
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: space-between; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
title="room8"
|
title="room8"
|
||||||
@@ -438,13 +438,13 @@ exports[`<RoomList /> should render a room list 1`] = `
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
aria-label="Open room room9"
|
aria-label="Open room room9"
|
||||||
class="mx_RoomListCell"
|
class="mx_RoomListItemView"
|
||||||
role="gridcell"
|
role="gridcell"
|
||||||
style="height: 48px; left: 0px; position: absolute; top: 432px; width: 100%;"
|
style="height: 48px; left: 0px; position: absolute; top: 432px; width: 100%;"
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="mx_Flex mx_RoomListCell_container"
|
class="mx_Flex mx_RoomListItemView_container"
|
||||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
|
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@@ -471,8 +471,8 @@ exports[`<RoomList /> should render a room list 1`] = `
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="mx_Flex mx_RoomListCell_content"
|
class="mx_Flex mx_RoomListItemView_content"
|
||||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: 0; --mx-flex-wrap: nowrap;"
|
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: space-between; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
title="room9"
|
title="room9"
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`<RoomListItemMenuView /> should render the more options menu 1`] = `
|
||||||
|
<DocumentFragment>
|
||||||
|
<div
|
||||||
|
class="mx_Flex mx_RoomListItemMenuView"
|
||||||
|
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-1x); --mx-flex-wrap: nowrap;"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
aria-disabled="false"
|
||||||
|
aria-expanded="false"
|
||||||
|
aria-haspopup="menu"
|
||||||
|
aria-label="More Options"
|
||||||
|
aria-labelledby=":r2:"
|
||||||
|
class="_icon-button_m2erp_8"
|
||||||
|
data-state="closed"
|
||||||
|
id="radix-:r0:"
|
||||||
|
role="button"
|
||||||
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
tabindex="0"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_indicator-icon_zr2a0_17"
|
||||||
|
style="--cpd-icon-button-size: 100%;"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
fill="currentColor"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="1em"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M6 14q-.824 0-1.412-.588A1.93 1.93 0 0 1 4 12q0-.825.588-1.412A1.93 1.93 0 0 1 6 10q.824 0 1.412.588Q8 11.175 8 12t-.588 1.412A1.93 1.93 0 0 1 6 14m6 0q-.825 0-1.412-.588A1.93 1.93 0 0 1 10 12q0-.825.588-1.412A1.93 1.93 0 0 1 12 10q.825 0 1.412.588Q14 11.175 14 12t-.588 1.412A1.93 1.93 0 0 1 12 14m6 0q-.824 0-1.413-.588A1.93 1.93 0 0 1 16 12q0-.825.587-1.412A1.93 1.93 0 0 1 18 10q.824 0 1.413.588Q20 11.175 20 12t-.587 1.412A1.93 1.93 0 0 1 18 14"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</DocumentFragment>
|
||||||
|
`;
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`<RoomListCell /> should render a room cell 1`] = `
|
exports[`<RoomListItemView /> should render a room item 1`] = `
|
||||||
<DocumentFragment>
|
<DocumentFragment>
|
||||||
<button
|
<button
|
||||||
aria-label="Open room room1"
|
aria-label="Open room room1"
|
||||||
class="mx_RoomListCell"
|
class="mx_RoomListItemView"
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="mx_Flex mx_RoomListCell_container"
|
class="mx_Flex mx_RoomListItemView_container"
|
||||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
|
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@@ -35,8 +35,8 @@ exports[`<RoomListCell /> should render a room cell 1`] = `
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="mx_Flex mx_RoomListCell_content"
|
class="mx_Flex mx_RoomListItemView_content"
|
||||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: 0; --mx-flex-wrap: nowrap;"
|
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: space-between; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
title="room1"
|
title="room1"
|
||||||
@@ -3577,10 +3577,10 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@vector-im/compound-design-tokens/-/compound-design-tokens-4.0.0.tgz#4922404f355f09fb753e6a91cc7efe4e661c9ff1"
|
resolved "https://registry.yarnpkg.com/@vector-im/compound-design-tokens/-/compound-design-tokens-4.0.0.tgz#4922404f355f09fb753e6a91cc7efe4e661c9ff1"
|
||||||
integrity sha512-hFfLSKrGc58rPRp9JH1mkgw3moFEgpL8RQzyDESHErq7P1lUmlIuwKFTVfK5SbdFM5GvHp7nQaFpVmxUQ3Xp+w==
|
integrity sha512-hFfLSKrGc58rPRp9JH1mkgw3moFEgpL8RQzyDESHErq7P1lUmlIuwKFTVfK5SbdFM5GvHp7nQaFpVmxUQ3Xp+w==
|
||||||
|
|
||||||
"@vector-im/compound-web@^7.6.4":
|
"@vector-im/compound-web@^7.7.2":
|
||||||
version "7.6.4"
|
version "7.7.2"
|
||||||
resolved "https://registry.yarnpkg.com/@vector-im/compound-web/-/compound-web-7.6.4.tgz#fa09587786befe873371173912a4b8ad82e5ca59"
|
resolved "https://registry.yarnpkg.com/@vector-im/compound-web/-/compound-web-7.7.2.tgz#07e04a546b86e568b13263092b324efc76398487"
|
||||||
integrity sha512-5jn7EeDej9Rz2cTA2MoYiPVoS/zRSNcjEF1d+LBv+Xo7OGik/uOIBQZcy2fGayVLRVm7L+kf8h3oT0Am4g7X9A==
|
integrity sha512-RhPyKzfPo1HRyFi3wy8oc25IXbLLzTmw6A5QvPJgRlMW+LidwqCCYqmFeZrvWxK3pZPqE7hTJbHgUhGe7kxznw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@floating-ui/react" "^0.27.0"
|
"@floating-ui/react" "^0.27.0"
|
||||||
"@radix-ui/react-context-menu" "^2.2.1"
|
"@radix-ui/react-context-menu" "^2.2.1"
|
||||||
|
|||||||
Reference in New Issue
Block a user