You've already forked matrix-react-sdk
mirror of
https://github.com/matrix-org/matrix-react-sdk.git
synced 2025-08-07 21:23:00 +03:00
Tooltip: migrate remaining tooltips from AccessibleTooltipButton
to AccessibleButton
(#12522)
* Use `AccessibleButton` in `RovingAccessibleTooltipButton` * Update snapshots * Update @vector-im/compound-web * Update composer * Update formating buttons * Update snapshots * Update `ContextMenuTooltipButton.tsx` * Fix placement * Update tests * Remove placement * Update space panel snapshot * Remove default placement * Update snapshots * Update snapshots * Use kbd * Update ``@vector-im/compound-web` * Migrate remaining files * Remove `AccessibleTooltipButton.tsx` * Add test to `InteractiveAuthEntryComponents` * Add test to `InteractiveAuthEntryComponents` * Back to old RoomList-test.tsx * Improve `InteractiveAuthEntryComponent` tests * Review changes
This commit is contained in:
@@ -26,10 +26,8 @@ import SettingsStore from "../../../settings/SettingsStore";
|
|||||||
import { LocalisedPolicy, Policies } from "../../../Terms";
|
import { LocalisedPolicy, Policies } from "../../../Terms";
|
||||||
import { AuthHeaderModifier } from "../../structures/auth/header/AuthHeaderModifier";
|
import { AuthHeaderModifier } from "../../structures/auth/header/AuthHeaderModifier";
|
||||||
import AccessibleButton, { AccessibleButtonKind, ButtonEvent } from "../elements/AccessibleButton";
|
import AccessibleButton, { AccessibleButtonKind, ButtonEvent } from "../elements/AccessibleButton";
|
||||||
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
|
||||||
import Field from "../elements/Field";
|
import Field from "../elements/Field";
|
||||||
import Spinner from "../elements/Spinner";
|
import Spinner from "../elements/Spinner";
|
||||||
import { Alignment } from "../elements/Tooltip";
|
|
||||||
import CaptchaForm from "./CaptchaForm";
|
import CaptchaForm from "./CaptchaForm";
|
||||||
|
|
||||||
/* This file contains a collection of components which are used by the
|
/* 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) => (
|
a: (text: string) => (
|
||||||
<AccessibleTooltipButton
|
<AccessibleButton
|
||||||
kind="link_inline"
|
kind="link_inline"
|
||||||
title={
|
title={
|
||||||
this.state.requested ? _t("auth|uia|email_resent") : _t("action|resend")
|
this.state.requested ? _t("auth|uia|email_resent") : _t("action|resend")
|
||||||
}
|
}
|
||||||
alignment={Alignment.Right}
|
onTooltipOpenChange={
|
||||||
onHideTooltip={
|
|
||||||
this.state.requested
|
this.state.requested
|
||||||
? () => this.setState({ requested: false })
|
? (open) => {
|
||||||
|
if (!open) this.setState({ requested: false });
|
||||||
|
}
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
onClick={async (): Promise<void> => {
|
onClick={async (): Promise<void> => {
|
||||||
@@ -524,7 +523,7 @@ export class EmailIdentityAuthEntry extends React.Component<
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{text}
|
{text}
|
||||||
</AccessibleTooltipButton>
|
</AccessibleButton>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
)}
|
)}
|
||||||
|
@@ -27,7 +27,6 @@ import SdkConfig from "../../../SdkConfig";
|
|||||||
import SettingsFlag from "../elements/SettingsFlag";
|
import SettingsFlag from "../elements/SettingsFlag";
|
||||||
import { useFeatureEnabled } from "../../../hooks/useSettings";
|
import { useFeatureEnabled } from "../../../hooks/useSettings";
|
||||||
import InlineSpinner from "../elements/InlineSpinner";
|
import InlineSpinner from "../elements/InlineSpinner";
|
||||||
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
|
||||||
import { shouldShowFeedback } from "../../../utils/Feedback";
|
import { shouldShowFeedback } from "../../../utils/Feedback";
|
||||||
|
|
||||||
// XXX: Keep this around for re-use in future Betas
|
// XXX: Keep this around for re-use in future Betas
|
||||||
@@ -50,19 +49,15 @@ export const BetaPill: React.FC<IBetaPillProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
if (onClick) {
|
if (onClick) {
|
||||||
return (
|
return (
|
||||||
<AccessibleTooltipButton
|
<AccessibleButton
|
||||||
className="mx_BetaCard_betaPill"
|
className="mx_BetaCard_betaPill"
|
||||||
title={`${tooltipTitle} ${tooltipCaption}`}
|
aria-label={`${tooltipTitle} ${tooltipCaption}`}
|
||||||
tooltip={
|
title={tooltipTitle}
|
||||||
<div>
|
caption={tooltipCaption}
|
||||||
<div className="mx_Tooltip_title">{tooltipTitle}</div>
|
|
||||||
<div className="mx_Tooltip_sub">{tooltipCaption}</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
>
|
>
|
||||||
{_t("common|beta")}
|
{_t("common|beta")}
|
||||||
</AccessibleTooltipButton>
|
</AccessibleButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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<T extends keyof JSX.IntrinsicElements> = ComponentProps<typeof AccessibleButton<T>> & {
|
|
||||||
/**
|
|
||||||
* 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 <T extends keyof JSX.IntrinsicElements>(
|
|
||||||
{ title, tooltip, children, forceHide, alignment, onHideTooltip, tooltipClassName, element, ...props }: Props<T>,
|
|
||||||
ref: Ref<HTMLElement>,
|
|
||||||
) {
|
|
||||||
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) && (
|
|
||||||
<Tooltip tooltipClassName={tooltipClassName} label={tooltip || title} alignment={alignment} />
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<AccessibleButton
|
|
||||||
{...props}
|
|
||||||
element={element as keyof JSX.IntrinsicElements}
|
|
||||||
onMouseOver={showTooltip}
|
|
||||||
onMouseLeave={hideTooltip}
|
|
||||||
onFocus={onFocus}
|
|
||||||
onBlur={hideTooltip}
|
|
||||||
aria-label={title || props["aria-label"]}
|
|
||||||
ref={ref}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
{props.label}
|
|
||||||
{(tooltip || title) && tip}
|
|
||||||
</AccessibleButton>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
export default AccessibleTooltipButton;
|
|
@@ -19,10 +19,10 @@ import React, { ComponentProps } from "react";
|
|||||||
|
|
||||||
import { Icon as CaretIcon } from "../../../../../res/img/feather-customised/dropdown-arrow.svg";
|
import { Icon as CaretIcon } from "../../../../../res/img/feather-customised/dropdown-arrow.svg";
|
||||||
import { _t } from "../../../../languageHandler";
|
import { _t } from "../../../../languageHandler";
|
||||||
import AccessibleTooltipButton from "../../elements/AccessibleTooltipButton";
|
import AccessibleButton from "../../elements/AccessibleButton";
|
||||||
|
|
||||||
type Props<T extends keyof JSX.IntrinsicElements> = Omit<
|
type Props<T extends keyof JSX.IntrinsicElements> = Omit<
|
||||||
ComponentProps<typeof AccessibleTooltipButton<T>>,
|
ComponentProps<typeof AccessibleButton<T>>,
|
||||||
"aria-label" | "title" | "kind" | "className" | "onClick" | "element"
|
"aria-label" | "title" | "kind" | "className" | "onClick" | "element"
|
||||||
> & {
|
> & {
|
||||||
isExpanded: boolean;
|
isExpanded: boolean;
|
||||||
@@ -36,7 +36,7 @@ export const DeviceExpandDetailsButton = <T extends keyof JSX.IntrinsicElements>
|
|||||||
}: Props<T>): JSX.Element => {
|
}: Props<T>): JSX.Element => {
|
||||||
const label = isExpanded ? _t("settings|sessions|hide_details") : _t("settings|sessions|show_details");
|
const label = isExpanded ? _t("settings|sessions|hide_details") : _t("settings|sessions|show_details");
|
||||||
return (
|
return (
|
||||||
<AccessibleTooltipButton
|
<AccessibleButton
|
||||||
{...rest}
|
{...rest}
|
||||||
aria-label={label}
|
aria-label={label}
|
||||||
title={label}
|
title={label}
|
||||||
@@ -47,6 +47,6 @@ export const DeviceExpandDetailsButton = <T extends keyof JSX.IntrinsicElements>
|
|||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
>
|
>
|
||||||
<CaretIcon className="mx_DeviceExpandDetailsButton_icon" />
|
<CaretIcon className="mx_DeviceExpandDetailsButton_icon" />
|
||||||
</AccessibleTooltipButton>
|
</AccessibleButton>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@@ -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("<EmailIdentityAuthEntry/>", () => {
|
||||||
|
const renderIdentityAuth = () => {
|
||||||
|
const matrixClient = createTestClient();
|
||||||
|
|
||||||
|
return render(
|
||||||
|
<EmailIdentityAuthEntry
|
||||||
|
matrixClient={matrixClient}
|
||||||
|
loginType={AuthType.Email}
|
||||||
|
onPhaseChange={jest.fn()}
|
||||||
|
submitAuthDict={jest.fn()}
|
||||||
|
fail={jest.fn()}
|
||||||
|
clientSecret="my secret"
|
||||||
|
showContinue={true}
|
||||||
|
inputs={{ emailAddress: "alice@example.xyz" }}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
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());
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,34 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`<EmailIdentityAuthEntry/> should render 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="mx_InteractiveAuthEntryComponents_emailWrapper"
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
<span>
|
||||||
|
To create your account, open the link in the email we just sent to
|
||||||
|
<b>
|
||||||
|
alice@example.xyz
|
||||||
|
</b>
|
||||||
|
.
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p
|
||||||
|
class="secondary"
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
Did not receive it?
|
||||||
|
<div
|
||||||
|
aria-label="Resend"
|
||||||
|
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_link_inline"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
Resend it
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
Reference in New Issue
Block a user