From b3bdf83b2771918d9a7c898b83a37c7ab51ce11c Mon Sep 17 00:00:00 2001 From: Presley Date: Thu, 24 Mar 2022 18:41:50 +0000 Subject: [PATCH 1/9] Remove wrapper from SignInForm --- site/src/components/SignIn/SignInForm.tsx | 88 +++++++++++------------ 1 file changed, 40 insertions(+), 48 deletions(-) diff --git a/site/src/components/SignIn/SignInForm.tsx b/site/src/components/SignIn/SignInForm.tsx index 880ddb093a202..c911099f9558c 100644 --- a/site/src/components/SignIn/SignInForm.tsx +++ b/site/src/components/SignIn/SignInForm.tsx @@ -4,9 +4,9 @@ import React from "react" import * as Yup from "yup" import { Welcome } from "./Welcome" -import { FormTextField } from "../Form" import FormHelperText from "@material-ui/core/FormHelperText" import { LoadingButton } from "./../Button" +import TextField from "@material-ui/core/TextField" /** * BuiltInAuthFormValues describes a form using built-in (email/password) @@ -18,8 +18,14 @@ interface BuiltInAuthFormValues { password: string } +export const LANGUAGE = { + emailInvalid: "Please enter a valid email address.", + emailRequired: "Please enter an email address.", + authErrorMessage: "Incorrect email or password." +} + const validationSchema = Yup.object({ - email: Yup.string().required("Email is required."), + email: Yup.string().trim().email(LANGUAGE.emailInvalid).required(LANGUAGE.emailRequired), password: Yup.string(), }) @@ -59,52 +65,38 @@ export const SignInForm: React.FC = ({ isLoading, authErrorMess <>
-
- email.trim()} - form={form} - formFieldName="email" - fullWidth - inputProps={{ - id: "signin-form-inpt-email", - }} - variant="outlined" - /> - - {authErrorMessage && ( - - {authErrorMessage} - - )} -
-
- - {isLoading ? "" : "Sign In"} - -
+ + + {authErrorMessage && ( + + {LANGUAGE.authErrorMessage} + + )} +
+ + {isLoading ? "" : "Sign In"} + +
) From d2741c375988da06f97c268e227a0e6732793aa4 Mon Sep 17 00:00:00 2001 From: Presley Date: Thu, 24 Mar 2022 18:56:10 +0000 Subject: [PATCH 2/9] Spruce up tests --- site/package.json | 1 + site/src/components/SignIn/SignInForm.tsx | 69 ++++++++++++----------- site/src/pages/login.test.tsx | 21 ++++--- site/yarn.lock | 7 +++ 4 files changed, 56 insertions(+), 42 deletions(-) diff --git a/site/package.json b/site/package.json index af8644e484898..7778f68e19ea6 100644 --- a/site/package.json +++ b/site/package.json @@ -48,6 +48,7 @@ "@storybook/addon-links": "6.4.19", "@storybook/react": "6.4.19", "@testing-library/react": "12.1.4", + "@testing-library/user-event": "^13.5.0", "@types/express": "4.17.13", "@types/jest": "27.4.1", "@types/node": "14.18.12", diff --git a/site/src/components/SignIn/SignInForm.tsx b/site/src/components/SignIn/SignInForm.tsx index c911099f9558c..3a0b1d9afced6 100644 --- a/site/src/components/SignIn/SignInForm.tsx +++ b/site/src/components/SignIn/SignInForm.tsx @@ -19,9 +19,12 @@ interface BuiltInAuthFormValues { } export const LANGUAGE = { + emailLabel: "Email", + passwordLabel: "Password", emailInvalid: "Please enter a valid email address.", emailRequired: "Please enter an email address.", - authErrorMessage: "Incorrect email or password." + authErrorMessage: "Incorrect email or password.", + signIn: "Sign In", } const validationSchema = Yup.object({ @@ -65,38 +68,38 @@ export const SignInForm: React.FC = ({ isLoading, authErrorMess <>
- - - {authErrorMessage && ( - - {LANGUAGE.authErrorMessage} - - )} -
- - {isLoading ? "" : "Sign In"} - -
+ + + {authErrorMessage && ( + + {LANGUAGE.authErrorMessage} + + )} +
+ + {isLoading ? "" : LANGUAGE.signIn} + +
) diff --git a/site/src/pages/login.test.tsx b/site/src/pages/login.test.tsx index 7b2abb966d4c9..c8ca917b07ded 100644 --- a/site/src/pages/login.test.tsx +++ b/site/src/pages/login.test.tsx @@ -1,9 +1,11 @@ import React from "react" -import { act, fireEvent, screen } from "@testing-library/react" +import { act, screen } from "@testing-library/react" +import userEvent from "@testing-library/user-event" import { history, render } from "../test_helpers" import { SignInPage } from "./login" import { server } from "../test_helpers/server" import { rest } from "msw" +import { LANGUAGE } from "../components/SignIn/SignInForm" describe("SignInPage", () => { beforeEach(() => { @@ -21,12 +23,12 @@ describe("SignInPage", () => { render() // Then - await screen.findByText("Sign In", { exact: false }) + await screen.findByText(LANGUAGE.signIn, { exact: false }) }) it("shows an error message if SignIn fails", async () => { // Given - const { container } = render() + render() // Make login fail server.use( rest.post("/api/v2/users/login", async (req, res, ctx) => { @@ -35,17 +37,18 @@ describe("SignInPage", () => { ) // When - // Set username / password - const [username, password] = container.querySelectorAll("input") - fireEvent.change(username, { target: { value: "test@coder.com" } }) - fireEvent.change(password, { target: { value: "password" } }) + // Set email / password + const email = screen.getByLabelText(LANGUAGE.emailLabel) + const password = screen.getByLabelText(LANGUAGE.passwordLabel) + userEvent.type(email, "test@coder.com") + userEvent.type(password, "password") // Click sign-in - const signInButton = await screen.findByText("Sign In") + const signInButton = await screen.findByText(LANGUAGE.signIn) act(() => signInButton.click()) // Then // Finding error by test id because it comes from the backend - const errorMessage = await screen.findByTestId("sign-in-error") + const errorMessage = await screen.findByText(LANGUAGE.authErrorMessage) expect(errorMessage).toBeDefined() expect(history.location.pathname).toEqual("/login") }) diff --git a/site/yarn.lock b/site/yarn.lock index c0bcbae52271c..fb9af76184e49 100644 --- a/site/yarn.lock +++ b/site/yarn.lock @@ -2742,6 +2742,13 @@ "@testing-library/dom" "^8.0.0" "@types/react-dom" "*" +"@testing-library/user-event@^13.5.0": + version "13.5.0" + resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-13.5.0.tgz#69d77007f1e124d55314a2b73fd204b333b13295" + integrity sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg== + dependencies: + "@babel/runtime" "^7.12.5" + "@tootallnate/once@1": version "1.1.2" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" From 918dd80bfc2f461520dad76f895618afc90439ca Mon Sep 17 00:00:00 2001 From: Presley Date: Thu, 24 Mar 2022 19:18:54 +0000 Subject: [PATCH 3/9] Add util for form props --- site/src/components/Form/index.ts | 13 +++++++++++++ site/src/components/SignIn/SignInForm.tsx | 8 +++----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/site/src/components/Form/index.ts b/site/src/components/Form/index.ts index 08456432c7f74..2e9aebbe782a5 100644 --- a/site/src/components/Form/index.ts +++ b/site/src/components/Form/index.ts @@ -1,5 +1,18 @@ +import { FormikContextType } from "formik/dist/types" + export * from "./FormCloseButton" export * from "./FormSection" export * from "./FormDropdownField" export * from "./FormTextField" export * from "./FormTitle" + +export function getFormHelpers(form: FormikContextType, name: keyof T) { + const touched = form.touched[name] + const errors = form.errors[name] + return { + ...form.getFieldProps(name), + id: name, + error: touched && Boolean(errors), + helperText: touched && errors + } + } diff --git a/site/src/components/SignIn/SignInForm.tsx b/site/src/components/SignIn/SignInForm.tsx index 3a0b1d9afced6..f0dec30cd8fbe 100644 --- a/site/src/components/SignIn/SignInForm.tsx +++ b/site/src/components/SignIn/SignInForm.tsx @@ -7,6 +7,7 @@ import { Welcome } from "./Welcome" import FormHelperText from "@material-ui/core/FormHelperText" import { LoadingButton } from "./../Button" import TextField from "@material-ui/core/TextField" +import { getFormHelpers } from "../Form" /** * BuiltInAuthFormValues describes a form using built-in (email/password) @@ -69,19 +70,16 @@ export const SignInForm: React.FC = ({ isLoading, authErrorMess
(form, "email")} autoFocus autoComplete="email" className={styles.loginTextField} - error={form.touched.email && Boolean(form.errors.email)} fullWidth - helperText={form.touched.email && form.errors.email} - id="email" label={LANGUAGE.emailLabel} variant="outlined" /> (form, "password")} autoComplete="current-password" className={styles.loginTextField} fullWidth From 73df2da3c57f646ee9afac57a3e7118d628a3f52 Mon Sep 17 00:00:00 2001 From: Presley Date: Thu, 24 Mar 2022 19:42:50 +0000 Subject: [PATCH 4/9] Add back trim --- site/src/components/Form/index.ts | 9 ++++++++- site/src/components/SignIn/SignInForm.tsx | 3 ++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/site/src/components/Form/index.ts b/site/src/components/Form/index.ts index 2e9aebbe782a5..4a4ead5491cfe 100644 --- a/site/src/components/Form/index.ts +++ b/site/src/components/Form/index.ts @@ -6,7 +6,7 @@ export * from "./FormDropdownField" export * from "./FormTextField" export * from "./FormTitle" -export function getFormHelpers(form: FormikContextType, name: keyof T) { +export function getFormHelpers(form: FormikContextType, name: keyof T) { const touched = form.touched[name] const errors = form.errors[name] return { @@ -16,3 +16,10 @@ export function getFormHelpers(form: FormikContextType, name: keyof T) { helperText: touched && errors } } + +export function onChangeTrimmed(form: FormikContextType) { + return (event: React.ChangeEvent) => { + event.target.value = event?.target?.value?.trim() + form.handleChange(event) + } +} diff --git a/site/src/components/SignIn/SignInForm.tsx b/site/src/components/SignIn/SignInForm.tsx index f0dec30cd8fbe..ec632fa25b24d 100644 --- a/site/src/components/SignIn/SignInForm.tsx +++ b/site/src/components/SignIn/SignInForm.tsx @@ -7,7 +7,7 @@ import { Welcome } from "./Welcome" import FormHelperText from "@material-ui/core/FormHelperText" import { LoadingButton } from "./../Button" import TextField from "@material-ui/core/TextField" -import { getFormHelpers } from "../Form" +import { getFormHelpers, onChangeTrimmed } from "../Form" /** * BuiltInAuthFormValues describes a form using built-in (email/password) @@ -71,6 +71,7 @@ export const SignInForm: React.FC = ({ isLoading, authErrorMess (form, "email")} + onChange={onChangeTrimmed(form)} autoFocus autoComplete="email" className={styles.loginTextField} From e1e12d808c527fd050ccc671b2cd7f21e36e9ea2 Mon Sep 17 00:00:00 2001 From: Presley Date: Thu, 24 Mar 2022 21:11:07 +0000 Subject: [PATCH 5/9] Add unit tests --- site/src/components/Form/index.test.tsx | 74 +++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 site/src/components/Form/index.test.tsx diff --git a/site/src/components/Form/index.test.tsx b/site/src/components/Form/index.test.tsx new file mode 100644 index 0000000000000..4a700dff12f5a --- /dev/null +++ b/site/src/components/Form/index.test.tsx @@ -0,0 +1,74 @@ +import { FormikContextType } from 'formik/dist/types' +import { getFormHelpers, onChangeTrimmed } from './index' + +interface TestType { + untouchedGoodField: string + untouchedBadField: string + touchedGoodField: string + touchedBadField: string +} + +const mockHandleChange = jest.fn() + +const form = { + errors: { + untouchedGoodField: undefined, + untouchedBadField: "oops!", + touchedGoodField: undefined, + touchedBadField: 'oops!', + }, + touched: { + untouchedGoodField: false, + untouchedBadField: false, + touchedGoodField: true, + touchedBadField: true, + }, + handleChange: mockHandleChange, + handleBlur: jest.fn(), + getFieldProps: (name: string) => { + return { + name, + onBlur: jest.fn(), + onChange: jest.fn(), + value: '' + } + } +} as unknown as FormikContextType + +describe("form util functions", () => { + describe("getFormHelpers", () => { + const untouchedGoodResult = getFormHelpers(form, "untouchedGoodField") + const untouchedBadResult = getFormHelpers(form, "untouchedBadField") + const touchedGoodResult = getFormHelpers(form, "touchedGoodField") + const touchedBadResult = getFormHelpers(form, "touchedBadField") + it("populates the 'field props'", () => { + expect(untouchedGoodResult.name).toEqual("untouchedGoodField") + expect(untouchedGoodResult.onBlur).toBeDefined() + expect(untouchedGoodResult.onChange).toBeDefined() + expect(untouchedGoodResult.value).toBeDefined() + }) + it("sets the id to the name", () => { + expect(untouchedGoodResult.id).toEqual("untouchedGoodField") + }) + it("sets error to true if touched and invalid", () => { + expect(untouchedGoodResult.error).toBeFalsy + expect(untouchedBadResult.error).toBeFalsy + expect(touchedGoodResult.error).toBeFalsy + expect(touchedBadResult.error).toBeTruthy + }) + it("sets helperText to the error message if touched and invalid", () => { + expect(untouchedGoodResult.helperText).toBeUndefined + expect(untouchedBadResult.helperText).toBeUndefined + expect(touchedGoodResult.helperText).toBeUndefined + expect(touchedBadResult.helperText).toEqual("oops!") + }) + }) + + describe("onChangeTrimmed", () => { + it("calls handleChange with trimmed value", () => { + const event = { target: { value: " hello "}} as React.ChangeEvent + onChangeTrimmed(form)(event) + expect(mockHandleChange).toHaveBeenCalledWith({ target: { value: "hello" } }) + }) + }) +}) From 108c77e727127e1b201301f79c51fedf49b12f65 Mon Sep 17 00:00:00 2001 From: Presley Date: Thu, 24 Mar 2022 21:55:55 +0000 Subject: [PATCH 6/9] Lint, type fixes --- site/src/components/Form/index.test.tsx | 14 ++++----- site/src/components/Form/index.ts | 38 ++++++++++++++++--------- 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/site/src/components/Form/index.test.tsx b/site/src/components/Form/index.test.tsx index 4a700dff12f5a..eae3486c539d9 100644 --- a/site/src/components/Form/index.test.tsx +++ b/site/src/components/Form/index.test.tsx @@ -1,5 +1,5 @@ -import { FormikContextType } from 'formik/dist/types' -import { getFormHelpers, onChangeTrimmed } from './index' +import { FormikContextType } from "formik/dist/types" +import { getFormHelpers, onChangeTrimmed } from "./index" interface TestType { untouchedGoodField: string @@ -10,12 +10,12 @@ interface TestType { const mockHandleChange = jest.fn() -const form = { +const form = { errors: { untouchedGoodField: undefined, untouchedBadField: "oops!", touchedGoodField: undefined, - touchedBadField: 'oops!', + touchedBadField: "oops!", }, touched: { untouchedGoodField: false, @@ -30,9 +30,9 @@ const form = { name, onBlur: jest.fn(), onChange: jest.fn(), - value: '' + value: "", } - } + }, } as unknown as FormikContextType describe("form util functions", () => { @@ -66,7 +66,7 @@ describe("form util functions", () => { describe("onChangeTrimmed", () => { it("calls handleChange with trimmed value", () => { - const event = { target: { value: " hello "}} as React.ChangeEvent + const event = { target: { value: " hello " } } as React.ChangeEvent onChangeTrimmed(form)(event) expect(mockHandleChange).toHaveBeenCalledWith({ target: { value: "hello" } }) }) diff --git a/site/src/components/Form/index.ts b/site/src/components/Form/index.ts index 4a4ead5491cfe..8a1af1a36eb0a 100644 --- a/site/src/components/Form/index.ts +++ b/site/src/components/Form/index.ts @@ -1,4 +1,5 @@ -import { FormikContextType } from "formik/dist/types" +import { FormikContextType, getIn } from "formik" +import { ChangeEvent, ChangeEventHandler, FocusEventHandler } from "react" export * from "./FormCloseButton" export * from "./FormSection" @@ -6,20 +7,31 @@ export * from "./FormDropdownField" export * from "./FormTextField" export * from "./FormTitle" -export function getFormHelpers(form: FormikContextType, name: keyof T) { - const touched = form.touched[name] - const errors = form.errors[name] - return { - ...form.getFieldProps(name), - id: name, - error: touched && Boolean(errors), - helperText: touched && errors - } +interface FormHelpers { + name: string + onBlur: FocusEventHandler + onChange: ChangeEventHandler + id: string + value?: string | number + error: boolean + helperText?: string +} + +export function getFormHelpers(form: FormikContextType, name: string): FormHelpers { + // getIn is a util function from Formik that gets at any depth of nesting, and is necessary for the types to work + const touched = getIn(form.touched, name) + const errors = getIn(form.errors, name) + return { + ...form.getFieldProps(name), + id: name, + error: touched && Boolean(errors), + helperText: touched && errors, } +} -export function onChangeTrimmed(form: FormikContextType) { - return (event: React.ChangeEvent) => { - event.target.value = event?.target?.value?.trim() +export function onChangeTrimmed(form: FormikContextType): (event: ChangeEvent) => void { + return (event: ChangeEvent): void => { + event.target.value = event.target.value.trim() form.handleChange(event) } } From 50e6e0c9c98b3893c36a1dd9a38ef21d33221294 Mon Sep 17 00:00:00 2001 From: Presley Date: Mon, 28 Mar 2022 20:11:37 +0000 Subject: [PATCH 7/9] Pascal case for language --- site/src/components/SignIn/SignInForm.tsx | 12 ++++++------ site/src/pages/login.test.tsx | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/site/src/components/SignIn/SignInForm.tsx b/site/src/components/SignIn/SignInForm.tsx index ec632fa25b24d..4506d20b5703e 100644 --- a/site/src/components/SignIn/SignInForm.tsx +++ b/site/src/components/SignIn/SignInForm.tsx @@ -19,7 +19,7 @@ interface BuiltInAuthFormValues { password: string } -export const LANGUAGE = { +export const Language = { emailLabel: "Email", passwordLabel: "Password", emailInvalid: "Please enter a valid email address.", @@ -29,7 +29,7 @@ export const LANGUAGE = { } const validationSchema = Yup.object({ - email: Yup.string().trim().email(LANGUAGE.emailInvalid).required(LANGUAGE.emailRequired), + email: Yup.string().trim().email(Language.emailInvalid).required(Language.emailRequired), password: Yup.string(), }) @@ -76,7 +76,7 @@ export const SignInForm: React.FC = ({ isLoading, authErrorMess autoComplete="email" className={styles.loginTextField} fullWidth - label={LANGUAGE.emailLabel} + label={Language.emailLabel} variant="outlined" /> = ({ isLoading, authErrorMess className={styles.loginTextField} fullWidth id="password" - label={LANGUAGE.passwordLabel} + label={Language.passwordLabel} type="password" variant="outlined" /> {authErrorMessage && ( - {LANGUAGE.authErrorMessage} + {Language.authErrorMessage} )}
- {isLoading ? "" : LANGUAGE.signIn} + {isLoading ? "" : Language.signIn}
diff --git a/site/src/pages/login.test.tsx b/site/src/pages/login.test.tsx index c8ca917b07ded..bb1caadb95d5f 100644 --- a/site/src/pages/login.test.tsx +++ b/site/src/pages/login.test.tsx @@ -5,7 +5,7 @@ import { history, render } from "../test_helpers" import { SignInPage } from "./login" import { server } from "../test_helpers/server" import { rest } from "msw" -import { LANGUAGE } from "../components/SignIn/SignInForm" +import { Language } from "../components/SignIn/SignInForm" describe("SignInPage", () => { beforeEach(() => { @@ -23,7 +23,7 @@ describe("SignInPage", () => { render() // Then - await screen.findByText(LANGUAGE.signIn, { exact: false }) + await screen.findByText(Language.signIn, { exact: false }) }) it("shows an error message if SignIn fails", async () => { @@ -38,17 +38,17 @@ describe("SignInPage", () => { // When // Set email / password - const email = screen.getByLabelText(LANGUAGE.emailLabel) - const password = screen.getByLabelText(LANGUAGE.passwordLabel) + const email = screen.getByLabelText(Language.emailLabel) + const password = screen.getByLabelText(Language.passwordLabel) userEvent.type(email, "test@coder.com") userEvent.type(password, "password") // Click sign-in - const signInButton = await screen.findByText(LANGUAGE.signIn) + const signInButton = await screen.findByText(Language.signIn) act(() => signInButton.click()) // Then // Finding error by test id because it comes from the backend - const errorMessage = await screen.findByText(LANGUAGE.authErrorMessage) + const errorMessage = await screen.findByText(Language.authErrorMessage) expect(errorMessage).toBeDefined() expect(history.location.pathname).toEqual("/login") }) From 17bc1137dded034d71ec7712c8533f1f0576bcc4 Mon Sep 17 00:00:00 2001 From: Presley Date: Mon, 28 Mar 2022 20:25:13 +0000 Subject: [PATCH 8/9] Arrow functions --- site/src/components/Form/index.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/site/src/components/Form/index.ts b/site/src/components/Form/index.ts index 8a1af1a36eb0a..3987a1cfb1579 100644 --- a/site/src/components/Form/index.ts +++ b/site/src/components/Form/index.ts @@ -17,7 +17,7 @@ interface FormHelpers { helperText?: string } -export function getFormHelpers(form: FormikContextType, name: string): FormHelpers { +export const getFormHelpers = (form: FormikContextType, name: string): FormHelpers => { // getIn is a util function from Formik that gets at any depth of nesting, and is necessary for the types to work const touched = getIn(form.touched, name) const errors = getIn(form.errors, name) @@ -29,9 +29,9 @@ export function getFormHelpers(form: FormikContextType, name: string): For } } -export function onChangeTrimmed(form: FormikContextType): (event: ChangeEvent) => void { - return (event: ChangeEvent): void => { +export const onChangeTrimmed = + (form: FormikContextType) => + (event: ChangeEvent): void => { event.target.value = event.target.value.trim() form.handleChange(event) } -} From c5619f8903fa7b5bd2d4e2cc252d39c2315298f7 Mon Sep 17 00:00:00 2001 From: Presley Date: Wed, 30 Mar 2022 00:26:13 +0000 Subject: [PATCH 9/9] Target text in e2e --- site/e2e/pom/SignInPage.ts | 6 +++--- site/src/components/SignIn/SignInForm.tsx | 6 +----- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/site/e2e/pom/SignInPage.ts b/site/e2e/pom/SignInPage.ts index 56a328d9a4d05..9f69fa4d0b3de 100644 --- a/site/e2e/pom/SignInPage.ts +++ b/site/e2e/pom/SignInPage.ts @@ -7,8 +7,8 @@ export class SignInPage extends BasePom { } async submitBuiltInAuthentication(email: string, password: string): Promise { - await this.page.fill("id=signin-form-inpt-email", email) - await this.page.fill("id=signin-form-inpt-password", password) - await this.page.click("id=signin-form-submit") + await this.page.fill("text=Email", email) + await this.page.fill("text=Password", password) + await this.page.click("text=Sign In") } } diff --git a/site/src/components/SignIn/SignInForm.tsx b/site/src/components/SignIn/SignInForm.tsx index 4506d20b5703e..2e36f2d6b2c6e 100644 --- a/site/src/components/SignIn/SignInForm.tsx +++ b/site/src/components/SignIn/SignInForm.tsx @@ -89,11 +89,7 @@ export const SignInForm: React.FC = ({ isLoading, authErrorMess type="password" variant="outlined" /> - {authErrorMessage && ( - - {Language.authErrorMessage} - - )} + {authErrorMessage && {Language.authErrorMessage}}
{isLoading ? "" : Language.signIn}