feat: Pre-fill param inputs with query string values (#5758)

This commit is contained in:
Bruno Quaresma
2023-01-17 19:56:29 -03:00
committed by GitHub
parent 28b2bbd095
commit a13614e93d
6 changed files with 97 additions and 50 deletions

View File

@ -35,11 +35,15 @@ export interface ParameterInputProps {
disabled?: boolean
schema: ParameterSchema
onChange: (value: string) => void
defaultValue?: string
}
export const ParameterInput: FC<
React.PropsWithChildren<ParameterInputProps>
> = ({ disabled, onChange, schema }) => {
export const ParameterInput: FC<ParameterInputProps> = ({
disabled,
onChange,
schema,
defaultValue,
}) => {
const styles = useStyles()
return (
@ -50,21 +54,25 @@ export const ParameterInput: FC<
disabled={disabled}
onChange={onChange}
schema={schema}
defaultValue={defaultValue}
/>
</div>
</Stack>
)
}
const ParameterField: React.FC<
React.PropsWithChildren<ParameterInputProps>
> = ({ disabled, onChange, schema }) => {
const ParameterField: React.FC<ParameterInputProps> = ({
disabled,
onChange,
schema,
defaultValue,
}) => {
if (schema.validation_contains && schema.validation_contains.length > 0) {
return (
<TextField
id={schema.name}
size="small"
defaultValue={schema.default_source_value}
defaultValue={defaultValue ?? schema.default_source_value}
placeholder={schema.default_source_value}
disabled={disabled}
onChange={(event) => {
@ -116,6 +124,7 @@ const ParameterField: React.FC<
size="small"
disabled={disabled}
placeholder={schema.default_source_value}
defaultValue={defaultValue ?? schema.default_source_value}
onChange={(event) => {
onChange(event.target.value)
}}

View File

@ -3,6 +3,7 @@ import userEvent from "@testing-library/user-event"
import * as API from "api/api"
import i18next from "i18next"
import {
mockParameterSchema,
MockTemplate,
MockUser,
MockWorkspace,
@ -62,4 +63,23 @@ describe("CreateWorkspacePage", () => {
),
)
})
it("uses default param values passed from the URL", async () => {
const param = "dotfile_uri"
const paramValue = "localhost:3000"
jest.spyOn(API, "getTemplateVersionSchema").mockResolvedValueOnce([
mockParameterSchema({
name: param,
default_source_value: "",
}),
])
renderWithAuth(<CreateWorkspacePage />, {
route:
"/templates/" +
MockTemplate.name +
`/workspace?param.${param}=${paramValue}`,
path: "/templates/:template/workspace",
})
await screen.findByDisplayValue(paramValue)
})
})

View File

@ -1,29 +1,26 @@
import { useActor, useMachine } from "@xstate/react"
import { useMachine } from "@xstate/react"
import { useMe } from "hooks/useMe"
import { useOrganizationId } from "hooks/useOrganizationId"
import { FC, useContext } from "react"
import { FC } from "react"
import { Helmet } from "react-helmet-async"
import { useNavigate, useParams } from "react-router-dom"
import { useNavigate, useParams, useSearchParams } from "react-router-dom"
import { pageTitle } from "util/page"
import { createWorkspaceMachine } from "xServices/createWorkspace/createWorkspaceXService"
import { XServiceContext } from "xServices/StateContext"
import {
CreateWorkspaceErrors,
CreateWorkspacePageView,
} from "./CreateWorkspacePageView"
const CreateWorkspacePage: FC = () => {
const xServices = useContext(XServiceContext)
const organizationId = useOrganizationId()
const { template } = useParams()
const templateName = template ? template : ""
const { template: templateName } = useParams() as { template: string }
const navigate = useNavigate()
const [authState] = useActor(xServices.authXService)
const { me } = authState.context
const me = useMe()
const [createWorkspaceState, send] = useMachine(createWorkspaceMachine, {
context: {
organizationId,
templateName,
owner: me ?? null,
owner: me,
},
actions: {
onCreateWorkspace: (_, event) => {
@ -31,7 +28,6 @@ const CreateWorkspacePage: FC = () => {
},
},
})
const {
templates,
templateSchema,
@ -42,6 +38,8 @@ const CreateWorkspacePage: FC = () => {
permissions,
owner,
} = createWorkspaceState.context
const [searchParams] = useSearchParams()
const defaultParameterValues = getDefaultParameterValues(searchParams)
return (
<>
@ -49,6 +47,7 @@ const CreateWorkspacePage: FC = () => {
<title>{pageTitle("Create Workspace")}</title>
</Helmet>
<CreateWorkspacePageView
defaultParameterValues={defaultParameterValues}
loadingTemplates={createWorkspaceState.matches("gettingTemplates")}
loadingTemplateSchema={createWorkspaceState.matches(
"gettingTemplateSchema",
@ -89,4 +88,18 @@ const CreateWorkspacePage: FC = () => {
)
}
const getDefaultParameterValues = (
urlSearchParams: URLSearchParams,
): Record<string, string> => {
const paramValues: Record<string, string> = {}
Array.from(urlSearchParams.keys())
.filter((key) => key.startsWith("param."))
.forEach((key) => {
const paramName = key.replace("param.", "")
const paramValue = urlSearchParams.get(key)
paramValues[paramName] = paramValue ?? ""
})
return paramValues
}
export default CreateWorkspacePage

View File

@ -1,37 +1,15 @@
import { ComponentMeta, Story } from "@storybook/react"
import { ParameterSchema } from "../../api/typesGenerated"
import { makeMockApiError, MockTemplate } from "../../testHelpers/entities"
import {
makeMockApiError,
mockParameterSchema,
MockTemplate,
} from "../../testHelpers/entities"
import {
CreateWorkspaceErrors,
CreateWorkspacePageView,
CreateWorkspacePageViewProps,
} from "./CreateWorkspacePageView"
const createParameterSchema = (
partial: Partial<ParameterSchema>,
): ParameterSchema => {
return {
id: "000000",
job_id: "000000",
allow_override_destination: false,
allow_override_source: true,
created_at: "",
default_destination_scheme: "none",
default_refresh: "",
default_source_scheme: "data",
default_source_value: "default-value",
name: "parameter name",
description: "Some description!",
redisplay_value: false,
validation_condition: "",
validation_contains: [],
validation_error: "",
validation_type_system: "",
validation_value_type: "",
...partial,
}
}
export default {
title: "pages/CreateWorkspacePageView",
component: CreateWorkspacePageView,
@ -54,7 +32,7 @@ Parameters.args = {
templates: [MockTemplate],
selectedTemplate: MockTemplate,
templateSchema: [
createParameterSchema({
mockParameterSchema({
name: "region",
default_source_value: "🏈 US Central",
description: "Where would you like your workspace to live?",
@ -65,19 +43,19 @@ Parameters.args = {
"🦘 Australia South",
],
}),
createParameterSchema({
mockParameterSchema({
name: "instance_size",
default_source_value: "Big",
description: "How large should you instance be?",
validation_contains: ["Small", "Medium", "Big"],
}),
createParameterSchema({
mockParameterSchema({
name: "instance_size",
default_source_value: "Big",
description: "How large should your instance be?",
validation_contains: ["Small", "Medium", "Big"],
}),
createParameterSchema({
mockParameterSchema({
name: "disable_docker",
description: "Disable Docker?",
validation_value_type: "bool",

View File

@ -39,6 +39,7 @@ export interface CreateWorkspacePageViewProps {
onSubmit: (req: TypesGen.CreateWorkspaceRequest) => void
// initialTouched is only used for testing the error state of the form.
initialTouched?: FormikTouched<TypesGen.CreateWorkspaceRequest>
defaultParameterValues?: Record<string, string>
}
const { t } = i18n
@ -55,7 +56,7 @@ export const CreateWorkspacePageView: FC<
const formFooterStyles = useFormFooterStyles()
const [parameterValues, setParameterValues] = useState<
Record<string, string>
>({})
>(props.defaultParameterValues ?? {})
const form: FormikContextType<TypesGen.CreateWorkspaceRequest> =
useFormik<TypesGen.CreateWorkspaceRequest>({
@ -234,6 +235,7 @@ export const CreateWorkspacePageView: FC<
<ParameterInput
disabled={form.isSubmitting}
key={schema.id}
defaultValue={parameterValues[schema.name]}
onChange={(value) => {
setParameterValues({
...parameterValues,

View File

@ -1130,3 +1130,28 @@ export const MockAppearance: TypesGen.AppearanceConfig = {
enabled: false,
},
}
export const mockParameterSchema = (
partial: Partial<TypesGen.ParameterSchema>,
): TypesGen.ParameterSchema => {
return {
id: "000000",
job_id: "000000",
allow_override_destination: false,
allow_override_source: true,
created_at: "",
default_destination_scheme: "none",
default_refresh: "",
default_source_scheme: "data",
default_source_value: "default-value",
name: "parameter name",
description: "Some description!",
redisplay_value: false,
validation_condition: "",
validation_contains: [],
validation_error: "",
validation_type_system: "",
validation_value_type: "",
...partial,
}
}