New room list: basic flat list (#29368)

* chore: make the room list panel a flexbox

* feat(new room list): add `RoomListCell` component

* feat(new room list): add virtualized `RoomList` component

* feat(new room list): add `RoomListView` component

* feat(new room list): use `RoomListView` in `RoomListPanel`

* test(new room list): add test for room cell

* test(new room list): update room list panel tests

* test(new room list): add test to virtualized room list

* test(e2e): add room list tests

* test(e2e): update room panel tests
This commit is contained in:
Florian Duros 2025-03-06 11:01:55 +01:00 committed by GitHub
parent b6c872142b
commit 90cc44b340
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 948 additions and 63 deletions

View File

@ -25,10 +25,17 @@ test.describe("Room list panel", () => {
test.beforeEach(async ({ page, app, user }) => { test.beforeEach(async ({ page, app, user }) => {
// The notification toast is displayed above the search section // The notification toast is displayed above the search section
await app.closeNotificationToast(); await app.closeNotificationToast();
// Populate the room list
for (let i = 0; i < 20; i++) {
await app.client.createRoom({ name: `room${i}` });
}
}); });
test("should render the room list panel", { tag: "@screenshot" }, async ({ page, app, user }) => { test("should render the room list panel", { tag: "@screenshot" }, async ({ page, app, user }) => {
const roomListView = getRoomListView(page); const roomListView = getRoomListView(page);
// Wait for the last room to be visible
await expect(roomListView.getByRole("gridcell", { name: "Open room room19" })).toBeVisible();
await expect(roomListView).toMatchScreenshot("room-list-panel.png"); await expect(roomListView).toMatchScreenshot("room-list-panel.png");
}); });
}); });

View File

@ -0,0 +1,50 @@
/*
* 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 Page } from "@playwright/test";
import { test, expect } from "../../../element-web-test";
test.describe("Room list", () => {
test.use({
labsFlags: ["feature_new_room_list"],
});
/**
* Get the room list
* @param page
*/
function getRoomList(page: Page) {
return page.getByTestId("room-list");
}
test.beforeEach(async ({ page, app, user }) => {
// The notification toast is displayed above the search section
await app.closeNotificationToast();
for (let i = 0; i < 30; i++) {
await app.client.createRoom({ name: `room${i}` });
}
});
test("should render the room list", { tag: "@screenshot" }, async ({ page, app, user }) => {
const roomListView = getRoomList(page);
await expect(roomListView.getByRole("gridcell", { name: "Open room room29" })).toBeVisible();
await expect(roomListView).toMatchScreenshot("room-list.png");
await roomListView.hover();
// Scroll to the end of the room list
await page.mouse.wheel(0, 1000);
await expect(roomListView.getByRole("gridcell", { name: "Open room room0" })).toBeVisible();
await expect(roomListView).toMatchScreenshot("room-list-scrolled.png");
});
test("should open the room when it is clicked", async ({ page, app, user }) => {
const roomListView = getRoomList(page);
await roomListView.getByRole("gridcell", { name: "Open room room29" }).click();
await expect(page.getByRole("heading", { name: "room29", level: 1 })).toBeVisible();
});
});

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -269,6 +269,8 @@
@import "./views/right_panel/_VerificationPanel.pcss"; @import "./views/right_panel/_VerificationPanel.pcss";
@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/_RoomListCell.pcss";
@import "./views/rooms/RoomListPanel/_RoomListHeaderView.pcss"; @import "./views/rooms/RoomListPanel/_RoomListHeaderView.pcss";
@import "./views/rooms/RoomListPanel/_RoomListPanel.pcss"; @import "./views/rooms/RoomListPanel/_RoomListPanel.pcss";
@import "./views/rooms/RoomListPanel/_RoomListSearch.pcss"; @import "./views/rooms/RoomListPanel/_RoomListSearch.pcss";

View File

@ -0,0 +1,15 @@
/*
* 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_RoomList {
height: 100%;
.mx_RoomList_List {
/* Avoid when on hover, the background color to be on top of the right border */
padding-right: 1px;
}
}

View File

@ -0,0 +1,44 @@
/*
* 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.
*/
/**
* The RoomCell has the following structure:
* button----------------------------------------|
* | <-12px-> container--------------------------|
* | | room avatar <-12px-> content-----|
* | | | room_name |
* | | | ----------| <-- border
* |---------------------------------------------|
*/
.mx_RoomListCell {
all: unset;
&:hover {
background-color: var(--cpd-color-bg-action-secondary-hovered);
}
.mx_RoomListCell_container {
padding-left: var(--cpd-space-3x);
font: var(--cpd-font-body-md-regular);
height: 100%;
.mx_RoomListCell_content {
height: 100%;
flex: 1;
/* The border is only under the room name and the future hover menu */
border-bottom: var(--cpd-border-width-0-5) solid var(--cpd-color-bg-subtle-secondary);
box-sizing: border-box;
min-width: 0;
span {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
}

View File

@ -6,7 +6,7 @@
*/ */
.mx_RoomListHeaderView { .mx_RoomListHeaderView {
height: 60px; flex: 0 0 60px;
padding: 0 var(--cpd-space-3x); padding: 0 var(--cpd-space-3x);
.mx_RoomListHeaderView_title { .mx_RoomListHeaderView_title {

View File

@ -7,7 +7,7 @@
.mx_RoomListSearch { .mx_RoomListSearch {
/* From figma, this should be aligned with the room header */ /* From figma, this should be aligned with the room header */
height: 64px; flex: 0 0 64px;
box-sizing: border-box; box-sizing: border-box;
border-bottom: var(--cpd-border-width-1) solid var(--cpd-color-bg-subtle-primary); border-bottom: var(--cpd-border-width-1) solid var(--cpd-color-bg-subtle-primary);
padding: 0 var(--cpd-space-3x); padding: 0 var(--cpd-space-3x);

View File

@ -0,0 +1,51 @@
/*
* 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, { useCallback, type JSX } from "react";
import { AutoSizer, List, type ListRowProps } from "react-virtualized";
import { type RoomListViewState } from "../../../viewmodels/roomlist/RoomListViewModel";
import { _t } from "../../../../languageHandler";
import { RoomListCell } from "./RoomListCell";
interface RoomListProps {
/**
* The view model state for the room list.
*/
vm: RoomListViewState;
}
/**
* A virtualized list of rooms.
*/
export function RoomList({ vm: { rooms, openRoom } }: RoomListProps): JSX.Element {
const roomRendererMemoized = useCallback(
({ key, index, style }: ListRowProps) => (
<RoomListCell room={rooms[index]} key={key} style={style} onClick={() => openRoom(rooms[index].roomId)} />
),
[rooms, openRoom],
);
// The first div is needed to make the virtualized list take all the remaining space and scroll correctly
return (
<div className="mx_RoomList" data-testid="room-list">
<AutoSizer>
{({ height, width }) => (
<List
aria-label={_t("room_list|list_title")}
className="mx_RoomList_List"
rowRenderer={roomRendererMemoized}
rowCount={rooms.length}
rowHeight={48}
height={height}
width={width}
/>
)}
</AutoSizer>
</div>
);
}

View File

@ -0,0 +1,44 @@
/*
* 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>
);
}

View File

@ -6,14 +6,13 @@ Please see LICENSE files in the repository root for full details.
*/ */
import React from "react"; import React from "react";
import { AutoSizer, List } from "react-virtualized";
import type { ListRowProps } from "react-virtualized";
import { shouldShowComponent } from "../../../../customisations/helpers/UIComponents"; import { shouldShowComponent } from "../../../../customisations/helpers/UIComponents";
import { UIComponent } from "../../../../settings/UIFeature"; import { UIComponent } from "../../../../settings/UIFeature";
import { RoomListSearch } from "./RoomListSearch"; import { RoomListSearch } from "./RoomListSearch";
import { RoomListHeaderView } from "./RoomListHeaderView"; import { RoomListHeaderView } from "./RoomListHeaderView";
import { useRoomListViewModel } from "../../../viewmodels/roomlist/RoomListViewModel"; import { RoomListView } from "./RoomListView";
import { Flex } from "../../../utils/Flex";
type RoomListPanelProps = { type RoomListPanelProps = {
/** /**
@ -28,31 +27,18 @@ type RoomListPanelProps = {
*/ */
export const RoomListPanel: React.FC<RoomListPanelProps> = ({ activeSpace }) => { export const RoomListPanel: React.FC<RoomListPanelProps> = ({ activeSpace }) => {
const displayRoomSearch = shouldShowComponent(UIComponent.FilterContainer); const displayRoomSearch = shouldShowComponent(UIComponent.FilterContainer);
const { rooms } = useRoomListViewModel();
const rowRenderer = ({ key, index, style }: ListRowProps): React.JSX.Element => {
return (
<div key={key} style={style}>
{rooms[index].name}
</div>
);
};
return ( return (
<section className="mx_RoomListPanel" data-testid="room-list-panel"> <Flex
as="section"
className="mx_RoomListPanel"
data-testid="room-list-panel"
direction="column"
align="stretch"
>
{displayRoomSearch && <RoomListSearch activeSpace={activeSpace} />} {displayRoomSearch && <RoomListSearch activeSpace={activeSpace} />}
<RoomListHeaderView /> <RoomListHeaderView />
<AutoSizer> <RoomListView />
{({ height, width }) => ( </Flex>
<List
rowRenderer={rowRenderer}
rowCount={rooms.length}
rowHeight={20}
height={height}
width={width}
/>
)}
</AutoSizer>
</section>
); );
}; };

View File

@ -0,0 +1,20 @@
/*
* 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 { useRoomListViewModel } from "../../../viewmodels/roomlist/RoomListViewModel";
import { RoomList } from "./RoomList";
/**
* Host the room list and the (future) room filters
*/
export function RoomListView(): JSX.Element {
const vm = useRoomListViewModel();
// Room filters will be added soon
return <RoomList vm={vm} />;
}

View File

@ -2104,12 +2104,16 @@
"one": "Currently joining %(count)s room", "one": "Currently joining %(count)s room",
"other": "Currently joining %(count)s rooms" "other": "Currently joining %(count)s rooms"
}, },
"list_title": "Room list",
"notification_options": "Notification options", "notification_options": "Notification options",
"open_space_menu": "Open space menu", "open_space_menu": "Open space menu",
"redacting_messages_status": { "redacting_messages_status": {
"one": "Currently removing messages in %(count)s room", "one": "Currently removing messages in %(count)s room",
"other": "Currently removing messages in %(count)s rooms" "other": "Currently removing messages in %(count)s rooms"
}, },
"room": {
"open_room": "Open room %(roomName)s"
},
"show_less": "Show less", "show_less": "Show less",
"show_n_more": { "show_n_more": {
"one": "Show %(count)s more", "one": "Show %(count)s more",

View File

@ -0,0 +1,52 @@
/*
* 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 } from "matrix-js-sdk/src/matrix";
import { render, screen, waitFor } from "jest-matrix-react";
import userEvent from "@testing-library/user-event";
import { mkRoom, stubClient } from "../../../../../test-utils";
import { type RoomListViewState } from "../../../../../../src/components/viewmodels/roomlist/RoomListViewModel";
import { RoomList } from "../../../../../../src/components/views/rooms/RoomListPanel/RoomList";
import DMRoomMap from "../../../../../../src/utils/DMRoomMap";
describe("<RoomList />", () => {
let matrixClient: MatrixClient;
let vm: RoomListViewState;
beforeEach(() => {
// Needed to render the virtualized list in rtl tests
// https://github.com/bvaughn/react-virtualized/issues/493#issuecomment-640084107
jest.spyOn(HTMLElement.prototype, "offsetHeight", "get").mockReturnValue(1500);
jest.spyOn(HTMLElement.prototype, "offsetWidth", "get").mockReturnValue(1500);
matrixClient = stubClient();
const rooms = Array.from({ length: 10 }, (_, i) => mkRoom(matrixClient, `room${i}`));
vm = { rooms, openRoom: jest.fn() };
// Needed to render a room list cell
DMRoomMap.makeShared(matrixClient);
jest.spyOn(DMRoomMap.shared(), "getUserIdForRoomId").mockReturnValue(null);
});
it("should render a room list", () => {
const { asFragment } = render(<RoomList vm={vm} />);
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);
});
});

View File

@ -0,0 +1,44 @@
/*
* 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();
});
});

View File

@ -0,0 +1,504 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<RoomList /> should render a room list 1`] = `
<DocumentFragment>
<div
class="mx_RoomList"
data-testid="room-list"
>
<div
style="overflow: visible; height: 0px; width: 0px;"
>
<div
aria-label="Room list"
aria-readonly="true"
class="ReactVirtualized__Grid ReactVirtualized__List mx_RoomList_List"
role="grid"
style="box-sizing: border-box; direction: ltr; height: 1500px; position: relative; width: 1500px; will-change: transform; overflow-x: hidden; overflow-y: hidden;"
tabindex="0"
>
<div
class="ReactVirtualized__Grid__innerScrollContainer"
role="row"
style="width: auto; height: 480px; max-width: 1500px; max-height: 480px; overflow: hidden; position: relative;"
>
<button
aria-label="Open room room0"
class="mx_RoomListCell"
role="gridcell"
style="height: 48px; left: 0px; position: absolute; top: 0px; width: 100%;"
type="button"
>
<div
class="mx_Flex mx_RoomListCell_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);"
>
<div
class="mx_DecoratedRoomAvatar"
>
<span
aria-label="Avatar"
class="_avatar_1qbcf_8 mx_BaseAvatar"
data-color="2"
data-testid="avatar-img"
data-type="round"
style="--cpd-avatar-size: 32px;"
>
<img
alt=""
class="_image_1qbcf_41"
data-type="round"
height="32px"
loading="lazy"
referrerpolicy="no-referrer"
src="http://this.is.a.url/avatar.url/room.png"
width="32px"
/>
</span>
</div>
<div
class="mx_Flex mx_RoomListCell_content"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: 0;"
>
<span
title="room0"
>
room0
</span>
</div>
</div>
</button>
<button
aria-label="Open room room1"
class="mx_RoomListCell"
role="gridcell"
style="height: 48px; left: 0px; position: absolute; top: 48px; width: 100%;"
type="button"
>
<div
class="mx_Flex mx_RoomListCell_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);"
>
<div
class="mx_DecoratedRoomAvatar"
>
<span
aria-label="Avatar"
class="_avatar_1qbcf_8 mx_BaseAvatar"
data-color="3"
data-testid="avatar-img"
data-type="round"
style="--cpd-avatar-size: 32px;"
>
<img
alt=""
class="_image_1qbcf_41"
data-type="round"
height="32px"
loading="lazy"
referrerpolicy="no-referrer"
src="http://this.is.a.url/avatar.url/room.png"
width="32px"
/>
</span>
</div>
<div
class="mx_Flex mx_RoomListCell_content"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: 0;"
>
<span
title="room1"
>
room1
</span>
</div>
</div>
</button>
<button
aria-label="Open room room2"
class="mx_RoomListCell"
role="gridcell"
style="height: 48px; left: 0px; position: absolute; top: 96px; width: 100%;"
type="button"
>
<div
class="mx_Flex mx_RoomListCell_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);"
>
<div
class="mx_DecoratedRoomAvatar"
>
<span
aria-label="Avatar"
class="_avatar_1qbcf_8 mx_BaseAvatar"
data-color="4"
data-testid="avatar-img"
data-type="round"
style="--cpd-avatar-size: 32px;"
>
<img
alt=""
class="_image_1qbcf_41"
data-type="round"
height="32px"
loading="lazy"
referrerpolicy="no-referrer"
src="http://this.is.a.url/avatar.url/room.png"
width="32px"
/>
</span>
</div>
<div
class="mx_Flex mx_RoomListCell_content"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: 0;"
>
<span
title="room2"
>
room2
</span>
</div>
</div>
</button>
<button
aria-label="Open room room3"
class="mx_RoomListCell"
role="gridcell"
style="height: 48px; left: 0px; position: absolute; top: 144px; width: 100%;"
type="button"
>
<div
class="mx_Flex mx_RoomListCell_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);"
>
<div
class="mx_DecoratedRoomAvatar"
>
<span
aria-label="Avatar"
class="_avatar_1qbcf_8 mx_BaseAvatar"
data-color="5"
data-testid="avatar-img"
data-type="round"
style="--cpd-avatar-size: 32px;"
>
<img
alt=""
class="_image_1qbcf_41"
data-type="round"
height="32px"
loading="lazy"
referrerpolicy="no-referrer"
src="http://this.is.a.url/avatar.url/room.png"
width="32px"
/>
</span>
</div>
<div
class="mx_Flex mx_RoomListCell_content"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: 0;"
>
<span
title="room3"
>
room3
</span>
</div>
</div>
</button>
<button
aria-label="Open room room4"
class="mx_RoomListCell"
role="gridcell"
style="height: 48px; left: 0px; position: absolute; top: 192px; width: 100%;"
type="button"
>
<div
class="mx_Flex mx_RoomListCell_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);"
>
<div
class="mx_DecoratedRoomAvatar"
>
<span
aria-label="Avatar"
class="_avatar_1qbcf_8 mx_BaseAvatar"
data-color="6"
data-testid="avatar-img"
data-type="round"
style="--cpd-avatar-size: 32px;"
>
<img
alt=""
class="_image_1qbcf_41"
data-type="round"
height="32px"
loading="lazy"
referrerpolicy="no-referrer"
src="http://this.is.a.url/avatar.url/room.png"
width="32px"
/>
</span>
</div>
<div
class="mx_Flex mx_RoomListCell_content"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: 0;"
>
<span
title="room4"
>
room4
</span>
</div>
</div>
</button>
<button
aria-label="Open room room5"
class="mx_RoomListCell"
role="gridcell"
style="height: 48px; left: 0px; position: absolute; top: 240px; width: 100%;"
type="button"
>
<div
class="mx_Flex mx_RoomListCell_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);"
>
<div
class="mx_DecoratedRoomAvatar"
>
<span
aria-label="Avatar"
class="_avatar_1qbcf_8 mx_BaseAvatar"
data-color="1"
data-testid="avatar-img"
data-type="round"
style="--cpd-avatar-size: 32px;"
>
<img
alt=""
class="_image_1qbcf_41"
data-type="round"
height="32px"
loading="lazy"
referrerpolicy="no-referrer"
src="http://this.is.a.url/avatar.url/room.png"
width="32px"
/>
</span>
</div>
<div
class="mx_Flex mx_RoomListCell_content"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: 0;"
>
<span
title="room5"
>
room5
</span>
</div>
</div>
</button>
<button
aria-label="Open room room6"
class="mx_RoomListCell"
role="gridcell"
style="height: 48px; left: 0px; position: absolute; top: 288px; width: 100%;"
type="button"
>
<div
class="mx_Flex mx_RoomListCell_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);"
>
<div
class="mx_DecoratedRoomAvatar"
>
<span
aria-label="Avatar"
class="_avatar_1qbcf_8 mx_BaseAvatar"
data-color="2"
data-testid="avatar-img"
data-type="round"
style="--cpd-avatar-size: 32px;"
>
<img
alt=""
class="_image_1qbcf_41"
data-type="round"
height="32px"
loading="lazy"
referrerpolicy="no-referrer"
src="http://this.is.a.url/avatar.url/room.png"
width="32px"
/>
</span>
</div>
<div
class="mx_Flex mx_RoomListCell_content"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: 0;"
>
<span
title="room6"
>
room6
</span>
</div>
</div>
</button>
<button
aria-label="Open room room7"
class="mx_RoomListCell"
role="gridcell"
style="height: 48px; left: 0px; position: absolute; top: 336px; width: 100%;"
type="button"
>
<div
class="mx_Flex mx_RoomListCell_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);"
>
<div
class="mx_DecoratedRoomAvatar"
>
<span
aria-label="Avatar"
class="_avatar_1qbcf_8 mx_BaseAvatar"
data-color="3"
data-testid="avatar-img"
data-type="round"
style="--cpd-avatar-size: 32px;"
>
<img
alt=""
class="_image_1qbcf_41"
data-type="round"
height="32px"
loading="lazy"
referrerpolicy="no-referrer"
src="http://this.is.a.url/avatar.url/room.png"
width="32px"
/>
</span>
</div>
<div
class="mx_Flex mx_RoomListCell_content"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: 0;"
>
<span
title="room7"
>
room7
</span>
</div>
</div>
</button>
<button
aria-label="Open room room8"
class="mx_RoomListCell"
role="gridcell"
style="height: 48px; left: 0px; position: absolute; top: 384px; width: 100%;"
type="button"
>
<div
class="mx_Flex mx_RoomListCell_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);"
>
<div
class="mx_DecoratedRoomAvatar"
>
<span
aria-label="Avatar"
class="_avatar_1qbcf_8 mx_BaseAvatar"
data-color="4"
data-testid="avatar-img"
data-type="round"
style="--cpd-avatar-size: 32px;"
>
<img
alt=""
class="_image_1qbcf_41"
data-type="round"
height="32px"
loading="lazy"
referrerpolicy="no-referrer"
src="http://this.is.a.url/avatar.url/room.png"
width="32px"
/>
</span>
</div>
<div
class="mx_Flex mx_RoomListCell_content"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: 0;"
>
<span
title="room8"
>
room8
</span>
</div>
</div>
</button>
<button
aria-label="Open room room9"
class="mx_RoomListCell"
role="gridcell"
style="height: 48px; left: 0px; position: absolute; top: 432px; width: 100%;"
type="button"
>
<div
class="mx_Flex mx_RoomListCell_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);"
>
<div
class="mx_DecoratedRoomAvatar"
>
<span
aria-label="Avatar"
class="_avatar_1qbcf_8 mx_BaseAvatar"
data-color="5"
data-testid="avatar-img"
data-type="round"
style="--cpd-avatar-size: 32px;"
>
<img
alt=""
class="_image_1qbcf_41"
data-type="round"
height="32px"
loading="lazy"
referrerpolicy="no-referrer"
src="http://this.is.a.url/avatar.url/room.png"
width="32px"
/>
</span>
</div>
<div
class="mx_Flex mx_RoomListCell_content"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: 0;"
>
<span
title="room9"
>
room9
</span>
</div>
</div>
</button>
</div>
</div>
</div>
<div
class="resize-triggers"
>
<div
class="expand-trigger"
>
<div
style="width: 1501px; height: 1501px;"
/>
</div>
<div
class="contract-trigger"
/>
</div>
</div>
</DocumentFragment>
`;

View File

@ -0,0 +1,50 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<RoomListCell /> should render a room cell 1`] = `
<DocumentFragment>
<button
aria-label="Open room room1"
class="mx_RoomListCell"
type="button"
>
<div
class="mx_Flex mx_RoomListCell_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);"
>
<div
class="mx_DecoratedRoomAvatar"
>
<span
aria-label="Avatar"
class="_avatar_1qbcf_8 mx_BaseAvatar"
data-color="3"
data-testid="avatar-img"
data-type="round"
style="--cpd-avatar-size: 32px;"
>
<img
alt=""
class="_image_1qbcf_41"
data-type="round"
height="32px"
loading="lazy"
referrerpolicy="no-referrer"
src="http://this.is.a.url/avatar.url/room.png"
width="32px"
/>
</span>
</div>
<div
class="mx_Flex mx_RoomListCell_content"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: 0;"
>
<span
title="room1"
>
room1
</span>
</div>
</div>
</button>
</DocumentFragment>
`;

View File

@ -3,8 +3,9 @@
exports[`<RoomListPanel /> should not render the RoomListSearch component when UIComponent.FilterContainer is at false 1`] = ` exports[`<RoomListPanel /> should not render the RoomListSearch component when UIComponent.FilterContainer is at false 1`] = `
<DocumentFragment> <DocumentFragment>
<section <section
class="mx_RoomListPanel" class="mx_Flex mx_RoomListPanel"
data-testid="room-list-panel" data-testid="room-list-panel"
style="--mx-flex-display: flex; --mx-flex-direction: column; --mx-flex-align: stretch; --mx-flex-justify: start; --mx-flex-gap: 0;"
> >
<header <header
aria-label="Room options" aria-label="Room options"
@ -23,13 +24,17 @@ exports[`<RoomListPanel /> should not render the RoomListSearch component when U
</h1> </h1>
</div> </div>
</header> </header>
<div
class="mx_RoomList"
data-testid="room-list"
>
<div <div
style="overflow: visible; height: 0px; width: 0px;" style="overflow: visible; height: 0px; width: 0px;"
> >
<div <div
aria-label="grid" aria-label="Room list"
aria-readonly="true" aria-readonly="true"
class="ReactVirtualized__Grid ReactVirtualized__List" class="ReactVirtualized__Grid ReactVirtualized__List mx_RoomList_List"
role="grid" role="grid"
style="box-sizing: border-box; direction: ltr; height: 0px; position: relative; width: 0px; will-change: transform; overflow-x: hidden; overflow-y: hidden;" style="box-sizing: border-box; direction: ltr; height: 0px; position: relative; width: 0px; will-change: transform; overflow-x: hidden; overflow-y: hidden;"
tabindex="0" tabindex="0"
@ -49,6 +54,7 @@ exports[`<RoomListPanel /> should not render the RoomListSearch component when U
class="contract-trigger" class="contract-trigger"
/> />
</div> </div>
</div>
</section> </section>
</DocumentFragment> </DocumentFragment>
`; `;
@ -56,8 +62,9 @@ exports[`<RoomListPanel /> should not render the RoomListSearch component when U
exports[`<RoomListPanel /> should render the RoomListSearch component when UIComponent.FilterContainer is at true 1`] = ` exports[`<RoomListPanel /> should render the RoomListSearch component when UIComponent.FilterContainer is at true 1`] = `
<DocumentFragment> <DocumentFragment>
<section <section
class="mx_RoomListPanel" class="mx_Flex mx_RoomListPanel"
data-testid="room-list-panel" data-testid="room-list-panel"
style="--mx-flex-display: flex; --mx-flex-direction: column; --mx-flex-align: stretch; --mx-flex-justify: start; --mx-flex-gap: 0;"
> >
<div <div
class="mx_Flex mx_RoomListSearch" class="mx_Flex mx_RoomListSearch"
@ -167,13 +174,17 @@ exports[`<RoomListPanel /> should render the RoomListSearch component when UICom
</div> </div>
</button> </button>
</header> </header>
<div
class="mx_RoomList"
data-testid="room-list"
>
<div <div
style="overflow: visible; height: 0px; width: 0px;" style="overflow: visible; height: 0px; width: 0px;"
> >
<div <div
aria-label="grid" aria-label="Room list"
aria-readonly="true" aria-readonly="true"
class="ReactVirtualized__Grid ReactVirtualized__List" class="ReactVirtualized__Grid ReactVirtualized__List mx_RoomList_List"
role="grid" role="grid"
style="box-sizing: border-box; direction: ltr; height: 0px; position: relative; width: 0px; will-change: transform; overflow-x: hidden; overflow-y: hidden;" style="box-sizing: border-box; direction: ltr; height: 0px; position: relative; width: 0px; will-change: transform; overflow-x: hidden; overflow-y: hidden;"
tabindex="0" tabindex="0"
@ -193,6 +204,7 @@ exports[`<RoomListPanel /> should render the RoomListSearch component when UICom
class="contract-trigger" class="contract-trigger"
/> />
</div> </div>
</div>
</section> </section>
</DocumentFragment> </DocumentFragment>
`; `;