Skip to content

Commit 6294fb5

Browse files
feat(site): add change OIDC UI (#8182)
* Minor form spacing and verbiage improvements * Add basic flow using mocks * Change the UI flow * End * Add storybooks * Change label name * Fic previous tests * Add integration test * Fix issues * Fix storybook stories
1 parent c2aea70 commit 6294fb5

File tree

13 files changed

+565
-175
lines changed

13 files changed

+565
-175
lines changed

.vscode/settings.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,5 @@
211211
"go.testFlags": ["-short", "-coverpkg=./..."],
212212
// We often use a version of TypeScript that's ahead of the version shipped
213213
// with VS Code.
214-
"typescript.tsdk": "./site/node_modules/typescript/lib",
215-
"prettier.prettierPath": "./node_modules/prettier"
214+
"typescript.tsdk": "./site/node_modules/typescript/lib"
216215
}

coderd/userauth.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -840,7 +840,7 @@ func (api *API) userOIDC(rw http.ResponseWriter, r *http.Request) {
840840
// Convert the []interface{} we get to a []string.
841841
groupsInterface, ok := groupsRaw.([]interface{})
842842
if ok {
843-
logger.Debug(ctx, "groups returned in oidc claims",
843+
api.Logger.Debug(ctx, "groups returned in oidc claims",
844844
slog.F("len", len(groupsInterface)),
845845
slog.F("groups", groupsInterface),
846846
)
@@ -861,7 +861,7 @@ func (api *API) userOIDC(rw http.ResponseWriter, r *http.Request) {
861861
groups = append(groups, group)
862862
}
863863
} else {
864-
logger.Debug(ctx, "groups field was an unknown type",
864+
api.Logger.Debug(ctx, "groups field was an unknown type",
865865
slog.F("type", fmt.Sprintf("%T", groupsRaw)),
866866
)
867867
}

site/src/api/api.ts

+6-27
Original file line numberDiff line numberDiff line change
@@ -108,33 +108,12 @@ export const login = async (
108108
return response.data
109109
}
110110

111-
export const convertToOauth = async (
112-
email: string,
113-
password: string,
114-
to_login_type: string,
115-
): Promise<TypesGen.OauthConversionResponse | undefined> => {
116-
const payload = JSON.stringify({
117-
email,
118-
password,
119-
to_login_type,
120-
})
121-
122-
try {
123-
const response = await axios.post<TypesGen.OauthConversionResponse>(
124-
"/api/v2/users/convert-login",
125-
payload,
126-
{
127-
headers: { ...CONTENT_TYPE_JSON },
128-
},
129-
)
130-
return response.data
131-
} catch (error) {
132-
if (axios.isAxiosError(error) && error.response?.status === 401) {
133-
return undefined
134-
}
135-
136-
throw error
137-
}
111+
export const convertToOAUTH = async (request: TypesGen.ConvertLoginRequest) => {
112+
const response = await axios.post<TypesGen.OauthConversionResponse>(
113+
"/api/v2/users/convert-login",
114+
request,
115+
)
116+
return response.data
138117
}
139118

140119
export const logout = async (): Promise<void> => {

site/src/components/SettingsAccountForm/SettingsAccountForm.test.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ describe("AccountForm", () => {
3131
const el = await screen.findByLabelText("Username")
3232
expect(el).toBeEnabled()
3333
const btn = await screen.findByRole("button", {
34-
name: /Update settings/i,
34+
name: /Update account/i,
3535
})
3636
expect(btn).toBeEnabled()
3737
})
@@ -61,7 +61,7 @@ describe("AccountForm", () => {
6161
const el = await screen.findByLabelText("Username")
6262
expect(el).toBeDisabled()
6363
const btn = await screen.findByRole("button", {
64-
name: /Update settings/i,
64+
name: /Update account/i,
6565
})
6666
expect(btn).toBeDisabled()
6767
})

site/src/components/SettingsAccountForm/SettingsAccountForm.tsx

+6-6
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import {
88
onChangeTrimmed,
99
} from "../../utils/formUtils"
1010
import { LoadingButton } from "../LoadingButton/LoadingButton"
11-
import { Stack } from "../Stack/Stack"
1211
import { ErrorAlert } from "components/Alert/ErrorAlert"
12+
import { Form, FormFields } from "components/Form/Form"
1313

1414
export interface AccountFormValues {
1515
username: string
@@ -18,7 +18,7 @@ export interface AccountFormValues {
1818
export const Language = {
1919
usernameLabel: "Username",
2020
emailLabel: "Email",
21-
updateSettings: "Update settings",
21+
updateSettings: "Update account",
2222
}
2323

2424
const validationSchema = Yup.object({
@@ -59,8 +59,8 @@ export const AccountForm: FC<React.PropsWithChildren<AccountFormProps>> = ({
5959

6060
return (
6161
<>
62-
<form onSubmit={form.handleSubmit}>
63-
<Stack>
62+
<Form onSubmit={form.handleSubmit}>
63+
<FormFields>
6464
{Boolean(updateProfileError) && (
6565
<ErrorAlert error={updateProfileError} />
6666
)}
@@ -91,8 +91,8 @@ export const AccountForm: FC<React.PropsWithChildren<AccountFormProps>> = ({
9191
{isLoading ? "" : Language.updateSettings}
9292
</LoadingButton>
9393
</div>
94-
</Stack>
95-
</form>
94+
</FormFields>
95+
</Form>
9696
</>
9797
)
9898
}

site/src/components/SettingsLayout/Section.tsx

+9-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import { SectionAction } from "../SectionAction/SectionAction"
66
type SectionLayout = "fixed" | "fluid"
77

88
export interface SectionProps {
9+
// Useful for testing
10+
id?: string
911
title?: ReactNode | string
1012
description?: ReactNode
1113
toolbar?: ReactNode
@@ -20,6 +22,7 @@ type SectionFC = FC<PropsWithChildren<SectionProps>> & {
2022
}
2123

2224
export const Section: SectionFC = ({
25+
id,
2326
title,
2427
description,
2528
toolbar,
@@ -30,12 +33,16 @@ export const Section: SectionFC = ({
3033
}) => {
3134
const styles = useStyles({ layout })
3235
return (
33-
<section className={className}>
36+
<section className={className} id={id} data-testid={id}>
3437
<div className={styles.inner}>
3538
{(title || description) && (
3639
<div className={styles.header}>
3740
<div>
38-
{title && <Typography variant="h4">{title}</Typography>}
41+
{title && (
42+
<Typography variant="h4" sx={{ fontSize: 24 }}>
43+
{title}
44+
</Typography>
45+
)}
3946
{description && typeof description === "string" && (
4047
<Typography className={styles.description}>
4148
{description}

site/src/components/SettingsSecurityForm/SettingsSecurityForm.stories.tsx

+1-10
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,6 @@ const Template: Story<SecurityFormProps> = (args: SecurityFormProps) => (
1717
export const Example = Template.bind({})
1818
Example.args = {
1919
isLoading: false,
20-
initialValues: {
21-
old_password: "",
22-
password: "",
23-
confirm_password: "",
24-
},
25-
updateSecurityError: undefined,
2620
onSubmit: () => {
2721
return Promise.resolve()
2822
},
@@ -37,7 +31,7 @@ Loading.args = {
3731
export const WithError = Template.bind({})
3832
WithError.args = {
3933
...Example.args,
40-
updateSecurityError: mockApiError({
34+
error: mockApiError({
4135
message: "Old password is incorrect",
4236
validations: [
4337
{
@@ -46,7 +40,4 @@ WithError.args = {
4640
},
4741
],
4842
}),
49-
initialTouched: {
50-
old_password: true,
51-
},
5243
}

site/src/components/SettingsSecurityForm/SettingsSecurityForm.tsx

+26-22
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import TextField from "@mui/material/TextField"
2-
import { FormikContextType, FormikTouched, useFormik } from "formik"
2+
import { FormikContextType, useFormik } from "formik"
33
import { FC } from "react"
44
import * as Yup from "yup"
55
import { getFormHelpers } from "../../utils/formUtils"
66
import { LoadingButton } from "../LoadingButton/LoadingButton"
7-
import { Stack } from "../Stack/Stack"
87
import { ErrorAlert } from "components/Alert/ErrorAlert"
8+
import { Form, FormFields } from "components/Form/Form"
9+
import { Alert } from "components/Alert/Alert"
910

1011
interface SecurityFormValues {
1112
old_password: string
@@ -41,40 +42,43 @@ const validationSchema = Yup.object({
4142
})
4243

4344
export interface SecurityFormProps {
45+
disabled: boolean
4446
isLoading: boolean
45-
initialValues: SecurityFormValues
4647
onSubmit: (values: SecurityFormValues) => void
47-
updateSecurityError?: Error | unknown
48-
// initialTouched is only used for testing the error state of the form.
49-
initialTouched?: FormikTouched<SecurityFormValues>
48+
error?: unknown
5049
}
5150

5251
export const SecurityForm: FC<SecurityFormProps> = ({
52+
disabled,
5353
isLoading,
5454
onSubmit,
55-
initialValues,
56-
updateSecurityError,
57-
initialTouched,
55+
error,
5856
}) => {
5957
const form: FormikContextType<SecurityFormValues> =
6058
useFormik<SecurityFormValues>({
61-
initialValues,
59+
initialValues: {
60+
old_password: "",
61+
password: "",
62+
confirm_password: "",
63+
},
6264
validationSchema,
6365
onSubmit,
64-
initialTouched,
6566
})
66-
const getFieldHelpers = getFormHelpers<SecurityFormValues>(
67-
form,
68-
updateSecurityError,
69-
)
67+
const getFieldHelpers = getFormHelpers<SecurityFormValues>(form, error)
68+
69+
if (disabled) {
70+
return (
71+
<Alert severity="info">
72+
Password changes are only allowed for password based accounts.
73+
</Alert>
74+
)
75+
}
7076

7177
return (
7278
<>
73-
<form onSubmit={form.handleSubmit}>
74-
<Stack>
75-
{Boolean(updateSecurityError) && (
76-
<ErrorAlert error={updateSecurityError} />
77-
)}
79+
<Form onSubmit={form.handleSubmit}>
80+
<FormFields>
81+
{Boolean(error) && <ErrorAlert error={error} />}
7882
<TextField
7983
{...getFieldHelpers("old_password")}
8084
autoComplete="old_password"
@@ -106,8 +110,8 @@ export const SecurityForm: FC<SecurityFormProps> = ({
106110
{isLoading ? "" : Language.updatePassword}
107111
</LoadingButton>
108112
</div>
109-
</Stack>
110-
</form>
113+
</FormFields>
114+
</Form>
111115
</>
112116
)
113117
}

0 commit comments

Comments
 (0)