Use Compound buttons in auth screens (#32562)
* Use Compound buttons in auth screens Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Simplify Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Replace brandClass with testid Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Fix tests Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Update screenshots Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Replace OIDC native Continue buttons too Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Fix contrast issues on legacy sso buttons Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Tidy css Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Update test Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --------- Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
@@ -12,6 +12,7 @@ import React, { type JSX, type ReactNode } from "react";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { sleep } from "matrix-js-sdk/src/utils";
|
||||
import { LockSolidIcon, CheckIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
|
||||
import { Button } from "@vector-im/compound-web";
|
||||
|
||||
import { _t, _td } from "../../../languageHandler";
|
||||
import Modal from "../../../Modal";
|
||||
@@ -417,9 +418,9 @@ export default class ForgotPassword extends React.Component<Props, State> {
|
||||
</StyledCheckbox>
|
||||
</div>
|
||||
{this.state.errorText && <ErrorMessage message={this.state.errorText} />}
|
||||
<button type="submit" className="mx_Login_submit">
|
||||
<Button type="submit" className="mx_Login_submit" size="sm">
|
||||
{submitButtonChild}
|
||||
</button>
|
||||
</Button>
|
||||
</fieldset>
|
||||
</form>
|
||||
</>
|
||||
@@ -432,12 +433,9 @@ export default class ForgotPassword extends React.Component<Props, State> {
|
||||
<CheckIcon className="mx_Icon mx_Icon_32 mx_Icon_accent" />
|
||||
<h1>{_t("auth|reset_password|reset_successful")}</h1>
|
||||
{this.state.logoutDevices ? <p>{_t("auth|reset_password|devices_logout_success")}</p> : null}
|
||||
<input
|
||||
className="mx_Login_submit"
|
||||
type="button"
|
||||
onClick={this.props.onComplete}
|
||||
value={_t("auth|reset_password|return_to_login")}
|
||||
/>
|
||||
<Button className="mx_Login_submit" size="sm" type="button" onClick={this.props.onComplete}>
|
||||
{_t("auth|reset_password|return_to_login")}
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import React, { type JSX, type ReactNode } from "react";
|
||||
import classNames from "classnames";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { type SSOFlow, SSOAction } from "matrix-js-sdk/src/matrix";
|
||||
import { Button } from "@vector-im/compound-web";
|
||||
|
||||
import { _t, UserFriendlyError } from "../../../languageHandler";
|
||||
import Login, { type ClientLoginFlow, type OidcNativeFlow } from "../../../Login";
|
||||
@@ -439,9 +440,10 @@ export default class LoginComponent extends React.PureComponent<IProps, IState>
|
||||
private renderOidcNativeStep = (): React.ReactNode => {
|
||||
const flow = this.state.flows!.find((flow) => flow.type === "oidcNativeFlow")! as OidcNativeFlow;
|
||||
return (
|
||||
<AccessibleButton
|
||||
<Button
|
||||
className="mx_Login_fullWidthButton"
|
||||
kind="primary"
|
||||
size="sm"
|
||||
onClick={async () => {
|
||||
await startOidcLogin(
|
||||
this.props.serverConfig.delegatedAuthentication!,
|
||||
@@ -452,7 +454,7 @@ export default class LoginComponent extends React.PureComponent<IProps, IState>
|
||||
}}
|
||||
>
|
||||
{_t("action|continue")}
|
||||
</AccessibleButton>
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
import React, { type JSX, Fragment, type ReactNode } from "react";
|
||||
import classNames from "classnames";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { Button } from "@vector-im/compound-web";
|
||||
|
||||
import { _t } from "../../../languageHandler";
|
||||
import { adminContactStrings, messageForResourceLimitError, resourceLimitStrings } from "../../../utils/ErrorUtils";
|
||||
@@ -546,9 +547,10 @@ export default class Registration extends React.Component<IProps, IState> {
|
||||
);
|
||||
} else if (this.state.matrixClient && this.state.oidcNativeFlow) {
|
||||
return (
|
||||
<AccessibleButton
|
||||
<Button
|
||||
className="mx_Login_fullWidthButton"
|
||||
kind="primary"
|
||||
size="sm"
|
||||
onClick={async () => {
|
||||
await startOidcLogin(
|
||||
this.props.serverConfig.delegatedAuthentication!,
|
||||
@@ -560,7 +562,7 @@ export default class Registration extends React.Component<IProps, IState> {
|
||||
}}
|
||||
>
|
||||
{_t("action|continue")}
|
||||
</AccessibleButton>
|
||||
</Button>
|
||||
);
|
||||
} else if (this.state.matrixClient && this.state.flows.length) {
|
||||
let ssoSection: JSX.Element | undefined;
|
||||
|
||||
@@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React, { type ReactNode } from "react";
|
||||
import { Tooltip } from "@vector-im/compound-web";
|
||||
import { Button, Tooltip } from "@vector-im/compound-web";
|
||||
import { RestartIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
|
||||
|
||||
import AccessibleButton from "../../../views/elements/AccessibleButton";
|
||||
@@ -55,7 +55,9 @@ export const CheckEmail: React.FC<CheckEmailProps> = ({
|
||||
</div>
|
||||
</div>
|
||||
{errorText && <ErrorMessage message={errorText} />}
|
||||
<input onClick={onSubmitForm} type="button" className="mx_Login_submit" value={_t("action|next")} />
|
||||
<Button onClick={onSubmitForm} type="button" className="mx_Login_submit" size="sm">
|
||||
{_t("action|next")}
|
||||
</Button>
|
||||
<div className="mx_AuthBody_did-not-receive">
|
||||
<span className="mx_VerifyEMailDialog_text-light">{_t("auth|check_email_resend_prompt")}</span>
|
||||
<Tooltip description={_t("auth|check_email_resend_tooltip")} placement="top" open={tooltipVisible}>
|
||||
|
||||
@@ -8,6 +8,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
|
||||
import React, { type ReactNode, useRef } from "react";
|
||||
import { EmailSolidIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
|
||||
import { Button } from "@vector-im/compound-web";
|
||||
|
||||
import { _t, _td } from "../../../../languageHandler";
|
||||
import EmailField from "../../../views/auth/EmailField";
|
||||
@@ -74,9 +75,9 @@ export const EnterEmail: React.FC<EnterEmailProps> = ({
|
||||
/>
|
||||
</div>
|
||||
{errorText && <ErrorMessage message={errorText} />}
|
||||
<button type="submit" className="mx_Login_submit">
|
||||
<Button type="submit" className="mx_Login_submit" size="sm">
|
||||
{submitButtonChild}
|
||||
</button>
|
||||
</Button>
|
||||
<div className="mx_AuthBody_button-container">
|
||||
<AccessibleButton
|
||||
className="mx_AuthBody_sign-in-instead-button"
|
||||
|
||||
@@ -8,6 +8,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
|
||||
import React, { type JSX, type SyntheticEvent } from "react";
|
||||
import classNames from "classnames";
|
||||
import { Button } from "@vector-im/compound-web";
|
||||
|
||||
import { _t } from "../../../languageHandler";
|
||||
import SdkConfig from "../../../SdkConfig";
|
||||
@@ -433,12 +434,9 @@ export default class PasswordLogin extends React.PureComponent<IProps, IState> {
|
||||
/>
|
||||
{forgotPasswordJsx}
|
||||
{!this.props.busy && (
|
||||
<input
|
||||
className="mx_Login_submit"
|
||||
type="submit"
|
||||
value={_t("action|sign_in")}
|
||||
disabled={this.props.disableSubmit}
|
||||
/>
|
||||
<Button className="mx_Login_submit" size="sm" type="submit" disabled={this.props.disableSubmit}>
|
||||
{_t("action|sign_in")}
|
||||
</Button>
|
||||
)}
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -10,6 +10,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
import React, { type JSX, type BaseSyntheticEvent, type ComponentProps, type ReactNode } from "react";
|
||||
import { type MatrixClient, MatrixError } from "matrix-js-sdk/src/matrix";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { Button } from "@vector-im/compound-web";
|
||||
|
||||
import * as Email from "../../../email";
|
||||
import { looksValid as phoneNumberLooksValid, type PhoneNumberCountryDefinition } from "../../../phonenumber";
|
||||
@@ -548,12 +549,9 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
|
||||
|
||||
public render(): ReactNode {
|
||||
const registerButton = (
|
||||
<input
|
||||
className="mx_Login_submit"
|
||||
type="submit"
|
||||
value={_t("action|register")}
|
||||
disabled={!this.props.canSubmit}
|
||||
/>
|
||||
<Button className="mx_Login_submit" size="sm" type="submit" disabled={!this.props.canSubmit}>
|
||||
{_t("action|register")}
|
||||
</Button>
|
||||
);
|
||||
|
||||
let emailHelperText: JSX.Element | undefined;
|
||||
|
||||
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React, { type JSX } from "react";
|
||||
import React, { type ComponentProps, type JSX } from "react";
|
||||
import { chunk } from "lodash";
|
||||
import classNames from "classnames";
|
||||
import {
|
||||
@@ -18,12 +18,18 @@ import {
|
||||
DELEGATED_OIDC_COMPATIBILITY,
|
||||
} from "matrix-js-sdk/src/matrix";
|
||||
import { type Signup } from "@matrix-org/analytics-events/types/typescript/Signup";
|
||||
import { Button, Tooltip } from "@vector-im/compound-web";
|
||||
import { MacIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
|
||||
|
||||
import PlatformPeg from "../../../PlatformPeg";
|
||||
import AccessibleButton from "./AccessibleButton";
|
||||
import { _t } from "../../../languageHandler";
|
||||
import { mediaFromMxc } from "../../../customisations/Media";
|
||||
import { PosthogAnalytics } from "../../../PosthogAnalytics";
|
||||
import { Icon as FacebookIcon } from "../../../../res/img/element-icons/brands/facebook.svg";
|
||||
import { Icon as GithubIcon } from "../../../../res/img/element-icons/brands/github.svg";
|
||||
import { Icon as GitlabIcon } from "../../../../res/img/element-icons/brands/gitlab.svg";
|
||||
import { Icon as GoogleIcon } from "../../../../res/img/element-icons/brands/google.svg";
|
||||
import { Icon as TwitterIcon } from "../../../../res/img/element-icons/brands/twitter.svg";
|
||||
|
||||
interface ISSOButtonProps extends IProps {
|
||||
idp?: IIdentityProvider;
|
||||
@@ -31,24 +37,22 @@ interface ISSOButtonProps extends IProps {
|
||||
action?: SSOAction;
|
||||
}
|
||||
|
||||
const getIcon = (brand: IdentityProviderBrand | string): string | null => {
|
||||
const getIcon = (brand: IdentityProviderBrand | string): typeof FacebookIcon | null => {
|
||||
switch (brand) {
|
||||
/* eslint-disable @typescript-eslint/no-require-imports */
|
||||
case IdentityProviderBrand.Apple:
|
||||
return require("@vector-im/compound-design-tokens/icons/mac.svg").default;
|
||||
return MacIcon;
|
||||
case IdentityProviderBrand.Facebook:
|
||||
return require(`../../../../res/img/element-icons/brands/facebook.svg`).default;
|
||||
return FacebookIcon;
|
||||
case IdentityProviderBrand.Github:
|
||||
return require(`../../../../res/img/element-icons/brands/github.svg`).default;
|
||||
return GithubIcon;
|
||||
case IdentityProviderBrand.Gitlab:
|
||||
return require(`../../../../res/img/element-icons/brands/gitlab.svg`).default;
|
||||
return GitlabIcon;
|
||||
case IdentityProviderBrand.Google:
|
||||
return require(`../../../../res/img/element-icons/brands/google.svg`).default;
|
||||
return GoogleIcon;
|
||||
case IdentityProviderBrand.Twitter:
|
||||
return require(`../../../../res/img/element-icons/brands/twitter.svg`).default;
|
||||
return TwitterIcon;
|
||||
default:
|
||||
return null;
|
||||
/* eslint-enable @typescript-eslint/no-require-imports */
|
||||
}
|
||||
};
|
||||
|
||||
@@ -78,10 +82,10 @@ const SSOButton: React.FC<ISSOButtonProps> = ({
|
||||
fragmentAfterLogin,
|
||||
idp,
|
||||
primary,
|
||||
mini,
|
||||
mini: iconOnly,
|
||||
action,
|
||||
flow,
|
||||
...props
|
||||
disabled,
|
||||
}) => {
|
||||
let label: string;
|
||||
if (idp) {
|
||||
@@ -98,43 +102,43 @@ const SSOButton: React.FC<ISSOButtonProps> = ({
|
||||
PlatformPeg.get()?.startSingleSignOn(matrixClient, loginType, fragmentAfterLogin, idp?.id, action);
|
||||
};
|
||||
|
||||
const commonProps: Partial<ComponentProps<typeof Button>> & Record<`data-${string}`, string> = {
|
||||
iconOnly,
|
||||
className: classNames("mx_SSOButton", {
|
||||
mx_SSOButton_mini: iconOnly,
|
||||
}),
|
||||
onClick,
|
||||
kind: primary ? "primary" : "secondary",
|
||||
disabled,
|
||||
};
|
||||
|
||||
let icon: JSX.Element | undefined;
|
||||
let brandClass: string | undefined;
|
||||
const brandIcon = idp?.brand ? getIcon(idp.brand) : null;
|
||||
if (idp?.brand && brandIcon) {
|
||||
const BrandIcon = idp?.brand ? getIcon(idp.brand) : null;
|
||||
if (idp?.brand && BrandIcon) {
|
||||
const brandName = idp.brand.split(".").pop();
|
||||
brandClass = `mx_SSOButton_brand_${brandName}`;
|
||||
icon = <img src={brandIcon} height="24" width="24" alt={brandName} />;
|
||||
icon = <BrandIcon aria-label={brandName} />;
|
||||
commonProps["data-testid"] = `idp-${idp.id}`;
|
||||
} else if (typeof idp?.icon === "string" && idp.icon.startsWith("mxc://")) {
|
||||
const src = mediaFromMxc(idp.icon, matrixClient).getSquareThumbnailHttp(24) ?? undefined;
|
||||
icon = <img src={src} height="24" width="24" alt={idp.name} />;
|
||||
icon = <img src={src} alt={idp.name} />;
|
||||
}
|
||||
|
||||
const brandPart = brandClass ? { [brandClass]: brandClass } : undefined;
|
||||
const classes = classNames(
|
||||
"mx_SSOButton",
|
||||
{
|
||||
mx_SSOButton_mini: mini,
|
||||
mx_SSOButton_default: !idp,
|
||||
mx_SSOButton_primary: primary,
|
||||
},
|
||||
brandPart,
|
||||
);
|
||||
|
||||
if (mini) {
|
||||
// TODO fallback icon
|
||||
// TODO fallback icon
|
||||
if (iconOnly) {
|
||||
return (
|
||||
<AccessibleButton {...props} title={label} className={classes} onClick={onClick}>
|
||||
{icon}
|
||||
</AccessibleButton>
|
||||
<Tooltip label={label}>
|
||||
<Button {...commonProps} size="lg">
|
||||
{icon}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<AccessibleButton {...props} className={classes} onClick={onClick}>
|
||||
<Button {...commonProps} size="sm">
|
||||
{icon}
|
||||
{label}
|
||||
</AccessibleButton>
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user