Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 0128ca6

Browse files
authored
fix: manage backend authXService errors (#3190)
1 parent b19cf70 commit 0128ca6

File tree

15 files changed

+230
-118
lines changed

15 files changed

+230
-118
lines changed

site/src/components/ErrorSummary/ErrorSummary.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { ApiError, getErrorDetail, getErrorMessage } from "api/errors"
99
import { Stack } from "components/Stack/Stack"
1010
import { FC, useState } from "react"
1111

12-
const Language = {
12+
export const Language = {
1313
retryMessage: "Retry",
1414
unknownErrorMessage: "An unknown error has occurred",
1515
moreDetails: "More",
@@ -91,7 +91,6 @@ interface StyleProps {
9191
const useStyles = makeStyles<Theme, StyleProps>((theme) => ({
9292
root: {
9393
background: darken(theme.palette.error.main, 0.6),
94-
margin: `${theme.spacing(2)}px`,
9594
padding: `${theme.spacing(2)}px`,
9695
borderRadius: theme.shape.borderRadius,
9796
gap: 0,
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { Story } from "@storybook/react"
2+
import { AccountForm, AccountFormProps } from "./SettingsAccountForm"
3+
4+
export default {
5+
title: "components/SettingsAccountForm",
6+
component: AccountForm,
7+
argTypes: {
8+
onSubmit: { action: "Submit" },
9+
},
10+
}
11+
12+
const Template: Story<AccountFormProps> = (args: AccountFormProps) => <AccountForm {...args} />
13+
14+
export const Example = Template.bind({})
15+
Example.args = {
16+
17+
isLoading: false,
18+
initialValues: {
19+
username: "test-user",
20+
},
21+
updateProfileError: undefined,
22+
onSubmit: () => {
23+
return Promise.resolve()
24+
},
25+
}
26+
27+
export const Loading = Template.bind({})
28+
Loading.args = {
29+
...Example.args,
30+
isLoading: true,
31+
}
32+
33+
export const WithError = Template.bind({})
34+
WithError.args = {
35+
...Example.args,
36+
updateProfileError: {
37+
response: {
38+
data: {
39+
message: "Username is invalid",
40+
validations: [
41+
{
42+
field: "username",
43+
detail: "Username is too long.",
44+
},
45+
],
46+
},
47+
},
48+
isAxiosError: true,
49+
},
50+
initialTouched: {
51+
username: true,
52+
},
53+
}

site/src/components/SettingsAccountForm/SettingsAccountForm.tsx

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import FormHelperText from "@material-ui/core/FormHelperText"
21
import TextField from "@material-ui/core/TextField"
3-
import { FormikContextType, FormikErrors, useFormik } from "formik"
2+
import { ErrorSummary } from "components/ErrorSummary/ErrorSummary"
3+
import { FormikContextType, FormikTouched, useFormik } from "formik"
44
import { FC } from "react"
55
import * as Yup from "yup"
6-
import { getFormHelpers, nameValidator, onChangeTrimmed } from "../../util/formUtils"
6+
import { getFormHelpersWithError, nameValidator, onChangeTrimmed } from "../../util/formUtils"
77
import { LoadingButton } from "../LoadingButton/LoadingButton"
88
import { Stack } from "../Stack/Stack"
99

@@ -21,36 +21,37 @@ const validationSchema = Yup.object({
2121
username: nameValidator(Language.usernameLabel),
2222
})
2323

24-
export type AccountFormErrors = FormikErrors<AccountFormValues>
25-
2624
export interface AccountFormProps {
2725
email: string
2826
isLoading: boolean
2927
initialValues: AccountFormValues
3028
onSubmit: (values: AccountFormValues) => void
31-
formErrors?: AccountFormErrors
32-
error?: string
29+
updateProfileError?: Error | unknown
30+
// initialTouched is only used for testing the error state of the form.
31+
initialTouched?: FormikTouched<AccountFormValues>
3332
}
3433

3534
export const AccountForm: FC<AccountFormProps> = ({
3635
email,
3736
isLoading,
3837
onSubmit,
3938
initialValues,
40-
formErrors = {},
41-
error,
39+
updateProfileError,
40+
initialTouched,
4241
}) => {
4342
const form: FormikContextType<AccountFormValues> = useFormik<AccountFormValues>({
4443
initialValues,
4544
validationSchema,
4645
onSubmit,
46+
initialTouched,
4747
})
48-
const getFieldHelpers = getFormHelpers<AccountFormValues>(form, formErrors)
48+
const getFieldHelpers = getFormHelpersWithError<AccountFormValues>(form, updateProfileError)
4949

5050
return (
5151
<>
5252
<form onSubmit={form.handleSubmit}>
5353
<Stack>
54+
{updateProfileError && <ErrorSummary error={updateProfileError} />}
5455
<TextField
5556
disabled
5657
fullWidth
@@ -67,8 +68,6 @@ export const AccountForm: FC<AccountFormProps> = ({
6768
variant="outlined"
6869
/>
6970

70-
{error && <FormHelperText error>{error}</FormHelperText>}
71-
7271
<div>
7372
<LoadingButton loading={isLoading} type="submit" variant="contained">
7473
{isLoading ? "" : Language.updateSettings}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { Story } from "@storybook/react"
2+
import { SecurityForm, SecurityFormProps } from "./SettingsSecurityForm"
3+
4+
export default {
5+
title: "components/SettingsSecurityForm",
6+
component: SecurityForm,
7+
argTypes: {
8+
onSubmit: { action: "Submit" },
9+
},
10+
}
11+
12+
const Template: Story<SecurityFormProps> = (args: SecurityFormProps) => <SecurityForm {...args} />
13+
14+
export const Example = Template.bind({})
15+
Example.args = {
16+
isLoading: false,
17+
initialValues: {
18+
old_password: "",
19+
password: "",
20+
confirm_password: "",
21+
},
22+
updateSecurityError: undefined,
23+
onSubmit: () => {
24+
return Promise.resolve()
25+
},
26+
}
27+
28+
export const Loading = Template.bind({})
29+
Loading.args = {
30+
...Example.args,
31+
isLoading: true,
32+
}
33+
34+
export const WithError = Template.bind({})
35+
WithError.args = {
36+
...Example.args,
37+
updateSecurityError: {
38+
response: {
39+
data: {
40+
message: "Old password is incorrect",
41+
validations: [
42+
{
43+
field: "old_password",
44+
detail: "Old password is incorrect.",
45+
},
46+
],
47+
},
48+
},
49+
isAxiosError: true,
50+
},
51+
initialTouched: {
52+
old_password: true,
53+
},
54+
}

site/src/components/SettingsSecurityForm/SettingsSecurityForm.tsx

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import FormHelperText from "@material-ui/core/FormHelperText"
21
import TextField from "@material-ui/core/TextField"
3-
import { FormikContextType, FormikErrors, useFormik } from "formik"
2+
import { ErrorSummary } from "components/ErrorSummary/ErrorSummary"
3+
import { FormikContextType, FormikTouched, useFormik } from "formik"
44
import React from "react"
55
import * as Yup from "yup"
6-
import { getFormHelpers, onChangeTrimmed } from "../../util/formUtils"
6+
import { getFormHelpersWithError, onChangeTrimmed } from "../../util/formUtils"
77
import { LoadingButton } from "../LoadingButton/LoadingButton"
88
import { Stack } from "../Stack/Stack"
99

@@ -40,33 +40,35 @@ const validationSchema = Yup.object({
4040
}),
4141
})
4242

43-
export type SecurityFormErrors = FormikErrors<SecurityFormValues>
4443
export interface SecurityFormProps {
4544
isLoading: boolean
4645
initialValues: SecurityFormValues
4746
onSubmit: (values: SecurityFormValues) => void
48-
formErrors?: SecurityFormErrors
49-
error?: string
47+
updateSecurityError?: Error | unknown
48+
// initialTouched is only used for testing the error state of the form.
49+
initialTouched?: FormikTouched<SecurityFormValues>
5050
}
5151

5252
export const SecurityForm: React.FC<SecurityFormProps> = ({
5353
isLoading,
5454
onSubmit,
5555
initialValues,
56-
formErrors = {},
57-
error,
56+
updateSecurityError,
57+
initialTouched,
5858
}) => {
5959
const form: FormikContextType<SecurityFormValues> = useFormik<SecurityFormValues>({
6060
initialValues,
6161
validationSchema,
6262
onSubmit,
63+
initialTouched,
6364
})
64-
const getFieldHelpers = getFormHelpers<SecurityFormValues>(form, formErrors)
65+
const getFieldHelpers = getFormHelpersWithError<SecurityFormValues>(form, updateSecurityError)
6566

6667
return (
6768
<>
6869
<form onSubmit={form.handleSubmit}>
6970
<Stack>
71+
{updateSecurityError && <ErrorSummary error={updateSecurityError} />}
7072
<TextField
7173
{...getFieldHelpers("old_password")}
7274
onChange={onChangeTrimmed(form)}
@@ -95,8 +97,6 @@ export const SecurityForm: React.FC<SecurityFormProps> = ({
9597
type="password"
9698
/>
9799

98-
{error && <FormHelperText error>{error}</FormHelperText>}
99-
100100
<div>
101101
<LoadingButton loading={isLoading} type="submit" variant="contained">
102102
{isLoading ? "" : Language.updatePassword}

site/src/components/SignInForm/SignInForm.stories.tsx

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ export default {
66
component: SignInForm,
77
argTypes: {
88
isLoading: "boolean",
9-
authErrorMessage: "string",
109
onSubmit: { action: "Submit" },
1110
},
1211
}
@@ -16,7 +15,7 @@ const Template: Story<SignInFormProps> = (args: SignInFormProps) => <SignInForm
1615
export const SignedOut = Template.bind({})
1716
SignedOut.args = {
1817
isLoading: false,
19-
authErrorMessage: undefined,
18+
authError: undefined,
2019
onSubmit: () => {
2120
return Promise.resolve()
2221
},
@@ -33,12 +32,31 @@ Loading.args = {
3332
}
3433

3534
export const WithLoginError = Template.bind({})
36-
WithLoginError.args = { ...SignedOut.args, authErrorMessage: "Email or password was invalid" }
35+
WithLoginError.args = {
36+
...SignedOut.args,
37+
authError: {
38+
response: {
39+
data: {
40+
message: "Email or password was invalid",
41+
validations: [
42+
{
43+
field: "password",
44+
detail: "Password is invalid.",
45+
},
46+
],
47+
},
48+
},
49+
isAxiosError: true,
50+
},
51+
initialTouched: {
52+
password: true,
53+
},
54+
}
3755

3856
export const WithAuthMethodsError = Template.bind({})
3957
WithAuthMethodsError.args = {
4058
...SignedOut.args,
41-
methodsErrorMessage: "Failed to fetch auth methods",
59+
methodsError: new Error("Failed to fetch auth methods"),
4260
}
4361

4462
export const WithGithub = Template.bind({})

0 commit comments

Comments
 (0)