mirror of
https://github.com/coder/coder.git
synced 2025-07-12 00:14:10 +00:00
fix: update default value handling for dynamic defaults (#17609)
resolves coder/preview#102
This commit is contained in:
@ -32,7 +32,7 @@ import {
|
||||
TooltipTrigger,
|
||||
} from "components/Tooltip/Tooltip";
|
||||
import { Info, Settings, TriangleAlert } from "lucide-react";
|
||||
import { type FC, useId } from "react";
|
||||
import { type FC, useEffect, useId, useState } from "react";
|
||||
import type { AutofillBuildParameter } from "utils/richParameters";
|
||||
import * as Yup from "yup";
|
||||
|
||||
@ -164,14 +164,18 @@ const ParameterField: FC<ParameterFieldProps> = ({
|
||||
id,
|
||||
}) => {
|
||||
const value = validValue(parameter.value);
|
||||
const defaultValue = validValue(parameter.default_value);
|
||||
const [localValue, setLocalValue] = useState(value);
|
||||
|
||||
useEffect(() => {
|
||||
setLocalValue(value);
|
||||
}, [value]);
|
||||
|
||||
switch (parameter.form_type) {
|
||||
case "dropdown":
|
||||
return (
|
||||
<Select
|
||||
onValueChange={onChange}
|
||||
defaultValue={defaultValue}
|
||||
value={value}
|
||||
disabled={disabled}
|
||||
required={parameter.required}
|
||||
>
|
||||
@ -194,31 +198,48 @@ const ParameterField: FC<ParameterFieldProps> = ({
|
||||
);
|
||||
|
||||
case "multi-select": {
|
||||
let values: string[] = [];
|
||||
|
||||
if (value) {
|
||||
try {
|
||||
const parsed = JSON.parse(value);
|
||||
if (Array.isArray(parsed)) {
|
||||
values = parsed;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(
|
||||
"Error parsing parameter value with form_type multi-select",
|
||||
e,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Map parameter options to MultiSelectCombobox options format
|
||||
const comboboxOptions: Option[] = parameter.options.map((opt) => ({
|
||||
const options: Option[] = parameter.options.map((opt) => ({
|
||||
value: opt.value.value,
|
||||
label: opt.name,
|
||||
disable: false,
|
||||
}));
|
||||
|
||||
const defaultOptions: Option[] = JSON.parse(defaultValue).map(
|
||||
(val: string) => {
|
||||
const option = parameter.options.find((o) => o.value.value === val);
|
||||
return {
|
||||
value: val,
|
||||
label: option?.name || val,
|
||||
disable: false,
|
||||
};
|
||||
},
|
||||
const optionMap = new Map(
|
||||
parameter.options.map((opt) => [opt.value.value, opt.name]),
|
||||
);
|
||||
|
||||
const selectedOptions: Option[] = values.map((val) => {
|
||||
return {
|
||||
value: val,
|
||||
label: optionMap.get(val) || val,
|
||||
disable: false,
|
||||
};
|
||||
});
|
||||
|
||||
return (
|
||||
<MultiSelectCombobox
|
||||
inputProps={{
|
||||
id: `${id}-${parameter.name}`,
|
||||
}}
|
||||
options={comboboxOptions}
|
||||
defaultOptions={defaultOptions}
|
||||
options={options}
|
||||
defaultOptions={selectedOptions}
|
||||
onChange={(newValues) => {
|
||||
const values = newValues.map((option) => option.value);
|
||||
onChange(JSON.stringify(values));
|
||||
@ -251,11 +272,7 @@ const ParameterField: FC<ParameterFieldProps> = ({
|
||||
|
||||
case "radio":
|
||||
return (
|
||||
<RadioGroup
|
||||
onValueChange={onChange}
|
||||
disabled={disabled}
|
||||
defaultValue={defaultValue}
|
||||
>
|
||||
<RadioGroup onValueChange={onChange} disabled={disabled} value={value}>
|
||||
{parameter.options.map((option) => (
|
||||
<div
|
||||
key={option.value.value}
|
||||
@ -282,7 +299,6 @@ const ParameterField: FC<ParameterFieldProps> = ({
|
||||
<Checkbox
|
||||
id={parameter.name}
|
||||
checked={value === "true"}
|
||||
defaultChecked={defaultValue === "true"} // TODO: defaultChecked is always overridden by checked
|
||||
onCheckedChange={(checked) => {
|
||||
onChange(checked ? "true" : "false");
|
||||
}}
|
||||
@ -299,14 +315,11 @@ const ParameterField: FC<ParameterFieldProps> = ({
|
||||
<div className="flex flex-row items-baseline gap-3">
|
||||
<Slider
|
||||
className="mt-2"
|
||||
defaultValue={[
|
||||
Number(
|
||||
parameter.default_value.valid
|
||||
? parameter.default_value.value
|
||||
: 0,
|
||||
),
|
||||
]}
|
||||
onValueChange={([value]) => onChange(value.toString())}
|
||||
value={[Number(localValue ?? 0)]}
|
||||
onValueChange={([value]) => {
|
||||
setLocalValue(value.toString());
|
||||
onChange(value.toString());
|
||||
}}
|
||||
min={parameter.validations[0]?.validation_min ?? 0}
|
||||
max={parameter.validations[0]?.validation_max ?? 100}
|
||||
disabled={disabled}
|
||||
@ -319,8 +332,11 @@ const ParameterField: FC<ParameterFieldProps> = ({
|
||||
return (
|
||||
<Textarea
|
||||
className="max-w-2xl"
|
||||
defaultValue={defaultValue}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
value={localValue}
|
||||
onChange={(e) => {
|
||||
setLocalValue(e.target.value);
|
||||
onChange(e.target.value);
|
||||
}}
|
||||
onInput={(e) => {
|
||||
const target = e.currentTarget;
|
||||
target.style.maxHeight = "700px";
|
||||
@ -354,8 +370,11 @@ const ParameterField: FC<ParameterFieldProps> = ({
|
||||
return (
|
||||
<Input
|
||||
type={inputType}
|
||||
defaultValue={defaultValue}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
value={localValue}
|
||||
onChange={(e) => {
|
||||
setLocalValue(e.target.value);
|
||||
onChange(e.target.value);
|
||||
}}
|
||||
disabled={disabled}
|
||||
required={parameter.required}
|
||||
placeholder={
|
||||
@ -435,7 +454,7 @@ export const getInitialParameterValues = (
|
||||
if (parameter.ephemeral) {
|
||||
return {
|
||||
name: parameter.name,
|
||||
value: validValue(parameter.default_value),
|
||||
value: validValue(parameter.value),
|
||||
};
|
||||
}
|
||||
|
||||
@ -450,7 +469,7 @@ export const getInitialParameterValues = (
|
||||
isValidParameterOption(parameter, autofillParam) &&
|
||||
autofillParam.value
|
||||
? autofillParam.value
|
||||
: validValue(parameter.default_value),
|
||||
: validValue(parameter.value),
|
||||
};
|
||||
});
|
||||
};
|
||||
|
@ -213,17 +213,23 @@ export const CreateWorkspacePageViewExperimental: FC<
|
||||
parameters,
|
||||
]);
|
||||
|
||||
// send the last user modified parameter and all touched parameters to the websocket
|
||||
const sendDynamicParamsRequest = (
|
||||
parameter: PreviewParameter,
|
||||
value: string,
|
||||
) => {
|
||||
const formInputs = Object.fromEntries(
|
||||
form.values.rich_parameter_values?.map((value) => {
|
||||
return [value.name, value.value];
|
||||
}) ?? [],
|
||||
);
|
||||
// Update the input for the changed parameter
|
||||
const formInputs: { [k: string]: string } = {};
|
||||
formInputs[parameter.name] = value;
|
||||
const parameters = form.values.rich_parameter_values ?? [];
|
||||
|
||||
for (const [fieldName, isTouched] of Object.entries(form.touched)) {
|
||||
if (isTouched && fieldName !== parameter.name) {
|
||||
const param = parameters.find((p) => p.name === fieldName);
|
||||
if (param?.value) {
|
||||
formInputs[fieldName] = param.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sendMessage(formInputs);
|
||||
};
|
||||
@ -238,6 +244,7 @@ export const CreateWorkspacePageViewExperimental: FC<
|
||||
name: parameter.name,
|
||||
value,
|
||||
});
|
||||
form.setFieldTouched(parameter.name, true);
|
||||
sendDynamicParamsRequest(parameter, value);
|
||||
},
|
||||
500,
|
||||
@ -255,6 +262,7 @@ export const CreateWorkspacePageViewExperimental: FC<
|
||||
name: parameter.name,
|
||||
value,
|
||||
});
|
||||
form.setFieldTouched(parameter.name, true);
|
||||
sendDynamicParamsRequest(parameter, value);
|
||||
}
|
||||
};
|
||||
|
Reference in New Issue
Block a user