diff --git a/playwright/snapshots/forgot-password/forgot-password.spec.ts/forgot-password-linux.png b/playwright/snapshots/forgot-password/forgot-password.spec.ts/forgot-password-linux.png index e9eb06375f1..f764311250b 100644 Binary files a/playwright/snapshots/forgot-password/forgot-password.spec.ts/forgot-password-linux.png and b/playwright/snapshots/forgot-password/forgot-password.spec.ts/forgot-password-linux.png differ diff --git a/playwright/snapshots/register/register.spec.ts/email-prompt-linux.png b/playwright/snapshots/register/register.spec.ts/email-prompt-linux.png index e49db5d4089..406cddffdeb 100644 Binary files a/playwright/snapshots/register/register.spec.ts/email-prompt-linux.png and b/playwright/snapshots/register/register.spec.ts/email-prompt-linux.png differ diff --git a/playwright/snapshots/register/register.spec.ts/registration-linux.png b/playwright/snapshots/register/register.spec.ts/registration-linux.png index 1cca0eb56b9..2babcd23431 100644 Binary files a/playwright/snapshots/register/register.spec.ts/registration-linux.png and b/playwright/snapshots/register/register.spec.ts/registration-linux.png differ diff --git a/res/css/structures/auth/_Login.pcss b/res/css/structures/auth/_Login.pcss index a7192b1f0d9..e8445dc493b 100644 --- a/res/css/structures/auth/_Login.pcss +++ b/res/css/structures/auth/_Login.pcss @@ -7,19 +7,9 @@ Please see LICENSE files in the repository root for full details. */ .mx_Login_submit { - @mixin mx_DialogButton; - font-size: 15px; - font-weight: var(--cpd-font-weight-semibold); width: 100%; margin-top: 24px; margin-bottom: 24px; - box-sizing: border-box; - text-align: center; -} - -.mx_Login_submit:disabled { - opacity: 0.3; - cursor: default; } .mx_Login_loader { diff --git a/res/css/views/auth/_AuthBody.pcss b/res/css/views/auth/_AuthBody.pcss index f5f0f9cce57..dcff439107f 100644 --- a/res/css/views/auth/_AuthBody.pcss +++ b/res/css/views/auth/_AuthBody.pcss @@ -148,7 +148,6 @@ Please see LICENSE files in the repository root for full details. } .mx_Login_submit { - font-weight: var(--cpd-font-weight-semibold); margin: 0 0 $spacing-16; } diff --git a/res/css/views/elements/_SSOButtons.pcss b/res/css/views/elements/_SSOButtons.pcss index 7b4d2c973f2..2108b56c40a 100644 --- a/res/css/views/elements/_SSOButtons.pcss +++ b/res/css/views/elements/_SSOButtons.pcss @@ -11,55 +11,33 @@ Please see LICENSE files in the repository root for full details. flex-wrap: wrap; justify-content: center; - .mx_SSOButtons_row { - & + .mx_SSOButtons_row { - margin-top: 16px; - } + .mx_SSOButtons_row + .mx_SSOButtons_row { + margin-top: 16px; } .mx_SSOButton { position: relative; width: 100%; - padding: 7px 32px; - text-align: center; - border-radius: 8px; display: inline-block; - font: var(--cpd-font-body-md-semibold); - border: 1px solid $input-border-color; - color: $primary-content; - > img { + svg, + img { + width: 24px; + height: 24px; object-fit: contain; position: absolute; left: 8px; top: 4px; + color: var(--cpd-color-icon-primary); } } - .mx_SSOButton:hover { - background-color: $panel-actions; - } - - .mx_SSOButton_default { - color: $accent; - background-color: $button-secondary-bg-color; - border-color: $accent; - } - .mx_SSOButton_default.mx_SSOButton_primary { - color: $button-primary-fg-color; - background-color: $accent; - } - .mx_SSOButton_mini { - box-sizing: border-box; - width: 50px; /* 48px + 1px border on all sides */ - height: 50px; /* 48px + 1px border on all sides */ - min-width: 50px; /* prevent crushing by the flexbox */ - padding: 12px; - - > img { - left: 12px; - top: 12px; + svg, + img { + /* 30px parent, 24px self */ + padding: 3px; + position: unset; } & + .mx_SSOButton_mini { diff --git a/res/img/element-icons/brands/github.svg b/res/img/element-icons/brands/github.svg index 503719520ba..73369969d54 100644 --- a/res/img/element-icons/brands/github.svg +++ b/res/img/element-icons/brands/github.svg @@ -1,3 +1,3 @@ - + diff --git a/src/components/structures/auth/ForgotPassword.tsx b/src/components/structures/auth/ForgotPassword.tsx index 0a5080869b9..c9d5466801f 100644 --- a/src/components/structures/auth/ForgotPassword.tsx +++ b/src/components/structures/auth/ForgotPassword.tsx @@ -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 { {this.state.errorText && } - + @@ -432,12 +433,9 @@ export default class ForgotPassword extends React.Component {

{_t("auth|reset_password|reset_successful")}

{this.state.logoutDevices ?

{_t("auth|reset_password|devices_logout_success")}

: null} - + ); } diff --git a/src/components/structures/auth/Login.tsx b/src/components/structures/auth/Login.tsx index 9aca0046f25..006c6c7cb70 100644 --- a/src/components/structures/auth/Login.tsx +++ b/src/components/structures/auth/Login.tsx @@ -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 private renderOidcNativeStep = (): React.ReactNode => { const flow = this.state.flows!.find((flow) => flow.type === "oidcNativeFlow")! as OidcNativeFlow; return ( - { await startOidcLogin( this.props.serverConfig.delegatedAuthentication!, @@ -452,7 +454,7 @@ export default class LoginComponent extends React.PureComponent }} > {_t("action|continue")} - + ); }; diff --git a/src/components/structures/auth/Registration.tsx b/src/components/structures/auth/Registration.tsx index a5c713b3ea9..6c5df8cce2d 100644 --- a/src/components/structures/auth/Registration.tsx +++ b/src/components/structures/auth/Registration.tsx @@ -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 { ); } else if (this.state.matrixClient && this.state.oidcNativeFlow) { return ( - { await startOidcLogin( this.props.serverConfig.delegatedAuthentication!, @@ -560,7 +562,7 @@ export default class Registration extends React.Component { }} > {_t("action|continue")} - + ); } else if (this.state.matrixClient && this.state.flows.length) { let ssoSection: JSX.Element | undefined; diff --git a/src/components/structures/auth/forgot-password/CheckEmail.tsx b/src/components/structures/auth/forgot-password/CheckEmail.tsx index 64036507de6..1525f330d76 100644 --- a/src/components/structures/auth/forgot-password/CheckEmail.tsx +++ b/src/components/structures/auth/forgot-password/CheckEmail.tsx @@ -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 = ({ {errorText && } - +
{_t("auth|check_email_resend_prompt")} diff --git a/src/components/structures/auth/forgot-password/EnterEmail.tsx b/src/components/structures/auth/forgot-password/EnterEmail.tsx index 643cb6c9636..75907b2a5e6 100644 --- a/src/components/structures/auth/forgot-password/EnterEmail.tsx +++ b/src/components/structures/auth/forgot-password/EnterEmail.tsx @@ -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 = ({ />
{errorText && } - +
{ /> {forgotPasswordJsx} {!this.props.busy && ( - + )}
diff --git a/src/components/views/auth/RegistrationForm.tsx b/src/components/views/auth/RegistrationForm.tsx index 88e9aa06157..113ea833eae 100644 --- a/src/components/views/auth/RegistrationForm.tsx +++ b/src/components/views/auth/RegistrationForm.tsx @@ -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 + ); let emailHelperText: JSX.Element | undefined; diff --git a/src/components/views/elements/SSOButtons.tsx b/src/components/views/elements/SSOButtons.tsx index 52fdef752ca..9f321c42e20 100644 --- a/src/components/views/elements/SSOButtons.tsx +++ b/src/components/views/elements/SSOButtons.tsx @@ -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 = ({ fragmentAfterLogin, idp, primary, - mini, + mini: iconOnly, action, flow, - ...props + disabled, }) => { let label: string; if (idp) { @@ -98,43 +102,43 @@ const SSOButton: React.FC = ({ PlatformPeg.get()?.startSingleSignOn(matrixClient, loginType, fragmentAfterLogin, idp?.id, action); }; + const commonProps: Partial> & 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 = {brandName}; + icon = ; + 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 = {idp.name}; + icon = {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 ( - - {icon} - + + + ); } return ( - + ); }; diff --git a/test/unit-tests/components/structures/MatrixChat-test.tsx b/test/unit-tests/components/structures/MatrixChat-test.tsx index fe9676d7533..0612ee2bb87 100644 --- a/test/unit-tests/components/structures/MatrixChat-test.tsx +++ b/test/unit-tests/components/structures/MatrixChat-test.tsx @@ -1269,7 +1269,7 @@ describe("", () => { fireEvent.change(screen.getByLabelText("Password"), { target: { value: password } }); // sign in button is an input - fireEvent.click(screen.getByDisplayValue("Sign in")); + fireEvent.click(screen.getByRole("button", { name: "Sign in" })); }; beforeEach(() => { diff --git a/test/unit-tests/components/structures/auth/Login-test.tsx b/test/unit-tests/components/structures/auth/Login-test.tsx index 872ccbd3d01..431b96a7a7a 100644 --- a/test/unit-tests/components/structures/auth/Login-test.tsx +++ b/test/unit-tests/components/structures/auth/Login-test.tsx @@ -281,13 +281,13 @@ describe("Login", function () { ], }); - const { container } = getComponent(); + const { container, getByTestId } = getComponent(); await waitForElementToBeRemoved(() => screen.queryAllByLabelText("Loading…")); for (const idp of idpsWithIcons) { - const ssoButton = container.querySelector(`.mx_SSOButton.mx_SSOButton_brand_${idp.brand}`); + const ssoButton = getByTestId(`idp-${idp.id}`); expect(ssoButton).toBeTruthy(); - expect(ssoButton?.querySelector(`img[alt="${idp.brand}"]`)).toBeTruthy(); + expect(ssoButton.childNodes[0]).toHaveAccessibleName(idp.brand); } const ssoButtons = container.querySelectorAll(".mx_SSOButton");