diff --git a/src/components/views/auth/InteractiveAuthEntryComponents.tsx b/src/components/views/auth/InteractiveAuthEntryComponents.tsx index 2bc0fe80db9..7bed60d6037 100644 --- a/src/components/views/auth/InteractiveAuthEntryComponents.tsx +++ b/src/components/views/auth/InteractiveAuthEntryComponents.tsx @@ -26,10 +26,8 @@ import SettingsStore from "../../../settings/SettingsStore"; import { LocalisedPolicy, Policies } from "../../../Terms"; import { AuthHeaderModifier } from "../../structures/auth/header/AuthHeaderModifier"; import AccessibleButton, { AccessibleButtonKind, ButtonEvent } from "../elements/AccessibleButton"; -import AccessibleTooltipButton from "../elements/AccessibleTooltipButton"; import Field from "../elements/Field"; import Spinner from "../elements/Spinner"; -import { Alignment } from "../elements/Tooltip"; import CaptchaForm from "./CaptchaForm"; /* This file contains a collection of components which are used by the @@ -501,15 +499,16 @@ export class EmailIdentityAuthEntry extends React.Component< {}, { a: (text: string) => ( - this.setState({ requested: false }) + ? (open) => { + if (!open) this.setState({ requested: false }); + } : undefined } onClick={async (): Promise => { @@ -524,7 +523,7 @@ export class EmailIdentityAuthEntry extends React.Component< }} > {text} - + ), }, )} diff --git a/src/components/views/beta/BetaCard.tsx b/src/components/views/beta/BetaCard.tsx index 84c7a27fe0f..7d17c3af941 100644 --- a/src/components/views/beta/BetaCard.tsx +++ b/src/components/views/beta/BetaCard.tsx @@ -27,7 +27,6 @@ import SdkConfig from "../../../SdkConfig"; import SettingsFlag from "../elements/SettingsFlag"; import { useFeatureEnabled } from "../../../hooks/useSettings"; import InlineSpinner from "../elements/InlineSpinner"; -import AccessibleTooltipButton from "../elements/AccessibleTooltipButton"; import { shouldShowFeedback } from "../../../utils/Feedback"; // XXX: Keep this around for re-use in future Betas @@ -50,19 +49,15 @@ export const BetaPill: React.FC = ({ }) => { if (onClick) { return ( - -
{tooltipTitle}
-
{tooltipCaption}
- - } + aria-label={`${tooltipTitle} ${tooltipCaption}`} + title={tooltipTitle} + caption={tooltipCaption} onClick={onClick} > {_t("common|beta")} -
+ ); } diff --git a/src/components/views/elements/AccessibleTooltipButton.tsx b/src/components/views/elements/AccessibleTooltipButton.tsx deleted file mode 100644 index 759643da1cc..00000000000 --- a/src/components/views/elements/AccessibleTooltipButton.tsx +++ /dev/null @@ -1,118 +0,0 @@ -/* -Copyright 2019 Michael Telatynski <7t3chguy@gmail.com> -Copyright 2019 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, { SyntheticEvent, FocusEvent, forwardRef, useEffect, Ref, useState, ComponentProps } from "react"; - -import AccessibleButton from "./AccessibleButton"; -import Tooltip, { Alignment } from "./Tooltip"; - -/** - * Type of props accepted by {@link AccessibleTooltipButton}. - * - * Extends that of {@link AccessibleButton}. - */ -type Props = ComponentProps> & { - /** - * Title to show in the tooltip and use as aria-label - */ - title?: string; - /** - * Tooltip node to show in the tooltip, takes precedence over `title` - */ - tooltip?: React.ReactNode; - /** - * Trigger label to render - */ - label?: string; - /** - * Classname to apply to the tooltip - */ - tooltipClassName?: string; - /** - * Force the tooltip to be hidden - */ - forceHide?: boolean; - /** - * Alignment to render the tooltip with - */ - alignment?: Alignment; - /** - * Function to call when the children are hovered over - */ - onHover?: (hovering: boolean) => void; - /** - * Function to call when the tooltip goes from shown to hidden. - */ - onHideTooltip?(ev: SyntheticEvent): void; -}; - -/** - * @deprecated use AccessibleButton with `title` and `caption` instead. - */ -const AccessibleTooltipButton = forwardRef(function ( - { title, tooltip, children, forceHide, alignment, onHideTooltip, tooltipClassName, element, ...props }: Props, - ref: Ref, -) { - const [hover, setHover] = useState(false); - - useEffect(() => { - // If forceHide is set then force hover to off to hide the tooltip - if (forceHide && hover) { - setHover(false); - } - }, [forceHide, hover]); - - const showTooltip = (): void => { - props.onHover?.(true); - if (forceHide) return; - setHover(true); - }; - - const hideTooltip = (ev: SyntheticEvent): void => { - props.onHover?.(false); - setHover(false); - onHideTooltip?.(ev); - }; - - const onFocus = (ev: FocusEvent): void => { - // We only show the tooltip if focus arrived here from some other - // element, to avoid leaving tooltips hanging around when a modal closes - if (ev.relatedTarget) showTooltip(); - }; - - const tip = hover && (title || tooltip) && ( - - ); - return ( - - {children} - {props.label} - {(tooltip || title) && tip} - - ); -}); - -export default AccessibleTooltipButton; diff --git a/src/components/views/settings/devices/DeviceExpandDetailsButton.tsx b/src/components/views/settings/devices/DeviceExpandDetailsButton.tsx index 317afdfca15..791b6125917 100644 --- a/src/components/views/settings/devices/DeviceExpandDetailsButton.tsx +++ b/src/components/views/settings/devices/DeviceExpandDetailsButton.tsx @@ -19,10 +19,10 @@ import React, { ComponentProps } from "react"; import { Icon as CaretIcon } from "../../../../../res/img/feather-customised/dropdown-arrow.svg"; import { _t } from "../../../../languageHandler"; -import AccessibleTooltipButton from "../../elements/AccessibleTooltipButton"; +import AccessibleButton from "../../elements/AccessibleButton"; type Props = Omit< - ComponentProps>, + ComponentProps>, "aria-label" | "title" | "kind" | "className" | "onClick" | "element" > & { isExpanded: boolean; @@ -36,7 +36,7 @@ export const DeviceExpandDetailsButton = }: Props): JSX.Element => { const label = isExpanded ? _t("settings|sessions|hide_details") : _t("settings|sessions|show_details"); return ( - onClick={onClick} > - + ); }; diff --git a/test/components/views/auth/InteractiveAuthEntryComponents-test.tsx b/test/components/views/auth/InteractiveAuthEntryComponents-test.tsx new file mode 100644 index 00000000000..e6e3e1383e3 --- /dev/null +++ b/test/components/views/auth/InteractiveAuthEntryComponents-test.tsx @@ -0,0 +1,65 @@ +/* + * Copyright 2024 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 { render, screen, waitFor, act } from "@testing-library/react"; +import { AuthType } from "matrix-js-sdk/src/interactive-auth"; +import userEvent from "@testing-library/user-event"; + +import { EmailIdentityAuthEntry } from "../../../../src/components/views/auth/InteractiveAuthEntryComponents"; +import { createTestClient } from "../../../test-utils"; + +describe("", () => { + const renderIdentityAuth = () => { + const matrixClient = createTestClient(); + + return render( + , + ); + }; + + test("should render", () => { + const { container } = renderIdentityAuth(); + expect(container).toMatchSnapshot(); + }); + + test("should clear the requested state when the button tooltip is hidden", async () => { + renderIdentityAuth(); + + // After a click on the resend button, the button should display the resent label + screen.getByRole("button", { name: "Resend" }).click(); + await waitFor(() => expect(screen.queryByRole("button", { name: "Resent!" })).toBeInTheDocument()); + expect(screen.queryByRole("button", { name: "Resend" })).toBeNull(); + + const resentButton = screen.getByRole("button", { name: "Resent!" }); + // Hover briefly the button and wait for the tooltip to be displayed + await userEvent.hover(resentButton); + await waitFor(() => expect(screen.getByRole("tooltip", { name: "Resent!" })).toBeInTheDocument()); + + // On unhover, it should display again the resend button + await act(() => userEvent.unhover(resentButton)); + await waitFor(() => expect(screen.queryByRole("button", { name: "Resend" })).toBeInTheDocument()); + }); +}); diff --git a/test/components/views/auth/__snapshots__/InteractiveAuthEntryComponents-test.tsx.snap b/test/components/views/auth/__snapshots__/InteractiveAuthEntryComponents-test.tsx.snap new file mode 100644 index 00000000000..65f86a35d2a --- /dev/null +++ b/test/components/views/auth/__snapshots__/InteractiveAuthEntryComponents-test.tsx.snap @@ -0,0 +1,34 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` should render 1`] = ` +
+
+

+ + To create your account, open the link in the email we just sent to + + alice@example.xyz + + . + +

+

+ + Did not receive it? +

+ +

+
+
+`;