Add message preview support to the new room list (#29784)
* Add message preview support to the new room list * Support showing message previews in the room list items * Add the secondary filters bar with the '...' menu, containing just the option for message previews for now * Change message preview toggle hook to update when setting is updated * Use new compund release * Unused i18n keys * Unused imports * Fix test & update snapshot * Fix more snapshots * Fix test Split into two tests that test setting & updating * Type import * Snapshots * Remove unnecessary Flex container and update screenshots as the room list has got shorter from the added bar * More snapshots & screenshots * More snapshots * Add test and remove active filter that's not done yet * Update snapshots & screenshots again * Other screenshot * Add more tests * Fix syntax * Fix tests * Use setter directly Co-authored-by: Michael Telatynski <7t3chguy@gmail.com> * Fix CSS * Remopve filter button css for now * Update to remove forwardRef * Add comment on why lack of TypedEventEmitter * snapshots again * Screenshots again * Use original screenshots, maybe they'll work now * Add comment --------- Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
@@ -93,7 +93,7 @@
|
||||
"@types/png-chunks-extract": "^1.0.2",
|
||||
"@types/react-virtualized": "^9.21.30",
|
||||
"@vector-im/compound-design-tokens": "^4.0.0",
|
||||
"@vector-im/compound-web": "^7.10.1",
|
||||
"@vector-im/compound-web": "^7.10.2",
|
||||
"@vector-im/matrix-wysiwyg": "2.38.3",
|
||||
"@zxcvbn-ts/core": "^3.0.4",
|
||||
"@zxcvbn-ts/language-common": "^3.0.4",
|
||||
|
||||
@@ -271,6 +271,22 @@ test.describe("Room list", () => {
|
||||
await expect(room).toMatchScreenshot("room-list-item-mention.png");
|
||||
});
|
||||
|
||||
test("should render a message preview", { tag: "@screenshot" }, async ({ page, app, user, bot }) => {
|
||||
const roomListView = getRoomList(page);
|
||||
|
||||
await page.getByRole("button", { name: "Room Options" }).click();
|
||||
await page.getByRole("menuitemcheckbox", { name: "Show message previews" }).click();
|
||||
|
||||
const roomId = await app.client.createRoom({ name: "activity" });
|
||||
await app.client.inviteUser(roomId, bot.credentials.userId);
|
||||
await bot.joinRoom(roomId);
|
||||
await bot.sendMessage(roomId, "I am a robot. Beep.");
|
||||
|
||||
const room = roomListView.getByRole("gridcell", { name: "activity" });
|
||||
await expect(room.getByText("I am a robot. Beep.")).toBeVisible();
|
||||
await expect(room).toMatchScreenshot("room-list-item-message-preview.png");
|
||||
});
|
||||
|
||||
test("should render an activity decoration", { tag: "@screenshot" }, async ({ page, app, user, bot }) => {
|
||||
const roomListView = getRoomList(page);
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 19 KiB |
@@ -279,6 +279,7 @@
|
||||
@import "./views/rooms/RoomListPanel/_RoomListPanel.pcss";
|
||||
@import "./views/rooms/RoomListPanel/_RoomListPrimaryFilters.pcss";
|
||||
@import "./views/rooms/RoomListPanel/_RoomListSearch.pcss";
|
||||
@import "./views/rooms/RoomListPanel/_RoomListSecondaryFilters.pcss";
|
||||
@import "./views/rooms/_AppsDrawer.pcss";
|
||||
@import "./views/rooms/_Autocomplete.pcss";
|
||||
@import "./views/rooms/_AuxPanel.pcss";
|
||||
|
||||
@@ -35,11 +35,23 @@
|
||||
box-sizing: border-box;
|
||||
min-width: 0;
|
||||
|
||||
.mx_RoomListItemView_text {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.mx_RoomListItemView_roomName {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.mx_RoomListItemView_messagePreview {
|
||||
font: var(--cpd-font-body-sm-regular);
|
||||
color: var(--cpd-color-text-secondary);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
.mx_RoomListSecondaryFilters {
|
||||
font: var(--cpd-font-body-md-medium);
|
||||
margin: var(--cpd-space-2x);
|
||||
margin-left: var(--cpd-space-1x);
|
||||
}
|
||||
|
||||
.mx_RoomListSecondaryFilters_roomOptionsButton {
|
||||
/* Size the button appropriately (should this be in em, maybe,
|
||||
* so it gets bigger with font size? These values taken from the figma.
|
||||
*/
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
margin-left: auto;
|
||||
|
||||
svg {
|
||||
color: var(--cpd-color-icon-primary);
|
||||
}
|
||||
}
|
||||
@@ -16,11 +16,14 @@ import { _t } from "../../../languageHandler";
|
||||
import { type RoomNotificationState } from "../../../stores/notifications/RoomNotificationState";
|
||||
import { RoomNotificationStateStore } from "../../../stores/notifications/RoomNotificationStateStore";
|
||||
import { useMatrixClientContext } from "../../../contexts/MatrixClientContext";
|
||||
import { useEventEmitterState, useTypedEventEmitter } from "../../../hooks/useEventEmitter";
|
||||
import { useEventEmitter, useEventEmitterState, useTypedEventEmitter } from "../../../hooks/useEventEmitter";
|
||||
import { DefaultTagID } from "../../../stores/room-list/models";
|
||||
import { useCall, useConnectionState, useParticipantCount } from "../../../hooks/useCall";
|
||||
import { type ConnectionState } from "../../../models/Call";
|
||||
import { NotificationStateEvents } from "../../../stores/notifications/NotificationState";
|
||||
import DMRoomMap from "../../../utils/DMRoomMap";
|
||||
import { MessagePreviewStore } from "../../../stores/room-list/MessagePreviewStore";
|
||||
import { useMessagePreviewToggle } from "./useMessagePreviewToggle";
|
||||
|
||||
export interface RoomListItemViewState {
|
||||
/**
|
||||
@@ -60,6 +63,11 @@ export interface RoomListItemViewState {
|
||||
* Whether there are participants in the call.
|
||||
*/
|
||||
hasParticipantInCall: boolean;
|
||||
/**
|
||||
* Pre-rendered and translated preview for the latest message in the room, or undefined
|
||||
* if no preview should be shown.
|
||||
*/
|
||||
messagePreview: string | undefined;
|
||||
/**
|
||||
* Whether the notification decoration should be shown.
|
||||
*/
|
||||
@@ -104,6 +112,8 @@ export function useRoomListItemViewModel(room: Room): RoomListItemViewState {
|
||||
!invited &&
|
||||
(hasAccessToOptionsMenu(room) || hasAccessToNotificationMenu(room, matrixClient.isGuest(), isArchived));
|
||||
|
||||
const messagePreview = useRoomMessagePreview(room);
|
||||
|
||||
// Video room
|
||||
const isVideoRoom = room.isElementVideoRoom() || room.isCallRoom();
|
||||
// EC video call or video room
|
||||
@@ -134,6 +144,7 @@ export function useRoomListItemViewModel(room: Room): RoomListItemViewState {
|
||||
isVideoRoom,
|
||||
callConnectionState,
|
||||
hasParticipantInCall,
|
||||
messagePreview,
|
||||
showNotificationDecoration,
|
||||
};
|
||||
}
|
||||
@@ -190,3 +201,36 @@ function getA11yLabel(roomName: string, notificationState: RoomNotificationState
|
||||
return _t("room_list|room|open_room", { roomName });
|
||||
}
|
||||
}
|
||||
|
||||
function useRoomMessagePreview(room: Room): string | undefined {
|
||||
const { shouldShowMessagePreview } = useMessagePreviewToggle();
|
||||
const [previewText, setPreviewText] = useState<string | undefined>(undefined);
|
||||
|
||||
const updatePreview = useCallback(async () => {
|
||||
if (!shouldShowMessagePreview) {
|
||||
setPreviewText(undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
const roomIsDM = Boolean(DMRoomMap.shared().getUserIdForRoomId(room.roomId));
|
||||
// For the tag, we only care about whether the room is a DM or not as we don't show
|
||||
// display names in previewsd for DMs, so anything else we just say is 'untagged'
|
||||
// (even though it could actually be have other tags: we don't care about them).
|
||||
const messagePreview = await MessagePreviewStore.instance.getPreviewForRoom(
|
||||
room,
|
||||
roomIsDM ? DefaultTagID.DM : DefaultTagID.Untagged,
|
||||
);
|
||||
if (messagePreview) setPreviewText(messagePreview.text);
|
||||
}, [room, shouldShowMessagePreview]);
|
||||
|
||||
// MessagePreviewStore and the other AsyncStores need to be converted to TypedEventEmitter
|
||||
useEventEmitter(MessagePreviewStore.instance, MessagePreviewStore.getPreviewChangedEventName(room), () => {
|
||||
updatePreview();
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
updatePreview();
|
||||
}, [updatePreview]);
|
||||
|
||||
return previewText;
|
||||
}
|
||||
|
||||
@@ -4,10 +4,11 @@
|
||||
* 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, useState } from "react";
|
||||
import { useCallback } from "react";
|
||||
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import { SettingLevel } from "../../../settings/SettingLevel";
|
||||
import { useSettingValue } from "../../../hooks/useSettings";
|
||||
|
||||
interface MessagePreviewToggleState {
|
||||
shouldShowMessagePreview: boolean;
|
||||
@@ -20,17 +21,12 @@ interface MessagePreviewToggleState {
|
||||
* - Provides a function to toggle message previews.
|
||||
*/
|
||||
export function useMessagePreviewToggle(): MessagePreviewToggleState {
|
||||
const [shouldShowMessagePreview, setShouldShowMessagePreview] = useState(() =>
|
||||
SettingsStore.getValue("RoomList.showMessagePreview"),
|
||||
);
|
||||
const shouldShowMessagePreview = useSettingValue("RoomList.showMessagePreview");
|
||||
|
||||
const toggleMessagePreview = useCallback((): void => {
|
||||
setShouldShowMessagePreview((current) => {
|
||||
const toggled = !current;
|
||||
SettingsStore.setValue("RoomList.showMessagePreview", null, SettingLevel.DEVICE, toggled);
|
||||
return toggled;
|
||||
});
|
||||
}, []);
|
||||
const toggled = !shouldShowMessagePreview;
|
||||
SettingsStore.setValue("RoomList.showMessagePreview", null, SettingLevel.DEVICE, toggled);
|
||||
}, [shouldShowMessagePreview]);
|
||||
|
||||
return { toggleMessagePreview, shouldShowMessagePreview };
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import { RoomListItemMenuView } from "./RoomListItemMenuView";
|
||||
import { NotificationDecoration } from "../NotificationDecoration";
|
||||
import { RoomAvatarView } from "../../avatars/RoomAvatarView";
|
||||
|
||||
interface RoomListItemViewPropsProps extends React.HTMLAttributes<HTMLButtonElement> {
|
||||
interface RoomListItemViewProps extends React.HTMLAttributes<HTMLButtonElement> {
|
||||
/**
|
||||
* The room to display
|
||||
*/
|
||||
@@ -33,7 +33,7 @@ export const RoomListItemView = memo(function RoomListItemView({
|
||||
room,
|
||||
isSelected,
|
||||
...props
|
||||
}: RoomListItemViewPropsProps): JSX.Element {
|
||||
}: RoomListItemViewProps): JSX.Element {
|
||||
const vm = useRoomListItemViewModel(room);
|
||||
|
||||
const [isHover, setIsHover] = useState(false);
|
||||
@@ -73,9 +73,12 @@ export const RoomListItemView = memo(function RoomListItemView({
|
||||
justify="space-between"
|
||||
>
|
||||
{/* We truncate the room name when too long. Title here is to show the full name on hover */}
|
||||
<span className="mx_RoomListItemView_roomName" title={vm.name}>
|
||||
{vm.name}
|
||||
</span>
|
||||
<div className="mx_RoomListItemView_text">
|
||||
<div className="mx_RoomListItemView_roomName" title={vm.name}>
|
||||
{vm.name}
|
||||
</div>
|
||||
<div className="mx_RoomListItemView_messagePreview">{vm.messagePreview}</div>
|
||||
</div>
|
||||
{showHoverDecoration ? (
|
||||
<RoomListItemMenuView
|
||||
room={room}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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 { IconButton, Menu, MenuTitle, CheckboxMenuItem, Tooltip } from "@vector-im/compound-web";
|
||||
import React, { type Ref, type JSX, useState } from "react";
|
||||
import OverflowHorizontalIcon from "@vector-im/compound-design-tokens/assets/web/icons/overflow-horizontal";
|
||||
|
||||
import { _t } from "../../../../languageHandler";
|
||||
import { type RoomListViewState } from "../../../viewmodels/roomlist/RoomListViewModel";
|
||||
|
||||
interface MenuTriggerProps extends React.ComponentProps<typeof IconButton> {
|
||||
ref?: Ref<HTMLButtonElement>;
|
||||
}
|
||||
|
||||
const MenuTrigger = ({ ref, ...props }: MenuTriggerProps): JSX.Element => (
|
||||
<Tooltip label={_t("room_list|room_options")}>
|
||||
<IconButton
|
||||
className="mx_RoomListSecondaryFilters_roomOptionsButton"
|
||||
aria-label={_t("room_list|room_options")}
|
||||
{...props}
|
||||
ref={ref}
|
||||
>
|
||||
<OverflowHorizontalIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
interface Props {
|
||||
/**
|
||||
* The view model for the room list view
|
||||
*/
|
||||
vm: RoomListViewState;
|
||||
}
|
||||
|
||||
export function RoomListOptionsMenu({ vm }: Props): JSX.Element {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<Menu
|
||||
open={open}
|
||||
onOpenChange={setOpen}
|
||||
title={_t("room_list|room_options")}
|
||||
showTitle={false}
|
||||
align="start"
|
||||
trigger={<MenuTrigger />}
|
||||
>
|
||||
<MenuTitle title={_t("room_list|appearance")} />
|
||||
<CheckboxMenuItem
|
||||
label={_t("room_list|show_message_previews")}
|
||||
onSelect={vm.toggleMessagePreview}
|
||||
checked={vm.shouldShowMessagePreview}
|
||||
/>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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 { RoomListViewState } from "../../../viewmodels/roomlist/RoomListViewModel";
|
||||
import { Flex } from "../../../utils/Flex";
|
||||
import { _t } from "../../../../languageHandler";
|
||||
import { RoomListOptionsMenu } from "./RoomListOptionsMenu";
|
||||
|
||||
interface Props {
|
||||
/**
|
||||
* The view model for the room list
|
||||
*/
|
||||
vm: RoomListViewState;
|
||||
}
|
||||
|
||||
/**
|
||||
* The secondary filters for the room list (eg. mentions only / invites only).
|
||||
*/
|
||||
export function RoomListSecondaryFilters({ vm }: Props): JSX.Element {
|
||||
return (
|
||||
<Flex
|
||||
aria-label={_t("room_list|secondary_filters")}
|
||||
className="mx_RoomListSecondaryFilters"
|
||||
align="center"
|
||||
gap="8px"
|
||||
>
|
||||
<RoomListOptionsMenu vm={vm} />
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import { useRoomListViewModel } from "../../../viewmodels/roomlist/RoomListViewM
|
||||
import { RoomList } from "./RoomList";
|
||||
import { EmptyRoomList } from "./EmptyRoomList";
|
||||
import { RoomListPrimaryFilters } from "./RoomListPrimaryFilters";
|
||||
import { RoomListSecondaryFilters } from "./RoomListSecondaryFilters";
|
||||
|
||||
/**
|
||||
* Host the room list and the (future) room filters
|
||||
@@ -22,6 +23,7 @@ export function RoomListView(): JSX.Element {
|
||||
return (
|
||||
<>
|
||||
<RoomListPrimaryFilters vm={vm} />
|
||||
<RoomListSecondaryFilters vm={vm} />
|
||||
{isRoomListEmpty ? <EmptyRoomList vm={vm} /> : <RoomList vm={vm} />}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -2102,6 +2102,7 @@
|
||||
"room_list": {
|
||||
"add_room_label": "Add room",
|
||||
"add_space_label": "Add space",
|
||||
"appearance": "Appearance",
|
||||
"breadcrumbs_empty": "No recently visited rooms",
|
||||
"breadcrumbs_label": "Recently visited rooms",
|
||||
"empty": {
|
||||
@@ -2152,7 +2153,10 @@
|
||||
"more_options": "More Options",
|
||||
"open_room": "Open room %(roomName)s"
|
||||
},
|
||||
"room_options": "Room Options",
|
||||
"secondary_filters": "Secondary filters",
|
||||
"show_less": "Show less",
|
||||
"show_message_previews": "Show message previews",
|
||||
"show_n_more": {
|
||||
"one": "Show %(count)s more",
|
||||
"other": "Show %(count)s more"
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { renderHook } from "jest-matrix-react";
|
||||
import { renderHook, waitFor } from "jest-matrix-react";
|
||||
import { type Room } from "matrix-js-sdk/src/matrix";
|
||||
import { mocked } from "jest-mock";
|
||||
|
||||
@@ -20,22 +20,40 @@ import {
|
||||
import { RoomNotificationState } from "../../../../../src/stores/notifications/RoomNotificationState";
|
||||
import { RoomNotificationStateStore } from "../../../../../src/stores/notifications/RoomNotificationStateStore";
|
||||
import * as UseCallModule from "../../../../../src/hooks/useCall";
|
||||
import { type MessagePreview, MessagePreviewStore } from "../../../../../src/stores/room-list/MessagePreviewStore";
|
||||
import DMRoomMap from "../../../../../src/utils/DMRoomMap";
|
||||
import { useMessagePreviewToggle } from "../../../../../src/components/viewmodels/roomlist/useMessagePreviewToggle";
|
||||
|
||||
jest.mock("../../../../../src/components/viewmodels/roomlist/utils", () => ({
|
||||
hasAccessToOptionsMenu: jest.fn().mockReturnValue(false),
|
||||
hasAccessToNotificationMenu: jest.fn().mockReturnValue(false),
|
||||
}));
|
||||
|
||||
jest.mock("../../../../../src/components/viewmodels/roomlist/useMessagePreviewToggle", () => ({
|
||||
useMessagePreviewToggle: jest.fn().mockReturnValue({ shouldShowMessagePreview: true }),
|
||||
}));
|
||||
|
||||
describe("RoomListItemViewModel", () => {
|
||||
let room: Room;
|
||||
|
||||
beforeEach(() => {
|
||||
const matrixClient = createTestClient();
|
||||
room = mkStubRoom("roomId", "roomName", matrixClient);
|
||||
|
||||
const dmRoomMap = {
|
||||
getUserIdForRoomId: jest.fn(),
|
||||
getDMRoomsForUserId: jest.fn(),
|
||||
} as unknown as DMRoomMap;
|
||||
DMRoomMap.setShared(dmRoomMap);
|
||||
|
||||
mocked(useMessagePreviewToggle).mockReturnValue({
|
||||
shouldShowMessagePreview: false,
|
||||
toggleMessagePreview: jest.fn(),
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("should dispatch view room action on openRoom", async () => {
|
||||
@@ -87,6 +105,39 @@ describe("RoomListItemViewModel", () => {
|
||||
expect(vm.current.showHoverMenu).toBe(true);
|
||||
});
|
||||
|
||||
it("should return a message preview if one is available and they are enabled", async () => {
|
||||
jest.spyOn(MessagePreviewStore.instance, "getPreviewForRoom").mockResolvedValue({
|
||||
text: "Message look like this",
|
||||
} as MessagePreview);
|
||||
mocked(useMessagePreviewToggle).mockReturnValue({
|
||||
shouldShowMessagePreview: true,
|
||||
toggleMessagePreview: jest.fn(),
|
||||
});
|
||||
|
||||
const { result: vm } = renderHook(
|
||||
() => useRoomListItemViewModel(room),
|
||||
withClientContextRenderOptions(room.client),
|
||||
);
|
||||
await waitFor(() => expect(vm.current.messagePreview).toBe("Message look like this"));
|
||||
});
|
||||
|
||||
it("should hide message previews when disabled", async () => {
|
||||
jest.spyOn(MessagePreviewStore.instance, "getPreviewForRoom").mockResolvedValue({
|
||||
text: "Message look like this",
|
||||
} as MessagePreview);
|
||||
|
||||
const { result: vm, rerender } = renderHook(
|
||||
() => useRoomListItemViewModel(room),
|
||||
withClientContextRenderOptions(room.client),
|
||||
);
|
||||
|
||||
// This doesn't seem to test that the hook actually triggers an update,
|
||||
// but I can't see how to test that.
|
||||
rerender();
|
||||
|
||||
expect(vm.current.messagePreview).toBe(undefined);
|
||||
});
|
||||
|
||||
describe("notification", () => {
|
||||
let notificationState: RoomNotificationState;
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -17,13 +17,14 @@ import { FilterKey } from "../../../../../src/stores/room-list-v3/skip-list/filt
|
||||
import { SecondaryFilters } from "../../../../../src/components/viewmodels/roomlist/useFilteredRooms";
|
||||
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 SettingsStore, { type CallbackFn } 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";
|
||||
import { SdkContextClass } from "../../../../../src/contexts/SDKContext";
|
||||
import SpaceStore from "../../../../../src/stores/spaces/SpaceStore";
|
||||
import { UPDATE_SELECTED_SPACE } from "../../../../../src/stores/spaces";
|
||||
import { SettingLevel } from "../../../../../src/settings/SettingLevel";
|
||||
|
||||
jest.mock("../../../../../src/components/viewmodels/roomlist/utils", () => ({
|
||||
hasCreateRoomRights: jest.fn().mockReturnValue(false),
|
||||
@@ -308,6 +309,25 @@ describe("RoomListViewModel", () => {
|
||||
expect(vm.current.shouldShowMessagePreview).toEqual(true);
|
||||
});
|
||||
|
||||
it("should update when setting changes", () => {
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation(() => true);
|
||||
|
||||
let watchFn: CallbackFn;
|
||||
jest.spyOn(SettingsStore, "watchSetting").mockImplementation((_settingname, _roomId, fn) => {
|
||||
watchFn = fn;
|
||||
return "";
|
||||
});
|
||||
mockAndCreateRooms();
|
||||
const { result: vm } = renderHook(() => useRoomListViewModel());
|
||||
expect(vm.current.shouldShowMessagePreview).toEqual(true);
|
||||
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation(() => false);
|
||||
act(() => {
|
||||
watchFn("RoomList.showMessagePreview", "", SettingLevel.DEVICE, false, false);
|
||||
});
|
||||
expect(vm.current.shouldShowMessagePreview).toEqual(false);
|
||||
});
|
||||
|
||||
it("should change setting on toggle", () => {
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation(() => true);
|
||||
const fn = jest.spyOn(SettingsStore, "setValue").mockImplementation(async () => {});
|
||||
@@ -317,8 +337,7 @@ describe("RoomListViewModel", () => {
|
||||
act(() => {
|
||||
vm.current.toggleMessagePreview();
|
||||
});
|
||||
expect(vm.current.shouldShowMessagePreview).toEqual(false);
|
||||
expect(fn).toHaveBeenCalled();
|
||||
expect(fn).toHaveBeenCalledWith("RoomList.showMessagePreview", null, "device", false);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ describe("<RoomListItemView />", () => {
|
||||
hasParticipantInCall: false,
|
||||
name: room.name,
|
||||
showNotificationDecoration: false,
|
||||
messagePreview: undefined,
|
||||
};
|
||||
|
||||
mocked(useRoomListItemViewModel).mockReturnValue(defaultValue);
|
||||
@@ -62,6 +63,14 @@ describe("<RoomListItemView />", () => {
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("should render a room item with a message preview", () => {
|
||||
defaultValue.messagePreview = "The message looks list this";
|
||||
|
||||
const onClick = jest.fn();
|
||||
const { asFragment } = render(<RoomListItemView room={room} onClick={onClick} isSelected={false} />);
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("should call openRoom when clicked", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<RoomListItemView room={room} isSelected={false} />);
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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 { 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 { RoomListSecondaryFilters } from "../../../../../../src/components/views/rooms/RoomListPanel/RoomListSecondaryFilters";
|
||||
|
||||
describe("<RoomListSecondaryFilters />", () => {
|
||||
let vm: RoomListViewState;
|
||||
|
||||
beforeEach(() => {
|
||||
vm = {
|
||||
rooms: [],
|
||||
canCreateRoom: true,
|
||||
createRoom: jest.fn(),
|
||||
createChatRoom: jest.fn(),
|
||||
primaryFilters: [],
|
||||
activateSecondaryFilter: () => {},
|
||||
activeSecondaryFilter: SecondaryFilters.AllActivity,
|
||||
sort: jest.fn(),
|
||||
activeSortOption: SortOption.Activity,
|
||||
shouldShowMessagePreview: false,
|
||||
toggleMessagePreview: jest.fn(),
|
||||
activeIndex: undefined,
|
||||
};
|
||||
});
|
||||
|
||||
it("should render 'room options' button", async () => {
|
||||
const { asFragment } = render(<RoomListSecondaryFilters vm={vm} />);
|
||||
expect(screen.getByRole("button", { name: "Room Options" })).toBeInTheDocument();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -57,12 +57,19 @@ exports[`<RoomList /> should render a room list 1`] = `
|
||||
class="mx_Flex mx_RoomListItemView_content"
|
||||
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
|
||||
class="mx_RoomListItemView_roomName"
|
||||
title="room0"
|
||||
<div
|
||||
class="mx_RoomListItemView_text"
|
||||
>
|
||||
room0
|
||||
</span>
|
||||
<div
|
||||
class="mx_RoomListItemView_roomName"
|
||||
title="room0"
|
||||
>
|
||||
room0
|
||||
</div>
|
||||
<div
|
||||
class="mx_RoomListItemView_messagePreview"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
@@ -101,12 +108,19 @@ exports[`<RoomList /> should render a room list 1`] = `
|
||||
class="mx_Flex mx_RoomListItemView_content"
|
||||
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
|
||||
class="mx_RoomListItemView_roomName"
|
||||
title="room1"
|
||||
<div
|
||||
class="mx_RoomListItemView_text"
|
||||
>
|
||||
room1
|
||||
</span>
|
||||
<div
|
||||
class="mx_RoomListItemView_roomName"
|
||||
title="room1"
|
||||
>
|
||||
room1
|
||||
</div>
|
||||
<div
|
||||
class="mx_RoomListItemView_messagePreview"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
@@ -145,12 +159,19 @@ exports[`<RoomList /> should render a room list 1`] = `
|
||||
class="mx_Flex mx_RoomListItemView_content"
|
||||
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
|
||||
class="mx_RoomListItemView_roomName"
|
||||
title="room2"
|
||||
<div
|
||||
class="mx_RoomListItemView_text"
|
||||
>
|
||||
room2
|
||||
</span>
|
||||
<div
|
||||
class="mx_RoomListItemView_roomName"
|
||||
title="room2"
|
||||
>
|
||||
room2
|
||||
</div>
|
||||
<div
|
||||
class="mx_RoomListItemView_messagePreview"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
@@ -189,12 +210,19 @@ exports[`<RoomList /> should render a room list 1`] = `
|
||||
class="mx_Flex mx_RoomListItemView_content"
|
||||
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
|
||||
class="mx_RoomListItemView_roomName"
|
||||
title="room3"
|
||||
<div
|
||||
class="mx_RoomListItemView_text"
|
||||
>
|
||||
room3
|
||||
</span>
|
||||
<div
|
||||
class="mx_RoomListItemView_roomName"
|
||||
title="room3"
|
||||
>
|
||||
room3
|
||||
</div>
|
||||
<div
|
||||
class="mx_RoomListItemView_messagePreview"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
@@ -233,12 +261,19 @@ exports[`<RoomList /> should render a room list 1`] = `
|
||||
class="mx_Flex mx_RoomListItemView_content"
|
||||
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
|
||||
class="mx_RoomListItemView_roomName"
|
||||
title="room4"
|
||||
<div
|
||||
class="mx_RoomListItemView_text"
|
||||
>
|
||||
room4
|
||||
</span>
|
||||
<div
|
||||
class="mx_RoomListItemView_roomName"
|
||||
title="room4"
|
||||
>
|
||||
room4
|
||||
</div>
|
||||
<div
|
||||
class="mx_RoomListItemView_messagePreview"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
@@ -277,12 +312,19 @@ exports[`<RoomList /> should render a room list 1`] = `
|
||||
class="mx_Flex mx_RoomListItemView_content"
|
||||
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
|
||||
class="mx_RoomListItemView_roomName"
|
||||
title="room5"
|
||||
<div
|
||||
class="mx_RoomListItemView_text"
|
||||
>
|
||||
room5
|
||||
</span>
|
||||
<div
|
||||
class="mx_RoomListItemView_roomName"
|
||||
title="room5"
|
||||
>
|
||||
room5
|
||||
</div>
|
||||
<div
|
||||
class="mx_RoomListItemView_messagePreview"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
@@ -321,12 +363,19 @@ exports[`<RoomList /> should render a room list 1`] = `
|
||||
class="mx_Flex mx_RoomListItemView_content"
|
||||
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
|
||||
class="mx_RoomListItemView_roomName"
|
||||
title="room6"
|
||||
<div
|
||||
class="mx_RoomListItemView_text"
|
||||
>
|
||||
room6
|
||||
</span>
|
||||
<div
|
||||
class="mx_RoomListItemView_roomName"
|
||||
title="room6"
|
||||
>
|
||||
room6
|
||||
</div>
|
||||
<div
|
||||
class="mx_RoomListItemView_messagePreview"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
@@ -365,12 +414,19 @@ exports[`<RoomList /> should render a room list 1`] = `
|
||||
class="mx_Flex mx_RoomListItemView_content"
|
||||
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
|
||||
class="mx_RoomListItemView_roomName"
|
||||
title="room7"
|
||||
<div
|
||||
class="mx_RoomListItemView_text"
|
||||
>
|
||||
room7
|
||||
</span>
|
||||
<div
|
||||
class="mx_RoomListItemView_roomName"
|
||||
title="room7"
|
||||
>
|
||||
room7
|
||||
</div>
|
||||
<div
|
||||
class="mx_RoomListItemView_messagePreview"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
@@ -409,12 +465,19 @@ exports[`<RoomList /> should render a room list 1`] = `
|
||||
class="mx_Flex mx_RoomListItemView_content"
|
||||
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
|
||||
class="mx_RoomListItemView_roomName"
|
||||
title="room8"
|
||||
<div
|
||||
class="mx_RoomListItemView_text"
|
||||
>
|
||||
room8
|
||||
</span>
|
||||
<div
|
||||
class="mx_RoomListItemView_roomName"
|
||||
title="room8"
|
||||
>
|
||||
room8
|
||||
</div>
|
||||
<div
|
||||
class="mx_RoomListItemView_messagePreview"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
@@ -453,12 +516,19 @@ exports[`<RoomList /> should render a room list 1`] = `
|
||||
class="mx_Flex mx_RoomListItemView_content"
|
||||
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
|
||||
class="mx_RoomListItemView_roomName"
|
||||
title="room9"
|
||||
<div
|
||||
class="mx_RoomListItemView_text"
|
||||
>
|
||||
room9
|
||||
</span>
|
||||
<div
|
||||
class="mx_RoomListItemView_roomName"
|
||||
title="room9"
|
||||
>
|
||||
room9
|
||||
</div>
|
||||
<div
|
||||
class="mx_RoomListItemView_messagePreview"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
@@ -35,12 +35,19 @@ exports[`<RoomListItemView /> should be selected if isSelected=true 1`] = `
|
||||
class="mx_Flex mx_RoomListItemView_content"
|
||||
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
|
||||
class="mx_RoomListItemView_roomName"
|
||||
title="room1"
|
||||
<div
|
||||
class="mx_RoomListItemView_text"
|
||||
>
|
||||
room1
|
||||
</span>
|
||||
<div
|
||||
class="mx_RoomListItemView_roomName"
|
||||
title="room1"
|
||||
>
|
||||
room1
|
||||
</div>
|
||||
<div
|
||||
class="mx_RoomListItemView_messagePreview"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
@@ -82,12 +89,19 @@ exports[`<RoomListItemView /> should display notification decoration 1`] = `
|
||||
class="mx_Flex mx_RoomListItemView_content"
|
||||
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
|
||||
class="mx_RoomListItemView_roomName"
|
||||
title="room1"
|
||||
<div
|
||||
class="mx_RoomListItemView_text"
|
||||
>
|
||||
room1
|
||||
</span>
|
||||
<div
|
||||
class="mx_RoomListItemView_roomName"
|
||||
title="room1"
|
||||
>
|
||||
room1
|
||||
</div>
|
||||
<div
|
||||
class="mx_RoomListItemView_messagePreview"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
class="mx_Flex"
|
||||
@@ -141,12 +155,75 @@ exports[`<RoomListItemView /> should render a room item 1`] = `
|
||||
class="mx_Flex mx_RoomListItemView_content"
|
||||
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
|
||||
class="mx_RoomListItemView_roomName"
|
||||
title="room1"
|
||||
<div
|
||||
class="mx_RoomListItemView_text"
|
||||
>
|
||||
room1
|
||||
</span>
|
||||
<div
|
||||
class="mx_RoomListItemView_roomName"
|
||||
title="room1"
|
||||
>
|
||||
room1
|
||||
</div>
|
||||
<div
|
||||
class="mx_RoomListItemView_messagePreview"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<RoomListItemView /> should render a room item with a message preview 1`] = `
|
||||
<DocumentFragment>
|
||||
<button
|
||||
aria-label="Open room room1"
|
||||
aria-selected="false"
|
||||
class="mx_RoomListItemView mx_RoomListItemView_empty"
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
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;"
|
||||
>
|
||||
<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
|
||||
class="mx_Flex mx_RoomListItemView_content"
|
||||
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;"
|
||||
>
|
||||
<div
|
||||
class="mx_RoomListItemView_text"
|
||||
>
|
||||
<div
|
||||
class="mx_RoomListItemView_roomName"
|
||||
title="room1"
|
||||
>
|
||||
room1
|
||||
</div>
|
||||
<div
|
||||
class="mx_RoomListItemView_messagePreview"
|
||||
>
|
||||
The message looks list this
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
@@ -113,6 +113,43 @@ exports[`<RoomListPanel /> should not render the RoomListSearch component when U
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<div
|
||||
aria-label="Secondary filters"
|
||||
class="mx_Flex mx_RoomListSecondaryFilters"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: 8px; --mx-flex-wrap: nowrap;"
|
||||
>
|
||||
<button
|
||||
aria-disabled="false"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="menu"
|
||||
aria-label="Room Options"
|
||||
aria-labelledby="«rc»"
|
||||
class="_icon-button_m2erp_8 mx_RoomListSecondaryFilters_roomOptionsButton"
|
||||
data-state="closed"
|
||||
id="radix-«ra»"
|
||||
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>
|
||||
<div
|
||||
class="mx_Flex mx_EmptyRoomList_GenericPlaceholder"
|
||||
data-testid="empty-room-list"
|
||||
@@ -334,6 +371,43 @@ exports[`<RoomListPanel /> should render the RoomListSearch component when UICom
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<div
|
||||
aria-label="Secondary filters"
|
||||
class="mx_Flex mx_RoomListSecondaryFilters"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: 8px; --mx-flex-wrap: nowrap;"
|
||||
>
|
||||
<button
|
||||
aria-disabled="false"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="menu"
|
||||
aria-label="Room Options"
|
||||
aria-labelledby="«r4»"
|
||||
class="_icon-button_m2erp_8 mx_RoomListSecondaryFilters_roomOptionsButton"
|
||||
data-state="closed"
|
||||
id="radix-«r2»"
|
||||
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>
|
||||
<div
|
||||
class="mx_Flex mx_EmptyRoomList_GenericPlaceholder"
|
||||
data-testid="empty-room-list"
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<RoomListSecondaryFilters /> should render 'room options' button 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
aria-label="Secondary filters"
|
||||
class="mx_Flex mx_RoomListSecondaryFilters"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: 8px; --mx-flex-wrap: nowrap;"
|
||||
>
|
||||
<button
|
||||
aria-disabled="false"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="menu"
|
||||
aria-label="Room Options"
|
||||
aria-labelledby="«r2»"
|
||||
class="_icon-button_m2erp_8 mx_RoomListSecondaryFilters_roomOptionsButton"
|
||||
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>
|
||||
`;
|
||||
10
yarn.lock
@@ -3653,10 +3653,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@vector-im/compound-design-tokens/-/compound-design-tokens-4.0.2.tgz#27363d26446eaa21880ab126fa51fec112e6fd86"
|
||||
integrity sha512-y13bhPyJ5OzbGRl21F6+Y2adrjyK+mu67yKTx+o8MfmIpJzMSn4KkHZtcujMquWSh0e5ZAufsnk4VYvxbSpr1A==
|
||||
|
||||
"@vector-im/compound-web@^7.10.1":
|
||||
version "7.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@vector-im/compound-web/-/compound-web-7.10.1.tgz#9aa7fc93550b4b064484fa30226439b2d07bb35e"
|
||||
integrity sha512-3tVIPCNxXCrMz6TqJc5GiOndPC7bjCRdYIcSKIb7T3B0gVo81aAD2wWL5xSb33yDbXc/tdlKCiav57eQB8dRsQ==
|
||||
"@vector-im/compound-web@^7.10.2":
|
||||
version "7.10.2"
|
||||
resolved "https://registry.yarnpkg.com/@vector-im/compound-web/-/compound-web-7.10.2.tgz#2f62c6ab83269e5b957f53bb53413a74fb65e04d"
|
||||
integrity sha512-K9gA1Ah9CTJMeZTkcDFpAdVRNbu/rQEgV3PoDcEPI3e9iDds8Dhbo7EfOciPvtXCZw6Hr83lnhWDnwTFHVlahQ==
|
||||
dependencies:
|
||||
"@floating-ui/react" "^0.27.0"
|
||||
"@radix-ui/react-context-menu" "^2.2.1"
|
||||
@@ -3677,7 +3677,7 @@
|
||||
resolved "https://registry.yarnpkg.com/@vector-im/matrix-wysiwyg/-/matrix-wysiwyg-2.38.3.tgz#cc54d8b3e9472bcd8e622126ba364ee31952cd8a"
|
||||
integrity sha512-fqo8P55Vc/t0vxpFar9RDJN5gKEjJmzrLo+O4piDbFda6VrRoqrWAtiu0Au0g6B4hRDPKIuFupk8v9Ja7q8Hvg==
|
||||
dependencies:
|
||||
"@vector-im/matrix-wysiwyg-wasm" "link:../../../.cache/yarn/v6/npm-@vector-im-matrix-wysiwyg-2.38.3-cc54d8b3e9472bcd8e622126ba364ee31952cd8a-integrity/node_modules/bindings/wysiwyg-wasm"
|
||||
"@vector-im/matrix-wysiwyg-wasm" "link:../../Library/Caches/Yarn/v6/npm-@vector-im-matrix-wysiwyg-2.38.3-cc54d8b3e9472bcd8e622126ba364ee31952cd8a-integrity/node_modules/bindings/wysiwyg-wasm"
|
||||
|
||||
"@webassemblyjs/ast@1.14.1", "@webassemblyjs/ast@^1.14.1":
|
||||
version "1.14.1"
|
||||
|
||||