Use Compound tooltips instead of homegrown in TextWithTooltip & InfoTooltip (#12052)
* Migrate InfoTooltip to use Compound Tooltip Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Migrate DecoratedRoomAvatar.tsx to Compound tooltips Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Small type tweaks Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Remove unused props Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Migrate TextWithTooltip.tsx to Compound tooltips This adds `contain: strict` to #matrixchat which may have side effects. Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Update snapshots Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Add comment Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Update snapshot Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Revert accidental layout change to TextWithTooltip from inline to block Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Improve test coverage and simplify Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Update snapshots Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Ditch the sleep call Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Improve coverage Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --------- Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
parent
5f92dad273
commit
2212fbadd0
15 changed files with 209 additions and 75 deletions
|
@ -52,6 +52,11 @@ limitations under the License.
|
||||||
--dialog-zIndex-standard: calc(var(--dialog-zIndex-standard-background) + 1); /* 4012 */
|
--dialog-zIndex-standard: calc(var(--dialog-zIndex-standard-background) + 1); /* 4012 */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#matrixchat {
|
||||||
|
/* This is required to ensure Compound tooltips correctly draw where they should with z-index: auto */
|
||||||
|
contain: strict;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We need to increase the specificity of the selector to override the
|
* We need to increase the specificity of the selector to override the
|
||||||
* custom property set by the design tokens package
|
* custom property set by the design tokens package
|
||||||
|
|
|
@ -16,8 +16,9 @@ limitations under the License.
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { Room, RoomEvent, MatrixEvent, User, UserEvent, EventType, JoinRule } from "matrix-js-sdk/src/matrix";
|
import { EventType, JoinRule, MatrixEvent, Room, RoomEvent, User, UserEvent } from "matrix-js-sdk/src/matrix";
|
||||||
import { UnstableValue } from "matrix-js-sdk/src/NamespacedValue";
|
import { UnstableValue } from "matrix-js-sdk/src/NamespacedValue";
|
||||||
|
import { Tooltip } from "@vector-im/compound-web";
|
||||||
|
|
||||||
import RoomAvatar from "./RoomAvatar";
|
import RoomAvatar from "./RoomAvatar";
|
||||||
import NotificationBadge from "../rooms/NotificationBadge";
|
import NotificationBadge from "../rooms/NotificationBadge";
|
||||||
|
@ -26,10 +27,8 @@ import { NotificationState } from "../../../stores/notifications/NotificationSta
|
||||||
import { isPresenceEnabled } from "../../../utils/presence";
|
import { isPresenceEnabled } from "../../../utils/presence";
|
||||||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||||
import { _t } from "../../../languageHandler";
|
import { _t } from "../../../languageHandler";
|
||||||
import TextWithTooltip from "../elements/TextWithTooltip";
|
|
||||||
import DMRoomMap from "../../../utils/DMRoomMap";
|
import DMRoomMap from "../../../utils/DMRoomMap";
|
||||||
import { IOOBData } from "../../../stores/ThreepidInviteStore";
|
import { IOOBData } from "../../../stores/ThreepidInviteStore";
|
||||||
import TooltipTarget from "../elements/TooltipTarget";
|
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
room: Room;
|
room: Room;
|
||||||
|
@ -38,7 +37,9 @@ interface IProps {
|
||||||
forceCount?: boolean;
|
forceCount?: boolean;
|
||||||
oobData?: IOOBData;
|
oobData?: IOOBData;
|
||||||
viewAvatarOnClick?: boolean;
|
viewAvatarOnClick?: boolean;
|
||||||
tooltipProps?: Omit<React.ComponentProps<typeof TooltipTarget>, "label" | "tooltipClassName" | "className">;
|
tooltipProps?: {
|
||||||
|
tabIndex?: number;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
|
@ -94,9 +95,7 @@ export default class DecoratedRoomAvatar extends React.PureComponent<IProps, ISt
|
||||||
}
|
}
|
||||||
|
|
||||||
private get isPublicRoom(): boolean {
|
private get isPublicRoom(): boolean {
|
||||||
const joinRules = this.props.room.currentState.getStateEvents(EventType.RoomJoinRules, "");
|
return this.props.room.getJoinRule() === JoinRule.Public;
|
||||||
const joinRule = joinRules && joinRules.getContent().join_rule;
|
|
||||||
return joinRule === JoinRule.Public;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private get dmUser(): User | null {
|
private get dmUser(): User | null {
|
||||||
|
@ -191,10 +190,9 @@ export default class DecoratedRoomAvatar extends React.PureComponent<IProps, ISt
|
||||||
let icon: JSX.Element | undefined;
|
let icon: JSX.Element | undefined;
|
||||||
if (this.state.icon !== Icon.None) {
|
if (this.state.icon !== Icon.None) {
|
||||||
icon = (
|
icon = (
|
||||||
<TextWithTooltip
|
<div
|
||||||
tooltip={tooltipText(this.state.icon)}
|
tabIndex={this.props.tooltipProps?.tabIndex ?? 0}
|
||||||
tooltipProps={this.props.tooltipProps}
|
className={`mx_DecoratedRoomAvatar_icon mx_DecoratedRoomAvatar_icon_${this.state.icon.toLowerCase()}`}
|
||||||
class={`mx_DecoratedRoomAvatar_icon mx_DecoratedRoomAvatar_icon_${this.state.icon.toLowerCase()}`}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -211,7 +209,7 @@ export default class DecoratedRoomAvatar extends React.PureComponent<IProps, ISt
|
||||||
oobData={this.props.oobData}
|
oobData={this.props.oobData}
|
||||||
viewAvatarOnClick={this.props.viewAvatarOnClick}
|
viewAvatarOnClick={this.props.viewAvatarOnClick}
|
||||||
/>
|
/>
|
||||||
{icon}
|
{icon && <Tooltip label={tooltipText(this.state.icon)!}>{icon}</Tooltip>}
|
||||||
{badge}
|
{badge}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -182,7 +182,7 @@ export default class ServerPickerDialog extends React.PureComponent<IProps, ISta
|
||||||
let defaultServerName: React.ReactNode = this.defaultServer.hsName;
|
let defaultServerName: React.ReactNode = this.defaultServer.hsName;
|
||||||
if (this.defaultServer.hsNameIsDifferent) {
|
if (this.defaultServer.hsNameIsDifferent) {
|
||||||
defaultServerName = (
|
defaultServerName = (
|
||||||
<TextWithTooltip class="mx_Login_underlinedServerName" tooltip={this.defaultServer.hsUrl}>
|
<TextWithTooltip className="mx_Login_underlinedServerName" tooltip={this.defaultServer.hsUrl}>
|
||||||
{this.defaultServer.hsName}
|
{this.defaultServer.hsName}
|
||||||
</TextWithTooltip>
|
</TextWithTooltip>
|
||||||
);
|
);
|
||||||
|
|
|
@ -27,9 +27,9 @@ import MemberAvatar from "../avatars/MemberAvatar";
|
||||||
import BaseAvatar from "../avatars/BaseAvatar";
|
import BaseAvatar from "../avatars/BaseAvatar";
|
||||||
import Heading from "../typography/Heading";
|
import Heading from "../typography/Heading";
|
||||||
import AccessibleButton from "./AccessibleButton";
|
import AccessibleButton from "./AccessibleButton";
|
||||||
import TextWithTooltip from "./TextWithTooltip";
|
|
||||||
import { parseUrl } from "../../../utils/UrlUtils";
|
import { parseUrl } from "../../../utils/UrlUtils";
|
||||||
import { Icon as HelpIcon } from "../../../../res/img/feather-customised/help-circle.svg";
|
import { Icon as HelpIcon } from "../../../../res/img/feather-customised/help-circle.svg";
|
||||||
|
import TooltipTarget from "./TooltipTarget";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
url: string;
|
url: string;
|
||||||
|
@ -116,13 +116,14 @@ export default class AppPermission extends React.Component<IProps, IState> {
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
const warningTooltip = (
|
const warningTooltip = (
|
||||||
<TextWithTooltip
|
<TooltipTarget
|
||||||
tooltip={warningTooltipText}
|
label={warningTooltipText}
|
||||||
tooltipClass="mx_Tooltip--appPermission mx_Tooltip--appPermission--dark"
|
tooltipClassName="mx_Tooltip--appPermission mx_Tooltip--appPermission--dark"
|
||||||
class="mx_TextWithTooltip_target--helpIcon"
|
tooltipTargetClassName="mx_TextWithTooltip_target mx_TextWithTooltip_target--helpIcon"
|
||||||
|
className="mx_TextWithTooltip_tooltip"
|
||||||
>
|
>
|
||||||
<HelpIcon className="mx_Icon mx_Icon_12" />
|
<HelpIcon className="mx_Icon mx_Icon_12" />
|
||||||
</TextWithTooltip>
|
</TooltipTarget>
|
||||||
);
|
);
|
||||||
|
|
||||||
// Due to i18n limitations, we can't dedupe the code for variables in these two messages.
|
// Due to i18n limitations, we can't dedupe the code for variables in these two messages.
|
||||||
|
|
|
@ -17,47 +17,38 @@ limitations under the License.
|
||||||
|
|
||||||
import React, { ReactNode } from "react";
|
import React, { ReactNode } from "react";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
|
import { Tooltip } from "@vector-im/compound-web";
|
||||||
|
|
||||||
import { Alignment } from "./Tooltip";
|
|
||||||
import { _t } from "../../../languageHandler";
|
import { _t } from "../../../languageHandler";
|
||||||
import TooltipTarget from "./TooltipTarget";
|
|
||||||
|
|
||||||
export enum InfoTooltipKind {
|
export enum InfoTooltipKind {
|
||||||
Info = "info",
|
Info = "info",
|
||||||
Warning = "warning",
|
Warning = "warning",
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ITooltipProps {
|
interface TooltipProps {
|
||||||
tooltip?: React.ReactNode;
|
tooltip?: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
tooltipClassName?: string;
|
|
||||||
kind?: InfoTooltipKind;
|
kind?: InfoTooltipKind;
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
|
tabIndex?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class InfoTooltip extends React.PureComponent<ITooltipProps> {
|
export default class InfoTooltip extends React.PureComponent<TooltipProps> {
|
||||||
public constructor(props: ITooltipProps) {
|
|
||||||
super(props);
|
|
||||||
}
|
|
||||||
|
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
const { tooltip, children, tooltipClassName, className, kind } = this.props;
|
const { tooltip, children, className, kind } = this.props;
|
||||||
const title = _t("info_tooltip_title");
|
const title = _t("info_tooltip_title");
|
||||||
const iconClassName =
|
const iconClassName =
|
||||||
kind !== InfoTooltipKind.Warning ? "mx_InfoTooltip_icon_info" : "mx_InfoTooltip_icon_warning";
|
kind !== InfoTooltipKind.Warning ? "mx_InfoTooltip_icon_info" : "mx_InfoTooltip_icon_warning";
|
||||||
|
|
||||||
// Tooltip are forced on the right for a more natural feel to them on info icons
|
// Tooltip are forced on the right for a more natural feel to them on info icons
|
||||||
return (
|
return (
|
||||||
<TooltipTarget
|
<Tooltip label={tooltip || title} side="right">
|
||||||
tooltipTargetClassName={classNames("mx_InfoTooltip", className)}
|
<div className={classNames("mx_InfoTooltip", className)} tabIndex={this.props.tabIndex ?? 0}>
|
||||||
className="mx_InfoTooltip_container"
|
<span className={classNames("mx_InfoTooltip_icon", iconClassName)} aria-label={title} />
|
||||||
tooltipClassName={classNames("mx_InfoTooltip_tooltip", tooltipClassName)}
|
{children}
|
||||||
label={tooltip || title}
|
</div>
|
||||||
alignment={Alignment.Right}
|
</Tooltip>
|
||||||
>
|
|
||||||
<span className={classNames("mx_InfoTooltip_icon", iconClassName)} aria-label={title} />
|
|
||||||
{children}
|
|
||||||
</TooltipTarget>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,7 +77,7 @@ const ServerPicker: React.FC<IProps> = ({ title, dialogTitle, serverConfig, onSe
|
||||||
let serverName: React.ReactNode = serverConfig.isNameResolvable ? serverConfig.hsName : serverConfig.hsUrl;
|
let serverName: React.ReactNode = serverConfig.isNameResolvable ? serverConfig.hsName : serverConfig.hsUrl;
|
||||||
if (serverConfig.hsNameIsDifferent) {
|
if (serverConfig.hsNameIsDifferent) {
|
||||||
serverName = (
|
serverName = (
|
||||||
<TextWithTooltip class="mx_Login_underlinedServerName" tooltip={serverConfig.hsUrl}>
|
<TextWithTooltip className="mx_Login_underlinedServerName" tooltip={serverConfig.hsUrl}>
|
||||||
{serverConfig.hsName}
|
{serverConfig.hsName}
|
||||||
</TextWithTooltip>
|
</TextWithTooltip>
|
||||||
);
|
);
|
||||||
|
|
|
@ -15,16 +15,13 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { HTMLAttributes } from "react";
|
import React, { HTMLAttributes } from "react";
|
||||||
import classNames from "classnames";
|
import { Tooltip } from "@vector-im/compound-web";
|
||||||
|
|
||||||
import TooltipTarget from "./TooltipTarget";
|
|
||||||
|
|
||||||
interface IProps extends HTMLAttributes<HTMLSpanElement> {
|
interface IProps extends HTMLAttributes<HTMLSpanElement> {
|
||||||
class?: string;
|
tooltip: string;
|
||||||
tooltipClass?: string;
|
tooltipProps?: {
|
||||||
tooltip: React.ReactNode;
|
tabIndex?: number;
|
||||||
tooltipProps?: Omit<React.ComponentProps<typeof TooltipTarget>, "label" | "tooltipClassName" | "className">;
|
};
|
||||||
onClick?: (ev: React.MouseEvent) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class TextWithTooltip extends React.Component<IProps> {
|
export default class TextWithTooltip extends React.Component<IProps> {
|
||||||
|
@ -33,20 +30,14 @@ export default class TextWithTooltip extends React.Component<IProps> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
const { class: className, children, tooltip, tooltipClass, tooltipProps, ...props } = this.props;
|
const { className, children, tooltip, tooltipProps } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TooltipTarget
|
<Tooltip label={tooltip} side="right">
|
||||||
onClick={this.props.onClick}
|
<span className={className} tabIndex={tooltipProps?.tabIndex ?? 0}>
|
||||||
tooltipTargetClassName={classNames("mx_TextWithTooltip_target", className)}
|
{children}
|
||||||
{...tooltipProps}
|
</span>
|
||||||
label={tooltip}
|
</Tooltip>
|
||||||
tooltipClassName={tooltipClass}
|
|
||||||
className="mx_TextWithTooltip_tooltip"
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</TooltipTarget>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -170,7 +170,6 @@ const DmAuxButton: React.FC<IAuxButtonProps> = ({ tabIndex, dispatcher = default
|
||||||
tabIndex={tabIndex}
|
tabIndex={tabIndex}
|
||||||
onClick={openMenu}
|
onClick={openMenu}
|
||||||
className="mx_RoomSublist_auxButton"
|
className="mx_RoomSublist_auxButton"
|
||||||
tooltipClassName="mx_RoomSublist_addRoomTooltip"
|
|
||||||
aria-label={_t("action|add_people")}
|
aria-label={_t("action|add_people")}
|
||||||
title={_t("action|add_people")}
|
title={_t("action|add_people")}
|
||||||
isExpanded={menuDisplayed}
|
isExpanded={menuDisplayed}
|
||||||
|
@ -189,7 +188,6 @@ const DmAuxButton: React.FC<IAuxButtonProps> = ({ tabIndex, dispatcher = default
|
||||||
PosthogTrackers.trackInteraction("WebRoomListRoomsSublistPlusMenuCreateChatItem", e);
|
PosthogTrackers.trackInteraction("WebRoomListRoomsSublistPlusMenuCreateChatItem", e);
|
||||||
}}
|
}}
|
||||||
className="mx_RoomSublist_auxButton"
|
className="mx_RoomSublist_auxButton"
|
||||||
tooltipClassName="mx_RoomSublist_addRoomTooltip"
|
|
||||||
aria-label={_t("action|start_chat")}
|
aria-label={_t("action|start_chat")}
|
||||||
title={_t("action|start_chat")}
|
title={_t("action|start_chat")}
|
||||||
/>
|
/>
|
||||||
|
@ -355,7 +353,6 @@ const UntaggedAuxButton: React.FC<IAuxButtonProps> = ({ tabIndex }) => {
|
||||||
tabIndex={tabIndex}
|
tabIndex={tabIndex}
|
||||||
onClick={openMenu}
|
onClick={openMenu}
|
||||||
className="mx_RoomSublist_auxButton"
|
className="mx_RoomSublist_auxButton"
|
||||||
tooltipClassName="mx_RoomSublist_addRoomTooltip"
|
|
||||||
aria-label={_t("room_list|add_room_label")}
|
aria-label={_t("room_list|add_room_label")}
|
||||||
title={_t("room_list|add_room_label")}
|
title={_t("room_list|add_room_label")}
|
||||||
isExpanded={menuDisplayed}
|
isExpanded={menuDisplayed}
|
||||||
|
|
|
@ -58,7 +58,7 @@ function JoinCallButtonWithCall({ onClick, call }: JoinCallButtonWithCallProps):
|
||||||
className="mx_IncomingCallToast_joinButton"
|
className="mx_IncomingCallToast_joinButton"
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
disabled={disabledTooltip !== null}
|
disabled={disabledTooltip !== null}
|
||||||
tooltip={disabledTooltip}
|
tooltip={disabledTooltip ?? undefined}
|
||||||
kind="primary"
|
kind="primary"
|
||||||
>
|
>
|
||||||
{_t("action|join")}
|
{_t("action|join")}
|
||||||
|
|
|
@ -320,9 +320,9 @@ exports[`SpaceHierarchy <SpaceHierarchy /> renders 1`] = `
|
||||||
>
|
>
|
||||||
Join
|
Join
|
||||||
</div>
|
</div>
|
||||||
<div
|
<span
|
||||||
aria-describedby="mx_TooltipTarget_EetmBG4y"
|
class=""
|
||||||
class="mx_TextWithTooltip_target"
|
data-state="closed"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
|
@ -330,12 +330,12 @@ exports[`SpaceHierarchy <SpaceHierarchy /> renders 1`] = `
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
disabled=""
|
disabled=""
|
||||||
id="checkbox_VCeEefiPqp"
|
id="checkbox_EetmBG4yVC"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
for="checkbox_VCeEefiPqp"
|
for="checkbox_EetmBG4yVC"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="mx_Checkbox_background"
|
class="mx_Checkbox_background"
|
||||||
|
@ -346,7 +346,7 @@ exports[`SpaceHierarchy <SpaceHierarchy /> renders 1`] = `
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
66
test/components/views/avatars/DecoratedRoomAvatar-test.tsx
Normal file
66
test/components/views/avatars/DecoratedRoomAvatar-test.tsx
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { render, waitFor } from "@testing-library/react";
|
||||||
|
import { mocked } from "jest-mock";
|
||||||
|
import { JoinRule, MatrixClient, PendingEventOrdering, Room } from "matrix-js-sdk/src/matrix";
|
||||||
|
import React from "react";
|
||||||
|
import userEvent from "@testing-library/user-event";
|
||||||
|
|
||||||
|
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
|
||||||
|
import { stubClient } from "../../../test-utils";
|
||||||
|
import DecoratedRoomAvatar from "../../../../src/components/views/avatars/DecoratedRoomAvatar";
|
||||||
|
import DMRoomMap from "../../../../src/utils/DMRoomMap";
|
||||||
|
|
||||||
|
describe("DecoratedRoomAvatar", () => {
|
||||||
|
const ROOM_ID = "roomId";
|
||||||
|
|
||||||
|
let mockClient: MatrixClient;
|
||||||
|
let room: Room;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
stubClient();
|
||||||
|
mockClient = mocked(MatrixClientPeg.safeGet());
|
||||||
|
|
||||||
|
room = new Room(ROOM_ID, mockClient, mockClient.getUserId() ?? "", {
|
||||||
|
pendingEventOrdering: PendingEventOrdering.Detached,
|
||||||
|
});
|
||||||
|
|
||||||
|
const dmRoomMap = {
|
||||||
|
getUserIdForRoomId: jest.fn(),
|
||||||
|
} as unknown as DMRoomMap;
|
||||||
|
jest.spyOn(DMRoomMap, "shared").mockReturnValue(dmRoomMap);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows an avatar with globe icon and tooltip for public room", async () => {
|
||||||
|
room.getJoinRule = jest.fn().mockReturnValue(JoinRule.Public);
|
||||||
|
const { container, asFragment } = render(<DecoratedRoomAvatar room={room} size="32px" />);
|
||||||
|
|
||||||
|
const globe = container.querySelector(".mx_DecoratedRoomAvatar_icon_globe")!;
|
||||||
|
expect(globe).toBeVisible();
|
||||||
|
await userEvent.hover(globe!);
|
||||||
|
|
||||||
|
// wait for the tooltip to open
|
||||||
|
const tooltip = await waitFor(() => {
|
||||||
|
const tooltip = document.getElementById(globe.getAttribute("aria-describedby")!);
|
||||||
|
expect(tooltip).toBeVisible();
|
||||||
|
return tooltip;
|
||||||
|
});
|
||||||
|
expect(tooltip).toHaveTextContent("This room is public");
|
||||||
|
|
||||||
|
expect(asFragment()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,26 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`DecoratedRoomAvatar shows an avatar with globe icon and tooltip for public room 1`] = `
|
||||||
|
<DocumentFragment>
|
||||||
|
<div
|
||||||
|
class="mx_DecoratedRoomAvatar mx_DecoratedRoomAvatar_cutout"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="_avatar_1o69u_17 mx_BaseAvatar _avatar-imageless_1o69u_60"
|
||||||
|
data-color="3"
|
||||||
|
data-testid="avatar-img"
|
||||||
|
data-type="round"
|
||||||
|
role="presentation"
|
||||||
|
style="--cpd-avatar-size: 32px;"
|
||||||
|
>
|
||||||
|
r
|
||||||
|
</span>
|
||||||
|
<div
|
||||||
|
aria-describedby="radix-0"
|
||||||
|
class="mx_DecoratedRoomAvatar_icon mx_DecoratedRoomAvatar_icon_globe"
|
||||||
|
data-state="delayed-open"
|
||||||
|
tabindex="0"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</DocumentFragment>
|
||||||
|
`;
|
|
@ -55,13 +55,13 @@ exports[`<ServerPickerDialog /> should render dialog 1`] = `
|
||||||
<div
|
<div
|
||||||
class="mx_StyledRadioButton_content"
|
class="mx_StyledRadioButton_content"
|
||||||
>
|
>
|
||||||
<div
|
<span
|
||||||
aria-describedby="mx_TooltipTarget_vY7Q4uEh"
|
class="mx_Login_underlinedServerName"
|
||||||
class="mx_TextWithTooltip_target mx_Login_underlinedServerName"
|
data-state="closed"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
matrix.org
|
matrix.org
|
||||||
</div>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="mx_StyledRadioButton_spacer"
|
class="mx_StyledRadioButton_spacer"
|
||||||
|
|
41
test/components/views/elements/InfoTooltip-test.tsx
Normal file
41
test/components/views/elements/InfoTooltip-test.tsx
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import userEvent from "@testing-library/user-event";
|
||||||
|
import { render, waitFor } from "@testing-library/react";
|
||||||
|
|
||||||
|
import InfoTooltip from "../../../../src/components/views/elements/InfoTooltip";
|
||||||
|
|
||||||
|
describe("InfoTooltip", () => {
|
||||||
|
it("should show tooltip on hover", async () => {
|
||||||
|
const { getByText, asFragment } = render(<InfoTooltip tooltip="Tooltip text">Trigger text</InfoTooltip>);
|
||||||
|
|
||||||
|
const trigger = getByText("Trigger text");
|
||||||
|
expect(trigger).toBeVisible();
|
||||||
|
await userEvent.hover(trigger!);
|
||||||
|
|
||||||
|
// wait for the tooltip to open
|
||||||
|
const tooltip = await waitFor(() => {
|
||||||
|
const tooltip = document.getElementById(trigger.getAttribute("aria-describedby")!);
|
||||||
|
expect(tooltip).toBeVisible();
|
||||||
|
return tooltip;
|
||||||
|
});
|
||||||
|
expect(tooltip).toHaveTextContent("Tooltip text");
|
||||||
|
|
||||||
|
expect(asFragment()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,18 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`InfoTooltip should show tooltip on hover 1`] = `
|
||||||
|
<DocumentFragment>
|
||||||
|
<div
|
||||||
|
aria-describedby="radix-0"
|
||||||
|
class="mx_InfoTooltip"
|
||||||
|
data-state="delayed-open"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
aria-label="Information"
|
||||||
|
class="mx_InfoTooltip_icon mx_InfoTooltip_icon_info"
|
||||||
|
/>
|
||||||
|
Trigger text
|
||||||
|
</div>
|
||||||
|
</DocumentFragment>
|
||||||
|
`;
|
Loading…
Add table
Add a link
Reference in a new issue