From d0af0bbe6b10fde3059d21656041fdab14a4bfbf Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Wed, 28 Feb 2024 00:20:52 +0000 Subject: [PATCH 1/6] refactor: show parameter suggestions from user history below field --- .../RichParameterInput/RichParameterInput.tsx | 65 +++++++++++++++---- .../TemplateParameters/TemplateParameters.tsx | 55 ---------------- .../CreateWorkspacePageView.tsx | 24 +++---- .../BuildParametersPopover.tsx | 5 +- .../WorkspaceParametersForm.tsx | 1 - site/src/utils/richParameters.test.ts | 6 +- site/src/utils/richParameters.ts | 27 ++------ 7 files changed, 68 insertions(+), 115 deletions(-) delete mode 100644 site/src/modules/templates/TemplateParameters/TemplateParameters.tsx diff --git a/site/src/components/RichParameterInput/RichParameterInput.tsx b/site/src/components/RichParameterInput/RichParameterInput.tsx index b9ad1fba4e2f0..751281e42813d 100644 --- a/site/src/components/RichParameterInput/RichParameterInput.tsx +++ b/site/src/components/RichParameterInput/RichParameterInput.tsx @@ -3,14 +3,16 @@ import Radio from "@mui/material/Radio"; import RadioGroup from "@mui/material/RadioGroup"; import TextField, { TextFieldProps } from "@mui/material/TextField"; import Tooltip from "@mui/material/Tooltip"; +import Button from "@mui/material/Button"; +import FormHelperText from "@mui/material/FormHelperText"; import { type Interpolation, type Theme } from "@emotion/react"; -import { type FC } from "react"; +import { type FC, type ReactNode, useState } from "react"; import { TemplateVersionParameter } from "api/typesGenerated"; import { MemoizedMarkdown } from "components/Markdown/Markdown"; import { Stack } from "components/Stack/Stack"; import { MultiTextField } from "./MultiTextField"; import { ExternalImage } from "components/ExternalImage/ExternalImage"; -import { AutofillSource } from "utils/richParameters"; +import { AutofillBuildParameter, AutofillSource } from "utils/richParameters"; import { Pill } from "components/Pill/Pill"; import ErrorOutline from "@mui/icons-material/ErrorOutline"; @@ -103,6 +105,15 @@ const styles = { width: 16, }, }, + suggestion: (theme) => ({ + color: theme.roles.info.fill.solid, + marginLeft: "-4px", + padding: "4px 6px", + lineHeight: "inherit", + fontSize: "inherit", + height: "unset", + minWidth: "unset", + }), } satisfies Record>; export interface ParameterLabelProps { @@ -169,17 +180,26 @@ export type RichParameterInputProps = Omit< "size" | "onChange" > & { parameter: TemplateVersionParameter; - autofillSource?: AutofillSource; + parameterAutofill?: AutofillBuildParameter; onChange: (value: string) => void; size?: Size; }; +const autofillDescription: Partial> = { + url: " from the URL.", +}; + export const RichParameterInput: FC = ({ - parameter, size = "medium", - autofillSource, + parameter, + parameterAutofill, + onChange, ...fieldProps }) => { + const autofillSource = parameterAutofill?.source; + const autofillValue = parameterAutofill?.value; + const [hideSuggestion, setHideSuggestion] = useState(false); + return ( = ({ >
- - {autofillSource && autofillSource !== "active_build" && ( + + {!parameter.ephemeral && + autofillSource === "user_history" && + autofillValue && + !hideSuggestion && ( + + {" "} + was recently used for this parameter. + + )} + {autofillSource && autofillDescription[autofillSource] && (
- 🪄 Autofilled:{" "} - { - { - ["url"]: "value supplied by URL.", - ["user_history"]: "recently used value.", - }[autofillSource] - } + 🪄 Autofilled {autofillDescription[autofillSource]}
)}
diff --git a/site/src/modules/templates/TemplateParameters/TemplateParameters.tsx b/site/src/modules/templates/TemplateParameters/TemplateParameters.tsx deleted file mode 100644 index 8f20720856ca2..0000000000000 --- a/site/src/modules/templates/TemplateParameters/TemplateParameters.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { TemplateVersionParameter } from "api/typesGenerated"; -import { FormFields, FormSection } from "components/Form/Form"; -import { - RichParameterInput, - RichParameterInputProps, -} from "components/RichParameterInput/RichParameterInput"; -import { ComponentProps, FC } from "react"; -import { AutofillSource } from "utils/richParameters"; - -export type TemplateParametersSectionProps = { - templateParameters: TemplateVersionParameter[]; - autofillSources?: Record; - getInputProps: ( - parameter: TemplateVersionParameter, - index: number, - ) => Omit; -} & Pick, "classes">; - -export const TemplateParametersSection: FC = ({ - templateParameters, - getInputProps, - autofillSources, - ...formSectionProps -}) => { - const hasMutableParameters = - templateParameters.filter((p) => p.mutable).length > 0; - - return ( - <> - {hasMutableParameters && ( - - - {templateParameters.map( - (parameter, index) => - parameter.mutable && ( - - ), - )} - - - )} - - ); -}; diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx index a3ef3163775c8..de417984459d3 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx @@ -107,10 +107,7 @@ export const CreateWorkspacePageView: FC = ({ initialValues: { name: defaultName ?? "", template_id: template.id, - rich_parameter_values: getInitialRichParameterValues( - parameters, - autofillParameters, - ), + rich_parameter_values: getInitialRichParameterValues(parameters), }, validationSchema: Yup.object({ name: nameValidator("Workspace Name"), @@ -137,15 +134,13 @@ export const CreateWorkspacePageView: FC = ({ error, ); - const autofillSources = useMemo(() => { - return autofillParameters.reduce( - (acc, param) => { - acc[param.name] = param.source; - return acc; - }, - {} as Record, - ); - }, [autofillParameters]); + const autofillByName = useMemo( + () => + Object.fromEntries( + autofillParameters.map((param) => [param.name, param]), + ), + [autofillParameters], + ); const hasAllRequiredExternalAuth = externalAuth.every( (auth) => auth.optional || auth.authenticated, @@ -301,9 +296,9 @@ export const CreateWorkspacePageView: FC = ({ value, }); }} - autofillSource={autofillSources[parameter.name]} key={parameter.name} parameter={parameter} + parameterAutofill={autofillByName[parameter.name]} disabled={isDisabled} /> ); @@ -330,6 +325,7 @@ const styles = { lineHeight: "inherit", fontSize: "inherit", height: "unset", + minWidth: "unset", }), hasDescription: { paddingBottom: 16, diff --git a/site/src/pages/WorkspacePage/WorkspaceActions/BuildParametersPopover.tsx b/site/src/pages/WorkspacePage/WorkspaceActions/BuildParametersPopover.tsx index 905898023cb22..3949f62d72067 100644 --- a/site/src/pages/WorkspacePage/WorkspaceActions/BuildParametersPopover.tsx +++ b/site/src/pages/WorkspacePage/WorkspaceActions/BuildParametersPopover.tsx @@ -166,10 +166,7 @@ const Form: FC = ({ }) => { const form = useFormik({ initialValues: { - rich_parameter_values: getInitialRichParameterValues( - ephemeralParameters, - buildParameters, - ), + rich_parameter_values: getInitialRichParameterValues(ephemeralParameters), }, onSubmit: (values) => { onSubmit(values.rich_parameter_values); diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceParametersPage/WorkspaceParametersForm.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceParametersPage/WorkspaceParametersForm.tsx index 481053c1b777b..e3862f3cc2cf9 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceParametersPage/WorkspaceParametersForm.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceParametersPage/WorkspaceParametersForm.tsx @@ -51,7 +51,6 @@ export const WorkspaceParametersForm: FC = ({ initialValues: { rich_parameter_values: getInitialRichParameterValues( templateVersionRichParameters, - autofillParams, ), }, validationSchema: Yup.object({ diff --git a/site/src/utils/richParameters.test.ts b/site/src/utils/richParameters.test.ts index 97d12747bf053..5a29fa9a21a17 100644 --- a/site/src/utils/richParameters.test.ts +++ b/site/src/utils/richParameters.test.ts @@ -44,10 +44,8 @@ test("getInitialRichParameterValues return default value when default build para ]; const cpuParameter = templateParameters[0]; - const [cpuParameterInitialValue] = getInitialRichParameterValues( - templateParameters, - [{ name: cpuParameter.name, value: "100", source: "user_history" }], - ); + const [cpuParameterInitialValue] = + getInitialRichParameterValues(templateParameters); expect(cpuParameterInitialValue.value).toBe(cpuParameter.default_value); }); diff --git a/site/src/utils/richParameters.ts b/site/src/utils/richParameters.ts index a2baa4459ae68..cf4f3d3d7c81f 100644 --- a/site/src/utils/richParameters.ts +++ b/site/src/utils/richParameters.ts @@ -14,30 +14,11 @@ export type AutofillBuildParameter = { export const getInitialRichParameterValues = ( templateParams: TemplateVersionParameter[], - autofillParams?: AutofillBuildParameter[], ): WorkspaceBuildParameter[] => { - return templateParams.map((parameter) => { - // Short-circuit for ephemeral parameters, which are always reset to - // the template-defined default. - if (parameter.ephemeral) { - return { - name: parameter.name, - value: parameter.default_value, - }; - } - - const autofillParam = autofillParams?.find( - ({ name }) => name === parameter.name, - ); - - return { - name: parameter.name, - value: - autofillParam && isValidValue(parameter, autofillParam) - ? autofillParam.value - : parameter.default_value, - }; - }); + return templateParams.map((parameter) => ({ + name: parameter.name, + value: parameter.default_value, + })); }; const isValidValue = ( From a4d7f27ac9931c955f189b9eed792502096da052 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Wed, 28 Feb 2024 18:17:37 +0000 Subject: [PATCH 2/6] lint --- .../CreateWorkspacePageView.tsx | 7 +++--- .../BuildParametersPopover.tsx | 6 +---- .../WorkspaceParametersForm.tsx | 3 --- .../WorkspaceParametersPage.tsx | 25 +++++++------------ site/src/utils/richParameters.ts | 12 --------- 5 files changed, 13 insertions(+), 40 deletions(-) diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx index de417984459d3..eb928f0f78501 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx @@ -20,8 +20,7 @@ import { } from "components/Form/Form"; import { UserAutocomplete } from "components/UserAutocomplete/UserAutocomplete"; import { - AutofillBuildParameter, - AutofillSource, + type AutofillBuildParameter, getInitialRichParameterValues, useValidationSchemaForRichParameters, } from "utils/richParameters"; @@ -39,11 +38,11 @@ import { import { Pill } from "components/Pill/Pill"; import { RichParameterInput } from "components/RichParameterInput/RichParameterInput"; import { generateWorkspaceName } from "modules/workspaces/generateWorkspaceName"; -import { +import type { CreateWorkspaceMode, ExternalAuthPollingState, } from "./CreateWorkspacePage"; -import { CreateWSPermissions } from "./permissions"; +import type { CreateWSPermissions } from "./permissions"; export const Language = { duplicationWarning: diff --git a/site/src/pages/WorkspacePage/WorkspaceActions/BuildParametersPopover.tsx b/site/src/pages/WorkspacePage/WorkspaceActions/BuildParametersPopover.tsx index 3949f62d72067..5574aede16a91 100644 --- a/site/src/pages/WorkspacePage/WorkspaceActions/BuildParametersPopover.tsx +++ b/site/src/pages/WorkspacePage/WorkspaceActions/BuildParametersPopover.tsx @@ -159,11 +159,7 @@ interface FormProps { onSubmit: (buildParameters: WorkspaceBuildParameter[]) => void; } -const Form: FC = ({ - ephemeralParameters, - buildParameters, - onSubmit, -}) => { +const Form: FC = ({ ephemeralParameters, onSubmit }) => { const form = useFormik({ initialValues: { rich_parameter_values: getInitialRichParameterValues(ephemeralParameters), diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceParametersPage/WorkspaceParametersForm.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceParametersPage/WorkspaceParametersForm.tsx index e3862f3cc2cf9..3bde97f01b52c 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceParametersPage/WorkspaceParametersForm.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceParametersPage/WorkspaceParametersForm.tsx @@ -10,7 +10,6 @@ import { } from "components/Form/Form"; import { RichParameterInput } from "components/RichParameterInput/RichParameterInput"; import { - AutofillBuildParameter, getInitialRichParameterValues, useValidationSchemaForRichParameters, } from "utils/richParameters"; @@ -28,7 +27,6 @@ export type WorkspaceParametersFormValues = { interface WorkspaceParameterFormProps { workspace: Workspace; templateVersionRichParameters: TemplateVersionParameter[]; - autofillParams: AutofillBuildParameter[]; isSubmitting: boolean; canChangeVersions: boolean; error: unknown; @@ -41,7 +39,6 @@ export const WorkspaceParametersForm: FC = ({ onCancel, onSubmit, templateVersionRichParameters, - autofillParams, error, canChangeVersions, isSubmitting, diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceParametersPage/WorkspaceParametersPage.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceParametersPage/WorkspaceParametersPage.tsx index 296bd6500b42f..06cddec537a0a 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceParametersPage/WorkspaceParametersPage.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceParametersPage/WorkspaceParametersPage.tsx @@ -1,30 +1,29 @@ -import { getWorkspaceParameters, postWorkspaceBuild } from "api/api"; +import Button from "@mui/material/Button"; +import OpenInNewOutlined from "@mui/icons-material/OpenInNewOutlined"; +import { type FC } from "react"; import { Helmet } from "react-helmet-async"; +import { useMutation, useQuery } from "react-query"; +import { useNavigate } from "react-router-dom"; +import { getWorkspaceParameters, postWorkspaceBuild } from "api/api"; +import type { Workspace, WorkspaceBuildParameter } from "api/typesGenerated"; import { pageTitle } from "utils/page"; import { WorkspacePermissions, workspaceChecks, } from "../../WorkspacePage/permissions"; import { checkAuthorization } from "api/queries/authCheck"; -import { useWorkspaceSettings } from "../WorkspaceSettingsLayout"; import { templateByName } from "api/queries/templates"; -import { useMutation, useQuery } from "react-query"; +import { useWorkspaceSettings } from "../WorkspaceSettingsLayout"; import { Loader } from "components/Loader/Loader"; import { - WorkspaceParametersFormValues, + type WorkspaceParametersFormValues, WorkspaceParametersForm, } from "./WorkspaceParametersForm"; -import { useNavigate } from "react-router-dom"; import { PageHeader, PageHeaderTitle } from "components/PageHeader/PageHeader"; -import { type FC } from "react"; import { isApiValidationError } from "api/errors"; import { ErrorAlert } from "components/Alert/ErrorAlert"; -import { Workspace, WorkspaceBuildParameter } from "api/typesGenerated"; import { EmptyState } from "components/EmptyState/EmptyState"; -import Button from "@mui/material/Button"; -import OpenInNewOutlined from "@mui/icons-material/OpenInNewOutlined"; import { docs } from "utils/docs"; -import { AutofillBuildParameter } from "utils/richParameters"; const WorkspaceParametersPage: FC = () => { const workspace = useWorkspaceSettings(); @@ -127,12 +126,6 @@ export const WorkspaceParametersPageView: FC< ({ - ...p, - source: "active_build", - }), - )} templateVersionRichParameters={data.templateVersionRichParameters} error={submitError} isSubmitting={isSubmitting} diff --git a/site/src/utils/richParameters.ts b/site/src/utils/richParameters.ts index cf4f3d3d7c81f..0c9b03c49791b 100644 --- a/site/src/utils/richParameters.ts +++ b/site/src/utils/richParameters.ts @@ -21,18 +21,6 @@ export const getInitialRichParameterValues = ( })); }; -const isValidValue = ( - templateParam: TemplateVersionParameter, - buildParam: WorkspaceBuildParameter, -) => { - if (templateParam.options.length > 0) { - const validValues = templateParam.options.map((option) => option.value); - return validValues.includes(buildParam.value); - } - - return true; -}; - export const useValidationSchemaForRichParameters = ( templateParameters?: TemplateVersionParameter[], lastBuildParameters?: WorkspaceBuildParameter[], From f6eca56a4b222aa94f5f6cd1dad214869077b996 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Wed, 28 Feb 2024 19:07:47 +0000 Subject: [PATCH 3/6] =?UTF-8?q?=F0=9F=A7=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BuildParametersPopover.tsx | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/site/src/pages/WorkspacePage/WorkspaceActions/BuildParametersPopover.tsx b/site/src/pages/WorkspacePage/WorkspaceActions/BuildParametersPopover.tsx index 5574aede16a91..882fcd325a4e8 100644 --- a/site/src/pages/WorkspacePage/WorkspaceActions/BuildParametersPopover.tsx +++ b/site/src/pages/WorkspacePage/WorkspaceActions/BuildParametersPopover.tsx @@ -48,9 +48,9 @@ export const BuildParametersPopover: FC = ({ queryKey: ["workspace", workspace.id, "parameters"], queryFn: () => getWorkspaceParameters(workspace), }); - const ephemeralParameters = parameters - ? parameters.templateVersionRichParameters.filter((p) => p.ephemeral) - : undefined; + const ephemeralParameters = parameters?.templateVersionRichParameters.filter( + (p) => p.ephemeral, + ); return ( @@ -70,7 +70,6 @@ export const BuildParametersPopover: FC = ({ > @@ -80,13 +79,11 @@ export const BuildParametersPopover: FC = ({ interface BuildParametersPopoverContentProps { ephemeralParameters?: TemplateVersionParameter[]; - buildParameters?: WorkspaceBuildParameter[]; onSubmit: (buildParameters: WorkspaceBuildParameter[]) => void; } const BuildParametersPopoverContent: FC = ({ ephemeralParameters, - buildParameters, onSubmit, }) => { const theme = useTheme(); @@ -94,7 +91,7 @@ const BuildParametersPopoverContent: FC = ({ return ( <> - {buildParameters && ephemeralParameters ? ( + {ephemeralParameters ? ( ephemeralParameters.length > 0 ? ( <>
= ({ popover.setIsOpen(false); }} ephemeralParameters={ephemeralParameters} - buildParameters={buildParameters.map( - (p): AutofillBuildParameter => ({ - ...p, - source: "active_build", - }), - )} />
@@ -155,7 +146,6 @@ const BuildParametersPopoverContent: FC = ({ interface FormProps { ephemeralParameters: TemplateVersionParameter[]; - buildParameters: AutofillBuildParameter[]; onSubmit: (buildParameters: WorkspaceBuildParameter[]) => void; } From cdb9f26804ba7ecad4217f1d7426c09143b1fe64 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Wed, 28 Feb 2024 20:07:15 +0000 Subject: [PATCH 4/6] oh boy --- .../BuildParametersPopover.tsx | 29 +++++++++++--- .../WorkspaceParametersForm.tsx | 4 ++ .../WorkspaceParametersPage.tsx | 23 ++++++----- site/src/utils/richParameters.test.ts | 8 ++-- site/src/utils/richParameters.ts | 39 +++++++++++++++++-- 5 files changed, 81 insertions(+), 22 deletions(-) diff --git a/site/src/pages/WorkspacePage/WorkspaceActions/BuildParametersPopover.tsx b/site/src/pages/WorkspacePage/WorkspaceActions/BuildParametersPopover.tsx index 882fcd325a4e8..905898023cb22 100644 --- a/site/src/pages/WorkspacePage/WorkspaceActions/BuildParametersPopover.tsx +++ b/site/src/pages/WorkspacePage/WorkspaceActions/BuildParametersPopover.tsx @@ -48,9 +48,9 @@ export const BuildParametersPopover: FC = ({ queryKey: ["workspace", workspace.id, "parameters"], queryFn: () => getWorkspaceParameters(workspace), }); - const ephemeralParameters = parameters?.templateVersionRichParameters.filter( - (p) => p.ephemeral, - ); + const ephemeralParameters = parameters + ? parameters.templateVersionRichParameters.filter((p) => p.ephemeral) + : undefined; return ( @@ -70,6 +70,7 @@ export const BuildParametersPopover: FC = ({ > @@ -79,11 +80,13 @@ export const BuildParametersPopover: FC = ({ interface BuildParametersPopoverContentProps { ephemeralParameters?: TemplateVersionParameter[]; + buildParameters?: WorkspaceBuildParameter[]; onSubmit: (buildParameters: WorkspaceBuildParameter[]) => void; } const BuildParametersPopoverContent: FC = ({ ephemeralParameters, + buildParameters, onSubmit, }) => { const theme = useTheme(); @@ -91,7 +94,7 @@ const BuildParametersPopoverContent: FC = ({ return ( <> - {ephemeralParameters ? ( + {buildParameters && ephemeralParameters ? ( ephemeralParameters.length > 0 ? ( <>
= ({ popover.setIsOpen(false); }} ephemeralParameters={ephemeralParameters} + buildParameters={buildParameters.map( + (p): AutofillBuildParameter => ({ + ...p, + source: "active_build", + }), + )} />
@@ -146,13 +155,21 @@ const BuildParametersPopoverContent: FC = ({ interface FormProps { ephemeralParameters: TemplateVersionParameter[]; + buildParameters: AutofillBuildParameter[]; onSubmit: (buildParameters: WorkspaceBuildParameter[]) => void; } -const Form: FC = ({ ephemeralParameters, onSubmit }) => { +const Form: FC = ({ + ephemeralParameters, + buildParameters, + onSubmit, +}) => { const form = useFormik({ initialValues: { - rich_parameter_values: getInitialRichParameterValues(ephemeralParameters), + rich_parameter_values: getInitialRichParameterValues( + ephemeralParameters, + buildParameters, + ), }, onSubmit: (values) => { onSubmit(values.rich_parameter_values); diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceParametersPage/WorkspaceParametersForm.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceParametersPage/WorkspaceParametersForm.tsx index 3bde97f01b52c..481053c1b777b 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceParametersPage/WorkspaceParametersForm.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceParametersPage/WorkspaceParametersForm.tsx @@ -10,6 +10,7 @@ import { } from "components/Form/Form"; import { RichParameterInput } from "components/RichParameterInput/RichParameterInput"; import { + AutofillBuildParameter, getInitialRichParameterValues, useValidationSchemaForRichParameters, } from "utils/richParameters"; @@ -27,6 +28,7 @@ export type WorkspaceParametersFormValues = { interface WorkspaceParameterFormProps { workspace: Workspace; templateVersionRichParameters: TemplateVersionParameter[]; + autofillParams: AutofillBuildParameter[]; isSubmitting: boolean; canChangeVersions: boolean; error: unknown; @@ -39,6 +41,7 @@ export const WorkspaceParametersForm: FC = ({ onCancel, onSubmit, templateVersionRichParameters, + autofillParams, error, canChangeVersions, isSubmitting, @@ -48,6 +51,7 @@ export const WorkspaceParametersForm: FC = ({ initialValues: { rich_parameter_values: getInitialRichParameterValues( templateVersionRichParameters, + autofillParams, ), }, validationSchema: Yup.object({ diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceParametersPage/WorkspaceParametersPage.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceParametersPage/WorkspaceParametersPage.tsx index 06cddec537a0a..ee98e43d0d998 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceParametersPage/WorkspaceParametersPage.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceParametersPage/WorkspaceParametersPage.tsx @@ -1,29 +1,30 @@ -import Button from "@mui/material/Button"; -import OpenInNewOutlined from "@mui/icons-material/OpenInNewOutlined"; -import { type FC } from "react"; -import { Helmet } from "react-helmet-async"; -import { useMutation, useQuery } from "react-query"; -import { useNavigate } from "react-router-dom"; import { getWorkspaceParameters, postWorkspaceBuild } from "api/api"; -import type { Workspace, WorkspaceBuildParameter } from "api/typesGenerated"; +import { Helmet } from "react-helmet-async"; import { pageTitle } from "utils/page"; import { WorkspacePermissions, workspaceChecks, } from "../../WorkspacePage/permissions"; import { checkAuthorization } from "api/queries/authCheck"; -import { templateByName } from "api/queries/templates"; import { useWorkspaceSettings } from "../WorkspaceSettingsLayout"; +import { templateByName } from "api/queries/templates"; +import { useMutation, useQuery } from "react-query"; import { Loader } from "components/Loader/Loader"; import { - type WorkspaceParametersFormValues, + WorkspaceParametersFormValues, WorkspaceParametersForm, } from "./WorkspaceParametersForm"; +import { useNavigate } from "react-router-dom"; import { PageHeader, PageHeaderTitle } from "components/PageHeader/PageHeader"; +import { type FC } from "react"; import { isApiValidationError } from "api/errors"; import { ErrorAlert } from "components/Alert/ErrorAlert"; +import { Workspace, WorkspaceBuildParameter } from "api/typesGenerated"; import { EmptyState } from "components/EmptyState/EmptyState"; +import Button from "@mui/material/Button"; +import OpenInNewOutlined from "@mui/icons-material/OpenInNewOutlined"; import { docs } from "utils/docs"; +import { AutofillBuildParameter } from "utils/richParameters"; const WorkspaceParametersPage: FC = () => { const workspace = useWorkspaceSettings(); @@ -126,6 +127,10 @@ export const WorkspaceParametersPageView: FC< ({ + ...p, + source: "active_build", + }))} templateVersionRichParameters={data.templateVersionRichParameters} error={submitError} isSubmitting={isSubmitting} diff --git a/site/src/utils/richParameters.test.ts b/site/src/utils/richParameters.test.ts index 5a29fa9a21a17..a83f7942a8d30 100644 --- a/site/src/utils/richParameters.test.ts +++ b/site/src/utils/richParameters.test.ts @@ -1,4 +1,4 @@ -import { TemplateVersionParameter } from "api/typesGenerated"; +import type { TemplateVersionParameter } from "api/typesGenerated"; import { getInitialRichParameterValues } from "./richParameters"; test("getInitialRichParameterValues return default value when default build parameter is not valid", () => { @@ -44,8 +44,10 @@ test("getInitialRichParameterValues return default value when default build para ]; const cpuParameter = templateParameters[0]; - const [cpuParameterInitialValue] = - getInitialRichParameterValues(templateParameters); + const [cpuParameterInitialValue] = getInitialRichParameterValues( + templateParameters, + [{ name: cpuParameter.name, value: "100", source: "url" }], + ); expect(cpuParameterInitialValue.value).toBe(cpuParameter.default_value); }); diff --git a/site/src/utils/richParameters.ts b/site/src/utils/richParameters.ts index 0c9b03c49791b..afafa75dfc211 100644 --- a/site/src/utils/richParameters.ts +++ b/site/src/utils/richParameters.ts @@ -14,11 +14,42 @@ export type AutofillBuildParameter = { export const getInitialRichParameterValues = ( templateParams: TemplateVersionParameter[], + autofillParams: AutofillBuildParameter[], ): WorkspaceBuildParameter[] => { - return templateParams.map((parameter) => ({ - name: parameter.name, - value: parameter.default_value, - })); + return templateParams.map((parameter) => { + // Short-circuit for ephemeral parameters, which are always reset to + // the template-defined default. + if (parameter.ephemeral) { + return { + name: parameter.name, + value: parameter.default_value, + }; + } + + const autofillParam = autofillParams?.find( + ({ name }) => name === parameter.name, + ); + + return { + name: parameter.name, + value: + autofillParam && isValidValue(parameter, autofillParam) + ? autofillParam.value + : parameter.default_value, + }; + }); +}; + +const isValidValue = ( + templateParam: TemplateVersionParameter, + buildParam: WorkspaceBuildParameter, +) => { + if (templateParam.options.length > 0) { + const validValues = templateParam.options.map((option) => option.value); + return validValues.includes(buildParam.value); + } + + return true; }; export const useValidationSchemaForRichParameters = ( From be54b3476eb716d5b732540afc57ea460320e107 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Wed, 28 Feb 2024 20:13:50 +0000 Subject: [PATCH 5/6] test --- .../CreateWorkspacePage/CreateWorkspacePageView.stories.tsx | 3 ++- .../pages/CreateWorkspacePage/CreateWorkspacePageView.tsx | 5 ++++- site/src/utils/richParameters.ts | 6 ++++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx index 9c9d64be0c8db..59eb7ab2afb99 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx @@ -101,9 +101,10 @@ export const Parameters: Story = { autofillParameters: [ { name: "first_parameter", - value: "It works!", + value: "Cool suggestion", source: "user_history", }, + { name: "third_parameter", value: "aaaa", source: "url" }, ], }, }; diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx index eb928f0f78501..9bff96124662a 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx @@ -106,7 +106,10 @@ export const CreateWorkspacePageView: FC = ({ initialValues: { name: defaultName ?? "", template_id: template.id, - rich_parameter_values: getInitialRichParameterValues(parameters), + rich_parameter_values: getInitialRichParameterValues( + parameters, + autofillParameters, + ), }, validationSchema: Yup.object({ name: nameValidator("Workspace Name"), diff --git a/site/src/utils/richParameters.ts b/site/src/utils/richParameters.ts index afafa75dfc211..12182c1c89a8f 100644 --- a/site/src/utils/richParameters.ts +++ b/site/src/utils/richParameters.ts @@ -14,7 +14,7 @@ export type AutofillBuildParameter = { export const getInitialRichParameterValues = ( templateParams: TemplateVersionParameter[], - autofillParams: AutofillBuildParameter[], + autofillParams?: AutofillBuildParameter[], ): WorkspaceBuildParameter[] => { return templateParams.map((parameter) => { // Short-circuit for ephemeral parameters, which are always reset to @@ -33,7 +33,9 @@ export const getInitialRichParameterValues = ( return { name: parameter.name, value: - autofillParam && isValidValue(parameter, autofillParam) + autofillParam && + isValidValue(parameter, autofillParam) && + autofillParam.source !== "user_history" ? autofillParam.value : parameter.default_value, }; From 929391ec8ddd1ccd2311cab7c87e497ff7594c0d Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Wed, 28 Feb 2024 21:55:47 +0000 Subject: [PATCH 6/6] =?UTF-8?q?=F0=9F=A7=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CreateWorkspacePageView.stories.tsx | 6 +++++- .../WorkspaceParametersPage.tsx | 13 ++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx index 59eb7ab2afb99..d83edb55a3e88 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx @@ -104,7 +104,11 @@ export const Parameters: Story = { value: "Cool suggestion", source: "user_history", }, - { name: "third_parameter", value: "aaaa", source: "url" }, + { + name: "third_parameter", + value: "aaaa", + source: "url", + }, ], }, }; diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceParametersPage/WorkspaceParametersPage.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceParametersPage/WorkspaceParametersPage.tsx index ee98e43d0d998..d98f67f33c3a2 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceParametersPage/WorkspaceParametersPage.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceParametersPage/WorkspaceParametersPage.tsx @@ -1,5 +1,8 @@ -import { getWorkspaceParameters, postWorkspaceBuild } from "api/api"; +import Button from "@mui/material/Button"; +import OpenInNewOutlined from "@mui/icons-material/OpenInNewOutlined"; import { Helmet } from "react-helmet-async"; +import { getWorkspaceParameters, postWorkspaceBuild } from "api/api"; +import { EmptyState } from "components/EmptyState/EmptyState"; import { pageTitle } from "utils/page"; import { WorkspacePermissions, @@ -11,7 +14,7 @@ import { templateByName } from "api/queries/templates"; import { useMutation, useQuery } from "react-query"; import { Loader } from "components/Loader/Loader"; import { - WorkspaceParametersFormValues, + type WorkspaceParametersFormValues, WorkspaceParametersForm, } from "./WorkspaceParametersForm"; import { useNavigate } from "react-router-dom"; @@ -19,12 +22,8 @@ import { PageHeader, PageHeaderTitle } from "components/PageHeader/PageHeader"; import { type FC } from "react"; import { isApiValidationError } from "api/errors"; import { ErrorAlert } from "components/Alert/ErrorAlert"; -import { Workspace, WorkspaceBuildParameter } from "api/typesGenerated"; -import { EmptyState } from "components/EmptyState/EmptyState"; -import Button from "@mui/material/Button"; -import OpenInNewOutlined from "@mui/icons-material/OpenInNewOutlined"; +import type { Workspace, WorkspaceBuildParameter } from "api/typesGenerated"; import { docs } from "utils/docs"; -import { AutofillBuildParameter } from "utils/richParameters"; const WorkspaceParametersPage: FC = () => { const workspace = useWorkspaceSettings();