Room list: fix room options remaining on room item after mouse leaving (#31414)

* refactor(room list): remove extra component button and `setMenuOpen` callback

* fix(room list): replace js focus and hover handling by CSS to fix focus decoration

Closes https://github.com/element-hq/element-web/issues/31365

* test(room list): update jest test

* refactor(room list): remove irrelevant comment

* test(room list): update screenshots
This commit is contained in:
Florian Duros
2025-12-04 12:23:13 +01:00
committed by GitHub
parent 2ab9d345b4
commit 1a7023779c
14 changed files with 1316 additions and 751 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

After

Width:  |  Height:  |  Size: 91 KiB

@@ -28,6 +28,41 @@
padding-left: var(--cpd-space-3x); padding-left: var(--cpd-space-3x);
font: var(--cpd-font-body-md-regular); font: var(--cpd-font-body-md-regular);
/* Hide the menu by default */
.mx_RoomListItemView_menu {
display: none;
}
&:hover,
&:focus-visible,
/* When the context menu is opened */
&[data-state="open"],
/* When the options and notifications menu are opened */
&:has(.mx_RoomListItemMenuView > button[data-state="open"]) {
background-color: var(--cpd-color-bg-action-secondary-hovered);
.mx_RoomListItemView_menu {
display: flex;
}
&.mx_RoomListItemView_has_menu {
/**
* The figma uses 16px padding (--cpd-space-4x) but due to https://github.com/element-hq/compound-web/issues/331
* the icon size of the menu is 18px instead of 20px with a different internal padding
* We need to use 18px to align the icon with the others icons
* 18px is not available in compound spacing
*/
.mx_RoomListItemView_content {
padding-right: 18px;
}
/* When the menu is visible, hide the notification decoration to avoid clutter */
.mx_RoomListItemView_notificationDecoration {
display: none;
}
}
}
.mx_RoomListItemView_content { .mx_RoomListItemView_content {
height: 100%; height: 100%;
flex: 1; flex: 1;
@@ -57,20 +92,6 @@
} }
} }
.mx_RoomListItemView_hover {
background-color: var(--cpd-color-bg-action-secondary-hovered);
}
.mx_RoomListItemView_menu_open .mx_RoomListItemView_content {
/**
* The figma uses 16px padding (--cpd-space-4x) but due to https://github.com/element-hq/compound-web/issues/331
* the icon size of the menu is 18px instead of 20px with a different internal padding
* We need to use 18px to align the icon with the others icons
* 18px is not available in compound spacing
*/
padding-right: 18px;
}
.mx_RoomListItemView_selected { .mx_RoomListItemView_selected {
background-color: var(--cpd-color-bg-action-secondary-pressed); background-color: var(--cpd-color-bg-action-secondary-pressed);
} }
@@ -19,10 +19,6 @@ interface RoomListItemContextMenuViewProps {
* The room to display the menu for. * The room to display the menu for.
*/ */
room: Room; room: Room;
/**
* Set the menu open state.
*/
setMenuOpen: (isOpen: boolean) => void;
} }
/** /**
@@ -30,7 +26,6 @@ interface RoomListItemContextMenuViewProps {
*/ */
export function RoomListItemContextMenuView({ export function RoomListItemContextMenuView({
room, room,
setMenuOpen,
children, children,
}: PropsWithChildren<RoomListItemContextMenuViewProps>): JSX.Element { }: PropsWithChildren<RoomListItemContextMenuViewProps>): JSX.Element {
const vm = useRoomListItemMenuViewModel(room); const vm = useRoomListItemMenuViewModel(room);
@@ -42,7 +37,6 @@ export function RoomListItemContextMenuView({
// To not mess with the roving tab index of the button // To not mess with the roving tab index of the button
hasAccessibleAlternative={true} hasAccessibleAlternative={true}
trigger={children} trigger={children}
onOpenChange={setMenuOpen}
> >
<MoreOptionContent vm={vm} /> <MoreOptionContent vm={vm} />
</ContextMenu> </ContextMenu>
@@ -5,8 +5,8 @@
* Please see LICENSE files in the repository root for full details. * Please see LICENSE files in the repository root for full details.
*/ */
import React, { type ComponentProps, type JSX, type Ref, useState } from "react"; import React, { type JSX, useState } from "react";
import { IconButton, Menu, MenuItem, Separator, ToggleMenuItem, Tooltip } from "@vector-im/compound-web"; import { IconButton, Menu, MenuItem, Separator, ToggleMenuItem } from "@vector-im/compound-web";
import MarkAsReadIcon from "@vector-im/compound-design-tokens/assets/web/icons/mark-as-read"; import MarkAsReadIcon from "@vector-im/compound-design-tokens/assets/web/icons/mark-as-read";
import MarkAsUnreadIcon from "@vector-im/compound-design-tokens/assets/web/icons/mark-as-unread"; import MarkAsUnreadIcon from "@vector-im/compound-design-tokens/assets/web/icons/mark-as-unread";
import FavouriteIcon from "@vector-im/compound-design-tokens/assets/web/icons/favourite"; import FavouriteIcon from "@vector-im/compound-design-tokens/assets/web/icons/favourite";
@@ -20,6 +20,7 @@ import NotificationOffIcon from "@vector-im/compound-design-tokens/assets/web/ic
import CheckIcon from "@vector-im/compound-design-tokens/assets/web/icons/check"; import CheckIcon from "@vector-im/compound-design-tokens/assets/web/icons/check";
import { type Room } from "matrix-js-sdk/src/matrix"; import { type Room } from "matrix-js-sdk/src/matrix";
import { Flex } from "@element-hq/web-shared-components"; import { Flex } from "@element-hq/web-shared-components";
import classNames from "classnames";
import { _t } from "../../../../languageHandler"; import { _t } from "../../../../languageHandler";
import { import {
@@ -29,26 +30,27 @@ import {
import { RoomNotifState } from "../../../../RoomNotifs"; import { RoomNotifState } from "../../../../RoomNotifs";
interface RoomListItemMenuViewProps { interface RoomListItemMenuViewProps {
/**
* Additional class name for the root element.
*/
className?: string;
/** /**
* The room to display the menu for. * The room to display the menu for.
*/ */
room: Room; room: Room;
/**
* Set the menu open state.
*/
setMenuOpen: (isOpen: boolean) => void;
} }
/** /**
* A view for the room list item menu. * A view for the room list item menu.
*/ */
export function RoomListItemMenuView({ room, setMenuOpen }: RoomListItemMenuViewProps): JSX.Element { export function RoomListItemMenuView({ room, className }: RoomListItemMenuViewProps): JSX.Element {
const vm = useRoomListItemMenuViewModel(room); const vm = useRoomListItemMenuViewModel(room);
return ( return (
<Flex className="mx_RoomListItemMenuView" align="center" gap="var(--cpd-space-1x)"> <Flex className={classNames("mx_RoomListItemMenuView", className)} align="center" gap="var(--cpd-space-1x)">
{vm.showMoreOptionsMenu && <MoreOptionsMenu setMenuOpen={setMenuOpen} vm={vm} />} {vm.showMoreOptionsMenu && <MoreOptionsMenu vm={vm} />}
{vm.showNotificationMenu && <NotificationMenu setMenuOpen={setMenuOpen} vm={vm} />} {vm.showNotificationMenu && <NotificationMenu vm={vm} />}
</Flex> </Flex>
); );
} }
@@ -58,30 +60,30 @@ interface MoreOptionsMenuProps {
* The view model state for the menu. * The view model state for the menu.
*/ */
vm: RoomListItemMenuViewState; vm: RoomListItemMenuViewState;
/**
* Set the menu open state.
* @param isOpen
*/
setMenuOpen: (isOpen: boolean) => void;
} }
/** /**
* The more options menu for the room list item. * The more options menu for the room list item.
*/ */
function MoreOptionsMenu({ vm, setMenuOpen }: MoreOptionsMenuProps): JSX.Element { function MoreOptionsMenu({ vm }: MoreOptionsMenuProps): JSX.Element {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
return ( return (
<Menu <Menu
open={open} open={open}
onOpenChange={(isOpen) => { onOpenChange={setOpen}
setOpen(isOpen);
setMenuOpen(isOpen);
}}
title={_t("room_list|room|more_options")} title={_t("room_list|room|more_options")}
showTitle={false} showTitle={false}
align="start" align="start"
trigger={<MoreOptionsButton size="24px" />} trigger={
<IconButton
tooltip={_t("room_list|room|more_options")}
aria-label={_t("room_list|room|more_options")}
size="24px"
>
<OverflowIcon />
</IconButton>
}
> >
<MoreOptionContent vm={vm} /> <MoreOptionContent vm={vm} />
</Menu> </Menu>
@@ -164,55 +166,37 @@ export function MoreOptionContent({ vm }: MoreOptionContentProps): JSX.Element {
); );
} }
interface MoreOptionsButtonProps extends ComponentProps<typeof IconButton> {
ref?: Ref<HTMLButtonElement>;
}
/**
* A button to trigger the more options menu.
*/
const MoreOptionsButton = function MoreOptionsButton(props: MoreOptionsButtonProps): JSX.Element {
return (
<Tooltip label={_t("room_list|room|more_options")}>
<IconButton aria-label={_t("room_list|room|more_options")} {...props}>
<OverflowIcon />
</IconButton>
</Tooltip>
);
};
interface NotificationMenuProps { interface NotificationMenuProps {
/** /**
* The view model state for the menu. * The view model state for the menu.
*/ */
vm: RoomListItemMenuViewState; vm: RoomListItemMenuViewState;
/**
* Set the menu open state.
* @param isOpen
*/
setMenuOpen: (isOpen: boolean) => void;
} }
function NotificationMenu({ vm, setMenuOpen }: NotificationMenuProps): JSX.Element { function NotificationMenu({ vm }: NotificationMenuProps): JSX.Element {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const checkComponent = <CheckIcon width="24px" height="24px" color="var(--cpd-color-icon-primary)" />; const checkComponent = <CheckIcon width="24px" height="24px" color="var(--cpd-color-icon-primary)" />;
return ( return (
<div <Menu
// We don't want keyboard navigation events to bubble up to the ListView changing the focused item open={open}
onKeyDown={(e) => e.stopPropagation()} onOpenChange={setOpen}
title={_t("room_list|notification_options")}
showTitle={false}
align="start"
trigger={
<IconButton
size="24px"
tooltip={_t("room_list|notification_options")}
aria-label={_t("room_list|notification_options")}
>
{vm.isNotificationMute ? <NotificationOffIcon /> : <NotificationIcon />}
</IconButton>
}
> >
<Menu <div
open={open} // We don't want keyboard navigation events to bubble up to the ListView changing the focused item
onOpenChange={(isOpen) => { onKeyDown={(e) => e.stopPropagation()}
setOpen(isOpen);
setMenuOpen(isOpen);
}}
title={_t("room_list|notification_options")}
showTitle={false}
align="start"
trigger={<NotificationButton isRoomMuted={vm.isNotificationMute} size="24px" />}
> >
<MenuItem <MenuItem
aria-selected={vm.isNotificationAllMessage} aria-selected={vm.isNotificationAllMessage}
@@ -250,32 +234,7 @@ function NotificationMenu({ vm, setMenuOpen }: NotificationMenuProps): JSX.Eleme
> >
{vm.isNotificationMute && checkComponent} {vm.isNotificationMute && checkComponent}
</MenuItem> </MenuItem>
</Menu> </div>
</div> </Menu>
); );
} }
interface NotificationButtonProps extends ComponentProps<typeof IconButton> {
/**
* Whether the room is muted.
*/
isRoomMuted: boolean;
ref?: Ref<HTMLButtonElement>;
}
/**
* A button to trigger the notification menu.
*/
const NotificationButton = function MoreOptionsButton({
isRoomMuted,
ref,
...props
}: NotificationButtonProps): JSX.Element {
return (
<Tooltip label={_t("room_list|notification_options")}>
<IconButton aria-label={_t("room_list|notification_options")} {...props} ref={ref}>
{isRoomMuted ? <NotificationOffIcon /> : <NotificationIcon />}
</IconButton>
</Tooltip>
);
};
@@ -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 React, { type JSX, memo, useCallback, useEffect, useRef, useState } from "react"; import React, { type JSX, memo, useEffect, useRef } from "react";
import { type Room } from "matrix-js-sdk/src/matrix"; import { type Room } from "matrix-js-sdk/src/matrix";
import classNames from "classnames"; import classNames from "classnames";
import { Flex } from "@element-hq/web-shared-components"; import { Flex } from "@element-hq/web-shared-components";
@@ -57,18 +57,6 @@ export const RoomListItemView = memo(function RoomListItemView({
}: RoomListItemViewProps): JSX.Element { }: RoomListItemViewProps): JSX.Element {
const ref = useRef<HTMLButtonElement>(null); const ref = useRef<HTMLButtonElement>(null);
const vm = useRoomListItemViewModel(room); const vm = useRoomListItemViewModel(room);
const [isHover, setHover] = useState(false);
const [isMenuOpen, setIsMenuOpen] = useState(false);
// The compound menu in RoomListItemMenuView needs to be rendered when the hover menu is shown
// Using display: none; and then display:flex when hovered in CSS causes the menu to be misaligned
const showHoverDecoration = isMenuOpen || isFocused || isHover;
const showHoverMenu = showHoverDecoration && vm.showHoverMenu;
const closeMenu = useCallback(() => {
// To avoid icon blinking when closing the menu, we delay the state update
// Also, let the focus move to the menu trigger before closing the menu
setTimeout(() => setIsMenuOpen(false), 10);
}, []);
useEffect(() => { useEffect(() => {
if (isFocused) { if (isFocused) {
@@ -81,8 +69,7 @@ export const RoomListItemView = memo(function RoomListItemView({
as="button" as="button"
ref={ref} ref={ref}
className={classNames("mx_RoomListItemView", { className={classNames("mx_RoomListItemView", {
mx_RoomListItemView_hover: showHoverDecoration, mx_RoomListItemView_has_menu: vm.showHoverMenu,
mx_RoomListItemView_menu_open: showHoverMenu,
mx_RoomListItemView_selected: isSelected, mx_RoomListItemView_selected: isSelected,
mx_RoomListItemView_bold: vm.isBold, mx_RoomListItemView_bold: vm.isBold,
})} })}
@@ -96,8 +83,6 @@ export const RoomListItemView = memo(function RoomListItemView({
aria-label={vm.a11yLabel} aria-label={vm.a11yLabel}
onClick={() => vm.openRoom()} onClick={() => vm.openRoom()}
onFocus={(e: React.FocusEvent<HTMLButtonElement>) => onFocus(room, e)} onFocus={(e: React.FocusEvent<HTMLButtonElement>) => onFocus(room, e)}
onMouseOver={() => setHover(true)}
onMouseOut={() => setHover(false)}
tabIndex={isFocused ? 0 : -1} tabIndex={isFocused ? 0 : -1}
{...props} {...props}
> >
@@ -119,44 +104,21 @@ export const RoomListItemView = memo(function RoomListItemView({
</div> </div>
)} )}
</div> </div>
{showHoverMenu ? ( {vm.showHoverMenu && <RoomListItemMenuView className="mx_RoomListItemView_menu" room={room} />}
<RoomListItemMenuView
room={room} {/* aria-hidden because we summarise the unread count/notification status in a11yLabel variable */}
setMenuOpen={(isOpen) => (isOpen ? setIsMenuOpen(true) : closeMenu())} {vm.showNotificationDecoration && (
<NotificationDecoration
className="mx_RoomListItemView_notificationDecoration"
notificationState={vm.notificationState}
aria-hidden={true}
callType={vm.callType}
/> />
) : (
<>
{/* aria-hidden because we summarise the unread count/notification status in a11yLabel variable */}
{vm.showNotificationDecoration && (
<NotificationDecoration
notificationState={vm.notificationState}
aria-hidden={true}
callType={vm.callType}
/>
)}
</>
)} )}
</Flex> </Flex>
</Flex> </Flex>
); );
// Rendering multiple context menus can causes crashes in radix upstream,
// See https://github.com/radix-ui/primitives/issues/2717.
if (!vm.showContextMenu) return content; if (!vm.showContextMenu) return content;
return <RoomListItemContextMenuView room={room}>{content}</RoomListItemContextMenuView>;
return (
<RoomListItemContextMenuView
room={room}
setMenuOpen={(isOpen) => {
if (isOpen) {
// To avoid icon blinking when the context menu is re-opened
setTimeout(() => setIsMenuOpen(true), 0);
} else {
closeMenu();
}
}}
>
{content}
</RoomListItemContextMenuView>
);
}); });
@@ -56,8 +56,8 @@ describe("<RoomListItemMenuView />", () => {
room = mkRoom(matrixClient, "room1"); room = mkRoom(matrixClient, "room1");
}); });
function renderMenu(setMenuOpen = jest.fn()) { function renderMenu() {
return render(<RoomListItemMenuView room={room} setMenuOpen={setMenuOpen} />); return render(<RoomListItemMenuView room={room} />);
} }
it("should render the more options menu", () => { it("should render the more options menu", () => {
@@ -84,18 +84,6 @@ describe("<RoomListItemMenuView />", () => {
expect(screen.queryByRole("button", { name: "Notification options" })).toBeNull(); expect(screen.queryByRole("button", { name: "Notification options" })).toBeNull();
}); });
it.each([["More Options"], ["Notification options"]])(
"should call setMenuOpen when the menu is opened for %s menu",
async (label) => {
const user = userEvent.setup();
const setMenuOpen = jest.fn();
renderMenu(setMenuOpen);
await user.click(screen.getByRole("button", { name: label }));
expect(setMenuOpen).toHaveBeenCalledWith(true);
},
);
it("should display all the buttons and have the actions linked for the more options menu", async () => { it("should display all the buttons and have the actions linked for the more options menu", async () => {
const user = userEvent.setup(); const user = userEvent.setup();
renderMenu(); renderMenu();
@@ -102,41 +102,6 @@ describe("<RoomListItemView />", () => {
expect(defaultValue.openRoom).toHaveBeenCalled(); expect(defaultValue.openRoom).toHaveBeenCalled();
}); });
test("should hover decoration if hovered", async () => {
mocked(useRoomListItemViewModel).mockReturnValue({ ...defaultValue, showHoverMenu: true });
const user = userEvent.setup();
renderRoomListItem();
const listItem = screen.getByRole("option", { name: `Open room ${room.name}` });
expect(screen.queryByRole("button", { name: "More Options" })).toBeNull();
await user.hover(listItem);
await waitFor(() => expect(screen.getByRole("button", { name: "More Options" })).toBeInTheDocument());
});
test("should hover decoration if focused", async () => {
const { rerender } = renderRoomListItem({
isFocused: true,
});
const listItem = screen.getByRole("option", { name: `Open room ${room.name}` });
expect(listItem).toHaveClass("_flex_4dswl_9 mx_RoomListItemView mx_RoomListItemView_hover");
rerender(
<RoomListItemView
room={room}
isSelected={false}
isFocused={false}
onFocus={jest.fn()}
roomIndex={0}
roomCount={1}
/>,
);
await waitFor(() => expect(listItem).not.toHaveClass("flex mx_RoomListItemView mx_RoomListItemView_hover"));
});
test("should be selected if isSelected=true", async () => { test("should be selected if isSelected=true", async () => {
const { asFragment } = renderRoomListItem({ const { asFragment } = renderRoomListItem({
isSelected: true, isSelected: true,
@@ -38,43 +38,41 @@ exports[`<RoomListItemMenuView /> should render the more options menu 1`] = `
</svg> </svg>
</div> </div>
</button> </button>
<div> <button
<button aria-disabled="false"
aria-disabled="false" aria-expanded="false"
aria-expanded="false" aria-haspopup="menu"
aria-haspopup="menu" aria-label="Notification options"
aria-label="Notification options" aria-labelledby="_r_9_"
aria-labelledby="_r_9_" class="_icon-button_1pz9o_8"
class="_icon-button_1pz9o_8" data-kind="primary"
data-kind="primary" data-state="closed"
data-state="closed" id="radix-_r_7_"
id="radix-_r_7_" role="button"
role="button" style="--cpd-icon-button-size: 24px;"
style="--cpd-icon-button-size: 24px;" tabindex="0"
tabindex="0" type="button"
type="button" >
<div
class="_indicator-icon_147l5_17"
style="--cpd-icon-button-size: 100%;"
> >
<div <svg
class="_indicator-icon_147l5_17" fill="currentColor"
style="--cpd-icon-button-size: 100%;" height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
> >
<svg <path
fill="currentColor" d="m4.917 2.083 17 17a1 1 0 0 1-1.414 1.414L19.006 19H4.414c-.89 0-1.337-1.077-.707-1.707L5 16v-6s0-2.034 1.096-3.91L3.504 3.498a1 1 0 0 1 1.414-1.414M19 13.35 9.136 3.484C9.93 3.181 10.874 3 12 3c7 0 7 7 7 7z"
height="1em" />
viewBox="0 0 24 24" <path
width="1em" d="M10 20h4a2 2 0 0 1-4 0"
xmlns="http://www.w3.org/2000/svg" />
> </svg>
<path </div>
d="m4.917 2.083 17 17a1 1 0 0 1-1.414 1.414L19.006 19H4.414c-.89 0-1.337-1.077-.707-1.707L5 16v-6s0-2.034 1.096-3.91L3.504 3.498a1 1 0 0 1 1.414-1.414M19 13.35 9.136 3.484C9.93 3.181 10.874 3 12 3c7 0 7 7 7 7z" </button>
/>
<path
d="M10 20h4a2 2 0 0 1-4 0"
/>
</svg>
</div>
</button>
</div>
</div> </div>
</DocumentFragment> </DocumentFragment>
`; `;
@@ -117,43 +115,41 @@ exports[`<RoomListItemMenuView /> should render the notification options menu 1`
</svg> </svg>
</div> </div>
</button> </button>
<div> <button
<button aria-disabled="false"
aria-disabled="false" aria-expanded="false"
aria-expanded="false" aria-haspopup="menu"
aria-haspopup="menu" aria-label="Notification options"
aria-label="Notification options" aria-labelledby="_r_p_"
aria-labelledby="_r_p_" class="_icon-button_1pz9o_8"
class="_icon-button_1pz9o_8" data-kind="primary"
data-kind="primary" data-state="closed"
data-state="closed" id="radix-_r_n_"
id="radix-_r_n_" role="button"
role="button" style="--cpd-icon-button-size: 24px;"
style="--cpd-icon-button-size: 24px;" tabindex="0"
tabindex="0" type="button"
type="button" >
<div
class="_indicator-icon_147l5_17"
style="--cpd-icon-button-size: 100%;"
> >
<div <svg
class="_indicator-icon_147l5_17" fill="currentColor"
style="--cpd-icon-button-size: 100%;" height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
> >
<svg <path
fill="currentColor" d="m4.917 2.083 17 17a1 1 0 0 1-1.414 1.414L19.006 19H4.414c-.89 0-1.337-1.077-.707-1.707L5 16v-6s0-2.034 1.096-3.91L3.504 3.498a1 1 0 0 1 1.414-1.414M19 13.35 9.136 3.484C9.93 3.181 10.874 3 12 3c7 0 7 7 7 7z"
height="1em" />
viewBox="0 0 24 24" <path
width="1em" d="M10 20h4a2 2 0 0 1-4 0"
xmlns="http://www.w3.org/2000/svg" />
> </svg>
<path </div>
d="m4.917 2.083 17 17a1 1 0 0 1-1.414 1.414L19.006 19H4.414c-.89 0-1.337-1.077-.707-1.707L5 16v-6s0-2.034 1.096-3.91L3.504 3.498a1 1 0 0 1 1.414-1.414M19 13.35 9.136 3.484C9.93 3.181 10.874 3 12 3c7 0 7 7 7 7z" </button>
/>
<path
d="M10 20h4a2 2 0 0 1-4 0"
/>
</svg>
</div>
</button>
</div>
</div> </div>
</DocumentFragment> </DocumentFragment>
`; `;
@@ -99,7 +99,7 @@ exports[`<RoomListItemView /> should display notification decoration 1`] = `
</div> </div>
<div <div
aria-hidden="true" aria-hidden="true"
class="_flex_4dswl_9" class="_flex_4dswl_9 mx_RoomListItemView_notificationDecoration"
data-testid="notification-decoration" data-testid="notification-decoration"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: center; --mx-flex-gap: var(--cpd-space-1x); --mx-flex-wrap: nowrap;" style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: center; --mx-flex-gap: var(--cpd-space-1x); --mx-flex-wrap: nowrap;"
> >