New room list: add search section (#29251)

* feat(new room list): move `RoomListView` to its own folder and add styling

* feat(new room list): add search section

* test(new room list): add tests for `RoomListSearch`

* test(new room list): add tests for `RoomListView`

* test(e2e): add method to close notification toast to `ElementAppPage`

* test(e2e): add tests for the search section

* test(e2e): add tests for the room list view

* refactor: use Flex component

* fix: loop icon size in search button

* refactor: remove `focus_room_filter` listener
This commit is contained in:
Florian D
2025-02-13 16:49:09 +01:00
committed by GitHub
parent 85f80b1d0a
commit 2abd5342c2
18 changed files with 609 additions and 21 deletions

View File

@@ -0,0 +1,84 @@
/*
* 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 { render, screen } from "jest-matrix-react";
import { mocked } from "jest-mock";
import userEvent from "@testing-library/user-event";
import { RoomListSearch } from "../../../../../../src/components/views/rooms/RoomListView/RoomListSearch";
import { MetaSpace } from "../../../../../../src/stores/spaces";
import { shouldShowComponent } from "../../../../../../src/customisations/helpers/UIComponents";
import defaultDispatcher from "../../../../../../src/dispatcher/dispatcher";
import { Action } from "../../../../../../src/dispatcher/actions";
jest.mock("../../../../../../src/customisations/helpers/UIComponents", () => ({
shouldShowComponent: jest.fn(),
}));
describe("<RoomListSearch />", () => {
function renderComponent(activeSpace = MetaSpace.Home) {
return render(<RoomListSearch activeSpace={activeSpace} />);
}
beforeEach(() => {
// By default, we consider shouldShowComponent(UIComponent.ExploreRooms) should return true
mocked(shouldShowComponent).mockReturnValue(true);
});
it("should display all the buttons", () => {
const { asFragment } = renderComponent();
// The search and explore buttons should be displayed
expect(screen.getByRole("button", { name: "Search Ctrl K" })).toBeInTheDocument();
expect(screen.getByRole("button", { name: "Explore rooms" })).toBeInTheDocument();
expect(asFragment()).toMatchSnapshot();
});
it("should hide the explore button when the active space is not MetaSpace.Home", () => {
const { asFragment } = renderComponent(MetaSpace.VideoRooms);
// The search button should be displayed but not the explore button
expect(screen.getByRole("button", { name: "Search Ctrl K" })).toBeInTheDocument();
expect(screen.queryByRole("button", { name: "Explore rooms" })).not.toBeInTheDocument();
expect(asFragment()).toMatchSnapshot();
});
it("should hide the explore button when UIComponent.ExploreRooms is disabled", () => {
mocked(shouldShowComponent).mockReturnValue(false);
const { asFragment } = renderComponent();
// The search button should be displayed but not the explore button
expect(screen.getByRole("button", { name: "Search Ctrl K" })).toBeInTheDocument();
expect(screen.queryByRole("button", { name: "Explore rooms" })).not.toBeInTheDocument();
expect(asFragment()).toMatchSnapshot();
});
it("should open the spotlight when the search button is clicked", async () => {
const fireSpy = jest.spyOn(defaultDispatcher, "fire");
const user = userEvent.setup();
renderComponent();
// Click on the search button
await user.click(screen.getByRole("button", { name: "Search Ctrl K" }));
// The spotlight should be opened
expect(fireSpy).toHaveBeenCalledWith(Action.OpenSpotlight);
});
it("should open the room directory when the explore button is clicked", async () => {
const fireSpy = jest.spyOn(defaultDispatcher, "fire");
const user = userEvent.setup();
renderComponent();
// Click on the search button
await user.click(screen.getByRole("button", { name: "Explore rooms" }));
// The spotlight should be opened
expect(fireSpy).toHaveBeenCalledWith(Action.ViewRoomDirectory);
});
});

View File

@@ -0,0 +1,43 @@
/*
* 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 { render, screen } from "jest-matrix-react";
import { mocked } from "jest-mock";
import { RoomListView } from "../../../../../../src/components/views/rooms/RoomListView";
import { shouldShowComponent } from "../../../../../../src/customisations/helpers/UIComponents";
import { MetaSpace } from "../../../../../../src/stores/spaces";
jest.mock("../../../../../../src/customisations/helpers/UIComponents", () => ({
shouldShowComponent: jest.fn(),
}));
describe("<RoomListView />", () => {
function renderComponent() {
return render(<RoomListView activeSpace={MetaSpace.Home} />);
}
beforeEach(() => {
// By default, we consider shouldShowComponent(UIComponent.FilterContainer) should return true
mocked(shouldShowComponent).mockReturnValue(true);
});
it("should render the RoomListSearch component when UIComponent.FilterContainer is at true", () => {
const { asFragment } = renderComponent();
expect(screen.getByRole("button", { name: "Search Ctrl K" })).toBeInTheDocument();
expect(asFragment()).toMatchSnapshot();
});
it("should not render the RoomListSearch component when UIComponent.FilterContainer is at false", () => {
mocked(shouldShowComponent).mockReturnValue(false);
const { asFragment } = renderComponent();
expect(screen.queryByRole("button", { name: "Search Ctrl K" })).toBeNull();
expect(asFragment()).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,142 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<RoomListSearch /> should display all the buttons 1`] = `
<DocumentFragment>
<div
class="mx_Flex mx_RoomListSearch"
role="search"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-2x);"
>
<button
class="_button_i91xf_17 mx_RoomListSearch_search _has-icon_i91xf_66"
data-kind="secondary"
data-size="sm"
role="button"
tabindex="0"
>
<svg
aria-hidden="true"
fill="currentColor"
height="20"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M15.05 16.463a7.5 7.5 0 1 1 1.414-1.414l3.243 3.244a1 1 0 0 1-1.414 1.414l-3.244-3.244ZM16 10.5a5.5 5.5 0 1 0-11 0 5.5 5.5 0 0 0 11 0Z"
/>
</svg>
<span
class="mx_Flex"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: start; --mx-flex-justify: space-between; --mx-flex-gap: 0;"
>
Search
<kbd>
Ctrl K
</kbd>
</span>
</button>
<button
aria-label="Explore rooms"
class="_button_i91xf_17 mx_RoomListSearch_explore _has-icon_i91xf_66 _icon-only_i91xf_59"
data-kind="secondary"
data-size="sm"
role="button"
tabindex="0"
>
<svg
aria-hidden="true"
fill="currentColor"
height="20"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 13a.968.968 0 0 1-.713-.287A.968.968 0 0 1 11 12c0-.283.096-.52.287-.713A.968.968 0 0 1 12 11c.283 0 .52.096.713.287.191.192.287.43.287.713s-.096.52-.287.713A.968.968 0 0 1 12 13Zm0 9a9.738 9.738 0 0 1-3.9-.788 10.099 10.099 0 0 1-3.175-2.137c-.9-.9-1.612-1.958-2.137-3.175A9.738 9.738 0 0 1 2 12a9.74 9.74 0 0 1 .788-3.9 10.099 10.099 0 0 1 2.137-3.175c.9-.9 1.958-1.612 3.175-2.137A9.738 9.738 0 0 1 12 2a9.74 9.74 0 0 1 3.9.788 10.098 10.098 0 0 1 3.175 2.137c.9.9 1.613 1.958 2.137 3.175A9.738 9.738 0 0 1 22 12a9.738 9.738 0 0 1-.788 3.9 10.098 10.098 0 0 1-2.137 3.175c-.9.9-1.958 1.613-3.175 2.137A9.738 9.738 0 0 1 12 22Zm0-2c2.233 0 4.125-.775 5.675-2.325C19.225 16.125 20 14.233 20 12c0-2.233-.775-4.125-2.325-5.675C16.125 4.775 14.233 4 12 4c-2.233 0-4.125.775-5.675 2.325C4.775 7.875 4 9.767 4 12c0 2.233.775 4.125 2.325 5.675C7.875 19.225 9.767 20 12 20Zm0 0c-2.233 0-4.125-.775-5.675-2.325C4.775 16.125 4 14.233 4 12c0-2.233.775-4.125 2.325-5.675C7.875 4.775 9.767 4 12 4c2.233 0 4.125.775 5.675 2.325C19.225 7.875 20 9.767 20 12c0 2.233-.775 4.125-2.325 5.675C16.125 19.225 14.233 20 12 20Zm1.675-5.85c.1-.05.192-.117.275-.2.083-.083.15-.175.2-.275l2.925-6.25c.083-.167.063-.313-.063-.438-.125-.125-.27-.145-.437-.062l-6.25 2.925c-.1.05-.192.117-.275.2-.083.083-.15.175-.2.275l-2.925 6.25c-.083.167-.063.313.063.438.124.124.27.145.437.062l6.25-2.925Z"
/>
</svg>
</button>
</div>
</DocumentFragment>
`;
exports[`<RoomListSearch /> should hide the explore button when UIComponent.ExploreRooms is disabled 1`] = `
<DocumentFragment>
<div
class="mx_Flex mx_RoomListSearch"
role="search"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-2x);"
>
<button
class="_button_i91xf_17 mx_RoomListSearch_search _has-icon_i91xf_66"
data-kind="secondary"
data-size="sm"
role="button"
tabindex="0"
>
<svg
aria-hidden="true"
fill="currentColor"
height="20"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M15.05 16.463a7.5 7.5 0 1 1 1.414-1.414l3.243 3.244a1 1 0 0 1-1.414 1.414l-3.244-3.244ZM16 10.5a5.5 5.5 0 1 0-11 0 5.5 5.5 0 0 0 11 0Z"
/>
</svg>
<span
class="mx_Flex"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: start; --mx-flex-justify: space-between; --mx-flex-gap: 0;"
>
Search
<kbd>
Ctrl K
</kbd>
</span>
</button>
</div>
</DocumentFragment>
`;
exports[`<RoomListSearch /> should hide the explore button when the active space is not MetaSpace.Home 1`] = `
<DocumentFragment>
<div
class="mx_Flex mx_RoomListSearch"
role="search"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-2x);"
>
<button
class="_button_i91xf_17 mx_RoomListSearch_search _has-icon_i91xf_66"
data-kind="secondary"
data-size="sm"
role="button"
tabindex="0"
>
<svg
aria-hidden="true"
fill="currentColor"
height="20"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M15.05 16.463a7.5 7.5 0 1 1 1.414-1.414l3.243 3.244a1 1 0 0 1-1.414 1.414l-3.244-3.244ZM16 10.5a5.5 5.5 0 1 0-11 0 5.5 5.5 0 0 0 11 0Z"
/>
</svg>
<span
class="mx_Flex"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: start; --mx-flex-justify: space-between; --mx-flex-gap: 0;"
>
Search
<kbd>
Ctrl K
</kbd>
</span>
</button>
</div>
</DocumentFragment>
`;

View File

@@ -0,0 +1,76 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<RoomListView /> should not render the RoomListSearch component when UIComponent.FilterContainer is at false 1`] = `
<DocumentFragment>
<div
class="mx_RoomListView"
data-testid="room-list-view"
/>
</DocumentFragment>
`;
exports[`<RoomListView /> should render the RoomListSearch component when UIComponent.FilterContainer is at true 1`] = `
<DocumentFragment>
<div
class="mx_RoomListView"
data-testid="room-list-view"
>
<div
class="mx_Flex mx_RoomListSearch"
role="search"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-2x);"
>
<button
class="_button_i91xf_17 mx_RoomListSearch_search _has-icon_i91xf_66"
data-kind="secondary"
data-size="sm"
role="button"
tabindex="0"
>
<svg
aria-hidden="true"
fill="currentColor"
height="20"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M15.05 16.463a7.5 7.5 0 1 1 1.414-1.414l3.243 3.244a1 1 0 0 1-1.414 1.414l-3.244-3.244ZM16 10.5a5.5 5.5 0 1 0-11 0 5.5 5.5 0 0 0 11 0Z"
/>
</svg>
<span
class="mx_Flex"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: start; --mx-flex-justify: space-between; --mx-flex-gap: 0;"
>
Search
<kbd>
Ctrl K
</kbd>
</span>
</button>
<button
aria-label="Explore rooms"
class="_button_i91xf_17 mx_RoomListSearch_explore _has-icon_i91xf_66 _icon-only_i91xf_59"
data-kind="secondary"
data-size="sm"
role="button"
tabindex="0"
>
<svg
aria-hidden="true"
fill="currentColor"
height="20"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 13a.968.968 0 0 1-.713-.287A.968.968 0 0 1 11 12c0-.283.096-.52.287-.713A.968.968 0 0 1 12 11c.283 0 .52.096.713.287.191.192.287.43.287.713s-.096.52-.287.713A.968.968 0 0 1 12 13Zm0 9a9.738 9.738 0 0 1-3.9-.788 10.099 10.099 0 0 1-3.175-2.137c-.9-.9-1.612-1.958-2.137-3.175A9.738 9.738 0 0 1 2 12a9.74 9.74 0 0 1 .788-3.9 10.099 10.099 0 0 1 2.137-3.175c.9-.9 1.958-1.612 3.175-2.137A9.738 9.738 0 0 1 12 2a9.74 9.74 0 0 1 3.9.788 10.098 10.098 0 0 1 3.175 2.137c.9.9 1.613 1.958 2.137 3.175A9.738 9.738 0 0 1 22 12a9.738 9.738 0 0 1-.788 3.9 10.098 10.098 0 0 1-2.137 3.175c-.9.9-1.958 1.613-3.175 2.137A9.738 9.738 0 0 1 12 22Zm0-2c2.233 0 4.125-.775 5.675-2.325C19.225 16.125 20 14.233 20 12c0-2.233-.775-4.125-2.325-5.675C16.125 4.775 14.233 4 12 4c-2.233 0-4.125.775-5.675 2.325C4.775 7.875 4 9.767 4 12c0 2.233.775 4.125 2.325 5.675C7.875 19.225 9.767 20 12 20Zm0 0c-2.233 0-4.125-.775-5.675-2.325C4.775 16.125 4 14.233 4 12c0-2.233.775-4.125 2.325-5.675C7.875 4.775 9.767 4 12 4c2.233 0 4.125.775 5.675 2.325C19.225 7.875 20 9.767 20 12c0 2.233-.775 4.125-2.325 5.675C16.125 19.225 14.233 20 12 20Zm1.675-5.85c.1-.05.192-.117.275-.2.083-.083.15-.175.2-.275l2.925-6.25c.083-.167.063-.313-.063-.438-.125-.125-.27-.145-.437-.062l-6.25 2.925c-.1.05-.192.117-.275.2-.083.083-.15.175-.2.275l-2.925 6.25c-.083.167-.063.313.063.438.124.124.27.145.437.062l6.25-2.925Z"
/>
</svg>
</button>
</div>
</div>
</DocumentFragment>
`;