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

Skip to content

Commit a13614e

Browse files
feat: Pre-fill param inputs with query string values (#5758)
1 parent 28b2bbd commit a13614e

File tree

6 files changed

+97
-50
lines changed

6 files changed

+97
-50
lines changed

site/src/components/ParameterInput/ParameterInput.tsx

+16-7
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,15 @@ export interface ParameterInputProps {
3535
disabled?: boolean
3636
schema: ParameterSchema
3737
onChange: (value: string) => void
38+
defaultValue?: string
3839
}
3940

40-
export const ParameterInput: FC<
41-
React.PropsWithChildren<ParameterInputProps>
42-
> = ({ disabled, onChange, schema }) => {
41+
export const ParameterInput: FC<ParameterInputProps> = ({
42+
disabled,
43+
onChange,
44+
schema,
45+
defaultValue,
46+
}) => {
4347
const styles = useStyles()
4448

4549
return (
@@ -50,21 +54,25 @@ export const ParameterInput: FC<
5054
disabled={disabled}
5155
onChange={onChange}
5256
schema={schema}
57+
defaultValue={defaultValue}
5358
/>
5459
</div>
5560
</Stack>
5661
)
5762
}
5863

59-
const ParameterField: React.FC<
60-
React.PropsWithChildren<ParameterInputProps>
61-
> = ({ disabled, onChange, schema }) => {
64+
const ParameterField: React.FC<ParameterInputProps> = ({
65+
disabled,
66+
onChange,
67+
schema,
68+
defaultValue,
69+
}) => {
6270
if (schema.validation_contains && schema.validation_contains.length > 0) {
6371
return (
6472
<TextField
6573
id={schema.name}
6674
size="small"
67-
defaultValue={schema.default_source_value}
75+
defaultValue={defaultValue ?? schema.default_source_value}
6876
placeholder={schema.default_source_value}
6977
disabled={disabled}
7078
onChange={(event) => {
@@ -116,6 +124,7 @@ const ParameterField: React.FC<
116124
size="small"
117125
disabled={disabled}
118126
placeholder={schema.default_source_value}
127+
defaultValue={defaultValue ?? schema.default_source_value}
119128
onChange={(event) => {
120129
onChange(event.target.value)
121130
}}

site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx

+20
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import userEvent from "@testing-library/user-event"
33
import * as API from "api/api"
44
import i18next from "i18next"
55
import {
6+
mockParameterSchema,
67
MockTemplate,
78
MockUser,
89
MockWorkspace,
@@ -62,4 +63,23 @@ describe("CreateWorkspacePage", () => {
6263
),
6364
)
6465
})
66+
67+
it("uses default param values passed from the URL", async () => {
68+
const param = "dotfile_uri"
69+
const paramValue = "localhost:3000"
70+
jest.spyOn(API, "getTemplateVersionSchema").mockResolvedValueOnce([
71+
mockParameterSchema({
72+
name: param,
73+
default_source_value: "",
74+
}),
75+
])
76+
renderWithAuth(<CreateWorkspacePage />, {
77+
route:
78+
"/templates/" +
79+
MockTemplate.name +
80+
`/workspace?param.${param}=${paramValue}`,
81+
path: "/templates/:template/workspace",
82+
})
83+
await screen.findByDisplayValue(paramValue)
84+
})
6585
})

site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx

+24-11
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,33 @@
1-
import { useActor, useMachine } from "@xstate/react"
1+
import { useMachine } from "@xstate/react"
2+
import { useMe } from "hooks/useMe"
23
import { useOrganizationId } from "hooks/useOrganizationId"
3-
import { FC, useContext } from "react"
4+
import { FC } from "react"
45
import { Helmet } from "react-helmet-async"
5-
import { useNavigate, useParams } from "react-router-dom"
6+
import { useNavigate, useParams, useSearchParams } from "react-router-dom"
67
import { pageTitle } from "util/page"
78
import { createWorkspaceMachine } from "xServices/createWorkspace/createWorkspaceXService"
8-
import { XServiceContext } from "xServices/StateContext"
99
import {
1010
CreateWorkspaceErrors,
1111
CreateWorkspacePageView,
1212
} from "./CreateWorkspacePageView"
1313

1414
const CreateWorkspacePage: FC = () => {
15-
const xServices = useContext(XServiceContext)
1615
const organizationId = useOrganizationId()
17-
const { template } = useParams()
18-
const templateName = template ? template : ""
16+
const { template: templateName } = useParams() as { template: string }
1917
const navigate = useNavigate()
20-
const [authState] = useActor(xServices.authXService)
21-
const { me } = authState.context
18+
const me = useMe()
2219
const [createWorkspaceState, send] = useMachine(createWorkspaceMachine, {
2320
context: {
2421
organizationId,
2522
templateName,
26-
owner: me ?? null,
23+
owner: me,
2724
},
2825
actions: {
2926
onCreateWorkspace: (_, event) => {
3027
navigate(`/@${event.data.owner_name}/${event.data.name}`)
3128
},
3229
},
3330
})
34-
3531
const {
3632
templates,
3733
templateSchema,
@@ -42,13 +38,16 @@ const CreateWorkspacePage: FC = () => {
4238
permissions,
4339
owner,
4440
} = createWorkspaceState.context
41+
const [searchParams] = useSearchParams()
42+
const defaultParameterValues = getDefaultParameterValues(searchParams)
4543

4644
return (
4745
<>
4846
<Helmet>
4947
<title>{pageTitle("Create Workspace")}</title>
5048
</Helmet>
5149
<CreateWorkspacePageView
50+
defaultParameterValues={defaultParameterValues}
5251
loadingTemplates={createWorkspaceState.matches("gettingTemplates")}
5352
loadingTemplateSchema={createWorkspaceState.matches(
5453
"gettingTemplateSchema",
@@ -89,4 +88,18 @@ const CreateWorkspacePage: FC = () => {
8988
)
9089
}
9190

91+
const getDefaultParameterValues = (
92+
urlSearchParams: URLSearchParams,
93+
): Record<string, string> => {
94+
const paramValues: Record<string, string> = {}
95+
Array.from(urlSearchParams.keys())
96+
.filter((key) => key.startsWith("param."))
97+
.forEach((key) => {
98+
const paramName = key.replace("param.", "")
99+
const paramValue = urlSearchParams.get(key)
100+
paramValues[paramName] = paramValue ?? ""
101+
})
102+
return paramValues
103+
}
104+
92105
export default CreateWorkspacePage

site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx

+9-31
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,15 @@
11
import { ComponentMeta, Story } from "@storybook/react"
2-
import { ParameterSchema } from "../../api/typesGenerated"
3-
import { makeMockApiError, MockTemplate } from "../../testHelpers/entities"
2+
import {
3+
makeMockApiError,
4+
mockParameterSchema,
5+
MockTemplate,
6+
} from "../../testHelpers/entities"
47
import {
58
CreateWorkspaceErrors,
69
CreateWorkspacePageView,
710
CreateWorkspacePageViewProps,
811
} from "./CreateWorkspacePageView"
912

10-
const createParameterSchema = (
11-
partial: Partial<ParameterSchema>,
12-
): ParameterSchema => {
13-
return {
14-
id: "000000",
15-
job_id: "000000",
16-
allow_override_destination: false,
17-
allow_override_source: true,
18-
created_at: "",
19-
default_destination_scheme: "none",
20-
default_refresh: "",
21-
default_source_scheme: "data",
22-
default_source_value: "default-value",
23-
name: "parameter name",
24-
description: "Some description!",
25-
redisplay_value: false,
26-
validation_condition: "",
27-
validation_contains: [],
28-
validation_error: "",
29-
validation_type_system: "",
30-
validation_value_type: "",
31-
...partial,
32-
}
33-
}
34-
3513
export default {
3614
title: "pages/CreateWorkspacePageView",
3715
component: CreateWorkspacePageView,
@@ -54,7 +32,7 @@ Parameters.args = {
5432
templates: [MockTemplate],
5533
selectedTemplate: MockTemplate,
5634
templateSchema: [
57-
createParameterSchema({
35+
mockParameterSchema({
5836
name: "region",
5937
default_source_value: "🏈 US Central",
6038
description: "Where would you like your workspace to live?",
@@ -65,19 +43,19 @@ Parameters.args = {
6543
"🦘 Australia South",
6644
],
6745
}),
68-
createParameterSchema({
46+
mockParameterSchema({
6947
name: "instance_size",
7048
default_source_value: "Big",
7149
description: "How large should you instance be?",
7250
validation_contains: ["Small", "Medium", "Big"],
7351
}),
74-
createParameterSchema({
52+
mockParameterSchema({
7553
name: "instance_size",
7654
default_source_value: "Big",
7755
description: "How large should your instance be?",
7856
validation_contains: ["Small", "Medium", "Big"],
7957
}),
80-
createParameterSchema({
58+
mockParameterSchema({
8159
name: "disable_docker",
8260
description: "Disable Docker?",
8361
validation_value_type: "bool",

site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export interface CreateWorkspacePageViewProps {
3939
onSubmit: (req: TypesGen.CreateWorkspaceRequest) => void
4040
// initialTouched is only used for testing the error state of the form.
4141
initialTouched?: FormikTouched<TypesGen.CreateWorkspaceRequest>
42+
defaultParameterValues?: Record<string, string>
4243
}
4344

4445
const { t } = i18n
@@ -55,7 +56,7 @@ export const CreateWorkspacePageView: FC<
5556
const formFooterStyles = useFormFooterStyles()
5657
const [parameterValues, setParameterValues] = useState<
5758
Record<string, string>
58-
>({})
59+
>(props.defaultParameterValues ?? {})
5960

6061
const form: FormikContextType<TypesGen.CreateWorkspaceRequest> =
6162
useFormik<TypesGen.CreateWorkspaceRequest>({
@@ -234,6 +235,7 @@ export const CreateWorkspacePageView: FC<
234235
<ParameterInput
235236
disabled={form.isSubmitting}
236237
key={schema.id}
238+
defaultValue={parameterValues[schema.name]}
237239
onChange={(value) => {
238240
setParameterValues({
239241
...parameterValues,

site/src/testHelpers/entities.ts

+25
Original file line numberDiff line numberDiff line change
@@ -1130,3 +1130,28 @@ export const MockAppearance: TypesGen.AppearanceConfig = {
11301130
enabled: false,
11311131
},
11321132
}
1133+
1134+
export const mockParameterSchema = (
1135+
partial: Partial<TypesGen.ParameterSchema>,
1136+
): TypesGen.ParameterSchema => {
1137+
return {
1138+
id: "000000",
1139+
job_id: "000000",
1140+
allow_override_destination: false,
1141+
allow_override_source: true,
1142+
created_at: "",
1143+
default_destination_scheme: "none",
1144+
default_refresh: "",
1145+
default_source_scheme: "data",
1146+
default_source_value: "default-value",
1147+
name: "parameter name",
1148+
description: "Some description!",
1149+
redisplay_value: false,
1150+
validation_condition: "",
1151+
validation_contains: [],
1152+
validation_error: "",
1153+
validation_type_system: "",
1154+
validation_value_type: "",
1155+
...partial,
1156+
}
1157+
}

0 commit comments

Comments
 (0)