From 061c269f36c4b7f016ce1be3204926a65674fa39 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 9 Jan 2024 11:46:27 +0000 Subject: [PATCH] Use Compound Tooltips in StatelessNotificationBadge, VerifyEmailModal, CheckEmail (#12084) * Switch StatelessNotificationBadge to using Compound Tooltips Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Fix test Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Migrate CheckEmail & VerifyEmailModal to Compound tooltips Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Fix test Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Fix CSS stacking contexts for Dialogs & PersistedElement Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Switch to PersistedElement sharing a CSS stacking context for z-index to continue functioning Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Fix Widget PIP overlay being under the widget and dragging being broken Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Fix border-radius on widget pip Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Fix majority of tests Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Fix jest retryTimes applying outside of CI Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Fix remaining tests Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Fix React unique key warnings Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Fix sticker picker Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * id not class Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Fix widget pip button colour in light theme Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --------- Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- res/css/views/rooms/_NotificationBadge.pcss | 7 -- .../auth/forgot-password/CheckEmail.tsx | 26 ++---- .../auth/forgot-password/VerifyEmailModal.tsx | 26 ++---- .../views/rooms/NotificationBadge.tsx | 46 +++------- .../StatelessNotificationBadge.tsx | 85 ++++++++----------- .../structures/auth/ForgotPassword-test.tsx | 4 +- .../NotificationBadge-test.tsx | 19 +---- 7 files changed, 71 insertions(+), 142 deletions(-) diff --git a/res/css/views/rooms/_NotificationBadge.pcss b/res/css/views/rooms/_NotificationBadge.pcss index 85895b097e..d7f15eacde 100644 --- a/res/css/views/rooms/_NotificationBadge.pcss +++ b/res/css/views/rooms/_NotificationBadge.pcss @@ -75,10 +75,3 @@ limitations under the License. } } } - -.mx_NotificationBadge_tooltip { - display: inline-block; - position: relative; - top: -25px; - left: 6px; -} diff --git a/src/components/structures/auth/forgot-password/CheckEmail.tsx b/src/components/structures/auth/forgot-password/CheckEmail.tsx index 7ce5e6f20b..d9d9c4ca80 100644 --- a/src/components/structures/auth/forgot-password/CheckEmail.tsx +++ b/src/components/structures/auth/forgot-password/CheckEmail.tsx @@ -14,13 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { ReactNode, useRef } from "react"; +import React, { ReactNode } from "react"; +import { Tooltip } from "@vector-im/compound-web"; import AccessibleButton from "../../../views/elements/AccessibleButton"; import { Icon as EMailPromptIcon } from "../../../../../res/img/element-icons/email-prompt.svg"; import { Icon as RetryIcon } from "../../../../../res/img/compound/retry-16px.svg"; import { _t } from "../../../../languageHandler"; -import Tooltip, { Alignment } from "../../../views/elements/Tooltip"; import { useTimeoutToggle } from "../../../../hooks/useTimeoutToggle"; import { ErrorMessage } from "../../ErrorMessage"; @@ -42,7 +42,6 @@ export const CheckEmail: React.FC = ({ onSubmitForm, onResendClick, }) => { - const tooltipId = useRef(`mx_CheckEmail_${Math.random()}`).current; const { toggle: toggleTooltipVisible, value: tooltipVisible } = useTimeoutToggle(false, 2500); const onResendClickFn = async (): Promise => { @@ -67,21 +66,12 @@ export const CheckEmail: React.FC = ({
{_t("auth|check_email_resend_prompt")} - - - {_t("action|resend")} - - + + + + {_t("action|resend")} + +
); diff --git a/src/components/structures/auth/forgot-password/VerifyEmailModal.tsx b/src/components/structures/auth/forgot-password/VerifyEmailModal.tsx index a031b3da45..11ede00340 100644 --- a/src/components/structures/auth/forgot-password/VerifyEmailModal.tsx +++ b/src/components/structures/auth/forgot-password/VerifyEmailModal.tsx @@ -14,13 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { ReactNode, useRef } from "react"; +import React, { ReactNode } from "react"; +import { Tooltip } from "@vector-im/compound-web"; import { _t } from "../../../../languageHandler"; import AccessibleButton from "../../../views/elements/AccessibleButton"; import { Icon as RetryIcon } from "../../../../../res/img/compound/retry-16px.svg"; import { Icon as EmailPromptIcon } from "../../../../../res/img/element-icons/email-prompt.svg"; -import Tooltip, { Alignment } from "../../../views/elements/Tooltip"; import { useTimeoutToggle } from "../../../../hooks/useTimeoutToggle"; import { ErrorMessage } from "../../ErrorMessage"; @@ -40,7 +40,6 @@ export const VerifyEmailModal: React.FC = ({ onReEnterEmailClick, onResendClick, }) => { - const tooltipId = useRef(`mx_VerifyEmailModal_${Math.random()}`).current; const { toggle: toggleTooltipVisible, value: tooltipVisible } = useTimeoutToggle(false, 2500); const onResendClickFn = async (): Promise => { @@ -66,21 +65,12 @@ export const VerifyEmailModal: React.FC = ({
{_t("auth|check_email_resend_prompt")} - - - {_t("action|resend")} - - + + + + {_t("action|resend")} + + {errorText && }
diff --git a/src/components/views/rooms/NotificationBadge.tsx b/src/components/views/rooms/NotificationBadge.tsx index ee5b9ffb61..a8a34c17d4 100644 --- a/src/components/views/rooms/NotificationBadge.tsx +++ b/src/components/views/rooms/NotificationBadge.tsx @@ -14,12 +14,12 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { MouseEvent, ReactNode } from "react"; +import React, { ReactNode } from "react"; +import { Tooltip } from "@vector-im/compound-web"; import SettingsStore from "../../../settings/SettingsStore"; import { XOR } from "../../../@types/common"; import { NotificationState, NotificationStateEvents } from "../../../stores/notifications/NotificationState"; -import Tooltip from "../elements/Tooltip"; import { _t } from "../../../languageHandler"; import { NotificationColor } from "../../../stores/notifications/NotificationColor"; import { StatelessNotificationBadge } from "./NotificationBadge/StatelessNotificationBadge"; @@ -48,8 +48,7 @@ interface IClickableProps extends IProps, React.InputHTMLAttributes { } interface IState { - showCounts: boolean; // whether or not to show counts. Independent of props.forceCount - showTooltip: boolean; + showCounts: boolean; // whether to show counts. Independent of props.forceCount } export default class NotificationBadge extends React.PureComponent, IState> { @@ -61,7 +60,6 @@ export default class NotificationBadge extends React.PureComponent { - e.stopPropagation(); - this.setState({ - showTooltip: true, - }); - }; - - private onMouseLeave = (): void => { - this.setState({ - showTooltip: false, - }); - }; - public render(): ReactNode { /* eslint @typescript-eslint/no-unused-vars: ["error", { "ignoreRestSiblings": true }] */ const { notification, showUnsentTooltip, forceCount, onClick, tabIndex } = this.props; @@ -119,31 +104,28 @@ export default class NotificationBadge extends React.PureComponent; - } - const commonProps: React.ComponentProps = { - label, symbol: notification.symbol, count: notification.count, color: notification.color, knocked: notification.knocked, - onMouseOver: this.onMouseOver, - onMouseLeave: this.onMouseLeave, }; + let badge: JSX.Element; if (onClick) { + badge = ; + } else { + badge = ; + } + + if (showUnsentTooltip && notification.color === NotificationColor.Unsent) { return ( - - {tooltip} - + + {badge} + ); } - return {tooltip}; + return badge; } } diff --git a/src/components/views/rooms/NotificationBadge/StatelessNotificationBadge.tsx b/src/components/views/rooms/NotificationBadge/StatelessNotificationBadge.tsx index 770c549cba..76f1d8e838 100644 --- a/src/components/views/rooms/NotificationBadge/StatelessNotificationBadge.tsx +++ b/src/components/views/rooms/NotificationBadge/StatelessNotificationBadge.tsx @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { MouseEvent, ReactNode } from "react"; +import React, { forwardRef } from "react"; import classNames from "classnames"; import { formatCount } from "../../../../utils/FormattingUtils"; @@ -28,10 +28,6 @@ interface Props { count: number; color: NotificationColor; knocked?: boolean; - onMouseOver?: (ev: MouseEvent) => void; - onMouseLeave?: (ev: MouseEvent) => void; - children?: ReactNode; - label?: string; } interface ClickableProps extends Props { @@ -42,57 +38,46 @@ interface ClickableProps extends Props { tabIndex?: number; } -export function StatelessNotificationBadge({ - symbol, - count, - color, - knocked, - ...props -}: XOR): JSX.Element { - const hideBold = useSettingValue("feature_hidebold"); +export const StatelessNotificationBadge = forwardRef>( + ({ symbol, count, color, knocked, ...props }, ref) => { + const hideBold = useSettingValue("feature_hidebold"); - // Don't show a badge if we don't need to - if ((color === NotificationColor.None || (hideBold && color == NotificationColor.Bold)) && !knocked) { - return <>; - } + // Don't show a badge if we don't need to + if ((color === NotificationColor.None || (hideBold && color == NotificationColor.Bold)) && !knocked) { + return <>; + } - const hasUnreadCount = color >= NotificationColor.Grey && (!!count || !!symbol); + const hasUnreadCount = color >= NotificationColor.Grey && (!!count || !!symbol); - const isEmptyBadge = symbol === null && count === 0; + const isEmptyBadge = symbol === null && count === 0; - if (symbol === null && count > 0) { - symbol = formatCount(count); - } + if (symbol === null && count > 0) { + symbol = formatCount(count); + } - const classes = classNames({ - mx_NotificationBadge: true, - mx_NotificationBadge_visible: isEmptyBadge || knocked ? true : hasUnreadCount, - mx_NotificationBadge_highlighted: color >= NotificationColor.Red, - mx_NotificationBadge_dot: isEmptyBadge && !knocked, - mx_NotificationBadge_knocked: knocked, - mx_NotificationBadge_2char: symbol && symbol.length > 0 && symbol.length < 3, - mx_NotificationBadge_3char: symbol && symbol.length > 2, - }); + const classes = classNames({ + mx_NotificationBadge: true, + mx_NotificationBadge_visible: isEmptyBadge || knocked ? true : hasUnreadCount, + mx_NotificationBadge_highlighted: color >= NotificationColor.Red, + mx_NotificationBadge_dot: isEmptyBadge && !knocked, + mx_NotificationBadge_knocked: knocked, + mx_NotificationBadge_2char: symbol && symbol.length > 0 && symbol.length < 3, + mx_NotificationBadge_3char: symbol && symbol.length > 2, + }); + + if (props.onClick) { + return ( + + {symbol} + {props.children} + + ); + } - if (props.onClick) { return ( - +
{symbol} - {props.children} - +
); - } - - return ( -
- {symbol} -
- ); -} + }, +); diff --git a/test/components/structures/auth/ForgotPassword-test.tsx b/test/components/structures/auth/ForgotPassword-test.tsx index d6bc962082..6977fe9ec4 100644 --- a/test/components/structures/auth/ForgotPassword-test.tsx +++ b/test/components/structures/auth/ForgotPassword-test.tsx @@ -235,7 +235,9 @@ describe("", () => { expect.any(String), 2, // second send attempt ); - expect(screen.getByText("Verification link email resent!")).toBeInTheDocument(); + expect( + screen.getByRole("tooltip", { name: "Verification link email resent!" }), + ).toBeInTheDocument(); }); }); diff --git a/test/components/views/rooms/NotificationBadge/NotificationBadge-test.tsx b/test/components/views/rooms/NotificationBadge/NotificationBadge-test.tsx index 888bbcb081..e0aa0d89bd 100644 --- a/test/components/views/rooms/NotificationBadge/NotificationBadge-test.tsx +++ b/test/components/views/rooms/NotificationBadge/NotificationBadge-test.tsx @@ -26,25 +26,12 @@ describe("NotificationBadge", () => { it("lets you click it", () => { const cb = jest.fn(); - const { container } = render( - , + const { getByRole } = render( + , ); - fireEvent.click(container.firstChild!); + fireEvent.click(getByRole("button")!); expect(cb).toHaveBeenCalledTimes(1); - - fireEvent.mouseEnter(container.firstChild!); - expect(cb).toHaveBeenCalledTimes(2); - - fireEvent.mouseLeave(container.firstChild!); - expect(cb).toHaveBeenCalledTimes(3); }); it("hides the bold icon when the settings is set", () => {