mirror of
https://github.com/vector-im/element-web.git
synced 2025-11-09 04:31:15 +01:00
Fix tooltips within context menu portals being unreliable (#31129)
* Fix tooltips within context menu portals being unreliable Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Update snapshots Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Add test Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --------- Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
parent
bbb16f7ea9
commit
c7f07f4c29
@ -12,6 +12,7 @@ import React, { type JSX, type CSSProperties, type RefObject, type SyntheticEven
|
|||||||
import ReactDOM from "react-dom";
|
import ReactDOM from "react-dom";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import FocusLock from "react-focus-lock";
|
import FocusLock from "react-focus-lock";
|
||||||
|
import { TooltipProvider } from "@vector-im/compound-web";
|
||||||
|
|
||||||
import { type Writeable } from "../../@types/common";
|
import { type Writeable } from "../../@types/common";
|
||||||
import UIStore from "../../stores/UIStore";
|
import UIStore from "../../stores/UIStore";
|
||||||
@ -425,15 +426,17 @@ export default class ContextMenu extends React.PureComponent<React.PropsWithChil
|
|||||||
onContextMenu={this.onContextMenuPreventBubbling}
|
onContextMenu={this.onContextMenuPreventBubbling}
|
||||||
>
|
>
|
||||||
{background}
|
{background}
|
||||||
<div
|
<TooltipProvider>
|
||||||
className={menuClasses}
|
<div
|
||||||
style={menuStyle}
|
className={menuClasses}
|
||||||
ref={this.collectContextMenuRect}
|
style={menuStyle}
|
||||||
role={managed ? "menu" : undefined}
|
ref={this.collectContextMenuRect}
|
||||||
{...divProps}
|
role={managed ? "menu" : undefined}
|
||||||
>
|
{...divProps}
|
||||||
{body}
|
>
|
||||||
</div>
|
{body}
|
||||||
|
</div>
|
||||||
|
</TooltipProvider>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</RovingTabIndexProvider>
|
</RovingTabIndexProvider>
|
||||||
|
|||||||
@ -212,38 +212,41 @@ export function ReadReceiptPerson({
|
|||||||
onAfterClick,
|
onAfterClick,
|
||||||
}: ReadReceiptPersonProps): JSX.Element {
|
}: ReadReceiptPersonProps): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<Tooltip description={roomMember?.rawDisplayName ?? userId} caption={userId} placement="top">
|
<Tooltip
|
||||||
<div>
|
description={roomMember?.rawDisplayName ?? userId}
|
||||||
<MenuItem
|
caption={userId}
|
||||||
className="mx_ReadReceiptGroup_person"
|
placement="top"
|
||||||
onClick={() => {
|
isTriggerInteractive={false}
|
||||||
dis.dispatch({
|
>
|
||||||
action: Action.ViewUser,
|
<MenuItem
|
||||||
// XXX: We should be using a real member object and not assuming what the receiver wants.
|
className="mx_ReadReceiptGroup_person"
|
||||||
// The ViewUser action leads to the RightPanelStore, and RightPanelStoreIPanelState defines the
|
onClick={() => {
|
||||||
// member property of IRightPanelCardState as `RoomMember | User`, so we’re fine for now, but we
|
dis.dispatch({
|
||||||
// should definitely clean this up later
|
action: Action.ViewUser,
|
||||||
member: roomMember ?? ({ userId } as User),
|
// XXX: We should be using a real member object and not assuming what the receiver wants.
|
||||||
push: false,
|
// The ViewUser action leads to the RightPanelStore, and RightPanelStoreIPanelState defines the
|
||||||
});
|
// member property of IRightPanelCardState as `RoomMember | User`, so we’re fine for now, but we
|
||||||
onAfterClick?.();
|
// should definitely clean this up later
|
||||||
}}
|
member: roomMember ?? ({ userId } as User),
|
||||||
>
|
push: false,
|
||||||
<MemberAvatar
|
});
|
||||||
member={roomMember}
|
onAfterClick?.();
|
||||||
fallbackUserId={userId}
|
}}
|
||||||
size="24px"
|
>
|
||||||
aria-hidden="true"
|
<MemberAvatar
|
||||||
aria-live="off"
|
member={roomMember}
|
||||||
resizeMethod="crop"
|
fallbackUserId={userId}
|
||||||
hideTitle
|
size="24px"
|
||||||
/>
|
aria-hidden="true"
|
||||||
<div className="mx_ReadReceiptGroup_name">
|
aria-live="off"
|
||||||
<p>{roomMember?.name ?? userId}</p>
|
resizeMethod="crop"
|
||||||
<p className="mx_ReadReceiptGroup_secondary">{formatDate(new Date(ts), isTwelveHour)}</p>
|
hideTitle
|
||||||
</div>
|
/>
|
||||||
</MenuItem>
|
<div className="mx_ReadReceiptGroup_name">
|
||||||
</div>
|
<p>{roomMember?.name ?? userId}</p>
|
||||||
|
<p className="mx_ReadReceiptGroup_secondary">{formatDate(new Date(ts), isTwelveHour)}</p>
|
||||||
|
</div>
|
||||||
|
</MenuItem>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -142,5 +142,10 @@ describe("ReadReceiptGroup", () => {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should fall back to userId if roomMember unspecified", async () => {
|
||||||
|
const { container } = renderReadReceipt({ roomMember: null });
|
||||||
|
expect(container).toMatchSnapshot();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -6,14 +6,14 @@ exports[`ReadReceiptGroup <ReadReceiptPerson /> should display a tooltip 1`] = `
|
|||||||
data-floating-ui-focusable=""
|
data-floating-ui-focusable=""
|
||||||
id="_r_6_"
|
id="_r_6_"
|
||||||
role="tooltip"
|
role="tooltip"
|
||||||
style="position: absolute; left: 0px; top: 0px; transform: translate(0px, 0px);"
|
style="position: absolute; left: 0px; top: 0px; transform: translate(5px, -6px);"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
class="_arrow_6ode6_33"
|
class="_arrow_6ode6_33"
|
||||||
height="10"
|
height="10"
|
||||||
style="position: absolute; pointer-events: none; top: 100%;"
|
style="position: absolute; pointer-events: none; top: 100%; left: -1px;"
|
||||||
viewBox="0 0 10 10"
|
viewBox="0 0 10 10"
|
||||||
width="10"
|
width="10"
|
||||||
>
|
>
|
||||||
@ -46,9 +46,50 @@ exports[`ReadReceiptGroup <ReadReceiptPerson /> should display a tooltip 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`ReadReceiptGroup <ReadReceiptPerson /> should fall back to userId if roomMember unspecified 1`] = `
|
||||||
|
<div>
|
||||||
|
<span
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_AccessibleButton mx_ReadReceiptGroup_person"
|
||||||
|
role="menuitem"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
aria-hidden="true"
|
||||||
|
aria-live="off"
|
||||||
|
class="_avatar_1qbcf_8 mx_BaseAvatar _avatar-imageless_1qbcf_52"
|
||||||
|
data-color="3"
|
||||||
|
data-testid="avatar-img"
|
||||||
|
data-type="round"
|
||||||
|
role="presentation"
|
||||||
|
style="--cpd-avatar-size: 24px;"
|
||||||
|
>
|
||||||
|
a
|
||||||
|
</span>
|
||||||
|
<div
|
||||||
|
class="mx_ReadReceiptGroup_name"
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
@alice:example.org
|
||||||
|
</p>
|
||||||
|
<p
|
||||||
|
class="mx_ReadReceiptGroup_secondary"
|
||||||
|
>
|
||||||
|
==MOCK FORMATTED DATE==
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`ReadReceiptGroup <ReadReceiptPerson /> should render 1`] = `
|
exports[`ReadReceiptGroup <ReadReceiptPerson /> should render 1`] = `
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<span
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
class="mx_AccessibleButton mx_ReadReceiptGroup_person"
|
class="mx_AccessibleButton mx_ReadReceiptGroup_person"
|
||||||
role="menuitem"
|
role="menuitem"
|
||||||
@ -88,6 +129,6 @@ exports[`ReadReceiptGroup <ReadReceiptPerson /> should render 1`] = `
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user