Migrate the room list view to shared components (#31921)

* Add NotificationDecoration component

Add the NotificationDecoration component to shared-components.
This is a leaf component that renders notification badges and indicators
for rooms/items including mentions, unread counts, call indicators, etc.

* Add RoomListItem component

Add the RoomListItem component to shared-components.
Includes context menu, hover menu, notification menu, and more options menu.

* Add RoomListPrimaryFilters component

Add filter chips component for filtering the room list by
unread, people, rooms, favourites, mentions, invites, and low priority.

* Update VirtualizedList component

Update VirtualizedList to support the room list virtualization requirements.

* Add RoomList component

Add RoomList component that renders a virtualized list of room items.
Includes story mocks for testing.

* Add RoomListView component

Add RoomListView component that composes RoomList with filters,
empty states, and loading skeleton.

* Export room-list components from shared-components

Add exports for RoomListView, RoomListItem, RoomListPrimaryFilters, and RoomList.
Include i18n strings for room list components.

* Add RoomListItemViewModel

Add view model for individual room list items.
Manages per-room subscriptions and updates only when specific room data changes.

* Add RoomListViewViewModel

Add view model for the room list view.
Manages room list state, filtering, keyboard navigation, and child view models.

* Integrate shared components into RoomListView

Update RoomListView to use the new ViewModels and shared components.
Includes i18n string updates for element-web.

* Remove old room list implementation

Remove old ViewModels, hooks, and view components that are now
replaced by the shared-components implementation.

* Update sliding-sync playwright test

Update test expectations for new room list implementation.

* Add figma links

* Move viewModels to the right folder

* Rename to RoomListEmptyStateView

* Update VirtualizedRoomListView naming

* Update screenshots and snapshots

* Move viewmodel tests to the right location and fix some imports

* lint

* Use unknown as an Opaque type rather than any. It discourages property access within shared components and can still be cast back in EW.

* Update screenshots for new shared component rendering params

* Make room order tests deterministic
This commit is contained in:
David Langley
2026-02-05 21:05:14 +00:00
committed by GitHub
parent 6dba71a453
commit 6da1412de8
140 changed files with 20046 additions and 5950 deletions
@@ -18,19 +18,28 @@ test.describe("Read receipts", { tag: "@mergequeue" }, () => {
util,
msg,
page,
app,
bot,
}) => {
// Create a third room to navigate to
const room3Id = await app.client.createRoom({ name: "Room Gamma", invite: [bot.credentials.userId] });
await bot.awaitRoomMembership(room3Id);
const room3 = { name: "Room Gamma", roomId: room3Id };
await util.goTo(room2);
// Display the unread first room
await util.receiveMessages(room2, ["Msg2"]);
await util.receiveMessages(room1, ["Msg1"]);
await page.reload();
// switch rooms so they can re-order in the list
await util.goTo(room1);
// Switch to room3 so neither room1 nor room2 is selected/sticky
// This allows them to reorder based on activity
await util.goTo(room3);
// Room 1 has an unread message and should be displayed first
// (as the default is to sort by activity)
await util.assertRoomListOrder([room1, room2]);
await util.assertRoomListOrder([room1, room2, room3]);
});
test("Rooms with unread threads appear at the top of room list with default 'activity' order", async ({
@@ -38,18 +47,29 @@ test.describe("Read receipts", { tag: "@mergequeue" }, () => {
roomBeta: room2,
util,
msg,
app,
bot,
}) => {
// Create a third room to navigate to
const room3Id = await app.client.createRoom({ name: "Room Gamma", invite: [bot.credentials.userId] });
await bot.awaitRoomMembership(room3Id);
const room3 = { name: "Room Gamma", roomId: room3Id };
await util.goTo(room2);
await util.receiveMessages(room1, ["Msg1"]);
await util.receiveMessages(room2, ["Msg2"]);
await util.markAsRead(room1);
await util.assertRead(room1);
// Display the unread first room
// Display the unread first room (room1 moves above room2 as it has an unread thread)
await util.receiveMessages(room1, [msg.threadedOff("Msg1", "Resp1")]);
await util.saveAndReload();
// Switch to room3 so neither room1 nor room2 is selected/sticky
await util.goTo(room3);
// Room 1 has an unread message and should be displayed first
await util.assertRoomListOrder([room1, room2]);
await util.assertRoomListOrder([room1, room2, room3]);
});
});
});
@@ -38,7 +38,7 @@ const test = base.extend<{
test.describe("Sliding Sync", () => {
const checkOrder = async (wantOrder: string[], page: Page) => {
await expect(page.getByTestId("room-list").locator(".mx_RoomListItemView_text")).toHaveText(wantOrder);
await expect(page.getByTestId("room-list").getByTestId("room-name")).toHaveText(wantOrder);
};
const bumpRoom = async (roomId: string, app: ElementAppPage) => {