From 7ed308943489aa7392fc30dc877fcde5320582eb Mon Sep 17 00:00:00 2001 From: Michael Weimann Date: Mon, 9 May 2022 15:58:16 +0200 Subject: [PATCH] Improve tooltip positioning Signed-off-by: Michael Weimann --- .../views/elements/_MiniAvatarUploader.scss | 4 +- res/css/views/elements/_Tooltip.scss | 2 - res/css/views/rooms/_RoomSublist.scss | 4 - src/components/structures/SpaceHierarchy.tsx | 3 +- src/components/views/beta/BetaCard.tsx | 1 - .../views/dialogs/ForwardDialog.tsx | 2 - .../elements/AccessibleTooltipButton.tsx | 4 +- src/components/views/elements/FacePile.tsx | 2 +- src/components/views/elements/Tooltip.tsx | 48 +++++------- .../views/elements/TooltipTarget.tsx | 2 - .../views/right_panel/RoomSummaryCard.tsx | 4 - .../views/rooms/MessageComposer.tsx | 1 - .../views/rooms/ReadReceiptGroup.tsx | 2 +- .../views/voip/CallView/CallViewButtons.tsx | 6 -- .../views/elements/TooltipTarget-test.tsx | 19 +++-- .../__snapshots__/TooltipTarget-test.tsx.snap | 76 ++++++++++++++++++- 16 files changed, 112 insertions(+), 68 deletions(-) diff --git a/res/css/views/elements/_MiniAvatarUploader.scss b/res/css/views/elements/_MiniAvatarUploader.scss index 46ffd9a01c..577cf40727 100644 --- a/res/css/views/elements/_MiniAvatarUploader.scss +++ b/res/css/views/elements/_MiniAvatarUploader.scss @@ -25,7 +25,9 @@ limitations under the License. z-index: unset; width: max-content; left: 72px; - top: 0; + // top edge starting at 50 % of parent - 50 % of itself -> centered vertically + top: 50%; + transform: translateY(-50%); } .mx_MiniAvatarUploader_indicator { diff --git a/res/css/views/elements/_Tooltip.scss b/res/css/views/elements/_Tooltip.scss index c89c654178..40073f8f64 100644 --- a/res/css/views/elements/_Tooltip.scss +++ b/res/css/views/elements/_Tooltip.scss @@ -70,8 +70,6 @@ limitations under the License. font-weight: 500; max-width: 300px; word-break: break-word; - margin-left: 6px; - margin-right: 6px; background-color: #21262C; // Same on both themes color: $accent-fg-color; diff --git a/res/css/views/rooms/_RoomSublist.scss b/res/css/views/rooms/_RoomSublist.scss index c00ac227cf..5cd4d99673 100644 --- a/res/css/views/rooms/_RoomSublist.scss +++ b/res/css/views/rooms/_RoomSublist.scss @@ -406,10 +406,6 @@ limitations under the License. } } -.mx_RoomSublist_addRoomTooltip { - margin-top: -3px; -} - .mx_RoomSublist_skeletonUI { position: relative; margin-left: 4px; diff --git a/src/components/structures/SpaceHierarchy.tsx b/src/components/structures/SpaceHierarchy.tsx index 89790e4674..0d0dd7ba34 100644 --- a/src/components/structures/SpaceHierarchy.tsx +++ b/src/components/structures/SpaceHierarchy.tsx @@ -64,6 +64,7 @@ import { ViewRoomPayload } from "../../dispatcher/payloads/ViewRoomPayload"; import { JoinRoomReadyPayload } from "../../dispatcher/payloads/JoinRoomReadyPayload"; import { KeyBindingAction } from "../../accessibility/KeyboardShortcuts"; import { getKeyBindingsManager } from "../../KeyBindingsManager"; +import { Alignment } from "../views/elements/Tooltip"; interface IProps { space: Room; @@ -583,7 +584,7 @@ const ManageButtons = ({ hierarchy, selected, setSelected, setError }: IManageBu Button = AccessibleTooltipButton; props = { tooltip: _t("Select a room below first"), - yOffset: -40, + alignment: Alignment.Top, }; } diff --git a/src/components/views/beta/BetaCard.tsx b/src/components/views/beta/BetaCard.tsx index 4639886b70..c2a0e28295 100644 --- a/src/components/views/beta/BetaCard.tsx +++ b/src/components/views/beta/BetaCard.tsx @@ -60,7 +60,6 @@ export const BetaPill = ({ } onClick={onClick} - yOffset={-10} > { _t("Beta") } ; diff --git a/src/components/views/dialogs/ForwardDialog.tsx b/src/components/views/dialogs/ForwardDialog.tsx index bd81fa1857..e6de73e635 100644 --- a/src/components/views/dialogs/ForwardDialog.tsx +++ b/src/components/views/dialogs/ForwardDialog.tsx @@ -136,7 +136,6 @@ const Entry: React.FC = ({ room, type, content, matrixClient: cli, className="mx_ForwardList_roomButton" onClick={jumpToRoom} title={_t("Open room")} - yOffset={-20} alignment={Alignment.Top} > @@ -151,7 +150,6 @@ const Entry: React.FC = ({ room, type, content, matrixClient: cli, onClick={send} disabled={disabled} title={title} - yOffset={-20} alignment={Alignment.Top} >
{ _t("Send") }
diff --git a/src/components/views/elements/AccessibleTooltipButton.tsx b/src/components/views/elements/AccessibleTooltipButton.tsx index b52212b29d..0f52879cc8 100644 --- a/src/components/views/elements/AccessibleTooltipButton.tsx +++ b/src/components/views/elements/AccessibleTooltipButton.tsx @@ -26,7 +26,6 @@ interface IProps extends React.ComponentProps { label?: string; tooltipClassName?: string; forceHide?: boolean; - yOffset?: number; alignment?: Alignment; onHover?: (hovering: boolean) => void; onHideTooltip?(ev: SyntheticEvent): void; @@ -76,13 +75,12 @@ export default class AccessibleTooltipButton extends React.PureComponent; return ( diff --git a/src/components/views/elements/FacePile.tsx b/src/components/views/elements/FacePile.tsx index c9b9f634d2..92a175a1cb 100644 --- a/src/components/views/elements/FacePile.tsx +++ b/src/components/views/elements/FacePile.tsx @@ -51,7 +51,7 @@ const FacePile: FC = ({ members, faceSize, overflow, tooltip, children, return
{ tooltip ? ( - + { pileContents } ) : ( diff --git a/src/components/views/elements/Tooltip.tsx b/src/components/views/elements/Tooltip.tsx index 8d817551d0..3b7e6c9670 100644 --- a/src/components/views/elements/Tooltip.tsx +++ b/src/components/views/elements/Tooltip.tsx @@ -23,8 +23,6 @@ import classNames from 'classnames'; import UIStore from "../../../stores/UIStore"; -const MIN_TOOLTIP_HEIGHT = 25; - export enum Alignment { Natural, // Pick left or right Left, @@ -33,7 +31,6 @@ export enum Alignment { Bottom, // Centered InnerBottom, // Inside the target, at the bottom TopRight, // On top of the target, right aligned - TopCenter, // On top of the target, center aligned } export interface ITooltipProps { @@ -48,7 +45,6 @@ export interface ITooltipProps { // the react element to put into the tooltip label: React.ReactNode; alignment?: Alignment; // defaults to Natural - yOffset?: number; // id describing tooltip // used to associate tooltip with target for a11y id?: string; @@ -66,7 +62,6 @@ export default class Tooltip extends React.Component { public static readonly defaultProps = { visible: true, - yOffset: 0, alignment: Alignment.Natural, }; @@ -102,50 +97,49 @@ export default class Tooltip extends React.Component { // positioned, also taking into account any window zoom private updatePosition(style: CSSProperties) { const parentBox = this.parent.getBoundingClientRect(); - let offset = 0; - if (parentBox.height > MIN_TOOLTIP_HEIGHT) { - offset = Math.floor((parentBox.height - MIN_TOOLTIP_HEIGHT) / 2); - } else { - // The tooltip is larger than the parent height: figure out what offset - // we need so that we're still centered. - offset = Math.floor(parentBox.height - MIN_TOOLTIP_HEIGHT); - } const width = UIStore.instance.windowWidth; + const spacing = 6; const parentWidth = ( this.props.maxParentWidth ? Math.min(parentBox.width, this.props.maxParentWidth) : parentBox.width ); - const baseTop = (parentBox.top - 2 + this.props.yOffset) + window.scrollY; - const top = baseTop + offset; + const baseTop = parentBox.top + window.scrollY; + const centerTop = parentBox.top + window.scrollY + (parentBox.height / 2); const right = width - parentBox.left - window.scrollX; const left = parentBox.right + window.scrollX; const horizontalCenter = ( parentBox.left - window.scrollX + (parentWidth / 2) ); + switch (this.props.alignment) { case Alignment.Natural: if (parentBox.right > width / 2) { - style.right = right; - style.top = top; + style.right = right + spacing; + style.top = centerTop; + style.transform = "translateY(-50%)"; break; } // fall through to Right case Alignment.Right: - style.left = left; - style.top = top; + style.left = left + spacing; + style.top = centerTop; + style.transform = "translateY(-50%)"; break; case Alignment.Left: - style.right = right; - style.top = top; + style.right = right + spacing; + style.top = centerTop; + style.transform = "translateY(-50%)"; break; case Alignment.Top: - style.top = baseTop - 16; + style.top = baseTop - spacing; style.left = horizontalCenter; + style.transform = "translate(-50%, -100%)"; break; case Alignment.Bottom: - style.top = baseTop + parentBox.height; + style.top = baseTop + parentBox.height + spacing; style.left = horizontalCenter; + style.transform = "translate(-50%)"; break; case Alignment.InnerBottom: style.top = baseTop + parentBox.height - 50; @@ -153,14 +147,10 @@ export default class Tooltip extends React.Component { style.transform = "translate(-50%)"; break; case Alignment.TopRight: - style.top = baseTop - 5; + style.top = baseTop - spacing; style.right = width - parentBox.right - window.scrollX; - style.transform = "translate(5px, -100%)"; + style.transform = "translateY(-100%)"; break; - case Alignment.TopCenter: - style.top = baseTop - 5; - style.left = horizontalCenter; - style.transform = "translate(-50%, -100%)"; } return style; diff --git a/src/components/views/elements/TooltipTarget.tsx b/src/components/views/elements/TooltipTarget.tsx index 5d30bf2539..1f6339a072 100644 --- a/src/components/views/elements/TooltipTarget.tsx +++ b/src/components/views/elements/TooltipTarget.tsx @@ -34,7 +34,6 @@ const TooltipTarget: React.FC = ({ id, label, alignment, - yOffset, tooltipClassName, maxParentWidth, ...rest @@ -51,7 +50,6 @@ const TooltipTarget: React.FC = ({ className={className} tooltipClassName={tooltipClassName} label={label} - yOffset={yOffset} alignment={alignment} visible={isVisible} maxParentWidth={maxParentWidth} diff --git a/src/components/views/right_panel/RoomSummaryCard.tsx b/src/components/views/right_panel/RoomSummaryCard.tsx index fd69f46ef9..fe335fab3c 100644 --- a/src/components/views/right_panel/RoomSummaryCard.tsx +++ b/src/components/views/right_panel/RoomSummaryCard.tsx @@ -166,7 +166,6 @@ const AppRow: React.FC = ({ app, room }) => { title={openTitle} forceHide={!(isPinned || isMaximised)} disabled={isPinned || isMaximised} - yOffset={-48} > { name } @@ -178,7 +177,6 @@ const AppRow: React.FC = ({ app, room }) => { isExpanded={menuDisplayed} onClick={openMenu} title={_t("Options")} - yOffset={-24} /> } = ({ app, room }) => { onClick={togglePin} title={pinTitle} disabled={cannotPin} - yOffset={-24} /> { contextMenu } diff --git a/src/components/views/rooms/MessageComposer.tsx b/src/components/views/rooms/MessageComposer.tsx index aed4536aa0..0791258637 100644 --- a/src/components/views/rooms/MessageComposer.tsx +++ b/src/components/views/rooms/MessageComposer.tsx @@ -421,7 +421,6 @@ export default class MessageComposer extends React.Component { recordingTooltip = ; } diff --git a/src/components/views/rooms/ReadReceiptGroup.tsx b/src/components/views/rooms/ReadReceiptGroup.tsx index e1c0de1428..ac2aa43c17 100644 --- a/src/components/views/rooms/ReadReceiptGroup.tsx +++ b/src/components/views/rooms/ReadReceiptGroup.tsx @@ -233,7 +233,7 @@ interface ReadReceiptPersonProps extends IReadReceiptProps { function ReadReceiptPerson({ userId, roomMember, ts, isTwelveHour, onAfterClick }: ReadReceiptPersonProps) { const [{ showTooltip, hideTooltip }, tooltip] = useTooltip({ - alignment: Alignment.TopCenter, + alignment: Alignment.Top, tooltipClassName: "mx_ReadReceiptGroup_person--tooltip", label: ( <> diff --git a/src/components/views/voip/CallView/CallViewButtons.tsx b/src/components/views/voip/CallView/CallViewButtons.tsx index 02eb285158..d8f07c826b 100644 --- a/src/components/views/voip/CallView/CallViewButtons.tsx +++ b/src/components/views/voip/CallView/CallViewButtons.tsx @@ -39,8 +39,6 @@ import { MediaDeviceKindEnum } from "../../../../MediaDeviceHandler"; // height to get the max height of the video const CONTEXT_MENU_VPADDING = 8; // How far the context menu sits above the button (px) -const TOOLTIP_Y_OFFSET = -24; - const CONTROLS_HIDE_DELAY = 2000; interface IButtonProps extends Omit, "title"> { @@ -69,7 +67,6 @@ const CallViewToggleButton: React.FC = ({ className={classes} title={isOn ? onLabel : offLabel} alignment={Alignment.Top} - yOffset={TOOLTIP_Y_OFFSET} {...props} > { children } @@ -267,7 +264,6 @@ export default class CallViewButtons extends React.Component { isExpanded={this.state.showDialpad} title={_t("Dialpad")} alignment={Alignment.Top} - yOffset={TOOLTIP_Y_OFFSET} /> } { isExpanded={this.state.showMoreMenu} title={_t("More")} alignment={Alignment.Top} - yOffset={TOOLTIP_Y_OFFSET} /> }
); diff --git a/test/components/views/elements/TooltipTarget-test.tsx b/test/components/views/elements/TooltipTarget-test.tsx index 3fe891a49d..a57cdf7187 100644 --- a/test/components/views/elements/TooltipTarget-test.tsx +++ b/test/components/views/elements/TooltipTarget-test.tsx @@ -30,7 +30,6 @@ describe('', () => { "className": 'test className', "tooltipClassName": 'test tooltipClassName', "label": 'test label', - "yOffset": 1, "alignment": Alignment.Left, "id": 'test id', 'data-test-id': 'test', @@ -64,13 +63,17 @@ describe('', () => { expect(getVisibleTooltip()).toBeFalsy(); }); - it('displays tooltip on mouseover', () => { - const wrapper = getComponent(); - act(() => { - Simulate.mouseOver(wrapper); - }); - expect(getVisibleTooltip()).toMatchSnapshot(); - }); + for (const alignment in Alignment) { + if (isNaN(Number(alignment))) { + it(`displays ${alignment} aligned tooltip on mouseover`, () => { + const wrapper = getComponent({ alignment: Alignment[alignment] }); + act(() => { + Simulate.mouseOver(wrapper); + }); + expect(getVisibleTooltip()).toMatchSnapshot(); + }); + } + } it('hides tooltip on mouseleave', () => { const wrapper = getComponent(); diff --git a/test/components/views/elements/__snapshots__/TooltipTarget-test.tsx.snap b/test/components/views/elements/__snapshots__/TooltipTarget-test.tsx.snap index d0d01f5380..bdaab7071b 100644 --- a/test/components/views/elements/__snapshots__/TooltipTarget-test.tsx.snap +++ b/test/components/views/elements/__snapshots__/TooltipTarget-test.tsx.snap @@ -1,9 +1,81 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[` displays tooltip on mouseover 1`] = ` +exports[` displays Bottom aligned tooltip on mouseover 1`] = `
+
+ test label +
+`; + +exports[` displays InnerBottom aligned tooltip on mouseover 1`] = ` +
+
+ test label +
+`; + +exports[` displays Left aligned tooltip on mouseover 1`] = ` +
+
+ test label +
+`; + +exports[` displays Natural aligned tooltip on mouseover 1`] = ` +
+
+ test label +
+`; + +exports[` displays Right aligned tooltip on mouseover 1`] = ` +
+
+ test label +
+`; + +exports[` displays Top aligned tooltip on mouseover 1`] = ` +
+
+ test label +
+`; + +exports[` displays TopRight aligned tooltip on mouseover 1`] = ` +