Skip to content

Commit c7f07f4

Browse files
authored
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>
1 parent bbb16f7 commit c7f07f4

4 files changed

Lines changed: 97 additions & 45 deletions

File tree

src/components/structures/ContextMenu.tsx

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import React, { type JSX, type CSSProperties, type RefObject, type SyntheticEven
1212
import ReactDOM from "react-dom";
1313
import classNames from "classnames";
1414
import FocusLock from "react-focus-lock";
15+
import { TooltipProvider } from "@vector-im/compound-web";
1516

1617
import { type Writeable } from "../../@types/common";
1718
import UIStore from "../../stores/UIStore";
@@ -425,15 +426,17 @@ export default class ContextMenu extends React.PureComponent<React.PropsWithChil
425426
onContextMenu={this.onContextMenuPreventBubbling}
426427
>
427428
{background}
428-
<div
429-
className={menuClasses}
430-
style={menuStyle}
431-
ref={this.collectContextMenuRect}
432-
role={managed ? "menu" : undefined}
433-
{...divProps}
434-
>
435-
{body}
436-
</div>
429+
<TooltipProvider>
430+
<div
431+
className={menuClasses}
432+
style={menuStyle}
433+
ref={this.collectContextMenuRect}
434+
role={managed ? "menu" : undefined}
435+
{...divProps}
436+
>
437+
{body}
438+
</div>
439+
</TooltipProvider>
437440
</div>
438441
)}
439442
</RovingTabIndexProvider>

src/components/views/rooms/ReadReceiptGroup.tsx

Lines changed: 35 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -212,38 +212,41 @@ export function ReadReceiptPerson({
212212
onAfterClick,
213213
}: ReadReceiptPersonProps): JSX.Element {
214214
return (
215-
<Tooltip description={roomMember?.rawDisplayName ?? userId} caption={userId} placement="top">
216-
<div>
217-
<MenuItem
218-
className="mx_ReadReceiptGroup_person"
219-
onClick={() => {
220-
dis.dispatch({
221-
action: Action.ViewUser,
222-
// XXX: We should be using a real member object and not assuming what the receiver wants.
223-
// The ViewUser action leads to the RightPanelStore, and RightPanelStoreIPanelState defines the
224-
// member property of IRightPanelCardState as `RoomMember | User`, so we’re fine for now, but we
225-
// should definitely clean this up later
226-
member: roomMember ?? ({ userId } as User),
227-
push: false,
228-
});
229-
onAfterClick?.();
230-
}}
231-
>
232-
<MemberAvatar
233-
member={roomMember}
234-
fallbackUserId={userId}
235-
size="24px"
236-
aria-hidden="true"
237-
aria-live="off"
238-
resizeMethod="crop"
239-
hideTitle
240-
/>
241-
<div className="mx_ReadReceiptGroup_name">
242-
<p>{roomMember?.name ?? userId}</p>
243-
<p className="mx_ReadReceiptGroup_secondary">{formatDate(new Date(ts), isTwelveHour)}</p>
244-
</div>
245-
</MenuItem>
246-
</div>
215+
<Tooltip
216+
description={roomMember?.rawDisplayName ?? userId}
217+
caption={userId}
218+
placement="top"
219+
isTriggerInteractive={false}
220+
>
221+
<MenuItem
222+
className="mx_ReadReceiptGroup_person"
223+
onClick={() => {
224+
dis.dispatch({
225+
action: Action.ViewUser,
226+
// XXX: We should be using a real member object and not assuming what the receiver wants.
227+
// The ViewUser action leads to the RightPanelStore, and RightPanelStoreIPanelState defines the
228+
// member property of IRightPanelCardState as `RoomMember | User`, so we’re fine for now, but we
229+
// should definitely clean this up later
230+
member: roomMember ?? ({ userId } as User),
231+
push: false,
232+
});
233+
onAfterClick?.();
234+
}}
235+
>
236+
<MemberAvatar
237+
member={roomMember}
238+
fallbackUserId={userId}
239+
size="24px"
240+
aria-hidden="true"
241+
aria-live="off"
242+
resizeMethod="crop"
243+
hideTitle
244+
/>
245+
<div className="mx_ReadReceiptGroup_name">
246+
<p>{roomMember?.name ?? userId}</p>
247+
<p className="mx_ReadReceiptGroup_secondary">{formatDate(new Date(ts), isTwelveHour)}</p>
248+
</div>
249+
</MenuItem>
247250
</Tooltip>
248251
);
249252
}

test/unit-tests/components/views/rooms/ReadReceiptGroup-test.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,5 +142,10 @@ describe("ReadReceiptGroup", () => {
142142
}),
143143
);
144144
});
145+
146+
it("should fall back to userId if roomMember unspecified", async () => {
147+
const { container } = renderReadReceipt({ roomMember: null });
148+
expect(container).toMatchSnapshot();
149+
});
145150
});
146151
});

test/unit-tests/components/views/rooms/__snapshots__/ReadReceiptGroup-test.tsx.snap

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ exports[`ReadReceiptGroup <ReadReceiptPerson /> should display a tooltip 1`] = `
66
data-floating-ui-focusable=""
77
id="_r_6_"
88
role="tooltip"
9-
style="position: absolute; left: 0px; top: 0px; transform: translate(0px, 0px);"
9+
style="position: absolute; left: 0px; top: 0px; transform: translate(5px, -6px);"
1010
tabindex="-1"
1111
>
1212
<svg
1313
aria-hidden="true"
1414
class="_arrow_6ode6_33"
1515
height="10"
16-
style="position: absolute; pointer-events: none; top: 100%;"
16+
style="position: absolute; pointer-events: none; top: 100%; left: -1px;"
1717
viewBox="0 0 10 10"
1818
width="10"
1919
>
@@ -46,9 +46,50 @@ exports[`ReadReceiptGroup <ReadReceiptPerson /> should display a tooltip 1`] = `
4646
</div>
4747
`;
4848

49+
exports[`ReadReceiptGroup <ReadReceiptPerson /> should fall back to userId if roomMember unspecified 1`] = `
50+
<div>
51+
<span
52+
tabindex="0"
53+
>
54+
<div
55+
class="mx_AccessibleButton mx_ReadReceiptGroup_person"
56+
role="menuitem"
57+
tabindex="-1"
58+
>
59+
<span
60+
aria-hidden="true"
61+
aria-live="off"
62+
class="_avatar_1qbcf_8 mx_BaseAvatar _avatar-imageless_1qbcf_52"
63+
data-color="3"
64+
data-testid="avatar-img"
65+
data-type="round"
66+
role="presentation"
67+
style="--cpd-avatar-size: 24px;"
68+
>
69+
a
70+
</span>
71+
<div
72+
class="mx_ReadReceiptGroup_name"
73+
>
74+
<p>
75+
@alice:example.org
76+
</p>
77+
<p
78+
class="mx_ReadReceiptGroup_secondary"
79+
>
80+
==MOCK FORMATTED DATE==
81+
</p>
82+
</div>
83+
</div>
84+
</span>
85+
</div>
86+
`;
87+
4988
exports[`ReadReceiptGroup <ReadReceiptPerson /> should render 1`] = `
5089
<div>
51-
<div>
90+
<span
91+
tabindex="0"
92+
>
5293
<div
5394
class="mx_AccessibleButton mx_ReadReceiptGroup_person"
5495
role="menuitem"
@@ -88,6 +129,6 @@ exports[`ReadReceiptGroup <ReadReceiptPerson /> should render 1`] = `
88129
</p>
89130
</div>
90131
</div>
91-
</div>
132+
</span>
92133
</div>
93134
`;

0 commit comments

Comments
 (0)