refactor: show parameter suggestions from user history below field (#12340)

This commit is contained in:
Kayla Washburn-Love
2024-02-28 15:29:48 -07:00
committed by GitHub
parent b2a5e2f4c0
commit b24ad1bbf0
7 changed files with 84 additions and 100 deletions
@@ -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<string, Interpolation<Theme>>;
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<Record<AutofillSource, ReactNode>> = {
url: " from the URL.",
};
export const RichParameterInput: FC<RichParameterInputProps> = ({
parameter,
size = "medium",
autofillSource,
parameter,
parameterAutofill,
onChange,
...fieldProps
}) => {
const autofillSource = parameterAutofill?.source;
const autofillValue = parameterAutofill?.value;
const [hideSuggestion, setHideSuggestion] = useState(false);
return (
<Stack
direction="column"
@@ -189,16 +209,33 @@ export const RichParameterInput: FC<RichParameterInputProps> = ({
>
<ParameterLabel parameter={parameter} />
<div css={{ display: "flex", flexDirection: "column" }}>
<RichParameterField {...fieldProps} size={size} parameter={parameter} />
{autofillSource && autofillSource !== "active_build" && (
<RichParameterField
{...fieldProps}
onChange={onChange}
size={size}
parameter={parameter}
/>
{!parameter.ephemeral &&
autofillSource === "user_history" &&
autofillValue &&
!hideSuggestion && (
<FormHelperText>
<Button
variant="text"
css={styles.suggestion}
onClick={() => {
onChange(autofillValue);
setHideSuggestion(true);
}}
>
{autofillValue}
</Button>{" "}
was recently used for this parameter.
</FormHelperText>
)}
{autofillSource && autofillDescription[autofillSource] && (
<div css={{ marginTop: 4, fontSize: 12 }}>
🪄 Autofilled:{" "}
{
{
["url"]: "value supplied by URL.",
["user_history"]: "recently used value.",
}[autofillSource]
}
🪄 Autofilled {autofillDescription[autofillSource]}
</div>
)}
</div>
@@ -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<string, AutofillSource>;
getInputProps: (
parameter: TemplateVersionParameter,
index: number,
) => Omit<RichParameterInputProps, "parameter" | "index">;
} & Pick<ComponentProps<typeof FormSection>, "classes">;
export const TemplateParametersSection: FC<TemplateParametersSectionProps> = ({
templateParameters,
getInputProps,
autofillSources,
...formSectionProps
}) => {
const hasMutableParameters =
templateParameters.filter((p) => p.mutable).length > 0;
return (
<>
{hasMutableParameters && (
<FormSection
{...formSectionProps}
title="Parameters"
description="Settings used by your template"
>
<FormFields>
{templateParameters.map(
(parameter, index) =>
parameter.mutable && (
<RichParameterInput
{...getInputProps(parameter, index)}
key={parameter.name}
parameter={parameter}
autofillSource={
autofillSources && autofillSources[parameter.name]
}
/>
),
)}
</FormFields>
</FormSection>
)}
</>
);
};
@@ -101,9 +101,14 @@ export const Parameters: Story = {
autofillParameters: [
{
name: "first_parameter",
value: "It works!",
value: "Cool suggestion",
source: "user_history",
},
{
name: "third_parameter",
value: "aaaa",
source: "url",
},
],
},
};
@@ -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:
@@ -137,15 +136,13 @@ export const CreateWorkspacePageView: FC<CreateWorkspacePageViewProps> = ({
error,
);
const autofillSources = useMemo(() => {
return autofillParameters.reduce(
(acc, param) => {
acc[param.name] = param.source;
return acc;
},
{} as Record<string, AutofillSource>,
);
}, [autofillParameters]);
const autofillByName = useMemo(
() =>
Object.fromEntries(
autofillParameters.map((param) => [param.name, param]),
),
[autofillParameters],
);
const hasAllRequiredExternalAuth = externalAuth.every(
(auth) => auth.optional || auth.authenticated,
@@ -301,9 +298,9 @@ export const CreateWorkspacePageView: FC<CreateWorkspacePageViewProps> = ({
value,
});
}}
autofillSource={autofillSources[parameter.name]}
key={parameter.name}
parameter={parameter}
parameterAutofill={autofillByName[parameter.name]}
disabled={isDisabled}
/>
);
@@ -330,6 +327,7 @@ const styles = {
lineHeight: "inherit",
fontSize: "inherit",
height: "unset",
minWidth: "unset",
}),
hasDescription: {
paddingBottom: 16,
@@ -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();
@@ -127,12 +126,10 @@ export const WorkspaceParametersPageView: FC<
<WorkspaceParametersForm
workspace={workspace}
canChangeVersions={canChangeVersions}
autofillParams={data.buildParameters.map(
(p): AutofillBuildParameter => ({
...p,
source: "active_build",
}),
)}
autofillParams={data.buildParameters.map((p) => ({
...p,
source: "active_build",
}))}
templateVersionRichParameters={data.templateVersionRichParameters}
error={submitError}
isSubmitting={isSubmitting}
+2 -2
View File
@@ -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", () => {
@@ -46,7 +46,7 @@ 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" }],
[{ name: cpuParameter.name, value: "100", source: "url" }],
);
expect(cpuParameterInitialValue.value).toBe(cpuParameter.default_value);
+3 -1
View File
@@ -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,
};