New room list: add tooltip for presence and room status (#30472)

* feat: add tooltip to room avatar

* test: update snapshots
This commit is contained in:
Florian Duros
2025-08-04 13:32:32 +02:00
committed by GitHub
parent 30e7567064
commit 7faee3d1b7
2 changed files with 47 additions and 8 deletions

View File

@@ -14,6 +14,7 @@ import OnlineOrUnavailableIcon from "@vector-im/compound-design-tokens/assets/we
import OfflineIcon from "@vector-im/compound-design-tokens/assets/web/icons/presence-outline-8x8"; import OfflineIcon from "@vector-im/compound-design-tokens/assets/web/icons/presence-outline-8x8";
import BusyIcon from "@vector-im/compound-design-tokens/assets/web/icons/presence-strikethrough-8x8"; import BusyIcon from "@vector-im/compound-design-tokens/assets/web/icons/presence-strikethrough-8x8";
import classNames from "classnames"; import classNames from "classnames";
import { Tooltip } from "@vector-im/compound-web";
import RoomAvatar from "./RoomAvatar"; import RoomAvatar from "./RoomAvatar";
import { AvatarBadgeDecoration, useRoomAvatarViewModel } from "../../viewmodels/avatars/RoomAvatarViewModel"; import { AvatarBadgeDecoration, useRoomAvatarViewModel } from "../../viewmodels/avatars/RoomAvatarViewModel";
@@ -37,6 +38,7 @@ export function RoomAvatarView({ room }: RoomAvatarViewProps): JSX.Element {
if (!vm.badgeDecoration) return <RoomAvatar size="32px" room={room} />; if (!vm.badgeDecoration) return <RoomAvatar size="32px" room={room} />;
const icon = getAvatarDecoration(vm.badgeDecoration, vm.presence); const icon = getAvatarDecoration(vm.badgeDecoration, vm.presence);
const label = getDecorationLabel(vm.badgeDecoration, vm.presence);
// Presence indicator and video/public icons don't have the same size // Presence indicator and video/public icons don't have the same size
// We use different masks // We use different masks
@@ -48,7 +50,7 @@ export function RoomAvatarView({ room }: RoomAvatarViewProps): JSX.Element {
return ( return (
<div className="mx_RoomAvatarView"> <div className="mx_RoomAvatarView">
<RoomAvatar className={classNames("mx_RoomAvatarView_RoomAvatar", maskClass)} size="32px" room={room} /> <RoomAvatar className={classNames("mx_RoomAvatarView_RoomAvatar", maskClass)} size="32px" room={room} />
{icon} {label ? <Tooltip label={label}>{icon}</Tooltip> : icon}
</div> </div>
); );
} }
@@ -72,7 +74,7 @@ function PresenceDecoration({ presence }: PresenceDecorationProps): JSX.Element
height="8px" height="8px"
className="mx_RoomAvatarView_PresenceDecoration" className="mx_RoomAvatarView_PresenceDecoration"
color="var(--cpd-color-icon-accent-primary)" color="var(--cpd-color-icon-accent-primary)"
aria-label={_t("presence|online")} aria-label={getPresenceLabel(presence)}
/> />
); );
case Presence.Away: case Presence.Away:
@@ -82,7 +84,7 @@ function PresenceDecoration({ presence }: PresenceDecorationProps): JSX.Element
height="8px" height="8px"
className="mx_RoomAvatarView_PresenceDecoration" className="mx_RoomAvatarView_PresenceDecoration"
color="var(--cpd-color-icon-quaternary)" color="var(--cpd-color-icon-quaternary)"
aria-label={_t("presence|away")} aria-label={getPresenceLabel(presence)}
/> />
); );
case Presence.Offline: case Presence.Offline:
@@ -92,7 +94,7 @@ function PresenceDecoration({ presence }: PresenceDecorationProps): JSX.Element
height="8px" height="8px"
className="mx_RoomAvatarView_PresenceDecoration" className="mx_RoomAvatarView_PresenceDecoration"
color="var(--cpd-color-icon-tertiary)" color="var(--cpd-color-icon-tertiary)"
aria-label={_t("presence|offline")} aria-label={getPresenceLabel(presence)}
/> />
); );
case Presence.Busy: case Presence.Busy:
@@ -102,7 +104,7 @@ function PresenceDecoration({ presence }: PresenceDecorationProps): JSX.Element
height="8px" height="8px"
className="mx_RoomAvatarView_PresenceDecoration" className="mx_RoomAvatarView_PresenceDecoration"
color="var(--cpd-color-icon-tertiary)" color="var(--cpd-color-icon-tertiary)"
aria-label={_t("presence|busy")} aria-label={getPresenceLabel(presence)}
/> />
); );
} }
@@ -116,7 +118,7 @@ function getAvatarDecoration(decoration: AvatarBadgeDecoration, presence: Presen
height="16px" height="16px"
className="mx_RoomAvatarView_icon" className="mx_RoomAvatarView_icon"
color="var(--cpd-color-icon-tertiary)" color="var(--cpd-color-icon-tertiary)"
aria-label={_t("room|room_is_low_priority")} aria-label={getDecorationLabel(decoration, presence)}
/> />
); );
} else if (decoration === AvatarBadgeDecoration.VideoRoom) { } else if (decoration === AvatarBadgeDecoration.VideoRoom) {
@@ -126,7 +128,7 @@ function getAvatarDecoration(decoration: AvatarBadgeDecoration, presence: Presen
height="16px" height="16px"
className="mx_RoomAvatarView_icon" className="mx_RoomAvatarView_icon"
color="var(--cpd-color-icon-tertiary)" color="var(--cpd-color-icon-tertiary)"
aria-label={_t("room|video_room")} aria-label={getDecorationLabel(decoration, presence)}
/> />
); );
} else if (decoration === AvatarBadgeDecoration.PublicRoom) { } else if (decoration === AvatarBadgeDecoration.PublicRoom) {
@@ -136,10 +138,44 @@ function getAvatarDecoration(decoration: AvatarBadgeDecoration, presence: Presen
height="16px" height="16px"
className="mx_RoomAvatarView_icon" className="mx_RoomAvatarView_icon"
color="var(--cpd-color-icon-info-primary)" color="var(--cpd-color-icon-info-primary)"
aria-label={_t("room|header|room_is_public")} aria-label={getDecorationLabel(decoration, presence)}
/> />
); );
} else if (decoration === AvatarBadgeDecoration.Presence) { } else if (decoration === AvatarBadgeDecoration.Presence) {
return <PresenceDecoration presence={presence!} />; return <PresenceDecoration presence={presence!} />;
} }
} }
/**
* Get the label for the avatar decoration.
* This is used for the tooltip and a11y label.
*/
function getDecorationLabel(decoration: AvatarBadgeDecoration, presence: Presence | null): string | undefined {
switch (decoration) {
case AvatarBadgeDecoration.LowPriority:
return _t("room|room_is_low_priority");
case AvatarBadgeDecoration.VideoRoom:
return _t("room|video_room");
case AvatarBadgeDecoration.PublicRoom:
return _t("room|header|room_is_public");
case AvatarBadgeDecoration.Presence:
return getPresenceLabel(presence!);
}
}
/**
* Get the label for the presence.
* This is used for the tooltip and a11y label.
*/
function getPresenceLabel(presence: Presence): string {
switch (presence) {
case Presence.Online:
return _t("presence|online");
case Presence.Away:
return _t("presence|away");
case Presence.Offline:
return _t("presence|offline");
case Presence.Busy:
return _t("presence|busy");
}
}

View File

@@ -50,6 +50,7 @@ exports[`<RoomAvatarView /> should render a low priority room decoration 1`] = `
</span> </span>
<svg <svg
aria-label="This is a low priority room" aria-label="This is a low priority room"
aria-labelledby="«r0»"
class="mx_RoomAvatarView_icon" class="mx_RoomAvatarView_icon"
color="var(--cpd-color-icon-tertiary)" color="var(--cpd-color-icon-tertiary)"
fill="currentColor" fill="currentColor"
@@ -92,6 +93,7 @@ exports[`<RoomAvatarView /> should render a public room decoration 1`] = `
</span> </span>
<svg <svg
aria-label="This room is public" aria-label="This room is public"
aria-labelledby="«rc»"
class="mx_RoomAvatarView_icon" class="mx_RoomAvatarView_icon"
color="var(--cpd-color-icon-info-primary)" color="var(--cpd-color-icon-info-primary)"
fill="currentColor" fill="currentColor"
@@ -134,6 +136,7 @@ exports[`<RoomAvatarView /> should render a video room decoration 1`] = `
</span> </span>
<svg <svg
aria-label="This room is a video room" aria-label="This room is a video room"
aria-labelledby="«r6»"
class="mx_RoomAvatarView_icon" class="mx_RoomAvatarView_icon"
color="var(--cpd-color-icon-tertiary)" color="var(--cpd-color-icon-tertiary)"
fill="currentColor" fill="currentColor"