Add message preview toggle to room list header option (#31821)
* feat: add message preview action to room list header option https://github.com/element-hq/element-web/issues/31214 * test: add e2e test
This commit is contained in:
@@ -44,8 +44,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"room_list": {
|
"room_list": {
|
||||||
|
"appearance": "Appearance",
|
||||||
"open_space_menu": "Open space menu",
|
"open_space_menu": "Open space menu",
|
||||||
"room_options": "Room Options",
|
"room_options": "Room Options",
|
||||||
|
"show_message_previews": "Show message previews",
|
||||||
"sort": "Sort",
|
"sort": "Sort",
|
||||||
"sort_type": {
|
"sort_type": {
|
||||||
"activity": "Activity",
|
"activity": "Activity",
|
||||||
|
|||||||
+3
@@ -28,6 +28,7 @@ const RoomListHeaderViewWrapper = ({
|
|||||||
inviteInSpace,
|
inviteInSpace,
|
||||||
openSpacePreferences,
|
openSpacePreferences,
|
||||||
sort,
|
sort,
|
||||||
|
toggleMessagePreview,
|
||||||
...rest
|
...rest
|
||||||
}: RoomListHeaderProps): JSX.Element => {
|
}: RoomListHeaderProps): JSX.Element => {
|
||||||
const vm = useMockedViewModel(rest, {
|
const vm = useMockedViewModel(rest, {
|
||||||
@@ -39,6 +40,7 @@ const RoomListHeaderViewWrapper = ({
|
|||||||
inviteInSpace,
|
inviteInSpace,
|
||||||
sort,
|
sort,
|
||||||
openSpacePreferences,
|
openSpacePreferences,
|
||||||
|
toggleMessagePreview,
|
||||||
});
|
});
|
||||||
return <RoomListHeaderView vm={vm} />;
|
return <RoomListHeaderView vm={vm} />;
|
||||||
};
|
};
|
||||||
@@ -57,6 +59,7 @@ export default {
|
|||||||
inviteInSpace: fn(),
|
inviteInSpace: fn(),
|
||||||
sort: fn(),
|
sort: fn(),
|
||||||
openSpacePreferences: fn(),
|
openSpacePreferences: fn(),
|
||||||
|
toggleMessagePreview: fn(),
|
||||||
},
|
},
|
||||||
parameters: {
|
parameters: {
|
||||||
design: {
|
design: {
|
||||||
|
|||||||
@@ -56,6 +56,10 @@ export interface RoomListHeaderViewSnapshot {
|
|||||||
* The currently active sort option.
|
* The currently active sort option.
|
||||||
*/
|
*/
|
||||||
activeSortOption: SortOption;
|
activeSortOption: SortOption;
|
||||||
|
/**
|
||||||
|
* Whether message previews are enabled in the room list.
|
||||||
|
*/
|
||||||
|
isMessagePreviewEnabled: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RoomListHeaderViewActions {
|
export interface RoomListHeaderViewActions {
|
||||||
@@ -91,6 +95,10 @@ export interface RoomListHeaderViewActions {
|
|||||||
* Change the sort order of the room-list.
|
* Change the sort order of the room-list.
|
||||||
*/
|
*/
|
||||||
sort: (option: SortOption) => void;
|
sort: (option: SortOption) => void;
|
||||||
|
/**
|
||||||
|
* Toggle message preview display in the room list.
|
||||||
|
*/
|
||||||
|
toggleMessagePreview: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+13
@@ -77,4 +77,17 @@ describe("<OptionMenuView />", () => {
|
|||||||
|
|
||||||
expect(vm.sort).toHaveBeenCalledWith("recent");
|
expect(vm.sort).toHaveBeenCalledWith("recent");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should toggle message preview", async () => {
|
||||||
|
const user = userEvent.setup();
|
||||||
|
|
||||||
|
const vm = new MockedViewModel({ ...defaultSnapshot, isMessagePreviewEnabled: true });
|
||||||
|
render(<OptionMenuView vm={vm} />);
|
||||||
|
|
||||||
|
await user.click(screen.getByRole("button", { name: "Room Options" }));
|
||||||
|
expect(screen.getByRole("menuitemcheckbox", { name: "Show message previews" })).toBeChecked();
|
||||||
|
|
||||||
|
await user.click(screen.getByRole("menuitemcheckbox", { name: "Show message previews" }));
|
||||||
|
expect(vm.toggleMessagePreview).toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
* Please see LICENSE files in the repository root for full details.
|
* Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { IconButton, Menu, MenuTitle, RadioMenuItem } from "@vector-im/compound-web";
|
import { CheckboxMenuItem, IconButton, Menu, MenuTitle, RadioMenuItem } from "@vector-im/compound-web";
|
||||||
import React, { type JSX, useState } from "react";
|
import React, { type JSX, useState } from "react";
|
||||||
import OverflowHorizontalIcon from "@vector-im/compound-design-tokens/assets/web/icons/overflow-horizontal";
|
import OverflowHorizontalIcon from "@vector-im/compound-design-tokens/assets/web/icons/overflow-horizontal";
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@ interface OptionMenuViewProps {
|
|||||||
export function OptionMenuView({ vm }: OptionMenuViewProps): JSX.Element {
|
export function OptionMenuView({ vm }: OptionMenuViewProps): JSX.Element {
|
||||||
const { translate: _t } = useI18n();
|
const { translate: _t } = useI18n();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const { activeSortOption } = useViewModel(vm);
|
const { activeSortOption, isMessagePreviewEnabled } = useViewModel(vm);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Menu
|
<Menu
|
||||||
@@ -65,6 +65,12 @@ export function OptionMenuView({ vm }: OptionMenuViewProps): JSX.Element {
|
|||||||
checked={activeSortOption === "alphabetical"}
|
checked={activeSortOption === "alphabetical"}
|
||||||
onSelect={() => vm.sort("alphabetical")}
|
onSelect={() => vm.sort("alphabetical")}
|
||||||
/>
|
/>
|
||||||
|
<MenuTitle title={_t("room_list|appearance")} />
|
||||||
|
<CheckboxMenuItem
|
||||||
|
label={_t("room_list|show_message_previews")}
|
||||||
|
onSelect={vm.toggleMessagePreview}
|
||||||
|
checked={isMessagePreviewEnabled}
|
||||||
|
/>
|
||||||
</Menu>
|
</Menu>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export class MockedViewModel extends MockViewModel<RoomListHeaderViewSnapshot> i
|
|||||||
public inviteInSpace = jest.fn();
|
public inviteInSpace = jest.fn();
|
||||||
public sort = jest.fn();
|
public sort = jest.fn();
|
||||||
public openSpacePreferences = jest.fn();
|
public openSpacePreferences = jest.fn();
|
||||||
|
public toggleMessagePreview = jest.fn();
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defaultSnapshot: RoomListHeaderViewSnapshot = {
|
export const defaultSnapshot: RoomListHeaderViewSnapshot = {
|
||||||
@@ -31,4 +32,5 @@ export const defaultSnapshot: RoomListHeaderViewSnapshot = {
|
|||||||
canInviteInSpace: true,
|
canInviteInSpace: true,
|
||||||
canAccessSpaceSettings: true,
|
canAccessSpaceSettings: true,
|
||||||
activeSortOption: "recent",
|
activeSortOption: "recent",
|
||||||
|
isMessagePreviewEnabled: true,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
import { type Page } from "@playwright/test";
|
import { type Page } from "@playwright/test";
|
||||||
|
|
||||||
import { expect, test } from "../../../element-web-test";
|
import { expect, test } from "../../../element-web-test";
|
||||||
|
import { type Bot } from "../../../pages/bot";
|
||||||
|
import { type ElementAppPage } from "../../../pages/ElementAppPage";
|
||||||
|
|
||||||
test.describe("Room list", () => {
|
test.describe("Room list", () => {
|
||||||
test.use({
|
test.use({
|
||||||
@@ -392,13 +394,8 @@ test.describe("Room list", () => {
|
|||||||
await expect(room).toMatchScreenshot("room-list-item-mention.png");
|
await expect(room).toMatchScreenshot("room-list-item-mention.png");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should render a message preview", { tag: "@screenshot" }, async ({ page, app, user, bot }) => {
|
async function checkMessagePreview(page: Page, app: ElementAppPage, bot: Bot) {
|
||||||
await app.settings.openUserSettings("Preferences");
|
|
||||||
await page.getByRole("switch", { name: "Show message previews" }).click();
|
|
||||||
await app.closeDialog();
|
|
||||||
|
|
||||||
const roomListView = getRoomList(page);
|
const roomListView = getRoomList(page);
|
||||||
|
|
||||||
const roomId = await app.client.createRoom({ name: "activity" });
|
const roomId = await app.client.createRoom({ name: "activity" });
|
||||||
|
|
||||||
// focus the user menu to avoid to have hover decoration
|
// focus the user menu to avoid to have hover decoration
|
||||||
@@ -411,7 +408,30 @@ test.describe("Room list", () => {
|
|||||||
const room = roomListView.getByRole("option", { name: "activity" });
|
const room = roomListView.getByRole("option", { name: "activity" });
|
||||||
await expect(room.getByText("I am a robot. Beep.")).toBeVisible();
|
await expect(room.getByText("I am a robot. Beep.")).toBeVisible();
|
||||||
await expect(room).toMatchScreenshot("room-list-item-message-preview.png");
|
await expect(room).toMatchScreenshot("room-list-item-message-preview.png");
|
||||||
});
|
}
|
||||||
|
|
||||||
|
test(
|
||||||
|
"should render a message preview when enable in settings",
|
||||||
|
{ tag: "@screenshot" },
|
||||||
|
async ({ page, app, user, bot }) => {
|
||||||
|
await app.settings.openUserSettings("Preferences");
|
||||||
|
await page.getByRole("switch", { name: "Show message previews" }).click();
|
||||||
|
await app.closeDialog();
|
||||||
|
|
||||||
|
await checkMessagePreview(page, app, bot);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
test(
|
||||||
|
"should render a message preview when enabled in header",
|
||||||
|
{ tag: "@screenshot" },
|
||||||
|
async ({ page, app, user, bot }) => {
|
||||||
|
await page.getByRole("button", { name: "Room Options" }).click();
|
||||||
|
await page.getByRole("menuitemcheckbox", { name: "Show message previews" }).click();
|
||||||
|
|
||||||
|
await checkMessagePreview(page, app, bot);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
test("should render an activity decoration", { tag: "@screenshot" }, async ({ page, app, user, bot }) => {
|
test("should render an activity decoration", { tag: "@screenshot" }, async ({ page, app, user, bot }) => {
|
||||||
const roomListView = getRoomList(page);
|
const roomListView = getRoomList(page);
|
||||||
|
|||||||
@@ -30,12 +30,16 @@ import { createRoom, hasCreateRoomRights } from "../../components/viewmodels/roo
|
|||||||
import SettingsStore from "../../settings/SettingsStore";
|
import SettingsStore from "../../settings/SettingsStore";
|
||||||
import RoomListStoreV3 from "../../stores/room-list-v3/RoomListStoreV3";
|
import RoomListStoreV3 from "../../stores/room-list-v3/RoomListStoreV3";
|
||||||
import { SortingAlgorithm } from "../../stores/room-list-v3/skip-list/sorters";
|
import { SortingAlgorithm } from "../../stores/room-list-v3/skip-list/sorters";
|
||||||
|
import { SettingLevel } from "../../settings/SettingLevel";
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
/**
|
/**
|
||||||
* The Matrix client instance.
|
* The Matrix client instance.
|
||||||
*/
|
*/
|
||||||
matrixClient: MatrixClient;
|
matrixClient: MatrixClient;
|
||||||
|
/**
|
||||||
|
* The space store instance.
|
||||||
|
*/
|
||||||
spaceStore: SpaceStoreClass;
|
spaceStore: SpaceStoreClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,6 +174,12 @@ export class RoomListHeaderViewModel
|
|||||||
RoomListStoreV3.instance.resort(sortingAlgorithm);
|
RoomListStoreV3.instance.resort(sortingAlgorithm);
|
||||||
this.snapshot.merge({ activeSortOption: option });
|
this.snapshot.merge({ activeSortOption: option });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public toggleMessagePreview = (): void => {
|
||||||
|
const isMessagePreviewEnabled = SettingsStore.getValue("RoomList.showMessagePreview");
|
||||||
|
SettingsStore.setValue("RoomList.showMessagePreview", null, SettingLevel.DEVICE, !isMessagePreviewEnabled);
|
||||||
|
this.snapshot.merge({ isMessagePreviewEnabled });
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -182,9 +192,11 @@ function getInitialSnapshot(spaceStore: SpaceStoreClass, matrixClient: MatrixCli
|
|||||||
const sortingAlgorithm = SettingsStore.getValue("RoomList.preferredSorting");
|
const sortingAlgorithm = SettingsStore.getValue("RoomList.preferredSorting");
|
||||||
const activeSortOption =
|
const activeSortOption =
|
||||||
sortingAlgorithm === SortingAlgorithm.Recency ? ("recent" as const) : ("alphabetical" as const);
|
sortingAlgorithm === SortingAlgorithm.Recency ? ("recent" as const) : ("alphabetical" as const);
|
||||||
|
const isMessagePreviewEnabled = SettingsStore.getValue("RoomList.showMessagePreview");
|
||||||
|
|
||||||
return {
|
return {
|
||||||
activeSortOption,
|
activeSortOption,
|
||||||
|
isMessagePreviewEnabled,
|
||||||
...computeHeaderSpaceState(spaceStore, matrixClient),
|
...computeHeaderSpaceState(spaceStore, matrixClient),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -216,7 +228,7 @@ function getCanCreateVideoRoom(canCreateRoom: boolean): boolean {
|
|||||||
function computeHeaderSpaceState(
|
function computeHeaderSpaceState(
|
||||||
spaceStore: SpaceStoreClass,
|
spaceStore: SpaceStoreClass,
|
||||||
matrixClient: MatrixClient,
|
matrixClient: MatrixClient,
|
||||||
): Omit<RoomListHeaderViewSnapshot, "activeSortOption"> {
|
): Omit<RoomListHeaderViewSnapshot, "activeSortOption" | "isMessagePreviewEnabled"> {
|
||||||
const activeSpace = spaceStore.activeSpaceRoom;
|
const activeSpace = spaceStore.activeSpaceRoom;
|
||||||
const title = getHeaderTitle(spaceStore);
|
const title = getHeaderTitle(spaceStore);
|
||||||
|
|
||||||
|
|||||||
@@ -148,6 +148,16 @@ describe("RoomListHeaderViewModel", () => {
|
|||||||
vm = new RoomListHeaderViewModel({ matrixClient, spaceStore: SpaceStore.instance });
|
vm = new RoomListHeaderViewModel({ matrixClient, spaceStore: SpaceStore.instance });
|
||||||
expect(vm.getSnapshot().canAccessSpaceSettings).toBe(false);
|
expect(vm.getSnapshot().canAccessSpaceSettings).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should show message preview when RoomList.showMessagePreview is enabled", () => {
|
||||||
|
jest.spyOn(SettingsStore, "getValue").mockImplementation((settingName: string) => {
|
||||||
|
if (settingName === "RoomList.showMessagePreview") return true;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
vm = new RoomListHeaderViewModel({ matrixClient, spaceStore: SpaceStore.instance });
|
||||||
|
expect(vm.getSnapshot().isMessagePreviewEnabled).toBe(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("event listeners", () => {
|
describe("event listeners", () => {
|
||||||
@@ -268,5 +278,20 @@ describe("RoomListHeaderViewModel", () => {
|
|||||||
|
|
||||||
expect(resortSpy).toHaveBeenCalledWith(expectedAlgorithm);
|
expect(resortSpy).toHaveBeenCalledWith(expectedAlgorithm);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should toggle message preview from enabled to disabled", () => {
|
||||||
|
jest.spyOn(SettingsStore, "getValue").mockImplementation((settingName: string) => {
|
||||||
|
if (settingName === "RoomList.showMessagePreview") return true;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
const setValueSpy = jest.spyOn(SettingsStore, "setValue").mockImplementation(jest.fn());
|
||||||
|
|
||||||
|
vm = new RoomListHeaderViewModel({ matrixClient, spaceStore: SpaceStore.instance });
|
||||||
|
expect(vm.getSnapshot().isMessagePreviewEnabled).toBe(true);
|
||||||
|
|
||||||
|
vm.toggleMessagePreview();
|
||||||
|
|
||||||
|
expect(setValueSpy).toHaveBeenCalledWith("RoomList.showMessagePreview", null, expect.anything(), false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user