New room list: add empty state (#29512)
* refactor: extract room creation and right verification * refactor: update `RoomListHeaderViewModel` to use utils * feat(room list filter): add filter key to `PrimaryFilter` model * feat(room list filter): return active primary filter * feat(room list): add create room action and rights verification * test: update room list tests * feat(empty room list): add empty room list * test(empty room list): add empty room list tests * feat(room list): use empty room list in `RoomListView` * test(room list panel): update tests * test(e2e): add e2e tests for empty room list * test(e2e): update room list header snapshot
This commit is contained in:
@@ -6,13 +6,12 @@
|
||||
*/
|
||||
|
||||
import { renderHook } from "jest-matrix-react";
|
||||
import { JoinRule, type MatrixClient, type Room, type RoomState, RoomType } from "matrix-js-sdk/src/matrix";
|
||||
import { JoinRule, type MatrixClient, type Room, RoomType } from "matrix-js-sdk/src/matrix";
|
||||
import { mocked } from "jest-mock";
|
||||
|
||||
import { useRoomListHeaderViewModel } from "../../../../../src/components/viewmodels/roomlist/RoomListHeaderViewModel";
|
||||
import SpaceStore from "../../../../../src/stores/spaces/SpaceStore";
|
||||
import { mkStubRoom, stubClient, withClientContextRenderOptions } from "../../../../test-utils";
|
||||
import { shouldShowComponent } from "../../../../../src/customisations/helpers/UIComponents";
|
||||
import SettingsStore from "../../../../../src/settings/SettingsStore";
|
||||
import defaultDispatcher from "../../../../../src/dispatcher/dispatcher";
|
||||
import { Action } from "../../../../../src/dispatcher/actions";
|
||||
@@ -23,9 +22,11 @@ import {
|
||||
showSpacePreferences,
|
||||
showSpaceSettings,
|
||||
} from "../../../../../src/utils/space";
|
||||
import { createRoom, hasCreateRoomRights } from "../../../../../src/components/viewmodels/roomlist/utils";
|
||||
|
||||
jest.mock("../../../../../src/customisations/helpers/UIComponents", () => ({
|
||||
shouldShowComponent: jest.fn(),
|
||||
jest.mock("../../../../../src/components/viewmodels/roomlist/utils", () => ({
|
||||
hasCreateRoomRights: jest.fn().mockReturnValue(false),
|
||||
createRoom: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock("../../../../../src/utils/space", () => ({
|
||||
@@ -68,19 +69,19 @@ describe("useRoomListHeaderViewModel", () => {
|
||||
});
|
||||
|
||||
it("should be displayComposeMenu=true and canCreateRoom=true if the user can creates room", () => {
|
||||
mocked(shouldShowComponent).mockReturnValue(false);
|
||||
mocked(hasCreateRoomRights).mockReturnValue(false);
|
||||
const { result, rerender } = render();
|
||||
expect(result.current.displayComposeMenu).toBe(false);
|
||||
expect(result.current.canCreateRoom).toBe(false);
|
||||
|
||||
mocked(shouldShowComponent).mockReturnValue(true);
|
||||
mocked(hasCreateRoomRights).mockReturnValue(true);
|
||||
rerender();
|
||||
expect(result.current.displayComposeMenu).toBe(true);
|
||||
expect(result.current.canCreateRoom).toBe(true);
|
||||
});
|
||||
|
||||
it("should be displayComposeMenu=true if the user can creates video room", () => {
|
||||
mocked(shouldShowComponent).mockReturnValue(false);
|
||||
mocked(hasCreateRoomRights).mockReturnValue(false);
|
||||
jest.spyOn(SettingsStore, "getValue").mockReturnValue(true);
|
||||
|
||||
const { result } = render();
|
||||
@@ -93,25 +94,6 @@ describe("useRoomListHeaderViewModel", () => {
|
||||
expect(result.current.displaySpaceMenu).toBe(true);
|
||||
});
|
||||
|
||||
it("should be canCreateRoom=false if the user has not the right to create a room in a space", () => {
|
||||
mocked(shouldShowComponent).mockReturnValue(true);
|
||||
jest.spyOn(SpaceStore.instance, "activeSpaceRoom", "get").mockReturnValue(space);
|
||||
|
||||
const { result } = render();
|
||||
expect(result.current.canCreateRoom).toBe(false);
|
||||
});
|
||||
|
||||
it("should be canCreateRoom=true if the user has the right to create a room in a space", () => {
|
||||
mocked(shouldShowComponent).mockReturnValue(true);
|
||||
jest.spyOn(SpaceStore.instance, "activeSpaceRoom", "get").mockReturnValue(space);
|
||||
jest.spyOn(space.getLiveTimeline(), "getState").mockReturnValue({
|
||||
maySendStateEvent: jest.fn().mockReturnValue(true),
|
||||
} as unknown as RoomState);
|
||||
|
||||
const { result } = render();
|
||||
expect(result.current.canCreateRoom).toBe(true);
|
||||
});
|
||||
|
||||
it("should be canInviteInSpace=true if the space join rule is public", () => {
|
||||
jest.spyOn(SpaceStore.instance, "activeSpaceRoom", "get").mockReturnValue(space);
|
||||
jest.spyOn(space, "getJoinRule").mockReturnValue(JoinRule.Public);
|
||||
@@ -150,20 +132,19 @@ describe("useRoomListHeaderViewModel", () => {
|
||||
expect(spy).toHaveBeenCalledWith(Action.CreateChat);
|
||||
});
|
||||
|
||||
it("should fire Action.CreateRoom when createRoom is called", () => {
|
||||
const spy = jest.spyOn(defaultDispatcher, "fire");
|
||||
it("should call createRoom from utils when createRoom is called", () => {
|
||||
const { result } = render();
|
||||
result.current.createRoom(new Event("click"));
|
||||
|
||||
expect(spy).toHaveBeenCalledWith(Action.CreateRoom);
|
||||
expect(createRoom).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should call showCreateNewRoom when createRoom is called in a space", () => {
|
||||
it("should call createRoom from utils when createRoom is called in a space", () => {
|
||||
jest.spyOn(SpaceStore.instance, "activeSpaceRoom", "get").mockReturnValue(space);
|
||||
const { result } = render();
|
||||
result.current.createRoom(new Event("click"));
|
||||
|
||||
expect(showCreateNewRoom).toHaveBeenCalledWith(space);
|
||||
expect(createRoom).toHaveBeenCalledWith(space);
|
||||
});
|
||||
|
||||
it("should fire Action.CreateRoom with RoomType.UnstableCall when createVideoRoom is called and feature_element_call_video_rooms is enabled", () => {
|
||||
|
||||
@@ -7,6 +7,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
|
||||
import { range } from "lodash";
|
||||
import { act, renderHook, waitFor } from "jest-matrix-react";
|
||||
import { mocked } from "jest-mock";
|
||||
|
||||
import RoomListStoreV3 from "../../../../../src/stores/room-list-v3/RoomListStoreV3";
|
||||
import { mkStubRoom } from "../../../../test-utils";
|
||||
@@ -17,6 +18,14 @@ import { SecondaryFilters } from "../../../../../src/components/viewmodels/rooml
|
||||
import { SortingAlgorithm } from "../../../../../src/stores/room-list-v3/skip-list/sorters";
|
||||
import { SortOption } from "../../../../../src/components/viewmodels/roomlist/useSorter";
|
||||
import SettingsStore from "../../../../../src/settings/SettingsStore";
|
||||
import { hasCreateRoomRights, createRoom } from "../../../../../src/components/viewmodels/roomlist/utils";
|
||||
import dispatcher from "../../../../../src/dispatcher/dispatcher";
|
||||
import { Action } from "../../../../../src/dispatcher/actions";
|
||||
|
||||
jest.mock("../../../../../src/components/viewmodels/roomlist/utils", () => ({
|
||||
hasCreateRoomRights: jest.fn().mockReturnValue(false),
|
||||
createRoom: jest.fn(),
|
||||
}));
|
||||
|
||||
describe("RoomListViewModel", () => {
|
||||
function mockAndCreateRooms() {
|
||||
@@ -139,6 +148,19 @@ describe("RoomListViewModel", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("should return the current active primary filter", async () => {
|
||||
// Let's say that the user's preferred sorting is alphabetic
|
||||
mockAndCreateRooms();
|
||||
const { result: vm } = renderHook(() => useRoomListViewModel());
|
||||
// Toggle people filter
|
||||
const i = vm.current.primaryFilters.findIndex((f) => f.name === "People");
|
||||
expect(vm.current.primaryFilters[i].active).toEqual(false);
|
||||
act(() => vm.current.primaryFilters[i].toggle());
|
||||
|
||||
// The active primary filter should be the People filter
|
||||
expect(vm.current.activePrimaryFilter).toEqual(vm.current.primaryFilters[i]);
|
||||
});
|
||||
|
||||
const testcases: Array<[string, { secondary: SecondaryFilters; filterKey: FilterKey }, string]> = [
|
||||
[
|
||||
"Mentions only",
|
||||
@@ -240,4 +262,31 @@ describe("RoomListViewModel", () => {
|
||||
expect(fn).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Create room and chat", () => {
|
||||
it("should be canCreateRoom=false if hasCreateRoomRights=false", () => {
|
||||
mocked(hasCreateRoomRights).mockReturnValue(false);
|
||||
const { result } = renderHook(() => useRoomListViewModel());
|
||||
expect(result.current.canCreateRoom).toBe(false);
|
||||
});
|
||||
|
||||
it("should be canCreateRoom=true if hasCreateRoomRights=true", () => {
|
||||
mocked(hasCreateRoomRights).mockReturnValue(true);
|
||||
const { result } = renderHook(() => useRoomListViewModel());
|
||||
expect(result.current.canCreateRoom).toBe(true);
|
||||
});
|
||||
|
||||
it("should call createRoom", () => {
|
||||
const { result } = renderHook(() => useRoomListViewModel());
|
||||
result.current.createRoom();
|
||||
expect(mocked(createRoom)).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should dispatch Action.CreateChat", () => {
|
||||
const spy = jest.spyOn(dispatcher, "fire");
|
||||
const { result } = renderHook(() => useRoomListViewModel());
|
||||
result.current.createChatRoom();
|
||||
expect(spy).toHaveBeenCalledWith(Action.CreateChat);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
69
test/unit-tests/components/viewmodels/roomlist/utils-test.ts
Normal file
69
test/unit-tests/components/viewmodels/roomlist/utils-test.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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 { mocked } from "jest-mock";
|
||||
|
||||
import type { MatrixClient, Room, RoomState } from "matrix-js-sdk/src/matrix";
|
||||
import { createTestClient, mkStubRoom } from "../../../../test-utils";
|
||||
import { shouldShowComponent } from "../../../../../src/customisations/helpers/UIComponents";
|
||||
import { hasCreateRoomRights, createRoom } from "../../../../../src/components/viewmodels/roomlist/utils";
|
||||
import defaultDispatcher from "../../../../../src/dispatcher/dispatcher";
|
||||
import { Action } from "../../../../../src/dispatcher/actions";
|
||||
import { showCreateNewRoom } from "../../../../../src/utils/space";
|
||||
|
||||
jest.mock("../../../../../src/customisations/helpers/UIComponents", () => ({
|
||||
shouldShowComponent: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock("../../../../../src/utils/space", () => ({
|
||||
showCreateNewRoom: jest.fn(),
|
||||
}));
|
||||
|
||||
describe("utils", () => {
|
||||
let matrixClient: MatrixClient;
|
||||
let space: Room;
|
||||
|
||||
beforeEach(() => {
|
||||
matrixClient = createTestClient();
|
||||
space = mkStubRoom("spaceId", "spaceName", matrixClient);
|
||||
});
|
||||
|
||||
describe("createRoom", () => {
|
||||
it("should fire Action.CreateRoom when createRoom is called without a space", async () => {
|
||||
const spy = jest.spyOn(defaultDispatcher, "fire");
|
||||
await createRoom();
|
||||
|
||||
expect(spy).toHaveBeenCalledWith(Action.CreateRoom);
|
||||
});
|
||||
|
||||
it("should call showCreateNewRoom when createRoom is called in a space", async () => {
|
||||
await createRoom(space);
|
||||
expect(showCreateNewRoom).toHaveBeenCalledWith(space);
|
||||
});
|
||||
});
|
||||
|
||||
describe("hasCreateRoomRights", () => {
|
||||
it("should return false when UIComponent.CreateRooms is disabled", () => {
|
||||
mocked(shouldShowComponent).mockReturnValue(false);
|
||||
expect(hasCreateRoomRights(matrixClient, space)).toBe(false);
|
||||
});
|
||||
|
||||
it("should return true when UIComponent.CreateRooms is enabled and no space", () => {
|
||||
mocked(shouldShowComponent).mockReturnValue(true);
|
||||
expect(hasCreateRoomRights(matrixClient)).toBe(true);
|
||||
});
|
||||
|
||||
it("should return false in space when UIComponent.CreateRooms is enabled and the user doesn't have the rights", () => {
|
||||
mocked(shouldShowComponent).mockReturnValue(true);
|
||||
jest.spyOn(space.getLiveTimeline(), "getState").mockReturnValue({
|
||||
maySendStateEvent: jest.fn().mockReturnValue(true),
|
||||
} as unknown as RoomState);
|
||||
|
||||
expect(hasCreateRoomRights(matrixClient)).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* 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 userEvent from "@testing-library/user-event";
|
||||
|
||||
import { type RoomListViewState } from "../../../../../../src/components/viewmodels/roomlist/RoomListViewModel";
|
||||
import { SecondaryFilters } from "../../../../../../src/components/viewmodels/roomlist/useFilteredRooms";
|
||||
import { SortOption } from "../../../../../../src/components/viewmodels/roomlist/useSorter";
|
||||
import { EmptyRoomList } from "../../../../../../src/components/views/rooms/RoomListPanel/EmptyRoomList";
|
||||
import { FilterKey } from "../../../../../../src/stores/room-list-v3/skip-list/filters";
|
||||
|
||||
describe("<EmptyRoomList />", () => {
|
||||
let vm: RoomListViewState;
|
||||
|
||||
beforeEach(() => {
|
||||
vm = {
|
||||
rooms: [],
|
||||
primaryFilters: [],
|
||||
activateSecondaryFilter: jest.fn().mockReturnValue({}),
|
||||
activeSecondaryFilter: SecondaryFilters.AllActivity,
|
||||
sort: jest.fn(),
|
||||
activeSortOption: SortOption.Activity,
|
||||
createRoom: jest.fn(),
|
||||
createChatRoom: jest.fn(),
|
||||
canCreateRoom: true,
|
||||
shouldShowMessagePreview: false,
|
||||
toggleMessagePreview: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
test("should render the default placeholder when there is no filter", async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
const { asFragment } = render(<EmptyRoomList vm={vm} />);
|
||||
expect(screen.getByText("No chats yet")).toBeInTheDocument();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
|
||||
await user.click(screen.getByRole("button", { name: "New message" }));
|
||||
expect(vm.createChatRoom).toHaveBeenCalled();
|
||||
|
||||
await user.click(screen.getByRole("button", { name: "New room" }));
|
||||
expect(vm.createRoom).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("should not render the new room button if the user doesn't have the rights to create a room", async () => {
|
||||
const newState = { ...vm, canCreateRoom: false };
|
||||
|
||||
const { asFragment } = render(<EmptyRoomList vm={newState} />);
|
||||
expect(screen.queryByRole("button", { name: "New room" })).toBeNull();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should display the empty state for the unread filter", async () => {
|
||||
const user = userEvent.setup();
|
||||
const activePrimaryFilter = {
|
||||
toggle: jest.fn(),
|
||||
active: true,
|
||||
name: "unread",
|
||||
key: FilterKey.UnreadFilter,
|
||||
};
|
||||
const newState = {
|
||||
...vm,
|
||||
activePrimaryFilter,
|
||||
};
|
||||
|
||||
const { asFragment } = render(<EmptyRoomList vm={newState} />);
|
||||
await user.click(screen.getByRole("button", { name: "Show all chats" }));
|
||||
expect(activePrimaryFilter.toggle).toHaveBeenCalled();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ key: FilterKey.FavouriteFilter, name: "favourite" },
|
||||
{ key: FilterKey.PeopleFilter, name: "people" },
|
||||
{ key: FilterKey.RoomsFilter, name: "rooms" },
|
||||
])("should display empty state for filter $name", ({ name, key }) => {
|
||||
const activePrimaryFilter = {
|
||||
toggle: jest.fn(),
|
||||
active: true,
|
||||
name,
|
||||
key,
|
||||
};
|
||||
const newState = { ...vm, activePrimaryFilter };
|
||||
const { asFragment } = render(<EmptyRoomList vm={newState} />);
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -37,6 +37,9 @@ describe("<RoomList />", () => {
|
||||
activeSortOption: SortOption.Activity,
|
||||
shouldShowMessagePreview: false,
|
||||
toggleMessagePreview: jest.fn(),
|
||||
createRoom: jest.fn(),
|
||||
createChatRoom: jest.fn(),
|
||||
canCreateRoom: true,
|
||||
};
|
||||
|
||||
// Needed to render a room list cell
|
||||
|
||||
@@ -13,6 +13,7 @@ import { type RoomListViewState } from "../../../../../../src/components/viewmod
|
||||
import { SecondaryFilters } from "../../../../../../src/components/viewmodels/roomlist/useFilteredRooms";
|
||||
import { RoomListPrimaryFilters } from "../../../../../../src/components/views/rooms/RoomListPanel/RoomListPrimaryFilters";
|
||||
import { SortOption } from "../../../../../../src/components/viewmodels/roomlist/useSorter";
|
||||
import { FilterKey } from "../../../../../../src/stores/room-list-v3/skip-list/filters";
|
||||
|
||||
describe("<RoomListPrimaryFilters />", () => {
|
||||
let vm: RoomListViewState;
|
||||
@@ -20,9 +21,12 @@ describe("<RoomListPrimaryFilters />", () => {
|
||||
beforeEach(() => {
|
||||
vm = {
|
||||
rooms: [],
|
||||
canCreateRoom: true,
|
||||
createRoom: jest.fn(),
|
||||
createChatRoom: jest.fn(),
|
||||
primaryFilters: [
|
||||
{ name: "People", active: false, toggle: jest.fn() },
|
||||
{ name: "Rooms", active: true, toggle: jest.fn() },
|
||||
{ name: "People", active: false, toggle: jest.fn(), key: FilterKey.PeopleFilter },
|
||||
{ name: "Rooms", active: true, toggle: jest.fn(), key: FilterKey.RoomsFilter },
|
||||
],
|
||||
activateSecondaryFilter: () => {},
|
||||
activeSecondaryFilter: SecondaryFilters.AllActivity,
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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 { mocked } from "jest-mock";
|
||||
import { render, screen } from "jest-matrix-react";
|
||||
import React from "react";
|
||||
|
||||
import {
|
||||
type RoomListViewState,
|
||||
useRoomListViewModel,
|
||||
} from "../../../../../../src/components/viewmodels/roomlist/RoomListViewModel";
|
||||
import { SecondaryFilters } from "../../../../../../src/components/viewmodels/roomlist/useFilteredRooms";
|
||||
import { SortOption } from "../../../../../../src/components/viewmodels/roomlist/useSorter";
|
||||
import { RoomListView } from "../../../../../../src/components/views/rooms/RoomListPanel/RoomListView";
|
||||
import { mkRoom, stubClient } from "../../../../../test-utils";
|
||||
|
||||
jest.mock("../../../../../../src/components/viewmodels/roomlist/RoomListViewModel", () => ({
|
||||
useRoomListViewModel: jest.fn(),
|
||||
}));
|
||||
|
||||
describe("<RoomListView />", () => {
|
||||
const defaultValue: RoomListViewState = {
|
||||
rooms: [],
|
||||
primaryFilters: [],
|
||||
activateSecondaryFilter: jest.fn().mockReturnValue({}),
|
||||
activeSecondaryFilter: SecondaryFilters.AllActivity,
|
||||
sort: jest.fn(),
|
||||
activeSortOption: SortOption.Activity,
|
||||
createRoom: jest.fn(),
|
||||
createChatRoom: jest.fn(),
|
||||
canCreateRoom: true,
|
||||
toggleMessagePreview: jest.fn(),
|
||||
shouldShowMessagePreview: false,
|
||||
};
|
||||
const matrixClient = stubClient();
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it("should render an empty room list", () => {
|
||||
mocked(useRoomListViewModel).mockReturnValue(defaultValue);
|
||||
|
||||
render(<RoomListView />);
|
||||
expect(screen.getByText("No chats yet")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render a room list", () => {
|
||||
mocked(useRoomListViewModel).mockReturnValue({
|
||||
...defaultValue,
|
||||
rooms: [mkRoom(matrixClient, "testing room")],
|
||||
});
|
||||
|
||||
render(<RoomListView />);
|
||||
expect(screen.getByRole("grid", { name: "Room list" })).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,204 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<EmptyRoomList /> should display empty state for filter favourite 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mx_Flex mx_EmptyRoomList_GenericPlaceholder"
|
||||
data-testid="empty-room-list"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: column; --mx-flex-align: stretch; --mx-flex-justify: center; --mx-flex-gap: var(--cpd-space-2x); --mx-flex-wrap: nowrap;"
|
||||
>
|
||||
<span
|
||||
class="mx_EmptyRoomList_GenericPlaceholder_title"
|
||||
>
|
||||
You don't have favourite chat yet
|
||||
</span>
|
||||
<span
|
||||
class="mx_EmptyRoomList_GenericPlaceholder_description"
|
||||
>
|
||||
You can add a chat to your favourites in the chat settings
|
||||
</span>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<EmptyRoomList /> should display empty state for filter people 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mx_Flex mx_EmptyRoomList_GenericPlaceholder"
|
||||
data-testid="empty-room-list"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: column; --mx-flex-align: stretch; --mx-flex-justify: center; --mx-flex-gap: var(--cpd-space-2x); --mx-flex-wrap: nowrap;"
|
||||
>
|
||||
<span
|
||||
class="mx_EmptyRoomList_GenericPlaceholder_title"
|
||||
>
|
||||
You don’t have direct chats with anyone yet
|
||||
</span>
|
||||
<span
|
||||
class="mx_EmptyRoomList_GenericPlaceholder_description"
|
||||
>
|
||||
You can deselect filters in order to see your other chats
|
||||
</span>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<EmptyRoomList /> should display empty state for filter rooms 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mx_Flex mx_EmptyRoomList_GenericPlaceholder"
|
||||
data-testid="empty-room-list"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: column; --mx-flex-align: stretch; --mx-flex-justify: center; --mx-flex-gap: var(--cpd-space-2x); --mx-flex-wrap: nowrap;"
|
||||
>
|
||||
<span
|
||||
class="mx_EmptyRoomList_GenericPlaceholder_title"
|
||||
>
|
||||
You’re not in any room yet
|
||||
</span>
|
||||
<span
|
||||
class="mx_EmptyRoomList_GenericPlaceholder_description"
|
||||
>
|
||||
You can deselect filters in order to see your other chats
|
||||
</span>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<EmptyRoomList /> should display the empty state for the unread filter 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mx_Flex mx_EmptyRoomList_GenericPlaceholder"
|
||||
data-testid="empty-room-list"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: column; --mx-flex-align: stretch; --mx-flex-justify: center; --mx-flex-gap: var(--cpd-space-2x); --mx-flex-wrap: nowrap;"
|
||||
>
|
||||
<span
|
||||
class="mx_EmptyRoomList_GenericPlaceholder_title"
|
||||
>
|
||||
Congrats! You don’t have any unread messages
|
||||
</span>
|
||||
<button
|
||||
class="_button_vczzf_8"
|
||||
data-kind="tertiary"
|
||||
data-size="lg"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Show all chats
|
||||
</button>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<EmptyRoomList /> should not render the new room button if the user doesn't have the rights to create a room 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mx_Flex mx_EmptyRoomList_GenericPlaceholder"
|
||||
data-testid="empty-room-list"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: column; --mx-flex-align: stretch; --mx-flex-justify: center; --mx-flex-gap: var(--cpd-space-2x); --mx-flex-wrap: nowrap;"
|
||||
>
|
||||
<span
|
||||
class="mx_EmptyRoomList_GenericPlaceholder_title"
|
||||
>
|
||||
No chats yet
|
||||
</span>
|
||||
<span
|
||||
class="mx_EmptyRoomList_GenericPlaceholder_description"
|
||||
>
|
||||
Get started by messaging someone
|
||||
</span>
|
||||
<div
|
||||
class="mx_Flex mx_EmptyRoomList_DefaultPlaceholder"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: column; --mx-flex-align: center; --mx-flex-justify: center; --mx-flex-gap: var(--cpd-space-4x); --mx-flex-wrap: nowrap;"
|
||||
>
|
||||
<button
|
||||
class="_button_vczzf_8 _has-icon_vczzf_57"
|
||||
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="M10 12q-1.65 0-2.825-1.175T6 8t1.175-2.825T10 4t2.825 1.175T14 8t-1.175 2.825T10 12m-8 6v-.8q0-.85.438-1.562.437-.713 1.162-1.088a14.8 14.8 0 0 1 3.15-1.163A13.8 13.8 0 0 1 10 13q1.65 0 3.25.387 1.6.388 3.15 1.163.724.375 1.163 1.087Q18 16.35 18 17.2v.8q0 .824-.587 1.413A1.93 1.93 0 0 1 16 20H4q-.824 0-1.412-.587A1.93 1.93 0 0 1 2 18m2 0h12v-.8a.97.97 0 0 0-.5-.85q-1.35-.675-2.725-1.012a11.6 11.6 0 0 0-5.55 0Q5.85 15.675 4.5 16.35a.97.97 0 0 0-.5.85zm6-8q.825 0 1.412-.588Q12 8.826 12 8q0-.824-.588-1.412A1.93 1.93 0 0 0 10 6q-.825 0-1.412.588A1.93 1.93 0 0 0 8 8q0 .825.588 1.412Q9.175 10 10 10m7 1h2v2q0 .424.288.713.287.287.712.287.424 0 .712-.287A.97.97 0 0 0 21 13v-2h2q.424 0 .712-.287A.97.97 0 0 0 24 10a.97.97 0 0 0-.288-.713A.97.97 0 0 0 23 9h-2V7a.97.97 0 0 0-.288-.713A.97.97 0 0 0 20 6a.97.97 0 0 0-.712.287A.97.97 0 0 0 19 7v2h-2a.97.97 0 0 0-.712.287A.97.97 0 0 0 16 10q0 .424.288.713.287.287.712.287"
|
||||
/>
|
||||
</svg>
|
||||
New message
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<EmptyRoomList /> should render the default placeholder when there is no filter 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mx_Flex mx_EmptyRoomList_GenericPlaceholder"
|
||||
data-testid="empty-room-list"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: column; --mx-flex-align: stretch; --mx-flex-justify: center; --mx-flex-gap: var(--cpd-space-2x); --mx-flex-wrap: nowrap;"
|
||||
>
|
||||
<span
|
||||
class="mx_EmptyRoomList_GenericPlaceholder_title"
|
||||
>
|
||||
No chats yet
|
||||
</span>
|
||||
<span
|
||||
class="mx_EmptyRoomList_GenericPlaceholder_description"
|
||||
>
|
||||
Get started by messaging someone or by creating a room
|
||||
</span>
|
||||
<div
|
||||
class="mx_Flex mx_EmptyRoomList_DefaultPlaceholder"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: column; --mx-flex-align: center; --mx-flex-justify: center; --mx-flex-gap: var(--cpd-space-4x); --mx-flex-wrap: nowrap;"
|
||||
>
|
||||
<button
|
||||
class="_button_vczzf_8 _has-icon_vczzf_57"
|
||||
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="M10 12q-1.65 0-2.825-1.175T6 8t1.175-2.825T10 4t2.825 1.175T14 8t-1.175 2.825T10 12m-8 6v-.8q0-.85.438-1.562.437-.713 1.162-1.088a14.8 14.8 0 0 1 3.15-1.163A13.8 13.8 0 0 1 10 13q1.65 0 3.25.387 1.6.388 3.15 1.163.724.375 1.163 1.087Q18 16.35 18 17.2v.8q0 .824-.587 1.413A1.93 1.93 0 0 1 16 20H4q-.824 0-1.412-.587A1.93 1.93 0 0 1 2 18m2 0h12v-.8a.97.97 0 0 0-.5-.85q-1.35-.675-2.725-1.012a11.6 11.6 0 0 0-5.55 0Q5.85 15.675 4.5 16.35a.97.97 0 0 0-.5.85zm6-8q.825 0 1.412-.588Q12 8.826 12 8q0-.824-.588-1.412A1.93 1.93 0 0 0 10 6q-.825 0-1.412.588A1.93 1.93 0 0 0 8 8q0 .825.588 1.412Q9.175 10 10 10m7 1h2v2q0 .424.288.713.287.287.712.287.424 0 .712-.287A.97.97 0 0 0 21 13v-2h2q.424 0 .712-.287A.97.97 0 0 0 24 10a.97.97 0 0 0-.288-.713A.97.97 0 0 0 23 9h-2V7a.97.97 0 0 0-.288-.713A.97.97 0 0 0 20 6a.97.97 0 0 0-.712.287A.97.97 0 0 0 19 7v2h-2a.97.97 0 0 0-.712.287A.97.97 0 0 0 16 10q0 .424.288.713.287.287.712.287"
|
||||
/>
|
||||
</svg>
|
||||
New message
|
||||
</button>
|
||||
<button
|
||||
class="_button_vczzf_8 _has-icon_vczzf_57"
|
||||
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="m8.566 17-.944 4.094q-.086.406-.372.656t-.687.25q-.543 0-.887-.469a1.18 1.18 0 0 1-.2-1.031l.801-3.5H3.158q-.572 0-.916-.484a1.27 1.27 0 0 1-.2-1.078 1.12 1.12 0 0 1 1.116-.938H6.85l1.145-5h-3.12q-.57 0-.915-.484a1.27 1.27 0 0 1-.2-1.078A1.12 1.12 0 0 1 4.875 7h3.691l.945-4.094q.085-.406.372-.656.286-.25.686-.25.544 0 .887.469.345.468.2 1.031l-.8 3.5h4.578l.944-4.094q.085-.406.372-.656.286-.25.687-.25.543 0 .887.469t.2 1.031L17.723 7h3.119q.573 0 .916.484.343.485.2 1.079a1.12 1.12 0 0 1-1.116.937H17.15l-1.145 5h3.12q.57 0 .915.484.343.485.2 1.079a1.12 1.12 0 0 1-1.116.937h-3.691l-.944 4.094q-.087.406-.373.656t-.686.25q-.544 0-.887-.469a1.18 1.18 0 0 1-.2-1.031l.8-3.5zm.573-2.5h4.578l1.144-5h-4.578z"
|
||||
/>
|
||||
</svg>
|
||||
New room
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
@@ -113,34 +113,45 @@ exports[`<RoomListPanel /> should not render the RoomListSearch component when U
|
||||
</li>
|
||||
</ul>
|
||||
<div
|
||||
class="mx_RoomList"
|
||||
data-testid="room-list"
|
||||
class="mx_Flex mx_EmptyRoomList_GenericPlaceholder"
|
||||
data-testid="empty-room-list"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: column; --mx-flex-align: stretch; --mx-flex-justify: center; --mx-flex-gap: var(--cpd-space-2x); --mx-flex-wrap: nowrap;"
|
||||
>
|
||||
<div
|
||||
style="overflow: visible; height: 0px; width: 0px;"
|
||||
<span
|
||||
class="mx_EmptyRoomList_GenericPlaceholder_title"
|
||||
>
|
||||
<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: 0px; position: relative; width: 0px; will-change: transform; overflow-x: hidden; overflow-y: hidden;"
|
||||
No chats yet
|
||||
</span>
|
||||
<span
|
||||
class="mx_EmptyRoomList_GenericPlaceholder_description"
|
||||
>
|
||||
Get started by messaging someone
|
||||
</span>
|
||||
<div
|
||||
class="mx_Flex mx_EmptyRoomList_DefaultPlaceholder"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: column; --mx-flex-align: center; --mx-flex-justify: center; --mx-flex-gap: var(--cpd-space-4x); --mx-flex-wrap: nowrap;"
|
||||
>
|
||||
<button
|
||||
class="_button_vczzf_8 _has-icon_vczzf_57"
|
||||
data-kind="secondary"
|
||||
data-size="sm"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="resize-triggers"
|
||||
>
|
||||
<div
|
||||
class="expand-trigger"
|
||||
>
|
||||
<div
|
||||
style="width: 1px; height: 1px;"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="contract-trigger"
|
||||
/>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
width="20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M10 12q-1.65 0-2.825-1.175T6 8t1.175-2.825T10 4t2.825 1.175T14 8t-1.175 2.825T10 12m-8 6v-.8q0-.85.438-1.562.437-.713 1.162-1.088a14.8 14.8 0 0 1 3.15-1.163A13.8 13.8 0 0 1 10 13q1.65 0 3.25.387 1.6.388 3.15 1.163.724.375 1.163 1.087Q18 16.35 18 17.2v.8q0 .824-.587 1.413A1.93 1.93 0 0 1 16 20H4q-.824 0-1.412-.587A1.93 1.93 0 0 1 2 18m2 0h12v-.8a.97.97 0 0 0-.5-.85q-1.35-.675-2.725-1.012a11.6 11.6 0 0 0-5.55 0Q5.85 15.675 4.5 16.35a.97.97 0 0 0-.5.85zm6-8q.825 0 1.412-.588Q12 8.826 12 8q0-.824-.588-1.412A1.93 1.93 0 0 0 10 6q-.825 0-1.412.588A1.93 1.93 0 0 0 8 8q0 .825.588 1.412Q9.175 10 10 10m7 1h2v2q0 .424.288.713.287.287.712.287.424 0 .712-.287A.97.97 0 0 0 21 13v-2h2q.424 0 .712-.287A.97.97 0 0 0 24 10a.97.97 0 0 0-.288-.713A.97.97 0 0 0 23 9h-2V7a.97.97 0 0 0-.288-.713A.97.97 0 0 0 20 6a.97.97 0 0 0-.712.287A.97.97 0 0 0 19 7v2h-2a.97.97 0 0 0-.712.287A.97.97 0 0 0 16 10q0 .424.288.713.287.287.712.287"
|
||||
/>
|
||||
</svg>
|
||||
New message
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -322,34 +333,66 @@ exports[`<RoomListPanel /> should render the RoomListSearch component when UICom
|
||||
</li>
|
||||
</ul>
|
||||
<div
|
||||
class="mx_RoomList"
|
||||
data-testid="room-list"
|
||||
class="mx_Flex mx_EmptyRoomList_GenericPlaceholder"
|
||||
data-testid="empty-room-list"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: column; --mx-flex-align: stretch; --mx-flex-justify: center; --mx-flex-gap: var(--cpd-space-2x); --mx-flex-wrap: nowrap;"
|
||||
>
|
||||
<div
|
||||
style="overflow: visible; height: 0px; width: 0px;"
|
||||
<span
|
||||
class="mx_EmptyRoomList_GenericPlaceholder_title"
|
||||
>
|
||||
<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: 0px; position: relative; width: 0px; will-change: transform; overflow-x: hidden; overflow-y: hidden;"
|
||||
No chats yet
|
||||
</span>
|
||||
<span
|
||||
class="mx_EmptyRoomList_GenericPlaceholder_description"
|
||||
>
|
||||
Get started by messaging someone or by creating a room
|
||||
</span>
|
||||
<div
|
||||
class="mx_Flex mx_EmptyRoomList_DefaultPlaceholder"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: column; --mx-flex-align: center; --mx-flex-justify: center; --mx-flex-gap: var(--cpd-space-4x); --mx-flex-wrap: nowrap;"
|
||||
>
|
||||
<button
|
||||
class="_button_vczzf_8 _has-icon_vczzf_57"
|
||||
data-kind="secondary"
|
||||
data-size="sm"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="resize-triggers"
|
||||
>
|
||||
<div
|
||||
class="expand-trigger"
|
||||
>
|
||||
<div
|
||||
style="width: 1px; height: 1px;"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="contract-trigger"
|
||||
/>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
width="20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M10 12q-1.65 0-2.825-1.175T6 8t1.175-2.825T10 4t2.825 1.175T14 8t-1.175 2.825T10 12m-8 6v-.8q0-.85.438-1.562.437-.713 1.162-1.088a14.8 14.8 0 0 1 3.15-1.163A13.8 13.8 0 0 1 10 13q1.65 0 3.25.387 1.6.388 3.15 1.163.724.375 1.163 1.087Q18 16.35 18 17.2v.8q0 .824-.587 1.413A1.93 1.93 0 0 1 16 20H4q-.824 0-1.412-.587A1.93 1.93 0 0 1 2 18m2 0h12v-.8a.97.97 0 0 0-.5-.85q-1.35-.675-2.725-1.012a11.6 11.6 0 0 0-5.55 0Q5.85 15.675 4.5 16.35a.97.97 0 0 0-.5.85zm6-8q.825 0 1.412-.588Q12 8.826 12 8q0-.824-.588-1.412A1.93 1.93 0 0 0 10 6q-.825 0-1.412.588A1.93 1.93 0 0 0 8 8q0 .825.588 1.412Q9.175 10 10 10m7 1h2v2q0 .424.288.713.287.287.712.287.424 0 .712-.287A.97.97 0 0 0 21 13v-2h2q.424 0 .712-.287A.97.97 0 0 0 24 10a.97.97 0 0 0-.288-.713A.97.97 0 0 0 23 9h-2V7a.97.97 0 0 0-.288-.713A.97.97 0 0 0 20 6a.97.97 0 0 0-.712.287A.97.97 0 0 0 19 7v2h-2a.97.97 0 0 0-.712.287A.97.97 0 0 0 16 10q0 .424.288.713.287.287.712.287"
|
||||
/>
|
||||
</svg>
|
||||
New message
|
||||
</button>
|
||||
<button
|
||||
class="_button_vczzf_8 _has-icon_vczzf_57"
|
||||
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="m8.566 17-.944 4.094q-.086.406-.372.656t-.687.25q-.543 0-.887-.469a1.18 1.18 0 0 1-.2-1.031l.801-3.5H3.158q-.572 0-.916-.484a1.27 1.27 0 0 1-.2-1.078 1.12 1.12 0 0 1 1.116-.938H6.85l1.145-5h-3.12q-.57 0-.915-.484a1.27 1.27 0 0 1-.2-1.078A1.12 1.12 0 0 1 4.875 7h3.691l.945-4.094q.085-.406.372-.656.286-.25.686-.25.544 0 .887.469.345.468.2 1.031l-.8 3.5h4.578l.944-4.094q.085-.406.372-.656.286-.25.687-.25.543 0 .887.469t.2 1.031L17.723 7h3.119q.573 0 .916.484.343.485.2 1.079a1.12 1.12 0 0 1-1.116.937H17.15l-1.145 5h3.12q.57 0 .915.484.343.485.2 1.079a1.12 1.12 0 0 1-1.116.937h-3.691l-.944 4.094q-.087.406-.373.656t-.686.25q-.544 0-.887-.469a1.18 1.18 0 0 1-.2-1.031l.8-3.5zm.573-2.5h4.578l1.144-5h-4.578z"
|
||||
/>
|
||||
</svg>
|
||||
New room
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user